1 /* 2 * Copyright (c) 2009, 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package com.sun.hotspot.tools.compiler; 26 27 import java.io.ByteArrayOutputStream; 28 import java.io.File; 29 import java.io.FileNotFoundException; 30 import java.io.FileOutputStream; 31 import java.io.PrintStream; 32 import java.util.*; 33 34 import org.xml.sax.*; 35 import org.xml.sax.helpers.*; 36 37 /** 38 * The LogCompilation tool parses log files generated by HotSpot using the 39 * {@code -XX:+LogCompilation} command line flag, and outputs the data 40 * collected therein in a nicely formatted way. There are various sorting 41 * options available, as well as options that select specific compilation 42 * events (such as inlining decisions) for inclusion in the output. 43 * 44 * The tool is also capable of fixing broken compilation logs as sometimes 45 * generated by Java 1.5 JVMs. 46 */ 47 public class LogCompilation extends DefaultHandler implements ErrorHandler { 48 49 /** 50 * Print usage information and terminate with a given exit code. 51 */ 52 public static void usage(int exitcode) { 53 System.out.println("Usage: LogCompilation [ -v ] [ -c ] [ -s ] [ -e | -n ] file1 ..."); 54 System.out.println("By default, the tool will print the logged compilations ordered by start time."); 55 System.out.println(" -c: clean up malformed 1.5 xml"); 56 System.out.println(" -i: print inlining decisions"); 57 System.out.println(" -S: print compilation statistics"); 58 System.out.println(" -U: print uncommon trap statistics"); 59 System.out.println(" -t: print with time stamps"); 60 System.out.println(" -s: sort events by start time (default)"); 61 System.out.println(" -e: sort events by elapsed time"); 62 System.out.println(" -n: sort events by name and start"); 63 System.out.println(" -C: compare logs (give files to compare on command line)"); 64 System.out.println(" -d: do not print compilation IDs"); 65 System.exit(exitcode); 66 } 67 68 /** 69 * Process command line arguments, parse log files and trigger desired 70 * functionality. 71 */ 72 public static void main(String[] args) throws Exception { 73 Comparator<LogEvent> sort = LogParser.sortByStart; 74 boolean statistics = false; 75 boolean printInlining = false; 76 boolean cleanup = false; 77 boolean trapHistory = false; 78 boolean printTimeStamps = false; 79 boolean compare = false; 80 boolean printID = true; 81 int index = 0; 82 83 while (args.length > index) { 84 String a = args[index]; 85 if (a.equals("-e")) { 86 sort = LogParser.sortByElapsed; 87 index++; 88 } else if (a.equals("-n")) { 89 sort = LogParser.sortByNameAndStart; 90 index++; 91 } else if (a.equals("-s")) { 92 sort = LogParser.sortByStart; 93 index++; 94 } else if (a.equals("-t")) { 95 printTimeStamps = true; 96 index++; 97 } else if (a.equals("-c")) { 98 cleanup = true; 99 index++; 100 } else if (a.equals("-S")) { 101 statistics = true; 102 index++; 103 } else if (a.equals("-U")) { 104 trapHistory = true; 105 index++; 106 } else if (a.equals("-h")) { 107 usage(0); 108 } else if (a.equals("-i")) { 109 printInlining = true; 110 index++; 111 } else if (a.equals("-C")) { 112 compare = true; 113 index++; 114 } else if (a.equals("-d")) { 115 printID = false; 116 index++; 117 } else { 118 if (a.charAt(0) == '-') { 119 System.out.println("Unknown option '" + a + "', assuming file name."); 120 } 121 break; 122 } 123 } 124 125 if (index >= args.length) { 126 usage(1); 127 } 128 129 if (compare) { 130 compareLogs(index, args); 131 return; 132 } 133 134 while (index < args.length) { 135 ArrayList<LogEvent> events = null; 136 try { 137 events = LogParser.parse(args[index], cleanup); 138 } catch (FileNotFoundException fnfe) { 139 System.out.println("File not found: " + args[index]); 140 System.exit(1); 141 } 142 143 Collections.sort(events, sort); 144 145 if (statistics) { 146 printStatistics(events, System.out); 147 } else if (trapHistory) { 148 printTrapHistory(events, System.out); 149 } else { 150 for (LogEvent c : events) { 151 if (c instanceof NMethod) { 152 // skip these 153 continue; 154 } 155 if (printTimeStamps) { 156 System.out.print(c.getStart() + ": "); 157 } 158 if (c instanceof Compilation) { 159 Compilation comp = (Compilation) c; 160 comp.print(System.out, printID, printInlining); 161 } else { 162 c.print(System.out, printID); 163 } 164 } 165 } 166 index++; 167 } 168 } 169 170 /** 171 * Print extensive statistics from parsed log files. 172 */ 173 public static void printStatistics(ArrayList<LogEvent> events, PrintStream out) { 174 // track code cache size 175 long cacheSize = 0; 176 long maxCacheSize = 0; 177 // track number of nmethods 178 int nmethodsCreated = 0; 179 int nmethodsLive = 0; 180 // track how many compilations were attempted multiple times 181 // (indexed by attempts, mapping to number of compilations) 182 int[] attempts = new int[32]; 183 int maxattempts = 0; 184 185 // track time spent in compiler phases 186 LinkedHashMap<String, Double> phaseTime = new LinkedHashMap<>(7); 187 // track nodes created per phase 188 LinkedHashMap<String, Integer> phaseNodes = new LinkedHashMap<>(7); 189 double elapsed = 0; 190 191 for (LogEvent e : events) { 192 if (e instanceof Compilation) { 193 Compilation c = (Compilation) e; 194 c.printShort(out); 195 out.printf(" %6.4f\n", c.getElapsedTime()); 196 attempts[c.getAttempts()]++; 197 maxattempts = Math.max(maxattempts,c.getAttempts()); 198 elapsed += c.getElapsedTime(); 199 for (Phase phase : c.getPhases()) { 200 Double v = phaseTime.get(phase.getName()); 201 if (v == null) { 202 v = Double.valueOf(0.0); 203 } 204 phaseTime.put(phase.getName(), Double.valueOf(v.doubleValue() + phase.getElapsedTime())); 205 206 Integer v2 = phaseNodes.get(phase.getName()); 207 if (v2 == null) { 208 v2 = Integer.valueOf(0); 209 } 210 phaseNodes.put(phase.getName(), Integer.valueOf(v2.intValue() + phase.getNodes())); 211 // Print phase name, elapsed time, nodes at the start of 212 // the phase, nodes created in the phase, live nodes at the 213 // start of the phase, live nodes added in the phase. 214 out.printf("\t%s %6.4f %d %d %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes(), phase.getStartLiveNodes(), phase.getAddedLiveNodes()); 215 } 216 } else if (e instanceof MakeNotEntrantEvent) { 217 MakeNotEntrantEvent mne = (MakeNotEntrantEvent) e; 218 NMethod nm = mne.getNMethod(); 219 if (mne.isZombie()) { 220 if (nm == null) { 221 System.err.println("zombie make not entrant event without nmethod: " + mne.getId()); 222 } 223 cacheSize -= nm.getSize(); 224 nmethodsLive--; 225 } 226 } else if (e instanceof NMethod) { 227 nmethodsLive++; 228 nmethodsCreated++; 229 NMethod nm = (NMethod) e; 230 cacheSize += nm.getSize(); 231 maxCacheSize = Math.max(cacheSize, maxCacheSize); 232 } 233 } 234 out.printf("NMethods: %d created %d live %d bytes (%d peak) in the code cache\n", nmethodsCreated, nmethodsLive, cacheSize, maxCacheSize); 235 out.println("Phase times:"); 236 for (String name : phaseTime.keySet()) { 237 Double v = phaseTime.get(name); 238 Integer v2 = phaseNodes.get(name); 239 out.printf("%20s %6.4f %d\n", name, v.doubleValue(), v2.intValue()); 240 } 241 out.printf("%20s %6.4f\n", "total", elapsed); 242 243 if (maxattempts > 0) { 244 out.println("Distribution of regalloc passes:"); 245 for (int i = 0; i <= maxattempts; i++) { 246 out.printf("%2d %8d\n", i, attempts[i]); 247 } 248 } 249 } 250 251 /** 252 * Container class for a pair of a method and a bytecode instruction index 253 * used by a compiler. This is used in 254 * {@linkplain #compareLogs() comparing logs}. 255 */ 256 static class MethodBCIPair { 257 public MethodBCIPair(Method m, int b, String c) { 258 method = m; 259 bci = b; 260 compiler = c; 261 } 262 263 Method method; 264 int bci; 265 String compiler; 266 267 public boolean equals(Object other) { 268 if (!(other instanceof MethodBCIPair)) { 269 return false; 270 } 271 MethodBCIPair otherp = (MethodBCIPair)other; 272 return (otherp.bci == bci && 273 otherp.method.equals(method) && 274 otherp.compiler.equals(compiler)); 275 } 276 277 public int hashCode() { 278 return method.hashCode() + bci; 279 } 280 281 public String toString() { 282 if (bci != -1) { 283 return method + "@" + bci + " (" + compiler + ")"; 284 } else { 285 return method + " (" + compiler + ")"; 286 } 287 } 288 } 289 290 /** 291 * Compare a number of compilation log files. Each of the logs is parsed, 292 * and all compilations found therein are written to a sorted file (prefix 293 * {@code sorted-}. A summary is written to a new file {@code summary.txt}. 294 * 295 * @param index the index in the command line arguments at which to start 296 * looking for files to compare. 297 * @param args the command line arguments with which {@link LogCompilation} 298 * was originally invoked. 299 * 300 * @throws Exception in case any exceptions are thrown in the called 301 * methods. 302 */ 303 @SuppressWarnings("unchecked") 304 static void compareLogs(int index, String[] args) throws Exception { 305 HashMap<MethodBCIPair,MethodBCIPair> methods = new HashMap<>(); 306 ArrayList<HashMap<MethodBCIPair,Object>> logs = new ArrayList<>(); 307 PrintStream[] outs = new PrintStream[args.length - index]; 308 PrintStream summary = new PrintStream(new FileOutputStream("summary.txt")); 309 int o = 0; 310 // Process all logs given on the command line: collect compilation 311 // data; in particular, method/bci pairs. 312 while (index < args.length) { 313 String basename = new File(args[index]).getName(); 314 String outname = "sorted-" + basename; 315 System.out.println("Sorting " + basename + " to " + outname); 316 outs[o] = new PrintStream(new FileOutputStream(outname)); 317 o++; 318 System.out.println("Parsing " + args[index]); 319 ArrayList<LogEvent> events = LogParser.parse(args[index], false); 320 HashMap<MethodBCIPair,Object> compiles = new HashMap<>(); 321 logs.add(compiles); 322 for (LogEvent c : events) { 323 if (c instanceof Compilation) { 324 Compilation comp = (Compilation) c; 325 MethodBCIPair key = new MethodBCIPair(comp.getMethod(), comp.getBCI(), 326 comp.getCompiler()); 327 MethodBCIPair e = methods.get(key); 328 if (e == null) { 329 methods.put(key, key); 330 } else { 331 key = e; 332 } 333 Object other = compiles.get(key); 334 if (other == null) { 335 compiles.put(key, comp); 336 } else { 337 if (!(other instanceof List)) { 338 List<Object> l = new LinkedList<>(); 339 l.add(other); 340 l.add(comp); 341 compiles.put(key, l); 342 } else { 343 List<Object> l = (List<Object>) other; 344 l.add(comp); 345 } 346 } 347 } 348 } 349 index++; 350 } 351 352 // Process the collected method/bci pairs and write the output. 353 for (MethodBCIPair pair : methods.keySet()) { 354 summary.print(pair + " "); 355 int base = -1; 356 String first = null; 357 boolean mismatch = false; 358 boolean different = false; 359 String[] output = new String[outs.length]; 360 o = 0; 361 for (HashMap<MethodBCIPair,Object> set : logs) { 362 Object e = set.get(pair); 363 String thisone = null; 364 Compilation lastc = null; 365 int n; 366 if (e == null) { 367 n = 0; 368 } else if (e instanceof Compilation) { 369 n = 1; 370 lastc = (Compilation) e; 371 } else { 372 // Compare the last compilation that was done for this method 373 n = ((List<Object>) e).size(); 374 lastc = (Compilation) ((List<Object>) e).get(n - 1); 375 } 376 if (lastc != null) { 377 n = 1; 378 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 379 PrintStream ps = new PrintStream(baos); 380 lastc.print(ps, false); 381 ps.close(); 382 thisone = new String(baos.toByteArray()); 383 } 384 if (base == -1) { 385 base = n; 386 } else if (base != n) { 387 mismatch = true; 388 } 389 output[o++] = thisone; 390 if (thisone != null) { 391 if (first == null) { 392 first = thisone; 393 } else { 394 if (!first.equals(thisone)) { 395 different = true; 396 } 397 } 398 } 399 if (different) { 400 summary.print(n + "d "); 401 } else { 402 summary.print(n + " "); 403 } 404 } 405 if (mismatch) { 406 summary.print("mismatch"); 407 } 408 summary.println(); 409 if (different) { 410 for (int i = 0; i < outs.length; i++) { 411 if (output[i] != null) { 412 outs[i].println(output[i]); 413 } 414 } 415 } 416 } 417 for (int i = 0; i < outs.length; i++) { 418 outs[i].close(); 419 } 420 if (summary != System.out) { 421 summary.close(); 422 } 423 } 424 425 /** 426 * Print the history of uncommon trap events. 427 */ 428 public static void printTrapHistory(ArrayList<LogEvent> events, PrintStream out) { 429 // map method names to a list of log events 430 LinkedHashMap<String, ArrayList<LogEvent>> traps = new LinkedHashMap<>(); 431 // map compilation IDs to compilations 432 HashMap<Integer, Compilation> comps = new HashMap<>(); 433 434 // First, iterate over all logged events, collecting data about 435 // uncommon trap events. 436 for (LogEvent e : events) { 437 if (e instanceof NMethod) { 438 // skip these 439 continue; 440 } 441 if (e instanceof Compilation) { 442 Compilation c = (Compilation) e; 443 String name = c.getMethod().getFullName(); 444 ArrayList<LogEvent> elist = traps.get(name); 445 if (elist != null && comps.get(c.getId()) == null) { 446 comps.put(c.getId(), c); 447 // If there were previous events for the method 448 // then keep track of later compiles too. 449 elist.add(c); 450 } 451 continue; 452 } 453 if (e instanceof BasicLogEvent) { 454 BasicLogEvent ble = (BasicLogEvent) e; 455 Compilation c = ble.getCompilation(); 456 if (c == null) { 457 if (!(ble instanceof NMethod)) { 458 throw new InternalError("only nmethods should have a null compilation; here's a " + ble.getClass()); 459 } 460 continue; 461 } 462 String name = c.getMethod().getFullName(); 463 ArrayList<LogEvent> elist = traps.get(name); 464 if (elist == null) { 465 elist = new ArrayList<LogEvent>(); 466 traps.put(name, elist); 467 } 468 int bleId = Integer.parseInt(ble.getId()); 469 if (comps.get(bleId) == null) { 470 comps.put(bleId, c); 471 // Add the associated compile to the list. It 472 // will likely go at the end but we need to search 473 // backwards for the proper insertion point. 474 double start = c.getStart(); 475 int ipoint = 0; 476 while (ipoint < elist.size() && elist.get(ipoint).getStart() < start) { 477 ipoint++; 478 } 479 if (ipoint == elist.size()) { 480 elist.add(c); 481 } else { 482 elist.add(ipoint, c); 483 } 484 } 485 elist.add(ble); 486 } 487 } 488 489 // Second, iterate over collected traps and output information. 490 for (String c: traps.keySet()) { 491 ArrayList<LogEvent> elist = traps.get(c); 492 String name = ((Compilation) elist.get(0)).getMethod().getFullName(); 493 System.out.println(name); 494 double start = 0; 495 for (LogEvent e: elist) { 496 if (start > e.getStart() && e.getStart() != 0) { 497 throw new InternalError("wrong sorting order for traps"); 498 } 499 start = e.getStart(); 500 out.print(e.getStart() + ": "); 501 if (e instanceof Compilation) { 502 ((Compilation) e).print(out, true, true, true); 503 } else { 504 e.print(out, true); 505 } 506 } 507 out.println(); 508 } 509 } 510 511 }