1 /* 2 * Copyright (c) 2009, 2012, 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 /** 26 * A SAX based parser of LogCompilation output from HotSpot. It takes a complete 27 * @author never 28 */ 29 30 package com.sun.hotspot.tools.compiler; 31 32 import java.io.FileReader; 33 import java.io.Reader; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.Comparator; 37 import java.util.HashMap; 38 import java.util.LinkedHashMap; 39 import java.util.Stack; 40 import javax.xml.parsers.SAXParser; 41 import javax.xml.parsers.SAXParserFactory; 42 import org.xml.sax.Attributes; 43 import org.xml.sax.ErrorHandler; 44 import org.xml.sax.InputSource; 45 import org.xml.sax.helpers.DefaultHandler; 46 47 public class LogParser extends DefaultHandler implements ErrorHandler, Constants { 48 49 static final HashMap<String, String> typeMap; 50 static { 51 typeMap = new HashMap<String, String>(); 52 typeMap.put("[I", "int[]"); 53 typeMap.put("[C", "char[]"); 54 typeMap.put("[Z", "boolean[]"); 55 typeMap.put("[L", "Object[]"); 56 typeMap.put("[B", "byte[]"); 57 } 58 59 static Comparator<LogEvent> sortByStart = new Comparator<LogEvent>() { 60 61 public int compare(LogEvent a, LogEvent b) { 62 double difference = (a.getStart() - b.getStart()); 63 if (difference < 0) { 64 return -1; 65 } 66 if (difference > 0) { 67 return 1; 68 } 69 return 0; 70 } 71 72 @Override 73 public boolean equals(Object other) { 74 return false; 75 } 76 77 @Override 78 public int hashCode() { 79 return 7; 80 } 81 }; 82 static Comparator<LogEvent> sortByNameAndStart = new Comparator<LogEvent>() { 83 84 public int compare(LogEvent a, LogEvent b) { 85 Compilation c1 = a.getCompilation(); 86 Compilation c2 = b.getCompilation(); 87 if (c1 != null && c2 != null) { 88 int result = c1.getMethod().toString().compareTo(c2.getMethod().toString()); 89 if (result != 0) { 90 return result; 91 } 92 } 93 double difference = (a.getStart() - b.getStart()); 94 if (difference < 0) { 95 return -1; 96 } 97 if (difference > 0) { 98 return 1; 99 } 100 return 0; 101 } 102 103 public boolean equals(Object other) { 104 return false; 105 } 106 107 @Override 108 public int hashCode() { 109 return 7; 110 } 111 }; 112 static Comparator<LogEvent> sortByElapsed = new Comparator<LogEvent>() { 113 114 public int compare(LogEvent a, LogEvent b) { 115 double difference = (a.getElapsedTime() - b.getElapsedTime()); 116 if (difference < 0) { 117 return -1; 118 } 119 if (difference > 0) { 120 return 1; 121 } 122 return 0; 123 } 124 125 @Override 126 public boolean equals(Object other) { 127 return false; 128 } 129 130 @Override 131 public int hashCode() { 132 return 7; 133 } 134 }; 135 136 private ArrayList<LogEvent> events = new ArrayList<LogEvent>(); 137 138 private HashMap<String, String> types = new HashMap<String, String>(); 139 private HashMap<String, Method> methods = new HashMap<String, Method>(); 140 private LinkedHashMap<String, NMethod> nmethods = new LinkedHashMap<String, NMethod>(); 141 private HashMap<String, Compilation> compiles = new HashMap<String, Compilation>(); 142 private String failureReason; 143 private int bci; 144 private Stack<CallSite> scopes = new Stack<CallSite>(); 145 private Compilation compile; 146 private CallSite site; 147 private Stack<Phase> phaseStack = new Stack<Phase>(); 148 private UncommonTrapEvent currentTrap; 149 private Stack<CallSite> late_inline_scope; 150 151 long parseLong(String l) { 152 try { 153 return Long.decode(l).longValue(); 154 } catch (NumberFormatException nfe) { 155 int split = l.length() - 8; 156 String s1 = "0x" + l.substring(split); 157 String s2 = l.substring(0, split); 158 long v1 = Long.decode(s1).longValue() & 0xffffffffL; 159 long v2 = (Long.decode(s2).longValue() & 0xffffffffL) << 32; 160 if (!l.equals("0x" + Long.toHexString(v1 + v2))) { 161 System.out.println(l); 162 System.out.println(s1); 163 System.out.println(s2); 164 System.out.println(v1); 165 System.out.println(v2); 166 System.out.println(Long.toHexString(v1 + v2)); 167 throw new InternalError("bad conversion"); 168 } 169 return v1 + v2; 170 } 171 } 172 173 public static ArrayList<LogEvent> parse(String file, boolean cleanup) throws Exception { 174 return parse(new FileReader(file), cleanup); 175 } 176 177 public static ArrayList<LogEvent> parse(Reader reader, boolean cleanup) throws Exception { 178 // Create the XML input factory 179 SAXParserFactory factory = SAXParserFactory.newInstance(); 180 181 // Create the XML LogEvent reader 182 SAXParser p = factory.newSAXParser(); 183 184 if (cleanup) { 185 // some versions of the log have slightly malformed XML, so clean it 186 // up before passing it to SAX 187 reader = new LogCleanupReader(reader); 188 } 189 190 LogParser log = new LogParser(); 191 p.parse(new InputSource(reader), log); 192 193 // Associate compilations with their NMethods 194 for (NMethod nm : log.nmethods.values()) { 195 Compilation c = log.compiles.get(nm.getId()); 196 nm.setCompilation(c); 197 // Native wrappers for methods don't have a compilation 198 if (c != null) { 199 c.setNMethod(nm); 200 } 201 } 202 203 // Initially we want the LogEvent log sorted by timestamp 204 Collections.sort(log.events, sortByStart); 205 206 return log.events; 207 } 208 209 String search(Attributes attr, String name) { 210 String result = attr.getValue(name); 211 if (result != null) { 212 return result; 213 } else { 214 throw new InternalError("can't find " + name); 215 } 216 } 217 218 String search(Attributes attr, String name, String defaultValue) { 219 String result = attr.getValue(name); 220 if (result != null) { 221 return result; 222 } 223 return defaultValue; 224 } 225 int indent = 0; 226 227 String type(String id) { 228 String result = types.get(id); 229 if (result == null) { 230 throw new InternalError(id); 231 } 232 String remapped = typeMap.get(result); 233 if (remapped != null) { 234 return remapped; 235 } 236 return result; 237 } 238 239 void type(String id, String name) { 240 assert type(id) == null; 241 types.put(id, name); 242 } 243 244 Method method(String id) { 245 Method result = methods.get(id); 246 if (result == null) { 247 throw new InternalError(id); 248 } 249 return result; 250 } 251 252 public String makeId(Attributes atts) { 253 String id = atts.getValue("compile_id"); 254 String kind = atts.getValue("kind"); 255 if (kind != null && kind.equals("osr")) { 256 id += "%"; 257 } 258 return id; 259 } 260 261 @Override 262 public void startElement(String uri, 263 String localName, 264 String qname, 265 Attributes atts) { 266 if (qname.equals("phase")) { 267 Phase p = new Phase(search(atts, "name"), 268 Double.parseDouble(search(atts, "stamp")), 269 Integer.parseInt(search(atts, "nodes", "0")), 270 Integer.parseInt(search(atts, "live", "0"))); 271 phaseStack.push(p); 272 } else if (qname.equals("phase_done")) { 273 Phase p = phaseStack.pop(); 274 String phaseName = search(atts, "name", null); 275 if (phaseName != null && !p.getId().equals(phaseName)) { 276 System.out.println("phase: " + p.getId()); 277 throw new InternalError("phase name mismatch"); 278 } 279 p.setEnd(Double.parseDouble(search(atts, "stamp"))); 280 p.setEndNodes(Integer.parseInt(search(atts, "nodes", "0"))); 281 p.setEndLiveNodes(Integer.parseInt(search(atts, "live", "0"))); 282 compile.getPhases().add(p); 283 } else if (qname.equals("task")) { 284 compile = new Compilation(Integer.parseInt(search(atts, "compile_id", "-1"))); 285 compile.setStart(Double.parseDouble(search(atts, "stamp"))); 286 compile.setICount(search(atts, "count", "0")); 287 compile.setBCount(search(atts, "backedge_count", "0")); 288 289 String method = atts.getValue("method"); 290 int space = method.indexOf(' '); 291 method = method.substring(0, space) + "::" + 292 method.substring(space + 1, method.indexOf(' ', space + 1) + 1); 293 String compiler = atts.getValue("compiler"); 294 if (compiler == null) { 295 compiler = ""; 296 } 297 String kind = atts.getValue("compile_kind"); 298 if (kind == null) { 299 kind = "normal"; 300 } 301 if (kind.equals("osr")) { 302 compile.setOsr(true); 303 compile.setOsr_bci(Integer.parseInt(search(atts, "osr_bci"))); 304 } else if (kind.equals("c2i")) { 305 compile.setSpecial("--- adapter " + method); 306 } else { 307 compile.setSpecial(compile.getId() + " " + method + " (0 bytes)"); 308 } 309 events.add(compile); 310 compiles.put(makeId(atts), compile); 311 site = compile.getCall(); 312 } else if (qname.equals("type")) { 313 type(search(atts, "id"), search(atts, "name")); 314 } else if (qname.equals("bc")) { 315 bci = Integer.parseInt(search(atts, "bci")); 316 } else if (qname.equals("klass")) { 317 type(search(atts, "id"), search(atts, "name")); 318 } else if (qname.equals("method")) { 319 String id = search(atts, "id"); 320 Method m = new Method(); 321 m.setHolder(type(search(atts, "holder"))); 322 m.setName(search(atts, "name")); 323 m.setReturnType(type(search(atts, "return"))); 324 m.setArguments(search(atts, "arguments", "void")); 325 326 if (search(atts, "unloaded", "0").equals("0")) { 327 m.setBytes(search(atts, "bytes")); 328 m.setIICount(search(atts, "iicount")); 329 m.setFlags(search(atts, "flags")); 330 } 331 methods.put(id, m); 332 } else if (qname.equals("call")) { 333 site = new CallSite(bci, method(search(atts, "method"))); 334 site.setCount(Integer.parseInt(search(atts, "count", "0"))); 335 String receiver = atts.getValue("receiver"); 336 if (receiver != null) { 337 site.setReceiver(type(receiver)); 338 site.setReceiver_count(Integer.parseInt(search(atts, "receiver_count"))); 339 } 340 scopes.peek().add(site); 341 } else if (qname.equals("regalloc")) { 342 compile.setAttempts(Integer.parseInt(search(atts, "attempts"))); 343 } else if (qname.equals("inline_fail")) { 344 scopes.peek().last().setReason(search(atts, "reason")); 345 } else if (qname.equals("failure")) { 346 failureReason = search(atts, "reason"); 347 } else if (qname.equals("task_done")) { 348 compile.setEnd(Double.parseDouble(search(atts, "stamp"))); 349 if (Integer.parseInt(search(atts, "success")) == 0) { 350 compile.setFailureReason(failureReason); 351 } 352 } else if (qname.equals("make_not_entrant")) { 353 String id = makeId(atts); 354 NMethod nm = nmethods.get(id); 355 if (nm == null) throw new InternalError(); 356 LogEvent e = new MakeNotEntrantEvent(Double.parseDouble(search(atts, "stamp")), id, 357 atts.getValue("zombie") != null, nm); 358 events.add(e); 359 } else if (qname.equals("uncommon_trap")) { 360 String id = atts.getValue("compile_id"); 361 if (id != null) { 362 id = makeId(atts); 363 currentTrap = new UncommonTrapEvent(Double.parseDouble(search(atts, "stamp")), 364 id, 365 atts.getValue("reason"), 366 atts.getValue("action"), 367 Integer.parseInt(search(atts, "count", "0"))); 368 events.add(currentTrap); 369 } else { 370 // uncommon trap inserted during parsing. 371 // ignore for now 372 } 373 } else if (qname.equals("late_inline")) { 374 late_inline_scope = new Stack<CallSite>(); 375 site = new CallSite(-999, method(search(atts, "method"))); 376 late_inline_scope.push(site); 377 } else if (qname.equals("jvms")) { 378 // <jvms bci='4' method='java/io/DataInputStream readChar ()C' bytes='40' count='5815' iicount='20815'/> 379 if (currentTrap != null) { 380 currentTrap.addJVMS(atts.getValue("method"), Integer.parseInt(atts.getValue("bci"))); 381 } else if (late_inline_scope != null) { 382 bci = Integer.parseInt(search(atts, "bci")); 383 site = new CallSite(bci, method(search(atts, "method"))); 384 late_inline_scope.push(site); 385 } else { 386 // Ignore <eliminate_allocation type='667'>, 387 // <eliminate_lock lock='1'>, 388 // <replace_string_concat arguments='2' string_alloc='0' multiple='0'> 389 } 390 } else if (qname.equals("nmethod")) { 391 String id = makeId(atts); 392 NMethod nm = new NMethod(Double.parseDouble(search(atts, "stamp")), 393 id, 394 parseLong(atts.getValue("address")), 395 parseLong(atts.getValue("size"))); 396 nmethods.put(id, nm); 397 events.add(nm); 398 } else if (qname.equals("parse")) { 399 Method m = method(search(atts, "method")); 400 if (scopes.size() == 0) { 401 compile.setMethod(m); 402 scopes.push(site); 403 } else { 404 if (site.getMethod() == m) { 405 scopes.push(site); 406 } else if (scopes.peek().getCalls().size() > 2 && m == scopes.peek().last(-2).getMethod()) { 407 scopes.push(scopes.peek().last(-2)); 408 } else { 409 System.out.println(site.getMethod()); 410 System.out.println(m); 411 throw new InternalError("call site and parse don't match"); 412 } 413 } 414 } else if (qname.equals("parse_done")) { 415 CallSite call = scopes.pop(); 416 call.setEndNodes(Integer.parseInt(search(atts, "nodes", "0"))); 417 call.setEndLiveNodes(Integer.parseInt(search(atts, "live", "0"))); 418 call.setTimeStamp(Double.parseDouble(search(atts, "stamp"))); 419 scopes.push(call); 420 } 421 } 422 423 @Override 424 public void endElement(String uri, 425 String localName, 426 String qname) { 427 if (qname.equals("parse")) { 428 indent -= 2; 429 scopes.pop(); 430 } else if (qname.equals("uncommon_trap")) { 431 currentTrap = null; 432 } else if (qname.equals("late_inline")) { 433 // Populate late inlining info. 434 435 // late_inline scopes are specified in reverse order: 436 // compiled method should be on top of stack. 437 CallSite caller = late_inline_scope.pop(); 438 Method m = compile.getMethod(); 439 if (m != caller.getMethod()) { 440 System.out.println(m); 441 System.out.println(caller.getMethod() + " bci: " + bci); 442 throw new InternalError("call site and late_inline info don't match"); 443 } 444 445 // late_inline contains caller+bci info, convert it 446 // to bci+callee info used by LogCompilation. 447 site = compile.getLateInlineCall(); 448 do { 449 bci = caller.getBci(); 450 // Next inlined call. 451 caller = late_inline_scope.pop(); 452 CallSite callee = new CallSite(bci, caller.getMethod()); 453 site.add(callee); 454 site = callee; 455 } while (!late_inline_scope.empty()); 456 457 if (caller.getBci() != -999) { 458 System.out.println(caller.getMethod()); 459 throw new InternalError("broken late_inline info"); 460 } 461 if (site.getMethod() != caller.getMethod()) { 462 System.out.println(site.getMethod()); 463 System.out.println(caller.getMethod()); 464 throw new InternalError("call site and late_inline info don't match"); 465 } 466 // late_inline is followed by parse with scopes.size() == 0, 467 // 'site' will be pushed to scopes. 468 late_inline_scope = null; 469 } else if (qname.equals("task")) { 470 types.clear(); 471 methods.clear(); 472 site = null; 473 } 474 } 475 476 @Override 477 public void warning(org.xml.sax.SAXParseException e) { 478 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 479 e.printStackTrace(); 480 } 481 482 @Override 483 public void error(org.xml.sax.SAXParseException e) { 484 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 485 e.printStackTrace(); 486 } 487 488 @Override 489 public void fatalError(org.xml.sax.SAXParseException e) { 490 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 491 e.printStackTrace(); 492 } 493 }