1 /* 2 * Copyright (c) 2009, 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 150 long parseLong(String l) { 151 try { 152 return Long.decode(l).longValue(); 153 } catch (NumberFormatException nfe) { 154 int split = l.length() - 8; 155 String s1 = "0x" + l.substring(split); 156 String s2 = l.substring(0, split); 157 long v1 = Long.decode(s1).longValue() & 0xffffffffL; 158 long v2 = (Long.decode(s2).longValue() & 0xffffffffL) << 32; 159 if (!l.equals("0x" + Long.toHexString(v1 + v2))) { 160 System.out.println(l); 161 System.out.println(s1); 162 System.out.println(s2); 163 System.out.println(v1); 164 System.out.println(v2); 165 System.out.println(Long.toHexString(v1 + v2)); 166 throw new InternalError("bad conversion"); 167 } 168 return v1 + v2; 169 } 170 } 171 172 public static ArrayList<LogEvent> parse(String file, boolean cleanup) throws Exception { 173 return parse(new FileReader(file), cleanup); 174 } 175 176 public static ArrayList<LogEvent> parse(Reader reader, boolean cleanup) throws Exception { 177 // Create the XML input factory 178 SAXParserFactory factory = SAXParserFactory.newInstance(); 179 180 // Create the XML LogEvent reader 181 SAXParser p = factory.newSAXParser(); 182 183 if (cleanup) { 184 // some versions of the log have slightly malformed XML, so clean it 185 // up before passing it to SAX 186 reader = new LogCleanupReader(reader); 187 } 188 189 LogParser log = new LogParser(); 190 p.parse(new InputSource(reader), log); 191 192 // Associate compilations with their NMethods 193 for (NMethod nm : log.nmethods.values()) { 194 Compilation c = log.compiles.get(nm.getId()); 195 nm.setCompilation(c); 196 // Native wrappers for methods don't have a compilation 197 if (c != null) { 198 c.setNMethod(nm); 199 } 200 } 201 202 // Initially we want the LogEvent log sorted by timestamp 203 Collections.sort(log.events, sortByStart); 204 205 return log.events; 206 } 207 208 String search(Attributes attr, String name) { 209 return search(attr, name, null); 210 } 211 212 String search(Attributes attr, String name, String defaultValue) { 213 String result = attr.getValue(name); 214 if (result != null) { 215 return result; 216 } 217 if (defaultValue != null) { 218 return defaultValue; 219 } 220 for (int i = 0; i < attr.getLength(); i++) { 221 System.out.println(attr.getQName(i) + " " + attr.getValue(attr.getQName(i))); 222 } 223 throw new InternalError("can't find " + name); 224 } 225 int indent = 0; 226 String compile_id; 227 228 String type(String id) { 229 String result = types.get(id); 230 if (result == null) { 231 throw new InternalError(id); 232 } 233 String remapped = typeMap.get(result); 234 if (remapped != null) { 235 return remapped; 236 } 237 return result; 238 } 239 240 void type(String id, String name) { 241 assert type(id) == null; 242 types.put(id, name); 243 } 244 245 Method method(String id) { 246 Method result = methods.get(id); 247 if (result == null) { 248 throw new InternalError(id); 249 } 250 return result; 251 } 252 253 public String makeId(Attributes atts) { 254 String id = atts.getValue("compile_id"); 255 String kind = atts.getValue("kind"); 256 if (kind != null && kind.equals("osr")) { 257 id += "%"; 258 } 259 return id; 260 } 261 262 @Override 263 public void startElement(String uri, 264 String localName, 265 String qname, 266 Attributes atts) { 267 if (qname.equals("phase")) { 268 Phase p = new Phase(search(atts, "name"), 269 Double.parseDouble(search(atts, "stamp")), 270 Integer.parseInt(search(atts, "nodes"))); 271 phaseStack.push(p); 272 } else if (qname.equals("phase_done")) { 273 Phase p = phaseStack.pop(); 274 p.setEndNodes(Integer.parseInt(search(atts, "nodes"))); 275 p.setEnd(Double.parseDouble(search(atts, "stamp"))); 276 compile.getPhases().add(p); 277 } else if (qname.equals("task")) { 278 compile = new Compilation(Integer.parseInt(search(atts, "compile_id", "-1"))); 279 compile.setStart(Double.parseDouble(search(atts, "stamp"))); 280 compile.setICount(search(atts, "count", "0")); 281 compile.setBCount(search(atts, "backedge_count", "0")); 282 283 String method = atts.getValue("method"); 284 int space = method.indexOf(' '); 285 method = method.substring(0, space) + "::" + 286 method.substring(space + 1, method.indexOf(' ', space + 1) + 1); 287 String compiler = atts.getValue("compiler"); 288 if (compiler == null) { 289 compiler = ""; 290 } 291 String kind = atts.getValue("compile_kind"); 292 if (kind == null) { 293 kind = "normal"; 294 } 295 if (kind.equals("osr")) { 296 compile.setOsr(true); 297 compile.setOsr_bci(Integer.parseInt(search(atts, "osr_bci"))); 298 } else if (kind.equals("c2i")) { 299 compile.setSpecial("--- adapter " + method); 300 } else { 301 compile.setSpecial(compile.getId() + " " + method + " (0 bytes)"); 302 } 303 events.add(compile); 304 compiles.put(makeId(atts), compile); 305 } else if (qname.equals("type")) { 306 type(search(atts, "id"), search(atts, "name")); 307 } else if (qname.equals("bc")) { 308 bci = Integer.parseInt(search(atts, "bci")); 309 } else if (qname.equals("klass")) { 310 type(search(atts, "id"), search(atts, "name")); 311 } else if (qname.equals("method")) { 312 String id = search(atts, "id"); 313 Method m = new Method(); 314 m.setHolder(type(search(atts, "holder"))); 315 m.setName(search(atts, "name")); 316 m.setReturnType(type(search(atts, "return"))); 317 m.setArguments(search(atts, "arguments", "void")); 318 m.setBytes(search(atts, "bytes")); 319 m.setIICount(search(atts, "iicount")); 320 m.setFlags(search(atts, "flags")); 321 methods.put(id, m); 322 } else if (qname.equals("call")) { 323 site = new CallSite(bci, method(search(atts, "method"))); 324 site.setCount(Integer.parseInt(search(atts, "count"))); 325 String receiver = atts.getValue("receiver"); 326 if (receiver != null) { 327 site.setReceiver(type(receiver)); 328 site.setReceiver_count(Integer.parseInt(search(atts, "receiver_count"))); 329 } 330 scopes.peek().add(site); 331 } else if (qname.equals("regalloc")) { 332 compile.setAttempts(Integer.parseInt(search(atts, "attempts"))); 333 } else if (qname.equals("inline_fail")) { 334 scopes.peek().last().setReason(search(atts, "reason")); 335 } else if (qname.equals("failure")) { 336 failureReason = search(atts, "reason"); 337 } else if (qname.equals("task_done")) { 338 compile.setEnd(Double.parseDouble(search(atts, "stamp"))); 339 if (Integer.parseInt(search(atts, "success")) == 0) { 340 compile.setFailureReason(failureReason); 341 } 342 } else if (qname.equals("make_not_entrant")) { 343 String id = makeId(atts); 344 NMethod nm = nmethods.get(id); 345 if (nm == null) throw new InternalError(); 346 LogEvent e = new MakeNotEntrantEvent(Double.parseDouble(search(atts, "stamp")), id, 347 atts.getValue("zombie") != null, nm); 348 events.add(e); 349 } else if (qname.equals("uncommon_trap")) { 350 String id = atts.getValue("compile_id"); 351 if (id != null) { 352 id = makeId(atts); 353 currentTrap = new UncommonTrapEvent(Double.parseDouble(search(atts, "stamp")), 354 id, 355 atts.getValue("reason"), 356 atts.getValue("action"), 357 Integer.parseInt(search(atts, "count", "0"))); 358 events.add(currentTrap); 359 } else { 360 // uncommon trap inserted during parsing. 361 // ignore for now 362 } 363 } else if (qname.equals("jvms")) { 364 // <jvms bci='4' method='java/io/DataInputStream readChar ()C' bytes='40' count='5815' iicount='20815'/> 365 if (currentTrap != null) { 366 currentTrap.addJVMS(atts.getValue("method"), Integer.parseInt(atts.getValue("bci"))); 367 } else { 368 // Ignore <eliminate_allocation type='667'> and <eliminate_lock lock='1'> 369 } 370 } else if (qname.equals("nmethod")) { 371 String id = makeId(atts); 372 NMethod nm = new NMethod(Double.parseDouble(search(atts, "stamp")), 373 id, 374 parseLong(atts.getValue("address")), 375 parseLong(atts.getValue("size"))); 376 nmethods.put(id, nm); 377 events.add(nm); 378 } else if (qname.equals("parse")) { 379 Method m = method(search(atts, "method")); 380 if (scopes.size() == 0) { 381 compile.setMethod(m); 382 scopes.push(compile.getCall()); 383 } else { 384 if (site.getMethod() == m) { 385 scopes.push(site); 386 } else if (scopes.peek().getCalls().size() > 2 && m == scopes.peek().last(-2).getMethod()) { 387 scopes.push(scopes.peek().last(-2)); 388 } else { 389 System.out.println(site.getMethod()); 390 System.out.println(m); 391 throw new InternalError("call site and parse don't match"); 392 } 393 } 394 } else if (qname.equals("parse_done")) { 395 CallSite call = scopes.pop(); 396 call.setEndNodes(Integer.parseInt(search(atts, "nodes"))); 397 call.setTimeStamp(Double.parseDouble(search(atts, "stamp"))); 398 scopes.push(call); 399 } 400 } 401 402 @Override 403 public void endElement(String uri, 404 String localName, 405 String qname) { 406 if (qname.equals("parse")) { 407 indent -= 2; 408 scopes.pop(); 409 } else if (qname.equals("uncommon_trap")) { 410 currentTrap = null; 411 } else if (qname.equals("task")) { 412 types.clear(); 413 methods.clear(); 414 site = null; 415 } 416 } 417 418 @Override 419 public void warning(org.xml.sax.SAXParseException e) { 420 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 421 e.printStackTrace(); 422 } 423 424 @Override 425 public void error(org.xml.sax.SAXParseException e) { 426 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 427 e.printStackTrace(); 428 } 429 430 @Override 431 public void fatalError(org.xml.sax.SAXParseException e) { 432 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); 433 e.printStackTrace(); 434 } 435 }