1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.logging;
  27 
  28 import com.oracle.jrockit.jfr.EventToken;
  29 import com.oracle.jrockit.jfr.Producer;
  30 
  31 /**
  32  * Logs pulse related information with Java Flight Recorder.
  33  */
  34 class JFRLogger extends Logger {
  35     
  36     private static final String PRODUCER_URI = "http://www.oracle.com/technetwork/java/javafx/index.html";
  37     private static JFRLogger jfrLogger;
  38     
  39     private final Producer producer;
  40     private final EventToken pulseEventToken;
  41     private final EventToken inputEventToken;
  42     private final ThreadLocal<JFRPulseEvent> curPhaseEvent;
  43     private final ThreadLocal<JFRInputEvent> curInputEvent;
  44         
  45     private JFRLogger() throws Exception {
  46         producer = new Producer("JavaFX producer", "JavaFX producer.", PRODUCER_URI);
  47         pulseEventToken = producer.addEvent(JFRPulseEvent.class);
  48         inputEventToken = producer.addEvent(JFRInputEvent.class);
  49         producer.register();
  50         curPhaseEvent = new ThreadLocal() {
  51             @Override
  52             public JFRPulseEvent initialValue() {
  53                 return new JFRPulseEvent(pulseEventToken);
  54             }
  55         };
  56         curInputEvent = new ThreadLocal(){
  57             @Override
  58             public JFRInputEvent initialValue() {
  59                 return new JFRInputEvent(inputEventToken);
  60             }
  61         };
  62     }
  63     
  64     public static JFRLogger getInstance() {
  65         if (jfrLogger == null) {
  66             /* Guards against exceptions in the constructor and the absence of jfr.jar at run time */
  67             try {
  68                 Class klass = Class.forName("com.oracle.jrockit.jfr.FlightRecorder");
  69                 if (klass != null && com.oracle.jrockit.jfr.FlightRecorder.isActive()) {
  70                     jfrLogger = new JFRLogger();
  71                 }
  72             }
  73             catch (Exception e) {
  74                 jfrLogger = null;
  75             }
  76         }
  77         return jfrLogger;
  78     }
  79     
  80     /**
  81      *  Pulse number reconstruction for the render thread relies on the current synchronization 
  82      *  between the FX and render threads: renderStart() is called on the FX thread after all
  83      *  previous RenderJobs have finished and before any new RenderJob is pushed.
  84      */
  85     private int pulseNumber;
  86     private int fxPulseNumber;
  87     private int renderPulseNumber;
  88     private Thread fxThread;
  89     
  90     @Override
  91     public void pulseStart() {
  92         ++pulseNumber;
  93         fxPulseNumber = pulseNumber;
  94         fxThread = Thread.currentThread();
  95         newPhase("Pulse start");
  96     }
  97     
  98     @Override
  99     public void pulseEnd() {
 100         newPhase(null);
 101         fxPulseNumber = 0;
 102     }
 103     
 104     @Override
 105     public void renderStart() {
 106         renderPulseNumber = fxPulseNumber;
 107     }
 108 
 109     @Override
 110     public void renderEnd() {
 111         newPhase(null);
 112         renderPulseNumber = 0;
 113     }
 114     
 115     /**
 116      * Finishes the current phase and starts a new one if phaseName is not null.
 117      * @param phaseName The name for the new phase.
 118      */
 119     @Override
 120     public void newPhase(String phaseName) {
 121         if (pulseEventToken == null || !pulseEventToken.isEnabled()) {
 122             return;
 123         }
 124         
 125         /* Finish the previous phase if any */
 126         JFRPulseEvent event = curPhaseEvent.get();
 127         if (event.getPhase() != null) {
 128             event.end();
 129             event.commit();
 130         }
 131 
 132         /* Done if the new phase name is null */
 133         if (phaseName == null) {
 134             event.setPhase(null);
 135             return;
 136         }
 137                 
 138         event.reset();
 139         event.begin();
 140         event.setPhase(phaseName);
 141         event.setPulseNumber(Thread.currentThread() == fxThread ? fxPulseNumber : renderPulseNumber);
 142     }
 143 
 144     @Override
 145     public void newInput(String input) {
 146         if (inputEventToken == null || !inputEventToken.isEnabled()) {
 147             return;
 148         }
 149 
 150         /* Finish the previous input event if any */
 151         JFRInputEvent event = curInputEvent.get();
 152         if (event.getInput() != null) {
 153             event.end();
 154             event.commit();
 155         }
 156 
 157         /* Done if the new input is null */
 158         if (input == null) {
 159             event.setInput(null);
 160             return;
 161         }
 162         
 163         event.reset();
 164         event.begin();
 165         event.setInput(input);
 166     }    
 167 }