Mercurial > hg > graal-jvmci-8
comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java @ 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 | c5b20395a8bf |
children | b5467bb34b24 |
comparison
equal
deleted
inserted
replaced
19247:128586040207 | 19248:a2ff253c458f |
---|---|
27 import java.io.*; | 27 import java.io.*; |
28 import java.util.*; | 28 import java.util.*; |
29 import java.util.Map.Entry; | 29 import java.util.Map.Entry; |
30 import java.util.concurrent.atomic.*; | 30 import java.util.concurrent.atomic.*; |
31 | 31 |
32 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | |
33 import com.oracle.truffle.api.frame.*; | 32 import com.oracle.truffle.api.frame.*; |
34 import com.oracle.truffle.api.instrument.*; | 33 import com.oracle.truffle.api.instrument.*; |
35 import com.oracle.truffle.api.instrument.impl.*; | 34 import com.oracle.truffle.api.instrument.impl.*; |
36 import com.oracle.truffle.api.nodes.*; | 35 import com.oracle.truffle.api.nodes.*; |
37 import com.oracle.truffle.api.nodes.Node.Child; | 36 import com.oracle.truffle.api.nodes.Node.Child; |
50 * <p> | 49 * <p> |
51 * <b>Execution Counts</b> | 50 * <b>Execution Counts</b> |
52 * <p> | 51 * <p> |
53 * <ul> | 52 * <ul> |
54 * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented | 53 * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented |
55 * to produce the event {@link TruffleEventReceiver#enter(Node, VirtualFrame)};</li> | 54 * to produce the event {@link TruffleEventListener#enter(Node, VirtualFrame)};</li> |
56 * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which | 55 * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which |
57 * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li> | 56 * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li> |
58 * <li>Execution calls are tabulated only at nodes present in the AST when originally created; | 57 * <li>Execution calls are tabulated only at nodes present in the AST when originally created; |
59 * dynamically added nodes will not be instrumented.</li> | 58 * dynamically added nodes will not be instrumented.</li> |
60 * </ul> | 59 * </ul> |
73 * @see SyntaxTag | 72 * @see SyntaxTag |
74 */ | 73 */ |
75 public final class CoverageTracker extends InstrumentationTool { | 74 public final class CoverageTracker extends InstrumentationTool { |
76 | 75 |
77 /** Counting data. */ | 76 /** Counting data. */ |
78 private final Map<LineLocation, CoverageCounter> counters = new HashMap<>(); | 77 private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>(); |
79 | 78 |
80 /** For disposal. */ | 79 /** Needed for disposal. */ |
81 private final List<Instrument> instruments = new ArrayList<>(); | 80 private final List<Instrument> instruments = new ArrayList<>(); |
82 | 81 |
83 /** | 82 /** |
84 * Counting is restricted to nodes holding this tag. | 83 * Coverage counting is restricted to nodes holding this tag. |
85 */ | 84 */ |
86 private final SyntaxTag countingTag; | 85 private final SyntaxTag countingTag; |
87 | 86 |
88 private final ProbeListener probeListener; | 87 private final ProbeListener probeListener; |
89 | 88 |
110 return true; | 109 return true; |
111 } | 110 } |
112 | 111 |
113 @Override | 112 @Override |
114 protected void internalReset() { | 113 protected void internalReset() { |
115 counters.clear(); | 114 coverageMap.clear(); |
116 } | 115 } |
117 | 116 |
118 @Override | 117 @Override |
119 protected void internalDispose() { | 118 protected void internalDispose() { |
120 Probe.removeProbeListener(probeListener); | 119 Probe.removeProbeListener(probeListener); |
140 /** | 139 /** |
141 * Counters for every {Source, line number} for which a counter was installed, i.e. for | 140 * Counters for every {Source, line number} for which a counter was installed, i.e. for |
142 * every line associated with an appropriately tagged AST node; iterable in order of source | 141 * every line associated with an appropriately tagged AST node; iterable in order of source |
143 * name, then line number. | 142 * name, then line number. |
144 */ | 143 */ |
145 final TreeSet<Entry<LineLocation, CoverageCounter>> entries = new TreeSet<>(new LineLocationEntryComparator()); | 144 final TreeSet<Entry<LineLocation, CoverageRecord>> entries = new TreeSet<>(new LineLocationEntryComparator()); |
146 | 145 |
147 final Map<Source, Long[]> results = new HashMap<>(); | 146 for (Entry<LineLocation, CoverageRecord> entry : coverageMap.entrySet()) { |
148 | |
149 for (Entry<LineLocation, CoverageCounter> entry : counters.entrySet()) { | |
150 entries.add(entry); | 147 entries.add(entry); |
151 } | 148 } |
149 final Map<Source, Long[]> result = new HashMap<>(); | |
152 Source curSource = null; | 150 Source curSource = null; |
153 Long[] curLineTable = null; | 151 Long[] curLineTable = null; |
154 for (Entry<LineLocation, CoverageCounter> entry : entries) { | 152 for (Entry<LineLocation, CoverageRecord> entry : entries) { |
155 final LineLocation key = entry.getKey(); | 153 final LineLocation key = entry.getKey(); |
156 final Source source = key.getSource(); | 154 final Source source = key.getSource(); |
157 final int lineNo = key.getLineNumber(); | 155 final int lineNo = key.getLineNumber(); |
158 if (source != curSource) { | 156 if (source != curSource) { |
159 if (curSource != null) { | 157 if (curSource != null) { |
160 results.put(curSource, curLineTable); | 158 result.put(curSource, curLineTable); |
161 } | 159 } |
162 curSource = source; | 160 curSource = source; |
163 curLineTable = new Long[source.getLineCount()]; | 161 curLineTable = new Long[source.getLineCount()]; |
164 } | 162 } |
165 curLineTable[lineNo - 1] = entry.getValue().count.longValue(); | 163 curLineTable[lineNo - 1] = entry.getValue().count.longValue(); |
166 } | 164 } |
167 if (curSource != null) { | 165 if (curSource != null) { |
168 results.put(curSource, curLineTable); | 166 result.put(curSource, curLineTable); |
169 } | 167 } |
170 return results; | 168 return result; |
171 } | 169 } |
172 | 170 |
173 /** | 171 /** |
174 * A default printer for the current line counts, producing lines of the form " (<count>) <line | 172 * A default printer for the current line counts, producing lines of the form " (<count>) <line |
175 * number> : <text of line>", grouped by source. | 173 * number> : <text of line>", grouped by source. |
181 /** | 179 /** |
182 * Counters for every {Source, line number} for which a counter was installed, i.e. for | 180 * Counters for every {Source, line number} for which a counter was installed, i.e. for |
183 * every line associated with an appropriately tagged AST node; iterable in order of source | 181 * every line associated with an appropriately tagged AST node; iterable in order of source |
184 * name, then line number. | 182 * name, then line number. |
185 */ | 183 */ |
186 final TreeSet<Entry<LineLocation, CoverageCounter>> entries = new TreeSet<>(new LineLocationEntryComparator()); | 184 final TreeSet<Entry<LineLocation, CoverageRecord>> entries = new TreeSet<>(new LineLocationEntryComparator()); |
187 | 185 |
188 for (Entry<LineLocation, CoverageCounter> entry : counters.entrySet()) { | 186 for (Entry<LineLocation, CoverageRecord> entry : coverageMap.entrySet()) { |
189 entries.add(entry); | 187 entries.add(entry); |
190 } | 188 } |
191 Source curSource = null; | 189 Source curSource = null; |
192 int curLineNo = 1; | 190 int curLineNo = 1; |
193 for (Entry<LineLocation, CoverageCounter> entry : entries) { | 191 for (Entry<LineLocation, CoverageRecord> entry : entries) { |
194 final LineLocation key = entry.getKey(); | 192 final LineLocation key = entry.getKey(); |
195 final Source source = key.getSource(); | 193 final Source source = key.getSource(); |
196 final int lineNo = key.getLineNumber(); | 194 final int lineNo = key.getLineNumber(); |
197 if (source != curSource) { | 195 if (source != curSource) { |
198 if (curSource != null) { | 196 if (curSource != null) { |
226 out.format(" %3d: ", lineNo); | 224 out.format(" %3d: ", lineNo); |
227 out.println(source.getCode(lineNo)); | 225 out.println(source.getCode(lineNo)); |
228 } | 226 } |
229 | 227 |
230 /** | 228 /** |
231 * A receiver for events at each instrumented AST location. This receiver counts | 229 * A listener for events at each instrumented AST location. This listener counts |
232 * "execution calls" to the instrumented node and is <em>stateful</em>. State in receivers must | 230 * "execution calls" to the instrumented node and is <em>stateful</em>. State in listeners must |
233 * be considered carefully since ASTs, along with all instrumentation (including event receivers | 231 * be considered carefully since ASTs, along with all instrumentation (including event listener |
234 * such as this) are routinely cloned by the Truffle runtime. AST cloning is <em>shallow</em> | 232 * such as this) are routinely cloned by the Truffle runtime. AST cloning is <em>shallow</em> |
235 * (for non- {@link Child} nodes), so in this case the actual count <em>is shared</em> among all | 233 * (for non- {@link Child} nodes), so in this case the actual count <em>is shared</em> among all |
236 * the clones; the count is also held in a table indexed by source line. | 234 * the clones; the count is also held in a table indexed by source line. |
237 * <p> | 235 * <p> |
238 * In contrast, a primitive field would <em>not</em> be shared among clones and resulting counts | 236 * In contrast, a primitive field would <em>not</em> be shared among clones and resulting counts |
239 * would not be accurate. | 237 * would not be accurate. |
240 */ | 238 */ |
241 private final class CoverageEventReceiver extends DefaultEventReceiver { | 239 private final class CoverageEventListener extends DefaultEventListener { |
242 | 240 |
243 /** | 241 /** |
244 * Shared by all clones of the associated instrument and by the table of counters for the | 242 * Shared by all clones of the associated instrument and by the table of counters for the |
245 * line. | 243 * line. |
246 */ | 244 */ |
247 private final AtomicLong count; | 245 private final AtomicLong count; |
248 | 246 |
249 CoverageEventReceiver(AtomicLong count) { | 247 CoverageEventListener(AtomicLong count) { |
250 this.count = count; | 248 this.count = count; |
251 } | 249 } |
252 | 250 |
253 @Override | 251 @Override |
254 @TruffleBoundary | |
255 public void enter(Node node, VirtualFrame frame) { | 252 public void enter(Node node, VirtualFrame frame) { |
256 if (isEnabled()) { | 253 if (isEnabled()) { |
257 count.getAndIncrement(); | 254 count.getAndIncrement(); |
258 } | 255 } |
259 } | 256 } |
260 } | 257 } |
261 | 258 |
262 private static final class LineLocationEntryComparator implements Comparator<Entry<LineLocation, CoverageCounter>> { | 259 private static final class LineLocationEntryComparator implements Comparator<Entry<LineLocation, CoverageRecord>> { |
263 | 260 |
264 public int compare(Entry<LineLocation, CoverageCounter> e1, Entry<LineLocation, CoverageCounter> e2) { | 261 public int compare(Entry<LineLocation, CoverageRecord> e1, Entry<LineLocation, CoverageRecord> e2) { |
265 return LineLocation.COMPARATOR.compare(e1.getKey(), e2.getKey()); | 262 return LineLocation.COMPARATOR.compare(e1.getKey(), e2.getKey()); |
266 } | 263 } |
267 } | 264 } |
268 | 265 |
269 /** | 266 /** |
277 | 274 |
278 final SourceSection srcSection = probe.getProbedSourceSection(); | 275 final SourceSection srcSection = probe.getProbedSourceSection(); |
279 if (srcSection == null) { | 276 if (srcSection == null) { |
280 // TODO (mlvdv) report this? | 277 // TODO (mlvdv) report this? |
281 } else { | 278 } else { |
279 // Get the source line where the | |
282 final LineLocation lineLocation = srcSection.getLineLocation(); | 280 final LineLocation lineLocation = srcSection.getLineLocation(); |
283 CoverageCounter counter = counters.get(lineLocation); | 281 CoverageRecord record = coverageMap.get(lineLocation); |
284 if (counter != null) { | 282 if (record != null) { |
285 // Another node starts on same line; count only the first (textually) | 283 // Another node starts on same line; count only the first (textually) |
286 if (srcSection.getCharIndex() > counter.srcSection.getCharIndex()) { | 284 if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) { |
287 // Counter already in place, corresponds to code earlier on line | 285 // Record already in place, corresponds to code earlier on line |
288 return; | 286 return; |
289 } else { | 287 } else { |
290 // Counter already in place, corresponds to later code; replace it | 288 // Record already in place, corresponds to later code; replace it |
291 counter.instrument.dispose(); | 289 record.instrument.dispose(); |
292 } | 290 } |
293 } | 291 } |
294 final AtomicLong count = new AtomicLong(); | 292 final AtomicLong count = new AtomicLong(); |
295 final CoverageEventReceiver eventReceiver = new CoverageEventReceiver(count); | 293 final CoverageEventListener eventListener = new CoverageEventListener(count); |
296 final Instrument instrument = Instrument.create(eventReceiver, CoverageTracker.class.getSimpleName()); | 294 final Instrument instrument = Instrument.create(eventListener, CoverageTracker.class.getSimpleName()); |
297 instruments.add(instrument); | 295 instruments.add(instrument); |
298 probe.attach(instrument); | 296 probe.attach(instrument); |
299 counters.put(lineLocation, new CoverageCounter(srcSection, instrument, count)); | 297 coverageMap.put(lineLocation, new CoverageRecord(srcSection, instrument, count)); |
300 } | 298 } |
301 } | 299 } |
302 } | 300 } |
303 } | 301 } |
304 | 302 |
305 private class CoverageCounter { | 303 private class CoverageRecord { |
306 final SourceSection srcSection; | 304 |
307 final Instrument instrument; | 305 final SourceSection srcSection; // The text of the code being counted |
306 final Instrument instrument; // The attached Instrument, in case need to remove. | |
308 final AtomicLong count; | 307 final AtomicLong count; |
309 | 308 |
310 CoverageCounter(SourceSection srcSection, Instrument instrument, AtomicLong count) { | 309 CoverageRecord(SourceSection srcSection, Instrument instrument, AtomicLong count) { |
311 this.srcSection = srcSection; | 310 this.srcSection = srcSection; |
312 this.instrument = instrument; | 311 this.instrument = instrument; |
313 this.count = count; | 312 this.count = count; |
314 } | 313 } |
315 } | 314 } |