# HG changeset patch # User Lukas Stadler # Date 1380558746 -7200 # Node ID d7964e96b0b0287272d763faa3cad3389879530d # Parent 1d64bfb3f481dae6c28bdeb24d3a3409134a0411 move benchmark counters into separate class and make them correct for multithreaded applications diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Mon Sep 30 18:32:26 2013 +0200 @@ -246,6 +246,16 @@ public final int osThreadOffset = getUninitializedInt(); /** + * The value of JavaThread::graal_counters_offset(). + */ + public final int graalCountersThreadOffset = getUninitializedInt(); + + /** + * The length of the JavaThread::_graal_counters array. + */ + public final int graalCountersSize = getUninitializedInt(); + + /** * The value of OSThread::interrupted_offset(). */ public final int osThreadInterruptedOffset = getUninitializedInt(); diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Mon Sep 30 18:32:26 2013 +0200 @@ -228,4 +228,9 @@ void invalidateInstalledCode(HotSpotInstalledCode hotspotInstalledCode); boolean isTypeLinked(HotSpotResolvedObjectType hotSpotResolvedObjectType); + + /** + * Collects the current values of all Graal benchmark counters, summed up over all threads. + */ + long[] collectCounters(); } diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Mon Sep 30 18:32:26 2013 +0200 @@ -190,4 +190,6 @@ * verified entry point of the given native method. */ public static native Object executeCompiledMethodIntrinsic(Object arg1, Object arg2, Object arg3, HotSpotInstalledCode hotspotInstalledCode) throws InvalidInstalledCodeException; + + public native long[] collectCounters(); } diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Mon Sep 30 18:32:26 2013 +0200 @@ -47,7 +47,6 @@ import com.oracle.graal.hotspot.phases.*; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.debug.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.*; @@ -79,11 +78,6 @@ } }; - @Option(help = "") - private static final OptionValue GenericDynamicCounters = new OptionValue<>(false); - - @Option(help = "") - private static final OptionValue BenchmarkDynamicCounters = new OptionValue<>(null); //@formatter:on private final HotSpotGraalRuntime graalRuntime; @@ -234,28 +228,8 @@ t.start(); } - if (BenchmarkDynamicCounters.getValue() != null) { - String[] arguments = BenchmarkDynamicCounters.getValue().split(","); - if (arguments.length == 0 || (arguments.length % 3) != 0) { - throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: (err|out),start,end,(err|out),start,end,... (~ matches multiple digits)"); - } - for (int i = 0; i < arguments.length; i += 3) { - if (arguments[i].equals("err")) { - System.setErr(new PrintStream(new BenchmarkCountersOutputStream(System.err, arguments[i + 1], arguments[i + 2]))); - } else if (arguments[i].equals("out")) { - System.setOut(new PrintStream(new BenchmarkCountersOutputStream(System.out, arguments[i + 1], arguments[i + 2]))); - } else { - throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: err|out"); - } - // dacapo: "err, starting =====, PASSED in " - // specjvm2008: "out,Iteration ~ (~s) begins: ,Iteration ~ (~s) ends: " - } - DynamicCounterNode.excludedClassPrefix = "Lcom/oracle/graal/"; - DynamicCounterNode.enabled = true; - } - if (GenericDynamicCounters.getValue()) { - DynamicCounterNode.enabled = true; - } + BenchmarkCounters.initialize(graalRuntime.getCompilerToVM()); + compilerStartTime = System.nanoTime(); } @@ -284,84 +258,6 @@ } } - private final class BenchmarkCountersOutputStream extends CallbackOutputStream { - - private long startTime; - private boolean waitingForEnd; - - private BenchmarkCountersOutputStream(PrintStream delegate, String start, String end) { - super(delegate, new String[]{start, end, "\n"}); - } - - @Override - protected void patternFound(int index) { - switch (index) { - case 0: - startTime = System.nanoTime(); - DynamicCounterNode.clear(); - break; - case 1: - waitingForEnd = true; - break; - case 2: - if (waitingForEnd) { - waitingForEnd = false; - DynamicCounterNode.dump(delegate, (System.nanoTime() - startTime) / 1000000000d); - } - break; - } - } - } - - public abstract static class CallbackOutputStream extends OutputStream { - - protected final PrintStream delegate; - private final byte[][] patterns; - private final int[] positions; - - public CallbackOutputStream(PrintStream delegate, String... patterns) { - this.delegate = delegate; - this.positions = new int[patterns.length]; - this.patterns = new byte[patterns.length][]; - for (int i = 0; i < patterns.length; i++) { - this.patterns[i] = patterns[i].getBytes(); - } - } - - protected abstract void patternFound(int index); - - @Override - public void write(int b) throws IOException { - try { - delegate.write(b); - for (int i = 0; i < patterns.length; i++) { - int j = positions[i]; - byte[] cs = patterns[i]; - byte patternChar = cs[j]; - if (patternChar == '~' && Character.isDigit(b)) { - // nothing to do... - } else { - if (patternChar == '~') { - patternChar = cs[++positions[i]]; - } - if (b == patternChar) { - positions[i]++; - } else { - positions[i] = 0; - } - } - if (positions[i] == patterns[i].length) { - positions[i] = 0; - patternFound(i); - } - } - } catch (RuntimeException e) { - e.printStackTrace(delegate); - throw e; - } - } - } - /** * Take action related to entering a new execution phase. * @@ -528,9 +424,7 @@ } SnippetCounter.printGroups(TTY.out().out()); - if (GenericDynamicCounters.getValue()) { - DynamicCounterNode.dump(System.out, (System.nanoTime() - compilerStartTime) / 1000000000d); - } + BenchmarkCounters.shutdown(graalRuntime.getCompilerToVM(), compilerStartTime); } private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) { diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java Mon Sep 30 18:32:26 2013 +0200 @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2013, 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.hotspot.debug; + +import java.io.*; +import java.text.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +import sun.misc.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.hotspot.*; +import com.oracle.graal.hotspot.bridge.*; +import com.oracle.graal.hotspot.meta.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.HeapAccess.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.debug.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.options.*; +import com.oracle.graal.replacements.nodes.*; + +public class BenchmarkCounters { + + static class Options { + + //@formatter:off + @Option(help = "Turn on the benchmark counters, and displays the results on VM shutdown") + private static final OptionValue GenericDynamicCounters = new OptionValue<>(false); + + @Option(help = "Turn on the benchmark counters, and listen for specific patterns on System.out/System.err:%n" + + "Format: (err|out),start pattern,end pattern (~ matches multiple digits)%n" + + "Examples:%n" + + " dacapo = 'err, starting =====, PASSED in'%n" + + " specjvm2008 = 'out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends:'") + private static final OptionValue BenchmarkDynamicCounters = new OptionValue<>(null); + //@formatter:on + } + + private static final boolean DUMP_STATIC = false; + + public static String excludedClassPrefix = null; + public static boolean enabled = false; + + public static final ConcurrentHashMap indexes = new ConcurrentHashMap<>(); + public static final ArrayList groups = new ArrayList<>(); + public static long[] delta; + public static final ArrayList staticCounters = new ArrayList<>(); + + public static int getIndex(DynamicCounterNode counter) { + if (!enabled) { + throw new GraalInternalError("counter nodes shouldn't exist when counters are not enabled"); + } + String name = counter.getName(); + String group = counter.getGroup(); + name = counter.isWithContext() ? name + " @ " + counter.graph().graphId() + ":" + MetaUtil.format("%h.%n", counter.graph().method()) + "#" + group : name + "#" + group; + Integer index = indexes.get(name); + if (index == null) { + synchronized (BenchmarkCounters.class) { + index = indexes.get(name); + if (index == null) { + index = indexes.size(); + indexes.put(name, index); + groups.add(group); + staticCounters.add(new AtomicLong()); + } + } + } + assert groups.get(index).equals(group) : "mismatching groups: " + groups.get(index) + " vs. " + group; + if (counter.getIncrement().isConstant()) { + staticCounters.get(index).addAndGet(counter.getIncrement().asConstant().asLong()); + } + return index; + } + + public static synchronized void dump(PrintStream out, double seconds, long[] counters) { + if (!staticCounters.isEmpty()) { + out.println("====== dynamic counters (" + staticCounters.size() + " in total) ======"); + for (String group : new TreeSet<>(groups)) { + if (group != null) { + if (DUMP_STATIC) { + dumpCounters(out, seconds, counters, true, group); + } + dumpCounters(out, seconds, counters, false, group); + } + } + out.println("============================"); + + clear(counters); + } + } + + public static synchronized void clear(long[] counters) { + delta = counters; + } + + private static synchronized void dumpCounters(PrintStream out, double seconds, long[] counters, boolean staticCounter, String group) { + TreeMap sorted = new TreeMap<>(); + + long[] array; + if (staticCounter) { + array = new long[indexes.size()]; + for (int i = 0; i < array.length; i++) { + array[i] = staticCounters.get(i).get(); + } + } else { + array = counters.clone(); + for (int i = 0; i < array.length; i++) { + array[i] -= delta[i]; + } + } + long sum = 0; + for (Map.Entry entry : indexes.entrySet()) { + int index = entry.getValue(); + if (groups.get(index).equals(group)) { + sum += array[index]; + sorted.put(array[index] * array.length + index, entry.getKey().substring(0, entry.getKey().length() - group.length() - 1)); + } + } + + if (sum > 0) { + NumberFormat format = NumberFormat.getInstance(Locale.US); + long cutoff = sorted.size() < 10 ? 1 : Math.max(1, sum / 100); + if (staticCounter) { + out.println("=========== " + group + " static counters: "); + for (Map.Entry entry : sorted.entrySet()) { + long counter = entry.getKey() / array.length; + if (counter >= cutoff) { + out.println(format.format(counter) + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue()); + } + } + out.println(sum + ": total"); + } else { + if (group.startsWith("~")) { + out.println("=========== " + group + " dynamic counters"); + for (Map.Entry entry : sorted.entrySet()) { + long counter = entry.getKey() / array.length; + if (counter >= cutoff) { + out.println(format.format(counter) + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue()); + } + } + out.println(format.format(sum) + ": total"); + } else { + out.println("=========== " + group + " dynamic counters, time = " + seconds + " s"); + for (Map.Entry entry : sorted.entrySet()) { + long counter = entry.getKey() / array.length; + if (counter >= cutoff) { + out.println(format.format((long) (counter / seconds)) + "/s \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue()); + } + } + out.println(format.format((long) (sum / seconds)) + "/s: total"); + } + } + } + } + + public abstract static class CallbackOutputStream extends OutputStream { + + protected final PrintStream delegate; + private final byte[][] patterns; + private final int[] positions; + + public CallbackOutputStream(PrintStream delegate, String... patterns) { + this.delegate = delegate; + this.positions = new int[patterns.length]; + this.patterns = new byte[patterns.length][]; + for (int i = 0; i < patterns.length; i++) { + this.patterns[i] = patterns[i].getBytes(); + } + } + + protected abstract void patternFound(int index); + + @Override + public void write(int b) throws IOException { + try { + delegate.write(b); + for (int i = 0; i < patterns.length; i++) { + int j = positions[i]; + byte[] cs = patterns[i]; + byte patternChar = cs[j]; + if (patternChar == '~' && Character.isDigit(b)) { + // nothing to do... + } else { + if (patternChar == '~') { + patternChar = cs[++positions[i]]; + } + if (b == patternChar) { + positions[i]++; + } else { + positions[i] = 0; + } + } + if (positions[i] == patterns[i].length) { + positions[i] = 0; + patternFound(i); + } + } + } catch (RuntimeException e) { + e.printStackTrace(delegate); + throw e; + } + } + } + + public static void initialize(final CompilerToVM compilerToVM) { + final class BenchmarkCountersOutputStream extends CallbackOutputStream { + + private long startTime; + private boolean waitingForEnd; + + private BenchmarkCountersOutputStream(PrintStream delegate, String start, String end) { + super(delegate, new String[]{start, end, "\n"}); + } + + @Override + protected void patternFound(int index) { + switch (index) { + case 0: + startTime = System.nanoTime(); + BenchmarkCounters.clear(compilerToVM.collectCounters()); + break; + case 1: + waitingForEnd = true; + break; + case 2: + if (waitingForEnd) { + waitingForEnd = false; + BenchmarkCounters.dump(delegate, (System.nanoTime() - startTime) / 1000000000d, compilerToVM.collectCounters()); + } + break; + } + } + } + + if (Options.BenchmarkDynamicCounters.getValue() != null) { + String[] arguments = Options.BenchmarkDynamicCounters.getValue().split(","); + if (arguments.length == 0 || (arguments.length % 3) != 0) { + throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: (err|out),start,end,(err|out),start,end,... (~ matches multiple digits)"); + } + for (int i = 0; i < arguments.length; i += 3) { + if (arguments[i].equals("err")) { + System.setErr(new PrintStream(new BenchmarkCountersOutputStream(System.err, arguments[i + 1], arguments[i + 2]))); + } else if (arguments[i].equals("out")) { + System.setOut(new PrintStream(new BenchmarkCountersOutputStream(System.out, arguments[i + 1], arguments[i + 2]))); + } else { + throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: err|out"); + } + } + excludedClassPrefix = "Lcom/oracle/graal/"; + enabled = true; + } + if (Options.GenericDynamicCounters.getValue()) { + enabled = true; + } + if (Options.GenericDynamicCounters.getValue() || Options.BenchmarkDynamicCounters.getValue() != null) { + clear(compilerToVM.collectCounters()); + } + } + + public static void shutdown(CompilerToVM compilerToVM, long compilerStartTime) { + if (Options.GenericDynamicCounters.getValue()) { + dump(System.out, (System.nanoTime() - compilerStartTime) / 1000000000d, compilerToVM.collectCounters()); + } + } + + public static void lower(DynamicCounterNode counter, HotSpotRuntime runtime) { + StructuredGraph graph = counter.graph(); + if (excludedClassPrefix == null || !counter.graph().method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) { + HotSpotVMConfig config = runtime.config; + + ReadRegisterNode thread = graph.add(new ReadRegisterNode(runtime.threadRegister(), runtime.getTarget().wordKind, true, false)); + + int index = BenchmarkCounters.getIndex(counter); + if (index >= config.graalCountersSize) { + throw new GraalInternalError("too many counters, reduce number of counters or increase GRAAL_COUNTERS_SIZE (current value: " + config.graalCountersSize + ")"); + } + ConstantLocationNode location = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, config.graalCountersThreadOffset + Unsafe.ARRAY_LONG_INDEX_SCALE * index, graph); + ReadNode read = graph.add(new ReadNode(thread, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false)); + IntegerAddNode add = graph.unique(new IntegerAddNode(Kind.Long, read, counter.getIncrement())); + WriteNode write = graph.add(new WriteNode(thread, add, location, BarrierType.NONE, false)); + + graph.addBeforeFixed(counter, thread); + graph.addBeforeFixed(counter, read); + graph.addBeforeFixed(counter, write); + } + graph.removeFixed(counter); + } +} diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Mon Sep 30 18:32:26 2013 +0200 @@ -74,6 +74,7 @@ import com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition; import com.oracle.graal.hotspot.bridge.*; import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult; +import com.oracle.graal.hotspot.debug.*; import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.hotspot.phases.*; import com.oracle.graal.hotspot.replacements.*; @@ -82,6 +83,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.HeapAccess.BarrierType; import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.debug.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; @@ -788,6 +790,10 @@ osrStart.replaceAtUsages(newStart); osrStart.safeDelete(); } + } else if (n instanceof DynamicCounterNode) { + if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) { + BenchmarkCounters.lower((DynamicCounterNode) n, this); + } } else if (n instanceof CheckCastDynamicNode) { checkcastDynamicSnippets.lower((CheckCastDynamicNode) n); } else if (n instanceof InstanceOfNode) { diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java Mon Sep 30 18:32:26 2013 +0200 @@ -22,200 +22,61 @@ */ package com.oracle.graal.nodes.debug; -import java.io.*; -import java.util.*; - -import sun.misc.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.HeapAccess.BarrierType; -import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; /** * This node can be used to add a counter to the code that will estimate the dynamic number of calls * by adding an increment to the compiled code. This should of course only be used for - * debugging/testing purposes, and is not 100% accurate (because of concurrency issues while - * accessing the counters). + * debugging/testing purposes. * - * A unique counter will be created for each unique String passed to the constructor. + * A unique counter will be created for each unique name passed to the constructor. Depending on the + * value of withContext, the name of the root method is added to the counter's name. */ public class DynamicCounterNode extends FixedWithNextNode implements Lowerable { - private static final int MAX_COUNTERS = 10 * 1024; - public static final long[] COUNTERS = new long[MAX_COUNTERS]; - private static final long[] STATIC_COUNTERS = new long[MAX_COUNTERS]; - private static final String[] GROUPS = new String[MAX_COUNTERS]; - private static final HashMap INDEXES = new HashMap<>(); - public static String excludedClassPrefix = null; - public static boolean enabled = false; + @Input private ValueNode increment; private final String name; private final String group; - private final long increment; - private final boolean addContext; + private final boolean withContext; - public DynamicCounterNode(String name, String group, long increment, boolean addContext) { + public DynamicCounterNode(String name, String group, ValueNode increment, boolean withContext) { super(StampFactory.forVoid()); - if (!enabled) { - throw new GraalInternalError("dynamic counters not enabled"); - } this.name = name; this.group = group; this.increment = increment; - this.addContext = addContext; + this.withContext = withContext; + } + + public ValueNode getIncrement() { + return increment; } public String getName() { return name; } - public long getIncrement() { - return increment; - } - - public boolean isAddContext() { - return addContext; - } - - private static synchronized int getIndex(String name) { - Integer index = INDEXES.get(name); - if (index == null) { - index = INDEXES.size(); - INDEXES.put(name, index); - if (index >= MAX_COUNTERS) { - throw new GraalInternalError("too many dynamic counters"); - } - return index; - } else { - return index; - } - } - - public static synchronized void dump(PrintStream out, double seconds) { - for (String group : new HashSet<>(Arrays.asList(GROUPS))) { - if (group != null) { - dumpCounters(out, seconds, true, group); - dumpCounters(out, seconds, false, group); - } - } - out.println("============================"); - - clear(); + public String getGroup() { + return group; } - private static void dumpCounters(PrintStream out, double seconds, boolean staticCounter, String group) { - TreeMap sorted = new TreeMap<>(); - - long[] array = staticCounter ? STATIC_COUNTERS : COUNTERS; - long sum = 0; - for (Map.Entry entry : INDEXES.entrySet()) { - int index = entry.getValue(); - if (GROUPS[index].equals(group)) { - sum += array[index]; - sorted.put(array[index] * MAX_COUNTERS + index, entry.getKey()); - } - } - - if (sum > 0) { - long cutoff = sum / 1000; - long sum2 = 0; - if (staticCounter) { - out.println("=========== " + group + " static counters: "); - for (Map.Entry entry : sorted.entrySet()) { - long counter = entry.getKey() / MAX_COUNTERS; - sum2 += counter; - if (sum2 >= cutoff) { - out.println(counter + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue()); - } - } - out.println(sum + ": total"); - } else { - if (group.startsWith("~")) { - out.println("=========== " + group + " dynamic counters"); - for (Map.Entry entry : sorted.entrySet()) { - long counter = entry.getKey() / MAX_COUNTERS; - sum2 += counter; - if (sum2 >= cutoff) { - out.println(counter + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue()); - } - } - out.println(sum + "/s: total"); - } else { - out.println("=========== " + group + " dynamic counters, time = " + seconds + " s"); - for (Map.Entry entry : sorted.entrySet()) { - long counter = entry.getKey() / MAX_COUNTERS; - sum2 += counter; - if (sum2 >= cutoff) { - out.println((long) (counter / seconds) + "/s \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue()); - } - } - out.println((long) (sum / seconds) + "/s: total"); - } - } - } - } - - public static void clear() { - Arrays.fill(COUNTERS, 0); + public boolean isWithContext() { + return withContext; } @Override public void lower(LoweringTool tool) { - if (!enabled) { - throw new GraalInternalError("counter nodes shouldn't exist when not enabled"); - } - if (excludedClassPrefix == null || !graph().method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) { - int index = addContext ? getIndex(name + " @ " + MetaUtil.format("%h.%n", graph().method())) : getIndex(name); - STATIC_COUNTERS[index] += increment; - GROUPS[index] = group; - - ConstantNode arrayConstant = ConstantNode.forObject(COUNTERS, tool.getRuntime(), graph()); - ConstantNode indexConstant = ConstantNode.forInt(index, graph()); - LoadIndexedNode load = graph().add(new LoadIndexedNode(arrayConstant, indexConstant, Kind.Long)); - IntegerAddNode add = graph().add(new IntegerAddNode(Kind.Long, load, ConstantNode.forLong(increment, graph()))); - StoreIndexedNode store = graph().add(new StoreIndexedNode(arrayConstant, indexConstant, Kind.Long, add)); - - graph().addBeforeFixed(this, load); - graph().addBeforeFixed(this, store); - load.lower(tool); - store.lower(tool); - } - graph().removeFixed(this); + tool.getRuntime().lower(this, tool); } - public static void addLowLevel(String group, String name, long increment, boolean addContext, FixedNode position, MetaAccessProvider runtime) { - if (!enabled) { - throw new GraalInternalError("counter nodes shouldn't exist when not enabled"); - } + public static void addCounterBefore(String group, String name, long increment, boolean withContext, FixedNode position) { StructuredGraph graph = position.graph(); - if (excludedClassPrefix == null || !graph.method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) { - int index = addContext ? getIndex(name + " @ " + MetaUtil.format("%h.%n", graph.method())) : getIndex(name); - STATIC_COUNTERS[index] += increment; - GROUPS[index] = group; - - ConstantNode arrayConstant = ConstantNode.forObject(COUNTERS, runtime, graph); - ConstantLocationNode location = ConstantLocationNode.create(NamedLocationIdentity.getArrayLocation(Kind.Long), Kind.Long, Unsafe.ARRAY_LONG_BASE_OFFSET + Unsafe.ARRAY_LONG_INDEX_SCALE * - index, graph); - ReadNode read = graph.add(new ReadNode(arrayConstant, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false)); - IntegerAddNode add = graph.add(new IntegerAddNode(Kind.Long, read, ConstantNode.forLong(increment, graph))); - WriteNode write = graph.add(new WriteNode(arrayConstant, add, location, BarrierType.NONE, false)); - - graph.addBeforeFixed(position, read); - graph.addBeforeFixed(position, write); - } + graph.addBeforeFixed(position, position.graph().add(new DynamicCounterNode(name, group, ConstantNode.forLong(increment, position.graph()), withContext))); } - public static void addCounterBefore(String group, String name, long increment, boolean addContext, FixedNode position) { - if (enabled) { - StructuredGraph graph = position.graph(); - DynamicCounterNode counter = graph.add(new DynamicCounterNode(name, group, increment, addContext)); - graph.addBeforeFixed(position, counter); - } - } + @NodeIntrinsic + public static native void counter(@ConstantNodeParameter String name, @ConstantNodeParameter String group, long increment, @ConstantNodeParameter boolean addContext); + } diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java Mon Sep 30 18:32:26 2013 +0200 @@ -35,7 +35,7 @@ @Input private ValueNode checkedValue; - public SurvivingCounterNode(String group, String name, long increment, boolean addContext, ValueNode checkedValue) { + public SurvivingCounterNode(String group, String name, ValueNode increment, boolean addContext, ValueNode checkedValue) { super(group, name, increment, addContext); this.checkedValue = checkedValue; } @@ -55,4 +55,10 @@ tool.delete(); } } + + public static void addCounterBefore(String group, String name, long increment, boolean addContext, ValueNode checkedValue, FixedNode position) { + StructuredGraph graph = position.graph(); + SurvivingCounterNode counter = graph.add(new SurvivingCounterNode(name, group, ConstantNode.forLong(increment, graph), addContext, checkedValue)); + graph.addBeforeFixed(position, counter); + } } diff -r 1d64bfb3f481 -r d7964e96b0b0 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java Mon Sep 30 14:02:07 2013 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java Mon Sep 30 18:32:26 2013 +0200 @@ -24,7 +24,6 @@ import java.util.*; -import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; @@ -35,9 +34,6 @@ public class GraphEffectList extends EffectList { public void addCounterBefore(final String group, final String name, final int increment, final boolean addContext, final FixedNode position) { - if (!DynamicCounterNode.enabled) { - return; - } add(new Effect() { @Override @@ -48,16 +44,12 @@ @Override public void apply(StructuredGraph graph, ArrayList obsoleteNodes) { assert position.isAlive(); - DynamicCounterNode node = graph.add(new DynamicCounterNode(group, name, increment, addContext)); - graph.addBeforeFixed(position, node); + DynamicCounterNode.addCounterBefore(group, name, increment, addContext, position); } }); } public void addSurvivingCounterBefore(final String group, final String name, final int increment, final boolean addContext, final ValueNode checkedValue, final FixedNode position) { - if (!DynamicCounterNode.enabled) { - return; - } add(new Effect() { @Override @@ -68,8 +60,7 @@ @Override public void apply(StructuredGraph graph, ArrayList obsoleteNodes) { assert position.isAlive(); - DynamicCounterNode node = graph.add(new SurvivingCounterNode(group, name, increment, addContext, checkedValue)); - graph.addBeforeFixed(position, node); + SurvivingCounterNode.addCounterBefore(group, name, increment, addContext, checkedValue, position); } }); } @@ -368,24 +359,4 @@ } }); } - - public void addLowLevelCounterBefore(final String group, final String name, final int increment, final boolean addContext, final FixedNode position, final MetaAccessProvider runtime) { - add(new Effect() { - - @Override - public String name() { - return "addLowLevelCounterBefore"; - } - - @Override - public void apply(StructuredGraph graph, ArrayList obsoleteNodes) { - DynamicCounterNode.addLowLevel(group, name, increment, addContext, position, runtime); - } - - @Override - public boolean isVisible() { - return true; - } - }); - } } diff -r 1d64bfb3f481 -r d7964e96b0b0 src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Mon Sep 30 14:02:07 2013 +0200 +++ b/src/share/vm/classfile/systemDictionary.hpp Mon Sep 30 18:32:26 2013 +0200 @@ -202,6 +202,7 @@ do_klass(HotSpotResolvedObjectType_klass, com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType, Opt) \ do_klass(HotSpotMonitorValue_klass, com_oracle_graal_hotspot_meta_HotSpotMonitorValue, Opt) \ do_klass(LocalImpl_klass, com_oracle_graal_hotspot_debug_LocalImpl, Opt) \ + do_klass(CompilerThread_klass, com_oracle_graal_hotspot_CompilerThread, Opt) \ /* graal.api.code */ \ do_klass(Assumptions_klass, com_oracle_graal_api_code_Assumptions, Opt) \ do_klass(Assumptions_ConcreteMethod_klass, com_oracle_graal_api_code_Assumptions_ConcreteMethod, Opt) \ diff -r 1d64bfb3f481 -r d7964e96b0b0 src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Mon Sep 30 14:02:07 2013 +0200 +++ b/src/share/vm/classfile/vmSymbols.hpp Mon Sep 30 18:32:26 2013 +0200 @@ -311,6 +311,7 @@ template(com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType, "com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType") \ template(com_oracle_graal_hotspot_meta_HotSpotMonitorValue, "com/oracle/graal/hotspot/meta/HotSpotMonitorValue") \ template(com_oracle_graal_hotspot_debug_LocalImpl, "com/oracle/graal/hotspot/debug/LocalImpl") \ + template(com_oracle_graal_hotspot_CompilerThread, "com/oracle/graal/hotspot/CompilerThread") \ template(com_oracle_graal_hotspot_ptx_PTXHotSpotGraalRuntime, "com/oracle/graal/hotspot/ptx/PTXHotSpotGraalRuntime")\ AMD64_ONLY(template(com_oracle_graal_hotspot_amd64_AMD64HotSpotGraalRuntime,"com/oracle/graal/hotspot/amd64/AMD64HotSpotGraalRuntime"))\ SPARC_ONLY(template(com_oracle_graal_hotspot_sparc_SPARCHotSpotGraalRuntime,"com/oracle/graal/hotspot/sparc/SPARCHotSpotGraalRuntime"))\ diff -r 1d64bfb3f481 -r d7964e96b0b0 src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Mon Sep 30 14:02:07 2013 +0200 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Mon Sep 30 18:32:26 2013 +0200 @@ -875,6 +875,9 @@ set_int("arrayClassElementOffset", in_bytes(ObjArrayKlass::element_klass_offset())); + set_int("graalCountersThreadOffset", in_bytes(JavaThread::graal_counters_offset())); + set_int("graalCountersSize", (jint) GRAAL_COUNTERS_SIZE); + #undef set_boolean #undef set_int #undef set_long @@ -1145,6 +1148,12 @@ return klass; C2V_END +C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv *env, jobject)) + typeArrayOop arrayOop = oopFactory::new_longArray(GRAAL_COUNTERS_SIZE, CHECK_NULL); + JavaThread::collect_counters(arrayOop); + return (jlongArray) JNIHandles::make_local(arrayOop); +C2V_END + #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(c2v_ ## f)) @@ -1224,6 +1233,7 @@ {CC"invalidateInstalledCode", CC"("HS_INSTALLED_CODE")V", FN_PTR(invalidateInstalledCode)}, {CC"readUnsafeUncompressedPointer", CC"("OBJECT"J)"OBJECT, FN_PTR(readUnsafeUncompressedPointer)}, {CC"readUnsafeKlassPointer", CC"("OBJECT")J", FN_PTR(readUnsafeKlassPointer)}, + {CC"collectCounters", CC"()[J", FN_PTR(collectCounters)}, }; int CompilerToVM_methods_count() { diff -r 1d64bfb3f481 -r d7964e96b0b0 src/share/vm/runtime/thread.cpp --- a/src/share/vm/runtime/thread.cpp Mon Sep 30 14:02:07 2013 +0200 +++ b/src/share/vm/runtime/thread.cpp Mon Sep 30 18:32:26 2013 +0200 @@ -1411,6 +1411,36 @@ // ======= JavaThread ======== +#ifdef GRAAL + +#if GRAAL_COUNTERS_SIZE > 0 +jlong JavaThread::_graal_old_counters[GRAAL_COUNTERS_SIZE]; + +bool graal_counters_include(oop threadObj) { + return !GRAAL_COUNTERS_EXCLUDE_COMPILER_THREADS || threadObj == NULL || threadObj->klass() != SystemDictionary::CompilerThread_klass(); +} + +void JavaThread::collect_counters(typeArrayOop array) { + MutexLocker tl(Threads_lock); + for (int i = 0; i < array->length(); i++) { + array->long_at_put(i, _graal_old_counters[i]); + } + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + if (graal_counters_include(tp->threadObj())) { + for (int i = 0; i < array->length(); i++) { + array->long_at_put(i, array->long_at(i) + tp->_graal_counters[i]); + } + } + } +} +#else +void JavaThread::collect_counters(typeArrayOop array) { + // empty +} +#endif // GRAAL_COUNTERS_SIZE > 0 + +#endif // GRAAL + // A JavaThread is a normal Java thread void JavaThread::initialize() { @@ -1449,6 +1479,9 @@ _stack_guard_state = stack_guard_unused; #ifdef GRAAL _graal_alternate_call_target = NULL; + for (int i = 0; i < GRAAL_COUNTERS_SIZE; i++) { + _graal_counters[i] = 0; + } #endif _exception_oop = NULL; _exception_pc = 0; @@ -1638,6 +1671,14 @@ ThreadSafepointState::destroy(this); if (_thread_profiler != NULL) delete _thread_profiler; if (_thread_stat != NULL) delete _thread_stat; + +#if defined(GRAAL) && (GRAAL_COUNTERS_SIZE > 0) + if (graal_counters_include(threadObj())) { + for (int i = 0; i < GRAAL_COUNTERS_SIZE; i++) { + _graal_old_counters[i] += _graal_counters[i]; + } + } +#endif } diff -r 1d64bfb3f481 -r d7964e96b0b0 src/share/vm/runtime/thread.hpp --- a/src/share/vm/runtime/thread.hpp Mon Sep 30 14:02:07 2013 +0200 +++ b/src/share/vm/runtime/thread.hpp Mon Sep 30 18:32:26 2013 +0200 @@ -919,6 +919,17 @@ #ifdef GRAAL address _graal_alternate_call_target; address _graal_implicit_exception_pc; // pc at which the most recent implicit exception occurred + + // number of counters, increase as needed. 0 == disabled +#define GRAAL_COUNTERS_SIZE (0) +#define GRAAL_COUNTERS_EXCLUDE_COMPILER_THREADS (true) + + jlong _graal_counters[GRAAL_COUNTERS_SIZE]; + static jlong _graal_old_counters[GRAAL_COUNTERS_SIZE]; + + public: + static void collect_counters(typeArrayOop array); + private: #endif StackGuardState _stack_guard_state; @@ -1379,6 +1390,7 @@ #ifdef GRAAL static ByteSize graal_alternate_call_target_offset() { return byte_offset_of(JavaThread, _graal_alternate_call_target); } static ByteSize graal_implicit_exception_pc_offset() { return byte_offset_of(JavaThread, _graal_implicit_exception_pc); } + static ByteSize graal_counters_offset() { return byte_offset_of(JavaThread, _graal_counters ); } #endif static ByteSize exception_oop_offset() { return byte_offset_of(JavaThread, _exception_oop ); } static ByteSize exception_pc_offset() { return byte_offset_of(JavaThread, _exception_pc ); }