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 }