Timing.java revision 988:d81f7c897bdf
1/* 2 * Copyright (c) 2010, 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 */ 25package jdk.nashorn.internal.runtime; 26 27import java.io.BufferedReader; 28import java.io.IOException; 29import java.io.StringReader; 30import java.util.ArrayList; 31import java.util.LinkedHashMap; 32import java.util.List; 33import java.util.Map; 34import java.util.concurrent.TimeUnit; 35import java.util.function.Supplier; 36import jdk.nashorn.internal.runtime.logging.DebugLogger; 37import jdk.nashorn.internal.runtime.logging.Loggable; 38import jdk.nashorn.internal.runtime.logging.Logger; 39 40/** 41 * Simple wallclock timing framework 42 */ 43@Logger(name="time") 44public final class Timing implements Loggable { 45 46 private DebugLogger log; 47 private TimeSupplier timeSupplier; 48 private final boolean isEnabled; 49 private final long startTime; 50 51 private static final String LOGGER_NAME = Timing.class.getAnnotation(Logger.class).name(); 52 53 /** 54 * Instantiate singleton timer for ScriptEnvironment 55 * @param isEnabled true if enabled, otherwise we keep the instance around 56 * for code brevity and "isEnabled" checks, but never instantiate anything 57 * inside it 58 */ 59 public Timing(final boolean isEnabled) { 60 this.isEnabled = isEnabled; 61 this.startTime = System.nanoTime(); 62 } 63 64 /** 65 * Get the log info accumulated by this Timing instance 66 * @return log info as one string 67 */ 68 public String getLogInfo() { 69 assert isEnabled(); 70 return timeSupplier.get(); 71 } 72 73 /** 74 * Get the log info accumulated by this Timing instance 75 * @return log info as and array of strings, one per line 76 */ 77 public String[] getLogInfoLines() { 78 assert isEnabled(); 79 return timeSupplier.getStrings(); 80 } 81 82 /** 83 * Check if timing is enabled 84 * @return true if timing is enabled 85 */ 86 boolean isEnabled() { 87 return isEnabled; 88 } 89 90 /** 91 * When timing, this can be called to register a new module for timing 92 * or add to its accumulated time 93 * 94 * @param module module name 95 * @param durationNano duration to add to accumulated time for module, in nanoseconds. 96 */ 97 public void accumulateTime(final String module, final long durationNano) { 98 if (isEnabled()) { 99 ensureInitialized(Context.getContextTrusted()); 100 timeSupplier.accumulateTime(module, durationNano); 101 } 102 } 103 104 private DebugLogger ensureInitialized(final Context context) { 105 //lazy init, as there is not necessarily a context available when 106 //a ScriptEnvironment gets initialize 107 if (isEnabled() && log == null) { 108 log = initLogger(context); 109 if (log.isEnabled()) { 110 this.timeSupplier = new TimeSupplier(); 111 Runtime.getRuntime().addShutdownHook( 112 new Thread() { 113 @Override 114 public void run() { 115 //System.err.println because the context and the output streams may be gone 116 //when the shutdown hook executes 117 final StringBuilder sb = new StringBuilder(); 118 for (final String str : timeSupplier.getStrings()) { 119 sb.append('['). 120 append(Timing.getLoggerName()). 121 append("] "). 122 append(str). 123 append('\n'); 124 } 125 System.err.print(sb); 126 } 127 }); 128 } 129 } 130 return log; 131 } 132 133 static String getLoggerName() { 134 return LOGGER_NAME; 135 } 136 137 @Override 138 public DebugLogger initLogger(final Context context) { 139 return context.getLogger(this.getClass()); 140 } 141 142 @Override 143 public DebugLogger getLogger() { 144 return log; 145 } 146 147 /** 148 * Takes a duration in nanoseconds, and returns a string representation of it rounded to milliseconds. 149 * @param durationNano duration in nanoseconds 150 * @return the string representing the duration in milliseconds. 151 */ 152 public static String toMillisPrint(final long durationNano) { 153 return Long.toString(TimeUnit.NANOSECONDS.toMillis(durationNano)); 154 } 155 156 final class TimeSupplier implements Supplier<String> { 157 private final Map<String, Long> timings; 158 159 TimeSupplier() { 160 timings = new LinkedHashMap<>(); 161 } 162 163 String[] getStrings() { 164 final List<String> strs = new ArrayList<>(); 165 final BufferedReader br = new BufferedReader(new StringReader(get())); 166 String line; 167 try { 168 while ((line = br.readLine()) != null) { 169 strs.add(line); 170 } 171 } catch (final IOException e) { 172 throw new RuntimeException(e); 173 } 174 return strs.toArray(new String[strs.size()]); 175 } 176 177 @Override 178 public String get() { 179 final long t = System.nanoTime(); 180 181 long knownTime = 0L; 182 int maxKeyLength = 0; 183 int maxValueLength = 0; 184 185 for (final Map.Entry<String, Long> entry : timings.entrySet()) { 186 maxKeyLength = Math.max(maxKeyLength, entry.getKey().length()); 187 maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue()).length()); 188 } 189 maxKeyLength++; 190 191 final StringBuilder sb = new StringBuilder(); 192 sb.append("Accumulated complation phase Timings:\n\n"); 193 for (final Map.Entry<String, Long> entry : timings.entrySet()) { 194 int len; 195 196 len = sb.length(); 197 sb.append(entry.getKey()); 198 len = sb.length() - len; 199 200 while (len++ < maxKeyLength) { 201 sb.append(' '); 202 } 203 204 final Long duration = entry.getValue(); 205 final String strDuration = toMillisPrint(duration); 206 len = strDuration.length(); 207 for (int i = 0; i < maxValueLength - len; i++) { 208 sb.append(' '); 209 } 210 211 sb.append(strDuration). 212 append(" ms\n"); 213 214 knownTime += duration; 215 } 216 217 final long total = t - startTime; 218 sb.append('\n'); 219 sb.append("Total runtime: "). 220 append(toMillisPrint(total)). 221 append(" ms (Non-runtime: "). 222 append(toMillisPrint(knownTime)). 223 append(" ms ["). 224 append((int)(knownTime * 100.0 / total)). 225 append("%])"); 226 227 return sb.toString(); 228 } 229 230 private void accumulateTime(final String module, final long duration) { 231 Long accumulatedTime = timings.get(module); 232 if (accumulatedTime == null) { 233 accumulatedTime = 0L; 234 } 235 timings.put(module, accumulatedTime + duration); 236 } 237 } 238} 239