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 */
28
29 package com.sun.hotspot.tools.compiler;
30
31 import java.io.FileReader;
32 import java.io.PrintStream;
33 import java.io.Reader;
34 import java.util.ArrayDeque;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.HashMap;
39 import java.util.LinkedHashMap;
40 import java.util.Stack;
41 import javax.xml.parsers.SAXParser;
42 import javax.xml.parsers.SAXParserFactory;
43 import org.xml.sax.Attributes;
44 import org.xml.sax.ErrorHandler;
45 import org.xml.sax.InputSource;
46 import org.xml.sax.helpers.DefaultHandler;
47
48 public class LogParser extends DefaultHandler implements ErrorHandler, Constants {
49
50 static final HashMap<String, String> typeMap;
51 static {
52 typeMap = new HashMap<String, String>();
53 typeMap.put("[I", "int[]");
54 typeMap.put("[C", "char[]");
55 typeMap.put("[Z", "boolean[]");
56 typeMap.put("[L", "Object[]");
57 typeMap.put("[B", "byte[]");
58 }
59
60 static Comparator<LogEvent> sortByStart = new Comparator<LogEvent>() {
61
62 public int compare(LogEvent a, LogEvent b) {
63 double difference = (a.getStart() - b.getStart());
64 if (difference < 0) {
65 return -1;
66 }
67 if (difference > 0) {
68 return 1;
69 }
70 return 0;
71 }
72
73 @Override
74 public boolean equals(Object other) {
75 return false;
76 }
77
78 @Override
79 public int hashCode() {
80 return 7;
81 }
82 };
83 static Comparator<LogEvent> sortByNameAndStart = new Comparator<LogEvent>() {
84
85 public int compare(LogEvent a, LogEvent b) {
86 Compilation c1 = a.getCompilation();
87 Compilation c2 = b.getCompilation();
88 if (c1 != null && c2 != null) {
89 int result = c1.getMethod().toString().compareTo(c2.getMethod().toString());
90 if (result != 0) {
91 return result;
92 }
93 }
94 double difference = (a.getStart() - b.getStart());
95 if (difference < 0) {
96 return -1;
97 }
98 if (difference > 0) {
99 return 1;
100 }
101 return 0;
102 }
103
104 public boolean equals(Object other) {
105 return false;
106 }
107
108 @Override
109 public int hashCode() {
110 return 7;
111 }
112 };
113 static Comparator<LogEvent> sortByElapsed = new Comparator<LogEvent>() {
114
115 public int compare(LogEvent a, LogEvent b) {
116 double difference = (a.getElapsedTime() - b.getElapsedTime());
117 if (difference < 0) {
118 return -1;
119 }
120 if (difference > 0) {
121 return 1;
122 }
123 return 0;
124 }
125
126 @Override
127 public boolean equals(Object other) {
128 return false;
129 }
130
131 @Override
132 public int hashCode() {
133 return 7;
134 }
135 };
136
137 class Jvms {
138 Jvms(Method method, int bci) {
139 this.method = method;
140 this.bci = bci;
141 }
142 final public Method method;
143 final public int bci;
144 final public String toString() {
145 return "@" + bci + " " + method;
146 }
147 }
148
149 class LockElimination extends BasicLogEvent {
150
151 ArrayList<Jvms> jvms = new ArrayList<Jvms>(1);
152 final String kind;
153 final String classId;
154 final String tagName;
155 LockElimination(String tagName, double start, String id, String kind, String classId) {
156 super(start, id);
157 this.kind = kind;
158 this.classId = classId;
159 this.tagName = tagName;
160 }
161
162 @Override
163 public void print(PrintStream stream) {
164 stream.printf("%s %s %s %s %.3f ", getId(), tagName, kind, classId, getStart());
165 stream.print(jvms.toString());
166 stream.print("\n");
167 }
168
169 void addJVMS(Method method, int bci) {
170 jvms.add(new Jvms(method, bci));
171 }
172
173 }
174
175 private ArrayList<LogEvent> events = new ArrayList<LogEvent>();
176
177 private HashMap<String, String> types = new HashMap<String, String>();
178 private HashMap<String, Method> methods = new HashMap<String, Method>();
179 private LinkedHashMap<String, NMethod> nmethods = new LinkedHashMap<String, NMethod>();
180 private HashMap<String, Compilation> compiles = new HashMap<String, Compilation>();
181 private String failureReason;
182 private int bci;
183 private Stack<CallSite> scopes = new Stack<CallSite>();
184 private Compilation compile;
185 private CallSite site;
186 private CallSite methodHandleSite;
187 private Stack<Phase> phaseStack = new Stack<Phase>();
188 private LockElimination currentLockElimination;
189 private UncommonTrapEvent currentTrap;
190 private Stack<CallSite> lateInlineScope;
191 private boolean lateInlining;
192
193
194 long parseLong(String l) {
195 try {
196 return Long.decode(l).longValue();
197 } catch (NumberFormatException nfe) {
198 int split = l.length() - 8;
199 String s1 = "0x" + l.substring(split);
200 String s2 = l.substring(0, split);
201 long v1 = Long.decode(s1).longValue() & 0xffffffffL;
202 long v2 = (Long.decode(s2).longValue() & 0xffffffffL) << 32;
203 if (!l.equals("0x" + Long.toHexString(v1 + v2))) {
204 System.out.println(l);
205 System.out.println(s1);
206 System.out.println(s2);
207 System.out.println(v1);
208 System.out.println(v2);
209 System.out.println(Long.toHexString(v1 + v2));
210 throw new InternalError("bad conversion");
211 }
212 return v1 + v2;
213 }
214 }
215
216 public static ArrayList<LogEvent> parse(String file, boolean cleanup) throws Exception {
217 return parse(new FileReader(file), cleanup);
218 }
219
220 public static ArrayList<LogEvent> parse(Reader reader, boolean cleanup) throws Exception {
221 // Create the XML input factory
222 SAXParserFactory factory = SAXParserFactory.newInstance();
223
224 // Create the XML LogEvent reader
225 SAXParser p = factory.newSAXParser();
226
227 if (cleanup) {
228 // some versions of the log have slightly malformed XML, so clean it
229 // up before passing it to SAX
230 reader = new LogCleanupReader(reader);
231 }
232
233 LogParser log = new LogParser();
234 try {
235 p.parse(new InputSource(reader), log);
236 } catch (Throwable th) {
237 th.printStackTrace();
238 // Carry on with what we've got...
239 }
240
241 // Associate compilations with their NMethods
242 for (NMethod nm : log.nmethods.values()) {
243 Compilation c = log.compiles.get(nm.getId());
244 nm.setCompilation(c);
245 // Native wrappers for methods don't have a compilation
246 if (c != null) {
247 c.setNMethod(nm);
248 }
249 }
250
251 // Initially we want the LogEvent log sorted by timestamp
252 Collections.sort(log.events, sortByStart);
253
254 return log.events;
255 }
256
257 String search(Attributes attr, String name) {
258 String result = attr.getValue(name);
259 if (result != null) {
260 return result;
261 } else {
262 throw new InternalError("can't find " + name);
263 }
264 }
265
266 String search(Attributes attr, String name, String defaultValue) {
267 String result = attr.getValue(name);
268 if (result != null) {
269 return result;
270 }
271 return defaultValue;
272 }
273 int indent = 0;
274
275 String type(String id) {
276 String result = types.get(id);
277 if (result == null) {
278 throw new InternalError(id);
279 }
280 String remapped = typeMap.get(result);
281 if (remapped != null) {
282 return remapped;
283 }
284 return result;
285 }
286
287 void type(String id, String name) {
288 assert type(id) == null;
289 types.put(id, name);
290 }
291
292 Method method(String id) {
293 Method result = methods.get(id);
294 if (result == null) {
295 throw new InternalError(id);
296 }
297 return result;
298 }
299
300 public String makeId(Attributes atts) {
301 String id = atts.getValue("compile_id");
302 String kind = atts.getValue("kind");
303 if (kind != null && kind.equals("osr")) {
304 id += "%";
305 }
306 return id;
307 }
308
309 @Override
310 public void startElement(String uri,
311 String localName,
312 String qname,
313 Attributes atts) {
314 if (qname.equals("phase")) {
315 Phase p = new Phase(search(atts, "name"),
316 Double.parseDouble(search(atts, "stamp")),
317 Integer.parseInt(search(atts, "nodes", "0")),
318 Integer.parseInt(search(atts, "live", "0")));
319 phaseStack.push(p);
320 } else if (qname.equals("phase_done")) {
321 Phase p = phaseStack.pop();
322 String phaseName = search(atts, "name", null);
323 if (phaseName != null && !p.getId().equals(phaseName)) {
324 System.out.println("phase: " + p.getId());
325 throw new InternalError("phase name mismatch");
326 }
327 p.setEnd(Double.parseDouble(search(atts, "stamp")));
328 p.setEndNodes(Integer.parseInt(search(atts, "nodes", "0")));
329 p.setEndLiveNodes(Integer.parseInt(search(atts, "live", "0")));
330 compile.getPhases().add(p);
331 } else if (qname.equals("task")) {
332 compile = new Compilation(Integer.parseInt(search(atts, "compile_id", "-1")));
333 compile.setStart(Double.parseDouble(search(atts, "stamp")));
334 compile.setICount(search(atts, "count", "0"));
335 compile.setBCount(search(atts, "backedge_count", "0"));
336
337 String method = atts.getValue("method");
338 int space = method.indexOf(' ');
339 method = method.substring(0, space) + "::" +
340 method.substring(space + 1, method.indexOf(' ', space + 1) + 1);
341 String compiler = atts.getValue("compiler");
342 if (compiler == null) {
343 compiler = "";
344 }
345 String kind = atts.getValue("compile_kind");
346 if (kind == null) {
347 kind = "normal";
348 }
349 if (kind.equals("osr")) {
350 compile.setOsr(true);
351 compile.setOsr_bci(Integer.parseInt(search(atts, "osr_bci")));
352 } else if (kind.equals("c2i")) {
353 compile.setSpecial("--- adapter " + method);
354 } else {
355 compile.setSpecial(compile.getId() + " " + method + " (0 bytes)");
356 }
357 events.add(compile);
358 compiles.put(makeId(atts), compile);
359 site = compile.getCall();
360 } else if (qname.equals("type")) {
361 type(search(atts, "id"), search(atts, "name"));
362 } else if (qname.equals("bc")) {
363 bci = Integer.parseInt(search(atts, "bci"));
364 } else if (qname.equals("klass")) {
365 type(search(atts, "id"), search(atts, "name"));
366 } else if (qname.equals("method")) {
367 String id = search(atts, "id");
368 Method m = new Method();
369 m.setHolder(type(search(atts, "holder")));
370 m.setName(search(atts, "name"));
371 m.setReturnType(type(search(atts, "return")));
372 m.setArguments(search(atts, "arguments", "void"));
373
374 if (search(atts, "unloaded", "0").equals("0")) {
375 m.setBytes(search(atts, "bytes"));
376 m.setIICount(search(atts, "iicount"));
377 m.setFlags(search(atts, "flags"));
378 }
379 methods.put(id, m);
380 } else if (qname.equals("call")) {
381 if (methodHandleSite != null) {
382 methodHandleSite = null;
383 }
384 Method m = method(search(atts, "method"));
385 if (lateInlining && scopes.size() == 0) {
386 // re-attempting already seen call site (late inlining for MH invokes)
387 if (m != site.getMethod()) {
388 if (bci != site.getBci()) {
389 System.out.println(m + " bci: " + bci);
390 System.out.println(site.getMethod() + " bci: " + site.getBci());
391 throw new InternalError("bci mismatch after late inlining");
392 }
393 site.setMethod(m);
394 }
395 } else {
396 site = new CallSite(bci, m);
397 }
398 site.setCount(Integer.parseInt(search(atts, "count", "0")));
399 String receiver = atts.getValue("receiver");
400 if (receiver != null) {
401 site.setReceiver(type(receiver));
402 site.setReceiver_count(Integer.parseInt(search(atts, "receiver_count")));
403 }
404 int methodHandle = Integer.parseInt(search(atts, "method_handle_intrinsic", "0"));
405 if (lateInlining && scopes.size() == 0) {
406 // The call was added before this round of late inlining
407 } else if (methodHandle == 0) {
408 scopes.peek().add(site);
409 } else {
410 // method handle call site can be followed by another
411 // call (in case it is inlined). If that happens we
412 // discard the method handle call site. So we keep
413 // track of it but don't add it to the list yet.
414 methodHandleSite = site;
415 }
416 } else if (qname.equals("regalloc")) {
417 compile.setAttempts(Integer.parseInt(search(atts, "attempts")));
418 } else if (qname.equals("inline_fail")) {
419 if (methodHandleSite != null) {
420 scopes.peek().add(methodHandleSite);
421 methodHandleSite = null;
422 }
423 if (lateInlining && scopes.size() == 0) {
424 site.setReason(search(atts, "reason"));
425 lateInlining = false;
426 } else {
427 scopes.peek().last().setReason(search(atts, "reason"));
428 }
429 } else if (qname.equals("inline_success")) {
430 if (methodHandleSite != null) {
431 throw new InternalError("method handle site should have been replaced");
432 }
433 if (lateInlining && scopes.size() == 0) {
434 site.setReason(null);
435 }
436 } else if (qname.equals("failure")) {
437 failureReason = search(atts, "reason");
438 } else if (qname.equals("task_done")) {
439 compile.setEnd(Double.parseDouble(search(atts, "stamp")));
440 if (Integer.parseInt(search(atts, "success")) == 0) {
441 compile.setFailureReason(failureReason);
442 failureReason = null;
443 }
444 } else if (qname.equals("make_not_entrant")) {
445 String id = makeId(atts);
446 NMethod nm = nmethods.get(id);
447 if (nm == null) throw new InternalError();
448 LogEvent e = new MakeNotEntrantEvent(Double.parseDouble(search(atts, "stamp")), id,
449 atts.getValue("zombie") != null, nm);
450 events.add(e);
451 } else if (qname.equals("uncommon_trap")) {
452 String id = atts.getValue("compile_id");
453 if (id != null) {
454 id = makeId(atts);
455 currentTrap = new UncommonTrapEvent(Double.parseDouble(search(atts, "stamp")),
456 id,
457 atts.getValue("reason"),
458 atts.getValue("action"),
459 Integer.parseInt(search(atts, "count", "0")));
460 events.add(currentTrap);
461 } else {
462 // uncommon trap inserted during parsing.
463 // ignore for now
464 }
465 } else if (qname.startsWith("eliminate_lock")) {
466 String id = atts.getValue("compile_id");
467 if (id != null) {
468 id = makeId(atts);
469 String kind = atts.getValue("kind");
470 String classId = atts.getValue("class_id");
471 currentLockElimination = new LockElimination(qname, Double.parseDouble(search(atts, "stamp")), id, kind, classId);
472 events.add(currentLockElimination);
473 }
474 } else if (qname.equals("late_inline")) {
475 long inlineId = 0;
476 try {
477 Long.parseLong(search(atts, "inline_id"));
478 } catch (InternalError ex) {
479 // Log files from older hotspots may lack inline_id,
480 // and zero is an acceptable substitute that allows processing to continue.
481 }
482 lateInlineScope = new Stack<CallSite>();
483 site = new CallSite(-999, method(search(atts, "method")));
484 site.setInlineId(inlineId);
485 lateInlineScope.push(site);
486 } else if (qname.equals("jvms")) {
487 // <jvms bci='4' method='java/io/DataInputStream readChar ()C' bytes='40' count='5815' iicount='20815'/>
488 if (currentTrap != null) {
489 currentTrap.addJVMS(atts.getValue("method"), Integer.parseInt(atts.getValue("bci")));
490 } else if (currentLockElimination != null) {
491 currentLockElimination.addJVMS(method(atts.getValue("method")), Integer.parseInt(atts.getValue("bci")));
492 } else if (lateInlineScope != null) {
493 bci = Integer.parseInt(search(atts, "bci"));
494 site = new CallSite(bci, method(search(atts, "method")));
495 lateInlineScope.push(site);
496 } else {
497 // Ignore <eliminate_allocation type='667'>,
498 // <replace_string_concat arguments='2' string_alloc='0' multiple='0'>
499 }
500 } else if (qname.equals("inline_id")) {
501 if (methodHandleSite != null) {
502 throw new InternalError("method handle site should have been replaced");
503 }
504 long id = Long.parseLong(search(atts, "id"));
505 site.setInlineId(id);
506 } else if (qname.equals("nmethod")) {
507 String id = makeId(atts);
508 NMethod nm = new NMethod(Double.parseDouble(search(atts, "stamp")),
509 id,
510 parseLong(atts.getValue("address")),
511 parseLong(atts.getValue("size")));
512 nmethods.put(id, nm);
513 events.add(nm);
514 } else if (qname.equals("parse")) {
515 if (failureReason != null && scopes.size() == 0 && !lateInlining) {
516 failureReason = null;
517 compile.reset();
518 site = compile.getCall();
519 }
520
521 if (methodHandleSite != null) {
522 throw new InternalError("method handle site should have been replaced");
523 }
524 Method m = method(search(atts, "method"));
525 if (lateInlining && scopes.size() == 0) {
526 if (site.getMethod() != m) {
527 System.out.println(site.getMethod());
528 System.out.println(m);
529 throw new InternalError("Unexpected method mismatch during late inlining");
530 }
531 }
532 if (scopes.size() == 0 && !lateInlining) {
533 compile.setMethod(m);
534 scopes.push(site);
535 } else {
536 if (site.getMethod() == m) {
537 scopes.push(site);
538 } else if (scopes.peek().getCalls().size() > 2 && m == scopes.peek().last(-2).getMethod()) {
539 scopes.push(scopes.peek().last(-2));
540 } else {
541 // C1 prints multiple method tags during inlining when it narrows method being inlinied.
542 // Example:
543 // ...
544 // <method id="813" holder="694" name="toString" return="695" flags="1" bytes="36" iicount="1"/>
545 // <call method="813" instr="invokevirtual"/>
546 // <inline_success reason="receiver is statically known"/>
547 // <method id="814" holder="792" name="toString" return="695" flags="1" bytes="5" iicount="3"/>
548 // <parse method="814">
549 // ...
550 site.setMethod(m);
551 scopes.push(site);
552 }
553 }
554 } else if (qname.equals("parse_done")) {
555 CallSite call = scopes.pop();
556 call.setEndNodes(Integer.parseInt(search(atts, "nodes", "0")));
557 call.setEndLiveNodes(Integer.parseInt(search(atts, "live", "0")));
558 call.setTimeStamp(Double.parseDouble(search(atts, "stamp")));
559 scopes.push(call);
560 }
561 }
562
563 @Override
564 public void endElement(String uri,
565 String localName,
566 String qname) {
567 if (qname.equals("parse")) {
568 indent -= 2;
569 scopes.pop();
570 if (scopes.size() == 0) {
571 lateInlining = false;
572 }
573 } else if (qname.equals("uncommon_trap")) {
574 currentTrap = null;
575 } else if (qname.startsWith("eliminate_lock")) {
576 currentLockElimination = null;
577 } else if (qname.equals("late_inline")) {
578 // Populate late inlining info.
579 if (scopes.size() != 0) {
580 throw new InternalError("scopes should be empty for late inline");
581 }
582 // late inline scopes are specified in reverse order:
583 // compiled method should be on top of stack.
584 CallSite caller = lateInlineScope.pop();
585 Method m = compile.getMethod();
586 if (m != caller.getMethod()) {
587 System.err.println(m);
588 System.err.println(caller.getMethod() + " bci: " + bci);
589 throw new InternalError("call site and late_inline info don't match");
590 }
591
592 // late_inline contains caller+bci info, convert it
593 // to bci+callee info used by LogCompilation.
594 CallSite lateInlineSite = compile.getLateInlineCall();
595 ArrayDeque<CallSite> thisCallScopes = new ArrayDeque<CallSite>();
596 do {
597 bci = caller.getBci();
598 // Next inlined call.
599 caller = lateInlineScope.pop();
600 CallSite callee = new CallSite(bci, caller.getMethod());
601 callee.setInlineId(caller.getInlineId());
602 thisCallScopes.addLast(callee);
603 lateInlineSite.add(callee);
604 lateInlineSite = callee;
605 } while (!lateInlineScope.empty());
606
607 site = compile.getCall().findCallSite(thisCallScopes);
608 if (site == null) {
609 System.out.println("call scopes:");
610 for (CallSite c : thisCallScopes) {
611 System.out.println(c.getMethod() + " " + c.getBci() + " " + c.getInlineId());
612 }
613 CallSite c = thisCallScopes.getLast();
614 if (c.getInlineId() != 0) {
615 System.out.println("Looking for call site in entire tree:");
616 ArrayDeque<CallSite> stack = compile.getCall().findCallSite2(c);
617 for (CallSite c2 : stack) {
618 System.out.println(c2.getMethod() + " " + c2.getBci() + " " + c2.getInlineId());
619 }
620 }
621 System.out.println(caller.getMethod() + " bci: " + bci);
622 throw new InternalError("couldn't find call site");
623 }
624 lateInlining = true;
625
626 if (caller.getBci() != -999) {
627 System.out.println(caller.getMethod());
628 throw new InternalError("broken late_inline info");
629 }
630 if (site.getMethod() != caller.getMethod()) {
631 if (site.getInlineId() == caller.getInlineId()) {
632 site.setMethod(caller.getMethod());
633 } else {
634 System.out.println(site.getMethod());
635 System.out.println(caller.getMethod());
636 throw new InternalError("call site and late_inline info don't match");
637 }
638 }
639 // late_inline is followed by parse with scopes.size() == 0,
640 // 'site' will be pushed to scopes.
641 lateInlineScope = null;
642 } else if (qname.equals("task")) {
643 types.clear();
644 methods.clear();
645 site = null;
646 }
647 }
648
649 @Override
650 public void warning(org.xml.sax.SAXParseException e) {
651 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber());
652 e.printStackTrace();
653 }
654
655 @Override
656 public void error(org.xml.sax.SAXParseException e) {
657 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber());
658 e.printStackTrace();
659 }
660
661 @Override
662 public void fatalError(org.xml.sax.SAXParseException e) {
663 System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber());
664 e.printStackTrace();
665 }
666 }
|
|