--- old/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java 2017-07-07 09:30:17.000000000 -0700 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java 2017-07-07 09:30:17.000000000 -0700 @@ -22,11 +22,10 @@ */ package org.graalvm.compiler.hotspot.debug; -import java.io.FileNotFoundException; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; -import java.nio.file.Path; import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -39,7 +38,6 @@ import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.debug.CSVUtil; -import org.graalvm.compiler.debug.GraalDebugConfig; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; @@ -49,7 +47,6 @@ import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionType; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.compiler.options.UniquePathUtilities; import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; @@ -105,9 +102,9 @@ public static final OptionKey BenchmarkDynamicCounters = new OptionKey<>(null); @Option(help = "Use grouping separators for number printing", type = OptionType.Debug) public static final OptionKey DynamicCountersPrintGroupSeparator = new OptionKey<>(true); - @Option(help = "Print in human readable format", type = OptionType.Debug) - public static final OptionKey DynamicCountersHumanReadable = new OptionKey<>(true); - @Option(help = "Benchmark counters log file (default is stdout)", type = OptionType.Debug) + @Option(help = "File to which benchmark counters are dumped. A CSV format is used if the file ends with .csv " + + "otherwise a more human readable format is used. The fields in the CSV format are: " + + "category, group, name, value", type = OptionType.Debug) public static final OptionKey BenchmarkCountersFile = new OptionKey<>(null); @Option(help = "Dump dynamic counters", type = OptionType.Debug) public static final OptionKey BenchmarkCountersDumpDynamic = new OptionKey<>(true); @@ -170,112 +167,153 @@ private static synchronized void dump(OptionValues options, PrintStream out, double seconds, long[] counters, int maxRows) { if (!counterMap.isEmpty()) { - if (Options.DynamicCountersHumanReadable.getValue(options)) { - out.println("====== dynamic counters (" + counterMap.size() + " in total) ======"); - } - TreeSet set = new TreeSet<>(); - counterMap.forEach((nameGroup, counter) -> set.add(counter.group)); - for (String group : set) { - if (group != null) { - if (Options.BenchmarkCountersDumpStatic.getValue(options)) { - dumpCounters(options, out, seconds, counters, true, group, maxRows); - } - if (Options.BenchmarkCountersDumpDynamic.getValue(options)) { - dumpCounters(options, out, seconds, counters, false, group, maxRows); + try (Dumper dumper = Dumper.getDumper(options, out, counterMap.size(), seconds, maxRows)) { + TreeSet set = new TreeSet<>(); + counterMap.forEach((nameGroup, counter) -> set.add(counter.group)); + for (String group : set) { + if (group != null) { + if (Options.BenchmarkCountersDumpStatic.getValue(options)) { + dumper.dumpCounters(true, group, collectStaticCounters(), counterMap.entrySet(), options); + } + if (Options.BenchmarkCountersDumpDynamic.getValue(options)) { + dumper.dumpCounters(false, group, collectDynamicCounters(counters), counterMap.entrySet(), options); + } } } } - if (Options.DynamicCountersHumanReadable.getValue(options)) { - out.println("============================"); - } clear(counters); } } + private static synchronized long[] collectDynamicCounters(long[] counters) { + long[] array = counters.clone(); + for (int i = 0; i < array.length; i++) { + array[i] -= delta[i]; + } + return array; + } + + private static synchronized long[] collectStaticCounters() { + long[] array = new long[counterMap.size()]; + for (Counter counter : counterMap.values()) { + array[counter.index] = counter.staticCounters.get(); + } + return array; + } + private static synchronized void clear(long[] counters) { delta = counters; } - private static synchronized void dumpCounters(OptionValues options, PrintStream out, double seconds, long[] counters, boolean staticCounter, String group, int maxRows) { + private static boolean shouldDumpComputerReadable(OptionValues options) { + String dumpFile = Options.BenchmarkCountersFile.getValue(options); + return dumpFile != null && (dumpFile.endsWith(".csv") || dumpFile.endsWith(".CSV")); + } + + private abstract static class Dumper implements AutoCloseable { + public static Dumper getDumper(OptionValues options, PrintStream out, int counterSize, double second, int maxRows) { + Dumper dumper = shouldDumpComputerReadable(options) ? new ComputerReadableDumper(out) : new HumanReadableDumper(out, second, maxRows); + dumper.start(counterSize); + return dumper; + } + + protected final PrintStream out; - // collect the numbers - long[] array; - if (staticCounter) { - array = new long[counterMap.size()]; - for (Counter counter : counterMap.values()) { - array[counter.index] = counter.staticCounters.get(); - } - } else { - array = counters.clone(); - for (int i = 0; i < array.length; i++) { - array[i] -= delta[i]; - } - } - Set> counterEntrySet = counterMap.entrySet(); - if (Options.DynamicCountersHumanReadable.getValue(options)) { - dumpHumanReadable(options, out, seconds, staticCounter, group, maxRows, array, counterEntrySet); - } else { - dumpComputerReadable(out, staticCounter, group, array, counterEntrySet); + private Dumper(PrintStream out) { + this.out = out; } + + protected abstract void start(int size); + + public abstract void dumpCounters(boolean staticCounter, String group, long[] array, Set> counterEntrySet, OptionValues options); + + @Override + public abstract void close(); + } private static String getName(String nameGroup, String group) { return nameGroup.substring(0, nameGroup.length() - group.length() - 1); } - private static void dumpHumanReadable(OptionValues options, PrintStream out, double seconds, boolean staticCounter, String group, int maxRows, long[] array, - Set> counterEntrySet) { - // sort the counters by putting them into a sorted map - TreeMap sorted = new TreeMap<>(); - long sum = 0; - for (Map.Entry entry : counterEntrySet) { - Counter counter = entry.getValue(); - int index = counter.index; - if (counter.group.equals(group)) { - sum += array[index]; - sorted.put(array[index] * array.length + index, getName(entry.getKey(), group)); - } + private static long percentage(long counter, long sum) { + return (counter * 200 + 1) / sum / 2; + } + + private static class HumanReadableDumper extends Dumper { + private final double seconds; + private final int maxRows; + + HumanReadableDumper(PrintStream out, double seconds, int maxRows) { + super(out); + this.seconds = seconds; + this.maxRows = maxRows; + } + + @Override + public void start(int size) { + out.println("====== dynamic counters (" + size + " in total) ======"); + } + + @Override + public void close() { + out.println("============================"); } - if (sum > 0) { - long cutoff = sorted.size() < 10 ? 1 : Math.max(1, sum / 100); - int cnt = sorted.size(); - - // remove everything below cutoff and keep at most maxRows - Iterator> iter = sorted.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - long counter = entry.getKey() / array.length; - if (counter < cutoff || cnt > maxRows) { - iter.remove(); + @Override + public void dumpCounters(boolean staticCounter, String group, long[] array, Set> counterEntrySet, OptionValues options) { + // sort the counters by putting them into a sorted map + TreeMap sorted = new TreeMap<>(); + long sum = 0; + for (Map.Entry entry : counterEntrySet) { + Counter counter = entry.getValue(); + int index = counter.index; + if (counter.group.equals(group)) { + sum += array[index]; + sorted.put(array[index] * array.length + index, getName(entry.getKey(), group)); } - cnt--; } - String numFmt = Options.DynamicCountersPrintGroupSeparator.getValue(options) ? "%,19d" : "%19d"; - if (staticCounter) { - out.println("=========== " + group + " (static counters):"); - for (Map.Entry entry : sorted.entrySet()) { + if (sum > 0) { + long cutoff = sorted.size() < 10 ? 1 : Math.max(1, sum / 100); + int cnt = sorted.size(); + + // remove everything below cutoff and keep at most maxRows + Iterator> iter = sorted.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); long counter = entry.getKey() / array.length; - out.format(Locale.US, numFmt + " %3d%% %s\n", counter, percentage(counter, sum), entry.getValue()); - } - out.format(Locale.US, numFmt + " total\n", sum); - } else { - if (group.startsWith("~")) { - out.println("=========== " + group + " (dynamic counters), time = " + seconds + " s:"); - for (Map.Entry entry : sorted.entrySet()) { - long counter = entry.getKey() / array.length; - out.format(Locale.US, numFmt + "/s %3d%% %s\n", (long) (counter / seconds), percentage(counter, sum), entry.getValue()); + if (counter < cutoff || cnt > maxRows) { + iter.remove(); } - out.format(Locale.US, numFmt + "/s total\n", (long) (sum / seconds)); - } else { - out.println("=========== " + group + " (dynamic counters):"); + cnt--; + } + + String numFmt = Options.DynamicCountersPrintGroupSeparator.getValue(options) ? "%,19d" : "%19d"; + if (staticCounter) { + out.println("=========== " + group + " (static counters):"); for (Map.Entry entry : sorted.entrySet()) { long counter = entry.getKey() / array.length; out.format(Locale.US, numFmt + " %3d%% %s\n", counter, percentage(counter, sum), entry.getValue()); } out.format(Locale.US, numFmt + " total\n", sum); + } else { + if (group.startsWith("~")) { + out.println("=========== " + group + " (dynamic counters), time = " + seconds + " s:"); + for (Map.Entry entry : sorted.entrySet()) { + long counter = entry.getKey() / array.length; + out.format(Locale.US, numFmt + "/s %3d%% %s\n", (long) (counter / seconds), percentage(counter, sum), entry.getValue()); + } + out.format(Locale.US, numFmt + "/s total\n", (long) (sum / seconds)); + } else { + out.println("=========== " + group + " (dynamic counters):"); + for (Map.Entry entry : sorted.entrySet()) { + long counter = entry.getKey() / array.length; + out.format(Locale.US, numFmt + " %3d%% %s\n", counter, percentage(counter, sum), entry.getValue()); + } + out.format(Locale.US, numFmt + " total\n", sum); + } } } } @@ -283,21 +321,35 @@ private static final String CSV_FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%d"); - private static void dumpComputerReadable(PrintStream out, boolean staticCounter, String group, long[] array, Set> counterEntrySet) { - String category = staticCounter ? "static counters" : "dynamic counters"; - for (Map.Entry entry : counterEntrySet) { - Counter counter = entry.getValue(); - if (counter.group.equals(group)) { - String name = getName(entry.getKey(), group); - int index = counter.index; - long value = array[index]; - CSVUtil.Escape.println(out, CSV_FMT, category, group, name, value); - } + private static class ComputerReadableDumper extends Dumper { + + ComputerReadableDumper(PrintStream out) { + super(out); } - } - private static long percentage(long counter, long sum) { - return (counter * 200 + 1) / sum / 2; + @Override + public void start(int size) { + // do nothing + } + + @Override + public void close() { + // do nothing + } + + @Override + public void dumpCounters(boolean staticCounter, String group, long[] array, Set> counterEntrySet, OptionValues options) { + String category = staticCounter ? "static counters" : "dynamic counters"; + for (Map.Entry entry : counterEntrySet) { + Counter counter = entry.getValue(); + if (counter.group.equals(group)) { + String name = getName(entry.getKey(), group); + int index = counter.index; + long value = array[index]; + CSVUtil.Escape.println(out, CSV_FMT, category, group, name, value); + } + } + } } private abstract static class CallbackOutputStream extends OutputStream { @@ -440,10 +492,11 @@ private static PrintStream getPrintStream(OptionValues options) { if (Options.BenchmarkCountersFile.getValue(options) != null) { try { - Path path = UniquePathUtilities.getPathGlobal(options, Options.BenchmarkCountersFile, GraalDebugConfig.Options.DumpPath, "csv"); - TTY.println("Writing benchmark counters to '%s'", path); - return new PrintStream(path.toFile()); - } catch (FileNotFoundException e) { + + File file = new File(Options.BenchmarkCountersFile.getValue(options)); + TTY.println("Writing benchmark counters to '%s'", file.getAbsolutePath()); + return new PrintStream(file); + } catch (IOException e) { TTY.out().println(e.getMessage()); TTY.out().println("Fallback to default"); }