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 return search(attr, name, null); 211 } 212 213 String search(Attributes attr, String name, String defaultValue) { 214 String result = attr.getValue(name); 215 if (result != null) { 216 return result; 217 } 218 if (defaultValue != null) { 219 return defaultValue; 220 } 221 for (int i = 0; i < attr.getLength(); i++) { 222 System.out.println(attr.getQName(i) + " " + attr.getValue(attr.getQName(i))); 223 } 224 throw new InternalError("can't find " + name); 225 } 226 int indent = 0; 227 String compile_id; 228 229 String type(String id) { 230 String result = types.get(id); 231 if (result == null) { 232 throw new InternalError(id); 233 } 234 String remapped = typeMap.get(result); 235 if (remapped != null) { 236 return remapped; 237 } 238 return result; 239 } 240 241 void type(String id, String name) { 242 assert type(id) == null; 243 types.put(id, name); 244 } 245 246 Method method(String id) { 247 Method result = methods.get(id); 248 if (result == null) { 249 throw new InternalError(id); 250 } 251 return result; 252 } 253 254 public String makeId(Attributes atts) { 255 String id = atts.getValue("compile_id"); 256 String kind = atts.getValue("kind"); 257 if (kind != null && kind.equals("osr")) { 258 id += "%"; 259 } 260 return id; 261 } 262 263 @Override 264 public void startElement(String uri, 265 String localName, 266 String qname, 267 Attributes atts) { 268 if (qname.equals("phase")) { 269 Phase p = new Phase(search(atts, "name"), 270 Double.parseDouble(search(atts, "stamp")), 271 Integer.parseInt(search(atts, "nodes"))); 272 phaseStack.push(p); 273 } else if (qname.equals("phase_done")) { 274 Phase p = phaseStack.pop(); 275 p.setEndNodes(Integer.parseInt(search(atts, "nodes"))); 276 p.setEnd(Double.parseDouble(search(atts, "stamp"))); 277 compile.getPhases().add(p); 278 } else if (qname.equals("task")) { 279 compile = new Compilation(Integer.parseInt(search(atts, "compile_id", "-1"))); 280 compile.setStart(Double.parseDouble(search(atts, "stamp"))); 281 compile.setICount(search(atts, "count", "0")); 282 compile.setBCount(search(atts, "backedge_count", "0")); 283 284 String method = atts.getValue("method"); 285 int space = method.indexOf(' '); 286 method = method.substring(0, space) + "::" + 287 method.substring(space + 1, method.indexOf(' ', space + 1) + 1); 288 String compiler = atts.getValue("compiler"); 289 if (compiler == null) { 290 compiler = ""; 291 } 292 String kind = atts.getValue("compile_kind"); 293 if (kind == null) { 294 kind = "normal"; 295 } 296 if (kind.equals("osr")) { 297 compile.setOsr(true); 298 compile.setOsr_bci(Integer.parseInt(search(atts, "osr_bci"))); 299 } else if (kind.equals("c2i")) { 300 compile.setSpecial("--- adapter " + method); 301 } else { 302 compile.setSpecial(compile.getId() + " " + method + " (0 bytes)"); 303 } 304 events.add(compile); 305 compiles.put(makeId(atts), compile); 306 site = compile.getCall(); 307 } else if (qname.equals("type")) { 308 type(search(atts, "id"), search(atts, "name")); 309 } else if (qname.equals("bc")) { 310 bci = Integer.parseInt(search(atts, "bci")); 311 } else if (qname.equals("klass")) { 312 type(search(atts, "id"), search(atts, "name")); 313 } else if (qname.equals("method")) { 314 String id = search(atts, "id"); 315 Method m = new Method(); 316 m.setHolder(type(search(atts, "holder"))); 317 m.setName(search(atts, "name")); 318 m.setReturnType(type(search(atts, "return"))); 319 m.setArguments(search(atts, "arguments", "void")); 320 m.setBytes(search(atts, "bytes")); 321 m.setIICount(search(atts, "iicount")); 322 m.setFlags(search(atts, "flags")); 323 methods.put(id, m); 324 } else if (qname.equals("call")) { 325 site = new CallSite(bci, method(search(atts, "method"))); 326 site.setCount(Integer.parseInt(search(atts, "count"))); 327 String receiver = atts.getValue("receiver"); 328 if (receiver != null) { 329 site.setReceiver(type(receiver)); 330 site.setReceiver_count(Integer.parseInt(search(atts, "receiver_count"))); 331 } 332 scopes.peek().add(site); 333 } else if (qname.equals("regalloc")) { 334 compile.setAttempts(Integer.parseInt(search(atts, "attempts"))); 335 } else if (qname.equals("inline_fail")) { 336 scopes.peek().last().setReason(search(atts, "reason")); 337 } else if (qname.equals("failure")) { 338 failureReason = search(atts, "reason"); 339 } else if (qname.equals("task_done")) { 340 compile.setEnd(Double.parseDouble(search(atts, "stamp"))); 341 if (Integer.parseInt(search(atts, "success")) == 0) { 342 compile.setFailureReason(failureReason); 343 } 344 } else if (qname.equals("make_not_entrant")) { 345 String id = makeId(atts); 346 NMethod nm = nmethods.get(id); 347 if (nm == null) throw new InternalError(); 348 LogEvent e = new MakeNotEntrantEvent(Double.parseDouble(search(atts, "stamp")), id, 349 atts.getValue("zombie") != null, nm); 350 events.add(e); 351 } else if (qname.equals("uncommon_trap")) { 352 String id = atts.getValue("compile_id"); 353 if (id != null) { 354 id = makeId(atts); 355 currentTrap = new UncommonTrapEvent(Double.parseDouble(search(atts, "stamp")), 356 id, 357 atts.getValue("reason"), 358 atts.getValue("action"), 359 Integer.parseInt(search(atts, "count", "0"))); 360 events.add(currentTrap); 361 } else { 362 // uncommon trap inserted during parsing. 363 // ignore for now 364 } 365 } else if (qname.equals("late_inline")) { 366 late_inline_scope = new Stack<CallSite>(); 367 site = new CallSite(-999, method(search(atts, "method"))); 368 late_inline_scope.push(site); 369 } else if (qname.equals("jvms")) { 370 // <jvms bci='4' method='java/io/DataInputStream readChar ()C' bytes='40' count='5815' iicount='20815'/> 371 if (currentTrap != null) { 372 currentTrap.addJVMS(atts.getValue("method"), Integer.parseInt(atts.getValue("bci"))); 373 } else if (late_inline_scope != null) { 374 bci = Integer.parseInt(search(atts, "bci")); 375 site = new CallSite(bci, method(search(atts, "method"))); 376 late_inline_scope.push(site); 377 } else { 378 // Ignore <eliminate_allocation type='667'>, 379 // <eliminate_lock lock='1'>, 380 // <replace_string_concat arguments='2' string_alloc='0' multiple='0'> 381 } 382 } else if (qname.equals("nmethod")) { 383 String id = makeId(atts); 384 NMethod nm = new NMethod(Double.parseDouble(search(atts, "stamp")), 385 id, 386 parseLong(atts.getValue("address")), 387 parseLong(atts.getValue("size"))); 388 nmethods.put(id, nm); 389 events.add(nm); 390 } else if (qname.equals("parse")) { 391 Method m = method(search(atts, "method")); 392 if (scopes.size() == 0) { 393 compile.setMethod(m); 394 scopes.push(site); 395 } else { 396 if (site.getMethod() == m) { 397 scopes.push(site); 398 } else if (scopes.peek().getCalls().size() > 2 && m == scopes.peek().last(-2).getMethod()) { 399 scopes.push(scopes.peek().last(-2)); 400 } else { 401 System.out.println(site.getMethod()); 402 System.out.println(m); 403 throw new InternalError("call site and parse don't match"); 404 } 405 } 406 } else if (qname.equals("parse_done")) { 407 CallSite call = scopes.pop(); 408 call.setEndNodes(Integer.parseInt(search(atts, "nodes", "1"))); 409 call.setTimeStamp(Double.parseDouble(search(atts, "stamp"))); 410 scopes.push(call); 411 } 412 } 413 414 @Override 415 public void endElement(String uri, 416 String localName, 417 String qname) { 418 if (qname.equals("parse")) { 419 indent -= 2; 420 scopes.pop(); 421 } else if (qname.equals("uncommon_trap")) { 422 currentTrap = null; 423 } else if (qname.equals("late_inline")) { 424 // Populate late inlining info. 425 426 // late_inline scopes are specified in reverse order: 427 // compiled method should be on top of stack. 428 CallSite caller = late_inline_scope.pop(); 429 Method m = compile.getMethod(); 430 if (m != caller.getMethod()) { 431 System.out.println(m); 432 System.out.println(caller.getMethod() + " bci: " + bci); 433 throw new InternalError("call site and late_inline info don't match"); 434 } 435 436 // late_inline contains caller+bci info, convert it 437 // to bci+callee info used by LogCompilation. 438 site = compile.getLateInlineCall(); 439 do { 440 bci = caller.getBci(); 441 // Next inlined call. 442 caller = late_inline_scope.pop(); 443 CallSite callee = new CallSite(bci, caller.getMethod()); 444 site.add(callee); 445 site = callee; 446 } while (!late_inline_scope.empty()); 447 448 if (caller.getBci() != -999) { 449 System.out.println(caller.getMethod()); 450 throw new InternalError("broken late_inline info"); 451 } 452 if (site.getMethod() != caller.getMethod()) { 453 System.out.println(site.getMethod()); 454 System.out.println(caller.getMethod()); 455 throw new InternalError("call site and late_inline info don't match"); 456 } 457 // late_inline is followed by parse with scopes.size() == 0, 458 // 'site' will be pushed to scopes. 459 late_inline_scope = null; 460 } else if (qname.equals("task")) { 461 types.clear(); 462 methods.clear(); 463 site = null; 464 } 465 } 466 467 @Override 468 public void warning(org.xml.sax.SAXParseException e) { 469 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 470 e.printStackTrace(); 471 } 472 473 @Override 474 public void error(org.xml.sax.SAXParseException e) { 475 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 476 e.printStackTrace(); 477 } 478 479 @Override 480 public void fatalError(org.xml.sax.SAXParseException e) { 481 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 482 e.printStackTrace(); 483 } 484 }