changeset 11852:d7964e96b0b0

move benchmark counters into separate class and make them correct for multithreaded applications
author Lukas Stadler <lukas.stadler@jku.at>
date Mon, 30 Sep 2013 18:32:26 +0200
parents 1d64bfb3f481
children e994bf28ed2f
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java src/share/vm/classfile/systemDictionary.hpp src/share/vm/classfile/vmSymbols.hpp src/share/vm/graal/graalCompilerToVM.cpp src/share/vm/runtime/thread.cpp src/share/vm/runtime/thread.hpp
diffstat 14 files changed, 434 insertions(+), 301 deletions(-) [+]
line wrap: on
line diff
--- 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();
--- 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();
 }
--- 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();
 }
--- 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<Boolean> GenericDynamicCounters = new OptionValue<>(false);
-
-    @Option(help = "")
-    private static final OptionValue<String> 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) {
--- /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<Boolean> 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<String> 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<String, Integer> indexes = new ConcurrentHashMap<>();
+    public static final ArrayList<String> groups = new ArrayList<>();
+    public static long[] delta;
+    public static final ArrayList<AtomicLong> 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<Long, String> 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<String, Integer> 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<Long, String> 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<Long, String> 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<Long, String> 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);
+    }
+}
--- 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) {
--- 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<String, Integer> 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<Long, String> sorted = new TreeMap<>();
-
-        long[] array = staticCounter ? STATIC_COUNTERS : COUNTERS;
-        long sum = 0;
-        for (Map.Entry<String, Integer> 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<Long, String> 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<Long, String> 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<Long, String> 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);
+
 }
--- 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);
+    }
 }
--- 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<Node> 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<Node> 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<Node> obsoleteNodes) {
-                DynamicCounterNode.addLowLevel(group, name, increment, addContext, position, runtime);
-            }
-
-            @Override
-            public boolean isVisible() {
-                return true;
-            }
-        });
-    }
 }
--- 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) \
--- 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"))\
--- 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() {
--- 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
 }
 
 
--- 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        ); }