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 }