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 }