changeset 11147:e2b06fc13588

8088418: Reintroduce JFR Pulse Logger Reviewed-by: kcr, arapte, egahlin
author hirt
date Mon, 07 Jan 2019 13:07:08 -0800
parents 7bad2669efd7
children 15adcac0b7e5
files modules/javafx.base/src/main/java/com/sun/javafx/logging/PrintLogger.java modules/javafx.base/src/main/java/com/sun/javafx/logging/PulseLogger.java modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/JFRInputEvent.java modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/JFRPulseLogger.java modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/JFRPulsePhaseEvent.java modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/PulseId.java modules/javafx.base/src/main/java/module-info.java
diffstat 7 files changed, 362 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/modules/javafx.base/src/main/java/com/sun/javafx/logging/PrintLogger.java	Mon Jan 07 13:47:32 2019 -0800
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/logging/PrintLogger.java	Mon Jan 07 13:07:08 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,6 @@
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -51,12 +50,6 @@
 class PrintLogger extends Logger {
 
     /**
-     * A reference to the pulse logger. This will be null if pulse logging
-     * is not enabled.
-     */
-    private static PrintLogger printLogger;
-
-    /**
      * A time in milliseconds which defines the threshold. If a pulse lasts <em>longer</em> than
      * the threshold, then it is logged, otherwise an abbreviated representation including
      * only the time of the pulse is logged.
@@ -110,7 +103,7 @@
 
     private Thread fxThread;
     private final ThreadLocal<ThreadLocalData> phaseData =
-        new ThreadLocal() {
+        new ThreadLocal<>() {
             @Override
             public ThreadLocalData initialValue() {
                 return new ThreadLocalData();
@@ -149,14 +142,12 @@
         active = new AtomicInteger(0);
     }
 
-    public static Logger getInstance() {
-        if (printLogger == null) {
-            boolean enabled = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.pulseLogger"));
-            if (enabled) {
-                printLogger = new PrintLogger();
-            }
+    public static Logger createInstance() {
+        boolean enabled = PulseLogger.isPulseLoggingRequested();
+        if (enabled) {
+            return new PrintLogger();
         }
-        return printLogger;
+        return null;
     }
 
     /**
@@ -355,7 +346,7 @@
         int pulseCount;
         boolean pushedRender;
         StringBuffer message = new StringBuffer();
-        Map<String,Counter> counters = new ConcurrentHashMap();
+        Map<String,Counter> counters = new ConcurrentHashMap<>();
 
         void init(int n) {
             state = INCOMPLETE;
@@ -394,7 +385,7 @@
                 System.err.print(message);
                 if (!counters.isEmpty()) {
                     System.err.println("Counters:");
-                    List<Map.Entry<String,Counter>> entries = new ArrayList(counters.entrySet());
+                    List<Map.Entry<String,Counter>> entries = new ArrayList<>(counters.entrySet());
                     Collections.sort(entries, (a, b) -> a.getKey().compareTo(b.getKey()));
                     for (Map.Entry<String, Counter> entry : entries) {
                         System.err.println("\t" + entry.getKey() + ": " + entry.getValue().value);
--- a/modules/javafx.base/src/main/java/com/sun/javafx/logging/PulseLogger.java	Mon Jan 07 13:47:32 2019 -0800
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/logging/PulseLogger.java	Mon Jan 07 13:07:08 2019 -0800
@@ -27,37 +27,25 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.List;
 
 public class PulseLogger {
-
     public static final boolean PULSE_LOGGING_ENABLED;
 
+    private static final String [] DEFAULT_LOGGERS = {"com.sun.javafx.logging.PrintLogger", "com.sun.javafx.logging.jfr.JFRPulseLogger"};
     private static final Logger[] loggers;
 
     static {
-        List<Logger> list = new ArrayList();
-        Logger logger = PrintLogger.getInstance();
-        if (logger != null) {
-            list.add(logger);
+        List<Logger> list = new ArrayList<>();
+        for (String loggerClass : DEFAULT_LOGGERS) {
+            Logger logger = loadLogger(loggerClass);
+            if (logger != null) {
+                list.add(logger);
+            }
         }
-
-//        // Another optional logger could be added as follows:
-//        try {
-//            Class klass = Class.forName("com.sun.javafx.logging.OtherLogger");
-//            if (klass != null) {
-//                Method method = klass.getDeclaredMethod("getInstance");
-//                logger = (Logger) method.invoke(null);
-//                if (logger != null) {
-//                    list.add(logger);
-//                }
-//            }
-//        }
-//        catch (NoClassDefFoundError | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-//            // Ignore
-//        }
-
         loggers = list.toArray(new Logger[list.size()]);
         PULSE_LOGGING_ENABLED = loggers.length > 0;
     }
@@ -109,4 +97,26 @@
             logger.newInput(name);
         }
     }
+
+    /**
+     * @return true if the user requested pulse logging by setting the system
+     *         property javafx.pulseLogger to true, false otherwise.
+     */
+    public static boolean isPulseLoggingRequested() {
+        return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.pulseLogger"));
+    }
+
+    // Loading known loggers reflectively, in case an expected module isn't available
+    private static Logger loadLogger(String className) {
+        try {
+            Class<?> klass = Class.forName(className);
+            if (klass != null) {
+                Method method = klass.getDeclaredMethod("createInstance");
+                return (Logger) method.invoke(null);
+            }
+        } catch (NoClassDefFoundError | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            // Ignore
+        }
+        return null;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/JFRInputEvent.java	Mon Jan 07 13:07:08 2019 -0800
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.logging.jfr;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.StackTrace;
+
+@Name("javafx.Input")
+@Label("JavaFX Input")
+@Category("JavaFX")
+@Description("JavaFX input event")
+@StackTrace(false)
+@Enabled(false)
+public final class JFRInputEvent extends Event {
+    @Label("Input Type")
+    @Description("Input event type")
+    private String input;
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/JFRPulseLogger.java	Mon Jan 07 13:07:08 2019 -0800
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.javafx.logging.jfr;
+
+import com.sun.javafx.logging.Logger;
+import com.sun.javafx.logging.PulseLogger;
+
+import jdk.jfr.FlightRecorder;
+
+public final class JFRPulseLogger extends Logger {
+    private final ThreadLocal<JFRPulsePhaseEvent> currentPulsePhaseEvent;
+    private final ThreadLocal<JFRInputEvent> currentInputEvent;
+
+    private int pulseNumber;
+    private int fxPulseNumber;
+    private int renderPulseNumber;
+    private Thread fxThread;
+
+    public static Logger createInstance() {
+        if (FlightRecorder.isInitialized() || PulseLogger.isPulseLoggingRequested()) {
+            return new JFRPulseLogger();
+        }
+        return null;
+    }
+
+    private JFRPulseLogger() {
+        FlightRecorder.register(JFRInputEvent.class);
+        FlightRecorder.register(JFRPulsePhaseEvent.class);
+        currentPulsePhaseEvent = new ThreadLocal<JFRPulsePhaseEvent>() {
+            @Override
+            public JFRPulsePhaseEvent initialValue() {
+                return new JFRPulsePhaseEvent();
+            }
+        };
+        currentInputEvent = new ThreadLocal<JFRInputEvent>() {
+            @Override
+            public JFRInputEvent initialValue() {
+                return new JFRInputEvent();
+            }
+        };
+    }
+
+    @Override
+    public void pulseStart() {
+        ++pulseNumber;
+        fxPulseNumber = pulseNumber;
+        if (fxThread == null) {
+            fxThread = Thread.currentThread();
+        }
+        newPhase("Pulse start");
+    }
+
+    @Override
+    public void pulseEnd() {
+        newPhase(null);
+        fxPulseNumber = 0;
+    }
+
+    @Override
+    public void renderStart() {
+        renderPulseNumber = fxPulseNumber;
+    }
+
+    @Override
+    public void renderEnd() {
+        newPhase(null);
+        renderPulseNumber = 0;
+    }
+
+    /**
+     * Finishes the current phase and starts a new one if phaseName is not null.
+     *
+     * @param phaseName The name for the new phase.
+     */
+    @Override
+    public void newPhase(String phaseName) {
+        JFRPulsePhaseEvent event = currentPulsePhaseEvent.get();
+
+        /* Cleanup if no longer enabled */
+        if (!event.isEnabled()) {
+            event.setPhaseName(null);
+            return;
+        }
+
+        /* If there is an ongoing event, commit it */
+        if (event.getPhaseName() != null) {
+            event.commit();
+        }
+
+        /* Done if the new phase name is null */
+        if (phaseName == null) {
+            event.setPhaseName(null);
+            return;
+        }
+
+        event = new JFRPulsePhaseEvent();
+        event.begin();
+        event.setPhaseName(phaseName);
+        event.setPulseId(Thread.currentThread() == fxThread ? fxPulseNumber : renderPulseNumber);
+        currentPulsePhaseEvent.set(event);
+    }
+
+    @Override
+    public void newInput(String input) {
+        JFRInputEvent event = currentInputEvent.get();
+
+        /* Cleanup if no longer enabled */
+        if (!event.isEnabled()) {
+            event.setInput(null);
+            return;
+        }
+
+        /* If there is an ongoing event, commit it */
+        if (event.getInput() != null) {
+            event.commit();
+        }
+
+        /* Done if the new input is null */
+        if (input == null) {
+            event.setInput(null);
+            return;
+        }
+
+        event = new JFRInputEvent();
+        event.begin();
+        event.setInput(input);
+        currentInputEvent.set(event);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/JFRPulsePhaseEvent.java	Mon Jan 07 13:07:08 2019 -0800
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.logging.jfr;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.StackTrace;
+
+@Name("javafx.PulsePhase")
+@Label("JavaFX Pulse Phase")
+@Category("JavaFX")
+@Description("Describes a phase in JavaFX pulse processing")
+@StackTrace(false)
+@Enabled(false)
+public final class JFRPulsePhaseEvent extends Event {
+    @PulseId
+    @Label("Pulse Id")
+    private int pulseId;
+
+    @Label("Phase Name")
+    private String phaseName;
+
+    public int getPulseId() {
+        return pulseId;
+    }
+
+    public void setPulseId(int pulseId) {
+        this.pulseId = pulseId;
+    }
+
+    public String getPhaseName() {
+        return phaseName;
+    }
+
+    public void setPhaseName(String phaseName) {
+        this.phaseName = phaseName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/logging/jfr/PulseId.java	Mon Jan 07 13:07:08 2019 -0800
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.logging.jfr;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import jdk.jfr.Description;
+import jdk.jfr.Name;
+import jdk.jfr.Relational;
+
+/**
+ * This annotation defines a relation for future events that are related to the
+ * same pulse id. It also informs a user interface consuming events where a
+ * field contains a pulse id that the value could be useful to find other,
+ * related events.
+ */
+@Relational
+@Name("javafx.PulseId")
+@Retention(RUNTIME)
+@Target(FIELD)
+@Description("Binds events with same pulse id together")
+public @interface PulseId {
+
+}
--- a/modules/javafx.base/src/main/java/module-info.java	Mon Jan 07 13:47:32 2019 -0800
+++ b/modules/javafx.base/src/main/java/module-info.java	Mon Jan 07 13:07:08 2019 -0800
@@ -32,6 +32,7 @@
  */
 module javafx.base {
     requires java.desktop;
+    requires static jdk.jfr;
 
     exports javafx.beans;
     exports javafx.beans.binding;