LogParser.java revision 5776:de6a9e811145
1/*
2 * Copyright (c) 2009, 2013, 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
30package com.sun.hotspot.tools.compiler;
31
32import java.io.FileReader;
33import java.io.Reader;
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.Comparator;
37import java.util.HashMap;
38import java.util.LinkedHashMap;
39import java.util.Stack;
40import javax.xml.parsers.SAXParser;
41import javax.xml.parsers.SAXParserFactory;
42import org.xml.sax.Attributes;
43import org.xml.sax.ErrorHandler;
44import org.xml.sax.InputSource;
45import org.xml.sax.helpers.DefaultHandler;
46
47public 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}
494