changeset 19248:a2ff253c458f

Truffle/Instrumentation: code cleanups in tools CoverageTracker and NodeExecCounter, especially for tutorial value
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 10 Feb 2015 16:44:19 -0800
parents 128586040207
children ec8402f4e00a
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java
diffstat 2 files changed, 65 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java	Tue Feb 10 16:44:11 2015 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java	Tue Feb 10 16:44:19 2015 -0800
@@ -29,7 +29,6 @@
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.*;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.instrument.impl.*;
@@ -52,7 +51,7 @@
  * <p>
  * <ul>
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
- * to produce the event {@link TruffleEventReceiver#enter(Node, VirtualFrame)};</li>
+ * to produce the event {@link TruffleEventListener#enter(Node, VirtualFrame)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
  * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
@@ -75,13 +74,13 @@
 public final class CoverageTracker extends InstrumentationTool {
 
     /** Counting data. */
-    private final Map<LineLocation, CoverageCounter> counters = new HashMap<>();
+    private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>();
 
-    /** For disposal. */
+    /** Needed for disposal. */
     private final List<Instrument> instruments = new ArrayList<>();
 
     /**
-     * Counting is restricted to nodes holding this tag.
+     * Coverage counting is restricted to nodes holding this tag.
      */
     private final SyntaxTag countingTag;
 
@@ -112,7 +111,7 @@
 
     @Override
     protected void internalReset() {
-        counters.clear();
+        coverageMap.clear();
     }
 
     @Override
@@ -142,22 +141,21 @@
          * every line associated with an appropriately tagged AST node; iterable in order of source
          * name, then line number.
          */
-        final TreeSet<Entry<LineLocation, CoverageCounter>> entries = new TreeSet<>(new LineLocationEntryComparator());
+        final TreeSet<Entry<LineLocation, CoverageRecord>> entries = new TreeSet<>(new LineLocationEntryComparator());
 
-        final Map<Source, Long[]> results = new HashMap<>();
-
-        for (Entry<LineLocation, CoverageCounter> entry : counters.entrySet()) {
+        for (Entry<LineLocation, CoverageRecord> entry : coverageMap.entrySet()) {
             entries.add(entry);
         }
+        final Map<Source, Long[]> result = new HashMap<>();
         Source curSource = null;
         Long[] curLineTable = null;
-        for (Entry<LineLocation, CoverageCounter> entry : entries) {
+        for (Entry<LineLocation, CoverageRecord> entry : entries) {
             final LineLocation key = entry.getKey();
             final Source source = key.getSource();
             final int lineNo = key.getLineNumber();
             if (source != curSource) {
                 if (curSource != null) {
-                    results.put(curSource, curLineTable);
+                    result.put(curSource, curLineTable);
                 }
                 curSource = source;
                 curLineTable = new Long[source.getLineCount()];
@@ -165,9 +163,9 @@
             curLineTable[lineNo - 1] = entry.getValue().count.longValue();
         }
         if (curSource != null) {
-            results.put(curSource, curLineTable);
+            result.put(curSource, curLineTable);
         }
-        return results;
+        return result;
     }
 
     /**
@@ -183,14 +181,14 @@
          * every line associated with an appropriately tagged AST node; iterable in order of source
          * name, then line number.
          */
-        final TreeSet<Entry<LineLocation, CoverageCounter>> entries = new TreeSet<>(new LineLocationEntryComparator());
+        final TreeSet<Entry<LineLocation, CoverageRecord>> entries = new TreeSet<>(new LineLocationEntryComparator());
 
-        for (Entry<LineLocation, CoverageCounter> entry : counters.entrySet()) {
+        for (Entry<LineLocation, CoverageRecord> entry : coverageMap.entrySet()) {
             entries.add(entry);
         }
         Source curSource = null;
         int curLineNo = 1;
-        for (Entry<LineLocation, CoverageCounter> entry : entries) {
+        for (Entry<LineLocation, CoverageRecord> entry : entries) {
             final LineLocation key = entry.getKey();
             final Source source = key.getSource();
             final int lineNo = key.getLineNumber();
@@ -228,9 +226,9 @@
     }
 
     /**
-     * A receiver for events at each instrumented AST location. This receiver counts
-     * "execution calls" to the instrumented node and is <em>stateful</em>. State in receivers must
-     * be considered carefully since ASTs, along with all instrumentation (including event receivers
+     * A listener for events at each instrumented AST location. This listener counts
+     * "execution calls" to the instrumented node and is <em>stateful</em>. State in listeners must
+     * be considered carefully since ASTs, along with all instrumentation (including event listener
      * such as this) are routinely cloned by the Truffle runtime. AST cloning is <em>shallow</em>
      * (for non- {@link Child} nodes), so in this case the actual count <em>is shared</em> among all
      * the clones; the count is also held in a table indexed by source line.
@@ -238,7 +236,7 @@
      * In contrast, a primitive field would <em>not</em> be shared among clones and resulting counts
      * would not be accurate.
      */
-    private final class CoverageEventReceiver extends DefaultEventReceiver {
+    private final class CoverageEventListener extends DefaultEventListener {
 
         /**
          * Shared by all clones of the associated instrument and by the table of counters for the
@@ -246,12 +244,11 @@
          */
         private final AtomicLong count;
 
-        CoverageEventReceiver(AtomicLong count) {
+        CoverageEventListener(AtomicLong count) {
             this.count = count;
         }
 
         @Override
-        @TruffleBoundary
         public void enter(Node node, VirtualFrame frame) {
             if (isEnabled()) {
                 count.getAndIncrement();
@@ -259,9 +256,9 @@
         }
     }
 
-    private static final class LineLocationEntryComparator implements Comparator<Entry<LineLocation, CoverageCounter>> {
+    private static final class LineLocationEntryComparator implements Comparator<Entry<LineLocation, CoverageRecord>> {
 
-        public int compare(Entry<LineLocation, CoverageCounter> e1, Entry<LineLocation, CoverageCounter> e2) {
+        public int compare(Entry<LineLocation, CoverageRecord> e1, Entry<LineLocation, CoverageRecord> e2) {
             return LineLocation.COMPARATOR.compare(e1.getKey(), e2.getKey());
         }
     }
@@ -279,35 +276,37 @@
                 if (srcSection == null) {
                     // TODO (mlvdv) report this?
                 } else {
+                    // Get the source line where the
                     final LineLocation lineLocation = srcSection.getLineLocation();
-                    CoverageCounter counter = counters.get(lineLocation);
-                    if (counter != null) {
+                    CoverageRecord record = coverageMap.get(lineLocation);
+                    if (record != null) {
                         // Another node starts on same line; count only the first (textually)
-                        if (srcSection.getCharIndex() > counter.srcSection.getCharIndex()) {
-                            // Counter already in place, corresponds to code earlier on line
+                        if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) {
+                            // Record already in place, corresponds to code earlier on line
                             return;
                         } else {
-                            // Counter already in place, corresponds to later code; replace it
-                            counter.instrument.dispose();
+                            // Record already in place, corresponds to later code; replace it
+                            record.instrument.dispose();
                         }
                     }
                     final AtomicLong count = new AtomicLong();
-                    final CoverageEventReceiver eventReceiver = new CoverageEventReceiver(count);
-                    final Instrument instrument = Instrument.create(eventReceiver, CoverageTracker.class.getSimpleName());
+                    final CoverageEventListener eventListener = new CoverageEventListener(count);
+                    final Instrument instrument = Instrument.create(eventListener, CoverageTracker.class.getSimpleName());
                     instruments.add(instrument);
                     probe.attach(instrument);
-                    counters.put(lineLocation, new CoverageCounter(srcSection, instrument, count));
+                    coverageMap.put(lineLocation, new CoverageRecord(srcSection, instrument, count));
                 }
             }
         }
     }
 
-    private class CoverageCounter {
-        final SourceSection srcSection;
-        final Instrument instrument;
+    private class CoverageRecord {
+
+        final SourceSection srcSection; // The text of the code being counted
+        final Instrument instrument;  // The attached Instrument, in case need to remove.
         final AtomicLong count;
 
-        CoverageCounter(SourceSection srcSection, Instrument instrument, AtomicLong count) {
+        CoverageRecord(SourceSection srcSection, Instrument instrument, AtomicLong count) {
             this.srcSection = srcSection;
             this.instrument = instrument;
             this.count = count;
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java	Tue Feb 10 16:44:11 2015 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java	Tue Feb 10 16:44:19 2015 -0800
@@ -49,7 +49,7 @@
  * <p>
  * <ul>
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
- * to produce the event {@link TruffleEventReceiver#enter(Node, VirtualFrame)};</li>
+ * to produce the event {@link TruffleEventListener#enter(Node, VirtualFrame)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
  * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
@@ -92,26 +92,38 @@
     }
 
     /**
-     * Receiver for events at instrumented nodes. Counts are maintained in a shared table, so the
-     * receiver is stateless and can be shared by every {@link Instrument}.
+     * Listener for events at instrumented nodes. Counts are maintained in a shared table, so the
+     * listener is stateless and can be shared by every {@link Instrument}.
      */
-    private final TruffleEventReceiver eventReceiver = new DefaultEventReceiver() {
+    private final TruffleEventListener eventListener = new DefaultEventListener() {
         @Override
         public void enter(Node node, VirtualFrame frame) {
-            internalReceive(node);
+            if (isEnabled()) {
+                final Class<?> nodeClass = node.getClass();
+                /*
+                 * Everything up to here is inlined by Truffle compilation. Delegate the next part
+                 * to a method behind an inlining boundary.
+                 *
+                 * Note that it is not permitted to pass a {@link VirtualFrame} across an inlining
+                 * boundary; they are truly virtual in inlined code.
+                 */
+                AtomicLong nodeCounter = getCounter(nodeClass);
+                nodeCounter.getAndIncrement();
+            }
         }
 
+        /**
+         * Mark this method as a boundary that will stop Truffle inlining, which should not be
+         * allowed to inline the hash table method or any other complex library code.
+         */
         @TruffleBoundary
-        private void internalReceive(Node node) {
-            if (isEnabled()) {
-                final Class<?> nodeClass = node.getClass();
-                AtomicLong nodeCounter = counters.get(nodeClass);
-                if (nodeCounter == null) {
-                    nodeCounter = new AtomicLong();
-                    counters.put(nodeClass, nodeCounter);
-                }
-                nodeCounter.getAndIncrement();
+        private AtomicLong getCounter(Class<?> nodeClass) {
+            AtomicLong nodeCounter = counters.get(nodeClass);
+            if (nodeCounter == null) {
+                nodeCounter = new AtomicLong();
+                counters.put(nodeClass, nodeCounter);
             }
+            return nodeCounter;
         }
     };
 
@@ -268,7 +280,7 @@
 
             if (node.isInstrumentable()) {
                 try {
-                    final Instrument instrument = Instrument.create(eventReceiver, "NodeExecCounter");
+                    final Instrument instrument = Instrument.create(eventListener, "NodeExecCounter");
                     instruments.add(instrument);
                     node.probe().attach(instrument);
                 } catch (ProbeException ex) {
@@ -292,7 +304,7 @@
         @Override
         public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
             if (countingTag == tag) {
-                final Instrument instrument = Instrument.create(eventReceiver, NodeExecCounter.class.getSimpleName());
+                final Instrument instrument = Instrument.create(eventListener, NodeExecCounter.class.getSimpleName());
                 instruments.add(instrument);
                 probe.attach(instrument);
             }