1 /* 2 * Copyright (c) 2011, 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.webkit.perf; 27 28 import com.sun.javafx.logging.PlatformLogger; 29 import com.sun.javafx.logging.PlatformLogger.Level; 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.Comparator; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 public final class PerfLogger { 37 private static Thread shutdownHook; 38 private static Map<PlatformLogger, PerfLogger> loggers; 39 40 private final HashMap<String, ProbeStat> probes = 41 new HashMap<String, ProbeStat>(); 42 private final PlatformLogger log; 43 private final boolean isEnabled; // needed at shutdown time 44 45 /** 46 * Finds or creates a logger with the given {@code log}. 47 * In order the logger be enabled the {@code log} should be loggable. 48 * 49 * @param log associated {@code Logger} 50 */ 51 public synchronized static PerfLogger getLogger(PlatformLogger log) { 52 if (loggers == null) { 53 loggers = new HashMap<PlatformLogger, PerfLogger>(); 54 } 55 PerfLogger l = loggers.get(log); 56 if (l == null) { 57 l = new PerfLogger(log); 58 loggers.put(log, l); 59 } 60 // Add the hook if at least one logger is enabled. 61 if (l.isEnabled() && shutdownHook == null) { 62 shutdownHook = new Thread() { 63 @Override 64 public void run() { 65 for (PerfLogger l: loggers.values()) { 66 if (!l.isEnabled()) continue; 67 // Using stdout as logging might be off at shutdown time. 68 l.log(false); 69 } 70 } 71 }; 72 Runtime.getRuntime().addShutdownHook(shutdownHook); 73 } 74 return l; 75 } 76 77 /** 78 * Finds or creates a logger with {@code Logger} named 79 * com.sun.webkit.perf.{@code name}. 80 * 81 * @param name the {@code PerfLogger} short name 82 */ 83 public synchronized static PerfLogger getLogger(String name) { 84 return getLogger(PlatformLogger.getLogger("com.sun.webkit.perf." + name)); 85 } 86 87 private PerfLogger(PlatformLogger log) { 88 this.log = log; 89 this.isEnabled = log.isLoggable(Level.FINE); 90 startCount("TOTALTIME"); 91 } 92 93 /** 94 * The class contains perf statistics for a registered probe. 95 */ 96 public static final class ProbeStat { 97 private final String probe; 98 private int count; 99 private long totalTime; 100 private long startTime; 101 private boolean isRunning = false; 102 103 private ProbeStat(String probe) { 104 this.probe = probe; 105 } 106 107 public String getProbe() { 108 return probe; 109 } 110 111 public int getCount() { 112 return count; 113 } 114 115 public long getTotalTime() { 116 return totalTime; 117 } 118 119 private void reset() { 120 count = 0; 121 totalTime = startTime = 0; 122 } 123 124 private void suspend() { 125 if (isRunning) { 126 totalTime += System.currentTimeMillis() - startTime; 127 isRunning = false; 128 } 129 } 130 131 private void resume() { 132 isRunning = true; 133 count++; 134 startTime = System.currentTimeMillis(); 135 } 136 137 private void snapshot() { 138 if (isRunning) { 139 totalTime += System.currentTimeMillis() - startTime; 140 startTime = System.currentTimeMillis(); 141 } 142 } 143 144 @Override 145 public String toString() { 146 return super.toString() + "[count=" + count + ", time=" + totalTime + "]"; 147 } 148 } 149 150 public boolean isEnabled() { 151 return isEnabled; 152 } 153 154 private synchronized String fullName(String probe) { 155 return log.getName() + "." + probe; 156 } 157 158 private final Comparator timeComparator = (arg0, arg1) -> { 159 long t0 = probes.get((String)arg0).totalTime; 160 long t1 = probes.get((String)arg1).totalTime; 161 if (t0 > t1) { 162 return 1; 163 } else if (t0 < t1) { 164 return -1; 165 } 166 return 0; 167 }; 168 169 private final Comparator countComparator = (arg0, arg1) -> { 170 long c0 = probes.get((String)arg0).count; 171 long c1 = probes.get((String)arg1).count; 172 if (c0 > c1) { 173 return 1; 174 } else if (c0 < c1) { 175 return -1; 176 } 177 return 0; 178 }; 179 180 /** 181 * Resets perf statistics. 182 */ 183 public synchronized void reset() { 184 for (Map.Entry<String, ProbeStat> entry: probes.entrySet()) { 185 entry.getValue().reset(); 186 } 187 startCount("TOTALTIME"); 188 } 189 190 public synchronized static void resetAll() { 191 for (PerfLogger l: loggers.values()) { 192 l.reset(); 193 } 194 } 195 196 private synchronized ProbeStat registerProbe(String probe) { 197 String p = probe.intern(); 198 if (probes.containsKey(p)) { 199 log.fine("Warning: \"" + fullName(p) + "\" probe already exists"); 200 } else { 201 log.fine("Registering \"" + fullName(p) + "\" probe"); 202 } 203 ProbeStat stat = new ProbeStat(p); 204 probes.put(p, stat); 205 return stat; 206 } 207 208 public synchronized ProbeStat getProbeStat(String probe) { 209 String p = probe.intern(); 210 ProbeStat s = probes.get(p); 211 if (s != null) { 212 s.snapshot(); 213 } 214 return s; 215 } 216 217 /** 218 * Starts count statistics for the probe with zero initial data. 219 */ 220 public synchronized void startCount(String probe) { 221 if (!isEnabled()) { 222 return; 223 } 224 String p = probe.intern(); 225 ProbeStat stat = probes.get(p); 226 if (stat == null) { 227 stat = registerProbe(p); 228 } 229 stat.reset(); 230 stat.resume(); 231 } 232 233 /** 234 * Suspends count statistics for the probe. 235 */ 236 public synchronized void suspendCount(String probe) { 237 if (!isEnabled()) { 238 return; 239 } 240 String p = probe.intern(); 241 ProbeStat stat = probes.get(p); 242 if (stat != null) { 243 stat.suspend(); 244 } else { 245 log.fine("Warning: \"" + fullName(p) + "\" probe is not registered"); 246 } 247 } 248 249 /** 250 * Resumes count statistics for the probe, or starts if it's not yet started. 251 */ 252 public synchronized void resumeCount(String probe) { 253 if (!isEnabled()) { 254 return; 255 } 256 String p = probe.intern(); 257 ProbeStat stat = probes.get(p); 258 if (stat == null) { 259 stat = registerProbe(p); 260 } 261 stat.resume(); 262 } 263 264 /** 265 * Prints perf statistics to the buffer. 266 */ 267 public synchronized void log(StringBuffer buf) { 268 if (!isEnabled()) { 269 return; 270 } 271 buf.append("=========== Performance Statistics =============\n"); 272 273 ProbeStat total = getProbeStat("TOTALTIME"); 274 275 ArrayList<String> list = new ArrayList<String>(); 276 list.addAll(probes.keySet()); 277 278 buf.append("\nTime:\n"); 279 Collections.sort(list, timeComparator); 280 for (String p: list) { 281 ProbeStat s = getProbeStat(p); 282 buf.append(String.format("%s: %dms", fullName(p), s.totalTime)); 283 if (total.totalTime > 0){ 284 buf.append(String.format(", %.2f%%%n", (float)100*s.totalTime/total.totalTime)); 285 } else { 286 buf.append("\n"); 287 } 288 } 289 290 buf.append("\nInvocations count:\n"); 291 Collections.sort(list, countComparator); 292 for (String p: list) { 293 buf.append(String.format("%s: %d%n", fullName(p), getProbeStat(p).count)); 294 } 295 buf.append("================================================\n"); 296 } 297 298 /** 299 * Logs perf statistics. 300 */ 301 public synchronized void log() { 302 log(true); 303 } 304 305 private synchronized void log(boolean useLogger) { 306 StringBuffer buf = new StringBuffer(); 307 log(buf); 308 if (useLogger) { 309 log.fine(buf.toString()); 310 } else { 311 System.out.println(buf.toString()); 312 System.out.flush(); 313 } 314 } 315 316 /** 317 * Logs perf statistics of all loggers. 318 */ 319 public synchronized static void logAll() { 320 for (PerfLogger l: loggers.values()) { 321 l.log(); 322 } 323 } 324 }