6900757: minor bug fixes to LogCompilation tool

Improve internal error reporting (point to XML element causing trouble); fix comparator for sorting by name and start; make tool more robust wrt. incorrect options and files not found; make inlining decision output more clear; adopt uncommon traps history printing; properly mention compiler in generated logs; add options for printing time stamps and omitting compilation IDs; add option for comparing compilation logs; overall code cleanup and API documentation

Reviewed-by: kvn, vlivanov
This commit is contained in:
Michael Haupt 2015-03-31 21:46:44 +02:00
parent 2c695decc2
commit 590ec77481
16 changed files with 1780 additions and 339 deletions

View File

@ -10,3 +10,4 @@
.igv.log
^.hgtip
.DS_Store
\.class$

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -62,7 +62,7 @@ all: logc.jar
logc.jar: filelist manifest.mf
@mkdir -p $(OUTPUT_DIR)
$(JAVAC) -source 1.5 -deprecation -sourcepath $(SRC_DIR) -d $(OUTPUT_DIR) @filelist
$(JAVAC) -deprecation -sourcepath $(SRC_DIR) -d $(OUTPUT_DIR) @filelist
$(JAR) cvfm logc.jar manifest.mf -C $(OUTPUT_DIR) com
.PHONY: filelist

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,14 +27,29 @@ package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
/**
*
* @author never
* Provide basic data structures and behaviour for {@link LogEvent}s.
*/
public abstract class BasicLogEvent implements LogEvent {
/**
* The event's ID. This is a number; we represent it as a string for
* convenience.
*/
protected final String id;
/**
* The event's start time.
*/
protected final double start;
/**
* The event's end time.
*/
protected double end;
/**
* The compilation during which this event was signalled.
*/
protected Compilation compilation;
BasicLogEvent(double start, String id) {
@ -43,33 +58,37 @@ public abstract class BasicLogEvent implements LogEvent {
this.id = id;
}
public double getStart() {
public final double getStart() {
return start;
}
public double getEnd() {
public final double getEnd() {
return end;
}
public void setEnd(double end) {
public final void setEnd(double end) {
this.end = end;
}
public double getElapsedTime() {
public final double getElapsedTime() {
return ((int) ((getEnd() - getStart()) * 1000)) / 1000.0;
}
public String getId() {
public final String getId() {
return id;
}
public Compilation getCompilation() {
public final Compilation getCompilation() {
return compilation;
}
/**
* Set the compilation for this event. This is not a {@code final} method
* as it is overridden in {@link UncommonTrapEvent}.
*/
public void setCompilation(Compilation compilation) {
this.compilation = compilation;
}
abstract public void print(PrintStream stream);
abstract public void print(PrintStream stream, boolean printID);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,41 +29,119 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
/**
* Representation of a compilation scope in a compilation log. This class is a
* hybrid: its instances can represent original scopes of methods being
* compiled, but are also used to represent call sites in given methods.
*/
public class CallSite {
/**
* The index of the call in the caller. This will be 0 if this instance
* represents a compilation root.
*/
private int bci;
/**
* The method that is called at this call site. This will be {@code null}
* if this instance represents a compilation root.
*/
private Method method;
/**
* The invocation count for this call site.
*/
private int count;
/**
* The receiver type of the call represented by this instance, if known.
*/
private String receiver;
/**
* In case the {@linkplain receiver receiver type} of the call represented
* by this instance is known, this is how often the type was encountered.
*/
private int receiver_count;
/**
* The reason for a success or failure of an inlining operation at this
* call site.
*/
private String reason;
/**
* A list of all calls in this compilation scope.
*/
private List<CallSite> calls;
/**
* Number of nodes in the graph at the end of parsing this compilation
* scope.
*/
private int endNodes;
/**
* Number of live nodes in the graph at the end of parsing this compilation
* scope.
*/
private int endLiveNodes;
/**
* Time in seconds since VM startup at which parsing this compilation scope
* ended.
*/
private double timeStamp;
/**
* The inline ID in case the call represented by this instance is inlined,
* 0 otherwise.
*/
private long inlineId;
CallSite() {
}
/**
* List of uncommon traps in this compilation scope.
*/
private List<UncommonTrap> traps;
/**
* Default constructor: used to create an instance that represents the top
* scope of a compilation.
*/
CallSite() {}
/**
* Constructor to create an instance that represents an actual method call.
*/
CallSite(int bci, Method m) {
this.bci = bci;
this.method = m;
}
/**
* Add a call site to the compilation scope represented by this instance.
*/
void add(CallSite site) {
if (getCalls() == null) {
setCalls(new ArrayList<CallSite>());
calls = new ArrayList<>();
}
getCalls().add(site);
}
/**
* Return the last of the {@linkplain #getCalls() call sites} in this
* compilation scope.
*/
CallSite last() {
return last(-1);
return getCalls().get(getCalls().size() - 1);
}
CallSite last(int fromEnd) {
return getCalls().get(getCalls().size() + fromEnd);
/**
* Return the last-but-one of the {@linkplain #getCalls() call sites} in
* this compilation scope.
*/
CallSite lastButOne() {
return getCalls().get(getCalls().size() - 2);
}
public String toString() {
@ -84,7 +162,7 @@ public class CallSite {
}
public void print(PrintStream stream) {
print(stream, 0);
print(stream, 0, true, false);
}
void emit(PrintStream stream, int indent) {
@ -92,21 +170,14 @@ public class CallSite {
stream.print(' ');
}
}
private static boolean compat = true;
public void print(PrintStream stream, int indent) {
public void print(PrintStream stream, int indent, boolean printInlining, boolean printUncommonTraps) {
emit(stream, indent);
String m = getMethod().getHolder() + "::" + getMethod().getName();
if (getReason() == null) {
stream.print(" @ " + getBci() + " " + m + " (" + getMethod().getBytes() + " bytes)");
} else {
if (isCompat()) {
stream.print(" @ " + getBci() + " " + m + " " + getReason());
} else {
stream.print("- @ " + getBci() + " " + m +
" (" + getMethod().getBytes() + " bytes) " + getReason());
}
stream.print(" @ " + getBci() + " " + m + " " + getReason());
}
stream.printf(" (end time: %6.4f", getTimeStamp());
if (getEndNodes() > 0) {
@ -116,13 +187,16 @@ public class CallSite {
if (getReceiver() != null) {
emit(stream, indent + 4);
// stream.println("type profile " + method.holder + " -> " + receiver + " (" +
// receiver_count + "/" + count + "," + (receiver_count * 100 / count) + "%)");
stream.println("type profile " + getMethod().getHolder() + " -> " + getReceiver() + " (" +
(getReceiverCount() * 100 / getCount()) + "%)");
}
if (getCalls() != null) {
if (printInlining && getCalls() != null) {
for (CallSite site : getCalls()) {
site.print(stream, indent + 2, printInlining, printUncommonTraps);
}
}
if (printUncommonTraps && getTraps() != null) {
for (UncommonTrap site : getTraps()) {
site.print(stream, indent + 2);
}
}
@ -180,16 +254,15 @@ public class CallSite {
return calls;
}
public void setCalls(List<CallSite> calls) {
this.calls = calls;
public List<UncommonTrap> getTraps() {
return traps;
}
public static boolean isCompat() {
return compat;
}
public static void setCompat(boolean aCompat) {
compat = aCompat;
void add(UncommonTrap e) {
if (traps == null) {
traps = new ArrayList<UncommonTrap>();
}
traps.add(e);
}
void setEndNodes(int n) {
@ -216,21 +289,30 @@ public class CallSite {
return timeStamp;
}
/**
* Check whether this call site matches another. Every late inline call
* site has a unique inline ID. If the call site we're looking for has one,
* then use it; otherwise rely on method name and byte code index.
*/
private boolean matches(CallSite other) {
// Every late inline call site has a unique inline id. If the
// call site we're looking for has one then use it other rely
// on method name and bci.
if (other.inlineId != 0) {
return inlineId == other.inlineId;
}
return method.equals(other.method) && bci == other.bci;
}
/**
* Locate a late inline call site: find, in this instance's
* {@linkplain #calls call sites}, the one furthest down the given call
* stack.
*
* Multiple chains of identical call sites with the same method name / bci
* combination are possible, so we have to try them all until we find the
* late inline call site that has a matching inline ID.
*
* @return a matching call site, or {@code null} if none was found.
*/
public CallSite findCallSite(ArrayDeque<CallSite> sites) {
// Locate a late inline call site. Multiple chains of
// identical call sites with the same method name/bci are
// possible so we have to try them all until we find the late
// inline call site that has a matching inline id.
if (calls == null) {
return null;
}
@ -253,6 +335,11 @@ public class CallSite {
return null;
}
/**
* Locate a late inline call site in the tree spanned by all this instance's
* {@linkplain #calls call sites}, and return the sequence of call sites
* (scopes) leading to that late inline call site.
*/
public ArrayDeque<CallSite> findCallSite2(CallSite site) {
if (calls == null) {
return null;
@ -260,7 +347,7 @@ public class CallSite {
for (CallSite c : calls) {
if (c.matches(site)) {
ArrayDeque<CallSite> stack = new ArrayDeque<CallSite>();
ArrayDeque<CallSite> stack = new ArrayDeque<>();
stack.push(c);
return stack;
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,22 +27,94 @@ package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
import java.util.ArrayList;
/**
* One particular compilation, represented in the compilation log file as a
* {@code task} element.
*/
public class Compilation implements LogEvent {
/**
* The compilation ID.
*/
private int id;
/**
* Whether this is a compilation for on-stack replacement (OSR).
*/
private boolean osr;
/**
* The method being compiled.
*/
private Method method;
/**
* The {@linkplain CallSite scope} of this compilation. This is created as
* an empty {@link CallSite} instance, to be filled with data (and
* meaning) later on.
*/
private CallSite call = new CallSite();
/**
* In case a {@code late_inline} event occurs during the compilation, this
* field holds the information about it.
*/
private CallSite lateInlineCall = new CallSite();
private int osrBci;
/**
* The bytecode instruction index for on-stack replacement compilations; -1
* if this is not an OSR compilation.
*/
private int bci;
/**
* The method under compilation's invocation count.
*/
private String icount;
/**
* The method under compilation's backedge count.
*/
private String bcount;
/**
* Additional information for special compilations (e.g., adapters).
*/
private String special;
/**
* The name of the compiler performing this compilation.
*/
private String compiler;
/**
* Start time stamp.
*/
private double start;
/**
* End time stamp.
*/
private double end;
/**
* Trip count of the register allocator.
*/
private int attempts;
/**
* The compilation result (a native method).
*/
private NMethod nmethod;
private ArrayList<Phase> phases = new ArrayList<Phase>(4);
/**
* The phases through which this compilation goes.
*/
private ArrayList<Phase> phases = new ArrayList<>(4);
/**
* In case this compilation fails, the reason for that.
*/
private String failureReason;
Compilation(int id) {
@ -52,9 +124,17 @@ public class Compilation implements LogEvent {
void reset() {
call = new CallSite();
lateInlineCall = new CallSite();
phases = new ArrayList<Phase>(4);
phases = new ArrayList<>(4);
}
/**
* Get a compilation phase by name, or {@code null}.
*
* @param s the name of the phase to retrieve in this compilation.
*
* @return a compilation phase, or {@code null} if no phase with the given
* name is found.
*/
Phase getPhase(String s) {
for (Phase p : getPhases()) {
if (p.getName().equals(s)) {
@ -72,20 +152,32 @@ public class Compilation implements LogEvent {
return start;
}
public void setCompiler(String compiler) {
this.compiler = compiler;
}
public String getCompiler() {
return compiler;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getId());
sb.append(" ");
sb.append(getCompiler());
sb.append(" ");
sb.append(getMethod());
sb.append(" ");
sb.append(getIcount());
sb.append("+");
sb.append(getBcount());
sb.append("\n");
for (CallSite site : getCall().getCalls()) {
sb.append(site);
sb.append("\n");
if (getCall() != null && getCall().getCalls() != null) {
for (CallSite site : getCall().getCalls()) {
sb.append(site);
sb.append("\n");
}
}
if (getLateInlineCall().getCalls() != null) {
sb.append("late inline:\n");
@ -101,38 +193,50 @@ public class Compilation implements LogEvent {
if (getMethod() == null) {
stream.println(getSpecial());
} else {
int bc = isOsr() ? getOsr_bci() : -1;
stream.print(getId() + getMethod().decodeFlags(bc) + getMethod().format(bc));
int bc = isOsr() ? getBCI() : -1;
stream.print(getId() + getMethod().decodeFlags(bc) + " " + compiler + " " + getMethod().format(bc));
}
}
public void print(PrintStream stream) {
print(stream, 0, false);
public void print(PrintStream stream, boolean printID) {
print(stream, 0, printID, true, false);
}
public void print(PrintStream stream, boolean printInlining) {
print(stream, 0, printInlining);
public void print(PrintStream stream, boolean printID, boolean printInlining) {
print(stream, 0, printID, printInlining, false);
}
public void print(PrintStream stream, int indent, boolean printInlining) {
public void print(PrintStream stream, boolean printID, boolean printInlining, boolean printUncommonTraps) {
print(stream, 0, printID, printInlining, printUncommonTraps);
}
public void print(PrintStream stream, int indent, boolean printID, boolean printInlining, boolean printUncommonTraps) {
if (getMethod() == null) {
stream.println(getSpecial());
} else {
int bc = isOsr() ? getOsr_bci() : -1;
stream.print(getId() + getMethod().decodeFlags(bc) + getMethod().format(bc));
if (printID) {
stream.print(getId());
}
int bc = isOsr() ? getBCI() : -1;
stream.print(getMethod().decodeFlags(bc) + " " + compiler + " " + getMethod().format(bc));
stream.println();
if (getFailureReason() != null) {
stream.println("COMPILE FAILED " + getFailureReason());
stream.println("COMPILE SKIPPED: " + getFailureReason() + " (not retryable)");
}
if (printInlining && call.getCalls() != null) {
for (CallSite site : call.getCalls()) {
site.print(stream, indent + 2, printInlining, printUncommonTraps);
}
}
if (printUncommonTraps && call.getTraps() != null) {
for (UncommonTrap site : call.getTraps()) {
site.print(stream, indent + 2);
}
}
if (printInlining && lateInlineCall.getCalls() != null) {
stream.println("late inline:");
for (CallSite site : lateInlineCall.getCalls()) {
site.print(stream, indent + 2);
site.print(stream, indent + 2, printInlining, printUncommonTraps);
}
}
}
@ -154,12 +258,12 @@ public class Compilation implements LogEvent {
this.osr = osr;
}
public int getOsr_bci() {
return osrBci;
public int getBCI() {
return bci;
}
public void setOsr_bci(int osrBci) {
this.osrBci = osrBci;
public void setBCI(int osrBci) {
this.bci = osrBci;
}
public String getIcount() {
@ -230,9 +334,13 @@ public class Compilation implements LogEvent {
return method;
}
/**
* Set the method under compilation. If it is already set, ignore the
* argument to avoid changing the method by post-parse inlining info.
*
* @param method the method under compilation. May be ignored.
*/
public void setMethod(Method method) {
// Don't change method if it is already set to avoid changing
// it by post parse inlining info.
if (getMethod() == null) {
this.method = method;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,10 +31,9 @@ import java.util.regex.*;
* This class is a filter class to deal with malformed XML that used
* to be produced by the JVM when generating LogCompilation. In 1.6
* and later releases it shouldn't be required.
* @author never
*/
class LogCleanupReader extends Reader {
private Reader reader;
private char[] buffer = new char[4096];
@ -55,32 +54,38 @@ class LogCleanupReader extends Reader {
reader = r;
}
static final private Matcher pattern = Pattern.compile(".+ compile_id='[0-9]+'.*( compile_id='[0-9]+)").matcher("");
static final private Matcher pattern2 = Pattern.compile("' (C[12]) compile_id=").matcher("");
static final private Matcher pattern3 = Pattern.compile("'(destroy_vm)/").matcher("");
static final private Matcher duplicateCompileID = Pattern.compile(".+ compile_id='[0-9]+'.*( compile_id='[0-9]+)").matcher("");
static final private Matcher compilerName = Pattern.compile("' (C[12]) compile_id=").matcher("");
static final private Matcher destroyVM = Pattern.compile("'(destroy_vm)/").matcher("");
/**
* The log cleanup takes place in this method. If any of the three patterns
* ({@link #duplicateCompileID}, {@link #compilerName}, {@link #destroyVM})
* match, that indicates a problem in the log. The cleanup is performed by
* correcting the input line and writing it back into the {@link #line}
* buffer.
*/
private void fill() throws IOException {
rawFill();
if (length != -1) {
boolean changed = false;
String s = new String(line, 0, length);
String orig = s;
pattern2.reset(s);
if (pattern2.find()) {
s = s.substring(0, pattern2.start(1)) + s.substring(pattern2.end(1) + 1);
compilerName.reset(s);
if (compilerName.find()) {
s = s.substring(0, compilerName.start(1)) + s.substring(compilerName.end(1) + 1);
changed = true;
}
pattern.reset(s);
if (pattern.lookingAt()) {
s = s.substring(0, pattern.start(1)) + s.substring(pattern.end(1) + 1);
duplicateCompileID.reset(s);
if (duplicateCompileID.lookingAt()) {
s = s.substring(0, duplicateCompileID.start(1)) + s.substring(duplicateCompileID.end(1) + 1);
changed = true;
}
pattern3.reset(s);
if (pattern3.find()) {
s = s.substring(0, pattern3.start(1)) + s.substring(pattern3.end(1));
destroyVM.reset(s);
if (destroyVM.find()) {
s = s.substring(0, destroyVM.start(1)) + s.substring(destroyVM.end(1));
changed = true;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -22,60 +22,102 @@
*
*/
/**
* The main command line driver of a parser for LogCompilation output.
* @author never
*/
package com.sun.hotspot.tools.compiler;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class LogCompilation extends DefaultHandler implements ErrorHandler, Constants {
/**
* The LogCompilation tool parses log files generated by HotSpot using the
* {@code -XX:+LogCompilation} command line flag, and outputs the data
* collected therein in a nicely formatted way. There are various sorting
* options available, as well as options that select specific compilation
* events (such as inlining decisions) for inclusion in the output.
*
* The tool is also capable of fixing broken compilation logs as sometimes
* generated by Java 1.5 JVMs.
*/
public class LogCompilation extends DefaultHandler implements ErrorHandler {
/**
* Print usage information and terminate with a given exit code.
*/
public static void usage(int exitcode) {
System.out.println("Usage: LogCompilation [ -v ] [ -c ] [ -s ] [ -e | -n ] file1 ...");
System.out.println("By default, the tool will print the logged compilations ordered by start time.");
System.out.println(" -c: clean up malformed 1.5 xml");
System.out.println(" -i: print inlining decisions");
System.out.println(" -S: print compilation statistics");
System.out.println(" -s: sort events by start time");
System.out.println(" -U: print uncommon trap statistics");
System.out.println(" -t: print with time stamps");
System.out.println(" -s: sort events by start time (default)");
System.out.println(" -e: sort events by elapsed time");
System.out.println(" -n: sort events by name and start");
System.out.println(" -C: compare logs (give files to compare on command line)");
System.out.println(" -d: do not print compilation IDs");
System.exit(exitcode);
}
/**
* Process command line arguments, parse log files and trigger desired
* functionality.
*/
public static void main(String[] args) throws Exception {
Comparator<LogEvent> defaultSort = LogParser.sortByStart;
Comparator<LogEvent> sort = LogParser.sortByStart;
boolean statistics = false;
boolean printInlining = false;
boolean cleanup = false;
boolean trapHistory = false;
boolean printTimeStamps = false;
boolean compare = false;
boolean printID = true;
int index = 0;
while (args.length > index) {
if (args[index].equals("-e")) {
defaultSort = LogParser.sortByElapsed;
String a = args[index];
if (a.equals("-e")) {
sort = LogParser.sortByElapsed;
index++;
} else if (args[index].equals("-n")) {
defaultSort = LogParser.sortByNameAndStart;
} else if (a.equals("-n")) {
sort = LogParser.sortByNameAndStart;
index++;
} else if (args[index].equals("-s")) {
defaultSort = LogParser.sortByStart;
} else if (a.equals("-s")) {
sort = LogParser.sortByStart;
index++;
} else if (args[index].equals("-c")) {
} else if (a.equals("-t")) {
printTimeStamps = true;
index++;
} else if (a.equals("-c")) {
cleanup = true;
index++;
} else if (args[index].equals("-S")) {
} else if (a.equals("-S")) {
statistics = true;
index++;
} else if (args[index].equals("-h")) {
} else if (a.equals("-U")) {
trapHistory = true;
index++;
} else if (a.equals("-h")) {
usage(0);
} else if (args[index].equals("-i")) {
} else if (a.equals("-i")) {
printInlining = true;
index++;
} else if (a.equals("-C")) {
compare = true;
index++;
} else if (a.equals("-d")) {
printID = false;
index++;
} else {
if (a.charAt(0) == '-') {
System.out.println("Unknown option '" + a + "', assuming file name.");
}
break;
}
}
@ -84,19 +126,40 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
usage(1);
}
if (compare) {
compareLogs(index, args);
return;
}
while (index < args.length) {
ArrayList<LogEvent> events = LogParser.parse(args[index], cleanup);
ArrayList<LogEvent> events = null;
try {
events = LogParser.parse(args[index], cleanup);
} catch (FileNotFoundException fnfe) {
System.out.println("File not found: " + args[index]);
System.exit(1);
}
Collections.sort(events, sort);
if (statistics) {
printStatistics(events, System.out);
} else if (trapHistory) {
printTrapHistory(events, System.out);
} else {
Collections.sort(events, defaultSort);
for (LogEvent c : events) {
if (printInlining && c instanceof Compilation) {
Compilation comp = (Compilation)c;
comp.print(System.out, true);
if (c instanceof NMethod) {
// skip these
continue;
}
if (printTimeStamps) {
System.out.print(c.getStart() + ": ");
}
if (c instanceof Compilation) {
Compilation comp = (Compilation) c;
comp.print(System.out, printID, printInlining);
} else {
c.print(System.out);
c.print(System.out, printID);
}
}
}
@ -104,17 +167,25 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
}
}
/**
* Print extensive statistics from parsed log files.
*/
public static void printStatistics(ArrayList<LogEvent> events, PrintStream out) {
// track code cache size
long cacheSize = 0;
long maxCacheSize = 0;
// track number of nmethods
int nmethodsCreated = 0;
int nmethodsLive = 0;
// track how many compilations were attempted multiple times
// (indexed by attempts, mapping to number of compilations)
int[] attempts = new int[32];
double regallocTime = 0;
int maxattempts = 0;
LinkedHashMap<String, Double> phaseTime = new LinkedHashMap<String, Double>(7);
LinkedHashMap<String, Integer> phaseNodes = new LinkedHashMap<String, Integer>(7);
// track time spent in compiler phases
LinkedHashMap<String, Double> phaseTime = new LinkedHashMap<>(7);
// track nodes created per phase
LinkedHashMap<String, Integer> phaseNodes = new LinkedHashMap<>(7);
double elapsed = 0;
for (LogEvent e : events) {
@ -137,18 +208,17 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
v2 = Integer.valueOf(0);
}
phaseNodes.put(phase.getName(), Integer.valueOf(v2.intValue() + phase.getNodes()));
/* Print phase name, elapsed time, nodes at the start of the phase,
nodes created in the phase, live nodes at the start of the phase,
live nodes added in the phase.
*/
out.printf("\t%s %6.4f %d %d %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes(), phase.getStartLiveNodes(), phase.getLiveNodes());
// Print phase name, elapsed time, nodes at the start of
// the phase, nodes created in the phase, live nodes at the
// start of the phase, live nodes added in the phase.
out.printf("\t%s %6.4f %d %d %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes(), phase.getStartLiveNodes(), phase.getAddedLiveNodes());
}
} else if (e instanceof MakeNotEntrantEvent) {
MakeNotEntrantEvent mne = (MakeNotEntrantEvent) e;
NMethod nm = mne.getNMethod();
if (mne.isZombie()) {
if (nm == null) {
System.err.println(mne.getId());
System.err.println("zombie make not entrant event without nmethod: " + mne.getId());
}
cacheSize -= nm.getSize();
nmethodsLive--;
@ -161,8 +231,7 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
maxCacheSize = Math.max(cacheSize, maxCacheSize);
}
}
out.printf("NMethods: %d created %d live %d bytes (%d peak) in the code cache\n",
nmethodsCreated, nmethodsLive, cacheSize, maxCacheSize);
out.printf("NMethods: %d created %d live %d bytes (%d peak) in the code cache\n", nmethodsCreated, nmethodsLive, cacheSize, maxCacheSize);
out.println("Phase times:");
for (String name : phaseTime.keySet()) {
Double v = phaseTime.get(name);
@ -178,4 +247,265 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
}
}
}
/**
* Container class for a pair of a method and a bytecode instruction index
* used by a compiler. This is used in
* {@linkplain #compareLogs() comparing logs}.
*/
static class MethodBCIPair {
public MethodBCIPair(Method m, int b, String c) {
method = m;
bci = b;
compiler = c;
}
Method method;
int bci;
String compiler;
public boolean equals(Object other) {
if (!(other instanceof MethodBCIPair)) {
return false;
}
MethodBCIPair otherp = (MethodBCIPair)other;
return (otherp.bci == bci &&
otherp.method.equals(method) &&
otherp.compiler.equals(compiler));
}
public int hashCode() {
return method.hashCode() + bci;
}
public String toString() {
if (bci != -1) {
return method + "@" + bci + " (" + compiler + ")";
} else {
return method + " (" + compiler + ")";
}
}
}
/**
* Compare a number of compilation log files. Each of the logs is parsed,
* and all compilations found therein are written to a sorted file (prefix
* {@code sorted-}. A summary is written to a new file {@code summary.txt}.
*
* @param index the index in the command line arguments at which to start
* looking for files to compare.
* @param args the command line arguments with which {@link LogCompilation}
* was originally invoked.
*
* @throws Exception in case any exceptions are thrown in the called
* methods.
*/
@SuppressWarnings("unchecked")
static void compareLogs(int index, String[] args) throws Exception {
HashMap<MethodBCIPair,MethodBCIPair> methods = new HashMap<>();
ArrayList<HashMap<MethodBCIPair,Object>> logs = new ArrayList<>();
PrintStream[] outs = new PrintStream[args.length - index];
PrintStream summary = new PrintStream(new FileOutputStream("summary.txt"));
int o = 0;
// Process all logs given on the command line: collect compilation
// data; in particular, method/bci pairs.
while (index < args.length) {
String basename = new File(args[index]).getName();
String outname = "sorted-" + basename;
System.out.println("Sorting " + basename + " to " + outname);
outs[o] = new PrintStream(new FileOutputStream(outname));
o++;
System.out.println("Parsing " + args[index]);
ArrayList<LogEvent> events = LogParser.parse(args[index], false);
HashMap<MethodBCIPair,Object> compiles = new HashMap<>();
logs.add(compiles);
for (LogEvent c : events) {
if (c instanceof Compilation) {
Compilation comp = (Compilation) c;
MethodBCIPair key = new MethodBCIPair(comp.getMethod(), comp.getBCI(),
comp.getCompiler());
MethodBCIPair e = methods.get(key);
if (e == null) {
methods.put(key, key);
} else {
key = e;
}
Object other = compiles.get(key);
if (other == null) {
compiles.put(key, comp);
} else {
if (!(other instanceof List)) {
List<Object> l = new LinkedList<>();
l.add(other);
l.add(comp);
compiles.put(key, l);
} else {
List<Object> l = (List<Object>) other;
l.add(comp);
}
}
}
}
index++;
}
// Process the collected method/bci pairs and write the output.
for (MethodBCIPair pair : methods.keySet()) {
summary.print(pair + " ");
int base = -1;
String first = null;
boolean mismatch = false;
boolean different = false;
String[] output = new String[outs.length];
o = 0;
for (HashMap<MethodBCIPair,Object> set : logs) {
Object e = set.get(pair);
String thisone = null;
Compilation lastc = null;
int n;
if (e == null) {
n = 0;
} else if (e instanceof Compilation) {
n = 1;
lastc = (Compilation) e;
} else {
// Compare the last compilation that was done for this method
n = ((List<Object>) e).size();
lastc = (Compilation) ((List<Object>) e).get(n - 1);
}
if (lastc != null) {
n = 1;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
lastc.print(ps, false);
ps.close();
thisone = new String(baos.toByteArray());
}
if (base == -1) {
base = n;
} else if (base != n) {
mismatch = true;
}
output[o++] = thisone;
if (thisone != null) {
if (first == null) {
first = thisone;
} else {
if (!first.equals(thisone)) {
different = true;
}
}
}
if (different) {
summary.print(n + "d ");
} else {
summary.print(n + " ");
}
}
if (mismatch) {
summary.print("mismatch");
}
summary.println();
if (different) {
for (int i = 0; i < outs.length; i++) {
if (output[i] != null) {
outs[i].println(output[i]);
}
}
}
}
for (int i = 0; i < outs.length; i++) {
outs[i].close();
}
if (summary != System.out) {
summary.close();
}
}
/**
* Print the history of uncommon trap events.
*/
public static void printTrapHistory(ArrayList<LogEvent> events, PrintStream out) {
// map method names to a list of log events
LinkedHashMap<String, ArrayList<LogEvent>> traps = new LinkedHashMap<>();
// map compilation IDs to compilations
HashMap<Integer, Compilation> comps = new HashMap<>();
// First, iterate over all logged events, collecting data about
// uncommon trap events.
for (LogEvent e : events) {
if (e instanceof NMethod) {
// skip these
continue;
}
if (e instanceof Compilation) {
Compilation c = (Compilation) e;
String name = c.getMethod().getFullName();
ArrayList<LogEvent> elist = traps.get(name);
if (elist != null && comps.get(c.getId()) == null) {
comps.put(c.getId(), c);
// If there were previous events for the method
// then keep track of later compiles too.
elist.add(c);
}
continue;
}
if (e instanceof BasicLogEvent) {
BasicLogEvent ble = (BasicLogEvent) e;
Compilation c = ble.getCompilation();
if (c == null) {
if (!(ble instanceof NMethod)) {
throw new InternalError("only nmethods should have a null compilation; here's a " + ble.getClass());
}
continue;
}
String name = c.getMethod().getFullName();
ArrayList<LogEvent> elist = traps.get(name);
if (elist == null) {
elist = new ArrayList<LogEvent>();
traps.put(name, elist);
}
int bleId = Integer.parseInt(ble.getId());
if (comps.get(bleId) == null) {
comps.put(bleId, c);
// Add the associated compile to the list. It
// will likely go at the end but we need to search
// backwards for the proper insertion point.
double start = c.getStart();
int ipoint = 0;
while (ipoint < elist.size() && elist.get(ipoint).getStart() < start) {
ipoint++;
}
if (ipoint == elist.size()) {
elist.add(c);
} else {
elist.add(ipoint, c);
}
}
elist.add(ble);
}
}
// Second, iterate over collected traps and output information.
for (String c: traps.keySet()) {
ArrayList<LogEvent> elist = traps.get(c);
String name = ((Compilation) elist.get(0)).getMethod().getFullName();
System.out.println(name);
double start = 0;
for (LogEvent e: elist) {
if (start > e.getStart() && e.getStart() != 0) {
throw new InternalError("wrong sorting order for traps");
}
start = e.getStart();
out.print(e.getStart() + ": ");
if (e instanceof Compilation) {
((Compilation) e).print(out, true, true, true);
} else {
e.print(out, true);
}
}
out.println();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,14 +25,31 @@
package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
import java.util.*;
/**
* The interface of an event from a HotSpot compilation log. Events can have a
* duration, e.g., a compiler {@link Phase} is an event, and so is an entire
* {@link Compilation}.
*/
public interface LogEvent {
/**
* The event's start time.
*/
public double getStart();
/**
* The event's duration in milliseconds.
*/
public double getElapsedTime();
/**
* The compilation during which this event was signalled.
*/
public Compilation getCompilation();
public void print(PrintStream stream);
/**
* Print the event to the given stream.
*/
public void print(PrintStream stream, boolean printID);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,14 +21,25 @@
* questions.
*
*/
package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
/**
* In a compilation log, represent the event of making a given compiled method
* not-entrant, e.g., during an OSR compilation.
*/
class MakeNotEntrantEvent extends BasicLogEvent {
/**
* Denote whether the method is marked as a zombie, i.e., no further
* activations exist.
*/
private final boolean zombie;
/**
* The method in question.
*/
private NMethod nmethod;
MakeNotEntrantEvent(double s, String i, boolean z, NMethod nm) {
@ -41,7 +52,7 @@ class MakeNotEntrantEvent extends BasicLogEvent {
return nmethod;
}
public void print(PrintStream stream) {
public void print(PrintStream stream, boolean printID) {
if (isZombie()) {
stream.printf("%s make_zombie\n", getId());
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,16 +26,58 @@ package com.sun.hotspot.tools.compiler;
import java.util.Arrays;
public class Method implements Constants {
import static com.sun.hotspot.tools.compiler.Constants.*;
/**
* Representation of a Java method in a compilation log.
*/
public class Method {
/**
* The name of the class holding the method.
*/
private String holder;
/**
* The method's name.
*/
private String name;
/**
* The return type of the method, as a fully qualified (source-level) class
* or primitive type name.
*/
private String returnType;
private String arguments;
/**
* The method's signature, in internal form.
*/
private String signature;
/**
* The length of the method's byte code.
*/
private String bytes;
/**
* The number of times this method was invoked in the interpreter.
*/
private String iicount;
/**
* The method's flags, in the form of a {@code String} representing the
* {@code int} encoding them.
*/
private String flags;
/**
* Decode the {@link flags} numerical string to a format for console
* output. The result does not honour all possible flags but includes
* information about OSR compilation.
*
* @param osr_bci the byte code index at which an OSR compilation takes
* place, or -1 if the compilation is not an OSR one.
*/
String decodeFlags(int osr_bci) {
int f = Integer.parseInt(getFlags());
char[] c = new char[4];
@ -49,6 +91,12 @@ public class Method implements Constants {
return new String(c);
}
/**
* Format this method for console output.
*
* @param osr_bci the byte code index at which OSR takes place, or -1 if no
* OSR compilation is going on.
*/
String format(int osr_bci) {
if (osr_bci >= 0) {
return getHolder() + "::" + getName() + " @ " + osr_bci + " (" + getBytes() + " bytes)";
@ -62,6 +110,10 @@ public class Method implements Constants {
return getHolder() + "::" + getName() + " (" + getBytes() + " bytes)";
}
public String getFullName() {
return getHolder().replace('/', '.') + "." + getName() + signature;
}
public String getHolder() {
return holder;
}
@ -86,12 +138,16 @@ public class Method implements Constants {
this.returnType = returnType;
}
public String getArguments() {
return arguments;
public String getSignature() {
return signature;
}
public void setArguments(String arguments) {
this.arguments = arguments;
public void setSignature(String signature) {
this.signature = signature.replace('/', '.');
}
public String getArguments() {
return signature.substring(0, signature.indexOf(')') + 1);
}
public String getBytes() {
@ -121,10 +177,13 @@ public class Method implements Constants {
@Override
public boolean equals(Object o) {
if (o instanceof Method) {
Method other = (Method)o;
return holder.equals(other.holder) && name.equals(other.name) &&
arguments.equals(other.arguments) && returnType.equals(other.returnType);
Method other = (Method) o;
return holder.equals(other.holder) && name.equals(other.name) && signature.equals(other.signature);
}
return false;
}
public int hashCode() {
return holder.hashCode() ^ name.hashCode();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,9 +26,20 @@ package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
/**
* A compilation log event that is signalled whenever a new nmethod (a native
* method, a compilation result) is created.
*/
public class NMethod extends BasicLogEvent {
/**
* The nmethod's starting address in memory.
*/
private long address;
/**
* The nmethod's size in bytes.
*/
private long size;
NMethod(double s, String i, long a, long sz) {
@ -37,7 +48,7 @@ public class NMethod extends BasicLogEvent {
size = sz;
}
public void print(PrintStream out) {
public void print(PrintStream out, boolean printID) {
// XXX Currently we do nothing
// throw new InternalError();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,11 +26,30 @@ package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
/**
* Representation of a compilation phase as a log event.
*/
public class Phase extends BasicLogEvent {
/**
* The number of nodes in the compilation at the beginning of this phase.
*/
private final int startNodes;
/**
* The number of nodes in the compilation at the end of this phase.
*/
private int endNodes;
/**
* The number of live nodes in the compilation at the beginning of this
* phase.
*/
private final int startLiveNodes;
/**
* The number of live nodes in the compilation at the end of this phase.
*/
private int endLiveNodes;
Phase(String n, double s, int nodes, int live) {
@ -58,8 +77,11 @@ public class Phase extends BasicLogEvent {
public int getEndNodes() {
return endNodes;
}
/* Number of live nodes added by the phase */
int getLiveNodes() {
/**
* The number of live nodes added by this phase.
*/
int getAddedLiveNodes() {
return getEndLiveNodes() - getStartLiveNodes();
}
@ -76,7 +98,7 @@ public class Phase extends BasicLogEvent {
}
@Override
public void print(PrintStream stream) {
public void print(PrintStream stream, boolean printID) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
/**
* An instance of this class represents an uncommon trap associated with a
* given bytecode instruction. An uncommon trap is described in terms of its
* reason and action to be taken. An instance of this class is always relative
* to a specific method and only contains the relevant bytecode instruction
* index.
*/
class UncommonTrap {
private int bci;
private String reason;
private String action;
private String bytecode;
public UncommonTrap(int b, String r, String a, String bc) {
bci = b;
reason = r;
action = a;
bytecode = bc;
}
public int getBCI() {
return bci;
}
public String getReason() {
return reason;
}
public String getAction() {
return action;
}
public String getBytecode() {
return bytecode;
}
void emit(PrintStream stream, int indent) {
for (int i = 0; i < indent; i++) {
stream.print(' ');
}
}
public void print(PrintStream stream, int indent) {
emit(stream, indent);
stream.println(this);
}
public String toString() {
return "@ " + bci + " " + getBytecode() + " uncommon trap " + getReason() + " " + getAction();
}
}

View File

@ -21,17 +21,33 @@
* questions.
*
*/
package com.sun.hotspot.tools.compiler;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
/**
* Represents an uncommon trap encountered during a compilation.
*/
class UncommonTrapEvent extends BasicLogEvent {
private final String reason;
private final String action;
/**
* Denote how many times this trap has been encountered.
*/
private int count;
private String jvms = "";
/**
* The name of the bytecode instruction at which the trap occurred.
*/
private String bytecode;
private List<String> jvmsMethods = new ArrayList<>();
private List<Integer> jvmsBCIs = new ArrayList<>();
UncommonTrapEvent(double s, String i, String r, String a, int c) {
super(s, i);
@ -40,20 +56,26 @@ class UncommonTrapEvent extends BasicLogEvent {
count = c;
}
public void addJVMS(String method, int bci) {
setJvms(getJvms() + " @" + bci + " " + method + "\n");
}
public void updateCount(UncommonTrapEvent trap) {
setCount(Math.max(getCount(), trap.getCount()));
}
public void print(PrintStream stream) {
stream.printf("%s uncommon trap %.3f %s %s\n", getId(), getStart(), getReason(), getAction());
stream.print(getJvms());
public void print(PrintStream stream, boolean printID) {
if (printID) {
stream.print(getId() + " ");
}
stream.printf("uncommon trap %s %s %s\n", bytecode, getReason(), getAction());
int indent = 2;
for (int j = 0; j < jvmsMethods.size(); j++) {
for (int i = 0; i < indent; i++) {
stream.print(' ');
}
stream.println("@ " + jvmsBCIs.get(j) + " " + jvmsMethods.get(j));
indent += 2;
}
}
public String getReason() {
return reason;
}
@ -70,15 +92,56 @@ class UncommonTrapEvent extends BasicLogEvent {
this.count = count;
}
public String getJvms() {
return jvms;
}
public void setJvms(String jvms) {
this.jvms = jvms;
}
/**
* Set the compilation for this event. This involves identifying the call
* site to which this uncommon trap event belongs. In addition to setting
* the {@link #compilation} link, this method will consequently also set
* the {@link #bytecode} field.
*/
public void setCompilation(Compilation compilation) {
this.compilation = compilation;
super.setCompilation(compilation);
// Attempt to associate a bytecode with with this trap
CallSite site = compilation.getCall();
int i = 0;
try {
List<UncommonTrap> traps = site.getTraps();
while (i + 1 < jvmsMethods.size()) {
if (!jvmsMethods.get(i).equals(site.getMethod().getFullName())) {
throw new InternalError(jvmsMethods.get(i) + " != " + site.getMethod().getFullName());
}
CallSite result = null;
for (CallSite call : site.getCalls()) {
if (call.getBci() == jvmsBCIs.get(i) &&
call.getMethod().getFullName().equals(jvmsMethods.get(i + 1)) &&
call.getReceiver() == null) {
result = call;
i++;
break;
}
}
if (result == null) {
throw new InternalError("couldn't find call site");
}
site = result;
traps = site.getTraps();
}
for (UncommonTrap trap : traps) {
if (trap.getBCI() == jvmsBCIs.get(i) &&
trap.getReason().equals(getReason()) &&
trap.getAction().equals(getAction())) {
bytecode = trap.getBytecode();
return;
}
}
throw new InternalError("couldn't find bytecode");
} catch (Exception e) {
bytecode = "<unknown>";
}
}
public void addMethodAndBCI(String method, int bci) {
jvmsMethods.add(0, method);
jvmsBCIs.add(0, bci);
}
}

View File

@ -501,8 +501,8 @@ void CompileTask::log_task(xmlStream* log) {
methodHandle method(thread, this->method());
ResourceMark rm(thread);
// <task id='9' method='M' osr_bci='X' level='1' blocking='1' stamp='1.234'>
log->print(" compile_id='%d'", _compile_id);
// <task compiler='Cx' id='9' method='M' osr_bci='X' level='1' blocking='1' stamp='1.234'>
log->print(" compiler='%s' compile_id='%d'", _comp_level <= CompLevel_full_profile ? "C1" : "C2", _compile_id);
if (_osr_bci != CompileBroker::standard_entry_bci) {
log->print(" compile_kind='osr'"); // same as nmethod::compile_kind
} // else compile_kind='c2c'