com.oracle.truffle.tools.CoverageTracker Maven / Gradle / Ivy
/*
* Copyright (c) 2015, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.truffle.tools;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeSet;
import com.oracle.truffle.api.instrument.Instrumenter;
import com.oracle.truffle.api.instrument.Probe;
import com.oracle.truffle.api.instrument.ProbeInstrument;
import com.oracle.truffle.api.instrument.ProbeListener;
import com.oracle.truffle.api.instrument.SimpleInstrumentListener;
import com.oracle.truffle.api.instrument.StandardSyntaxTag;
import com.oracle.truffle.api.instrument.SyntaxTag;
import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.LineLocation;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
/**
* An {@linkplain Instrumenter.Tool Instrumentation Tool} that counts interpreter
* execution calls to AST nodes that hold a specified {@linkplain SyntaxTag syntax tag},
* tabulated by source and line number associated with each node. Syntax tags are presumed to be
* applied external to the tool. If no tag is specified, {@linkplain StandardSyntaxTag#STATEMENT
* STATEMENT} is used, corresponding to conventional behavior for code coverage tools.
*
* Tool Life Cycle
*
* See {@linkplain Instrumenter.Tool Instrumentation Tool} for the life cycle common to all such
* tools.
*
* Execution Counts
*
*
* - "Execution call" on a node is is defined as invocation of a node method that is instrumented
* to produce the event {@link SimpleInstrumentListener#onEnter(Probe)};
* - Execution calls are tabulated only at nodes where the guest languages supports
* {@linkplain Instrumenter#probe(Node) probing}.
* - Execution calls are tabulated only at nodes present in the AST when originally created;
* dynamically added nodes will not be instrumented.
*
*
* Results
*
* A modification-safe copy of the {@linkplain #getCounts() counts} can be retrieved at any time,
* without effect on the state of the tool.
*
*
* A "default" {@linkplain #print(PrintStream) print()} method can summarizes the current counts at
* any time in a simple textual format, with no other effect on the state of the tool.
*
*
* @see ProbeInstrument
* @see SyntaxTag
*/
public final class CoverageTracker extends Instrumenter.Tool {
/** Counting data. */
private final Map coverageMap = new HashMap<>();
/** Needed for disposal. */
private final List instruments = new ArrayList<>();
/**
* Coverage counting is restricted to nodes holding this tag.
*/
private final SyntaxTag countingTag;
private final ProbeListener probeListener;
/**
* Create a per-line coverage tool for nodes tagged as {@linkplain StandardSyntaxTag#STATEMENT
* statements} in subsequently created ASTs.
*/
public CoverageTracker() {
this(StandardSyntaxTag.STATEMENT);
}
/**
* Create a per-line coverage tool for nodes tagged as specified, presuming that tags applied
* outside this tool.
*/
public CoverageTracker(SyntaxTag tag) {
this.probeListener = new CoverageProbeListener();
this.countingTag = tag;
}
@Override
protected boolean internalInstall() {
final Instrumenter instrumenter = getInstrumenter();
for (Probe probe : instrumenter.findProbesTaggedAs(countingTag)) {
addCoverageCounter(probe);
}
instrumenter.addProbeListener(probeListener);
return true;
}
@Override
protected void internalReset() {
coverageMap.clear();
}
@Override
protected void internalDispose() {
getInstrumenter().removeProbeListener(probeListener);
for (ProbeInstrument instrument : instruments) {
instrument.dispose();
}
}
/**
* Gets a modification-safe summary of the current per-type node execution counts; does not
* affect the counts.
*
* The map holds an array for each source, and elements of the a array corresponding to the
* textual lines of that source. An array entry contains null if the corresponding line of
* source is associated with no nodes of the relevant type, i.e. where no count was made. A
* numeric entry represents the execution count at the corresponding line of source.
*
* Note: source line numbers are 1-based, so array index {@code i} corresponds to source
* line number {@code i + 1}
*/
public Map