All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.tools.NodeExecCounter 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.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrument.ASTProber;
import com.oracle.truffle.api.instrument.Instrumenter;
import com.oracle.truffle.api.instrument.Probe;
import com.oracle.truffle.api.instrument.ProbeException;
import com.oracle.truffle.api.instrument.ProbeFailure;
import com.oracle.truffle.api.instrument.ProbeInstrument;
import com.oracle.truffle.api.instrument.ProbeListener;
import com.oracle.truffle.api.instrument.StandardInstrumentListener;
import com.oracle.truffle.api.instrument.SyntaxTag;
import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.Node.Child;
import com.oracle.truffle.api.nodes.NodeVisitor;
import com.oracle.truffle.api.nodes.RootNode;

/**
 * An {@linkplain Instrumenter.Tool Instrumentation Tool} that counts interpreter
 * execution calls to AST nodes, tabulated by the type of called nodes; counting can be
 * enabled all nodes or restricted to nodes with a specified {@linkplain SyntaxTag tag}
 * that is presumed to be applied external to the tool.
 * 

* s 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 StandardInstrumentListener#onEnter(Probe, Node, VirtualFrame)};
  • *
  • Execution calls are tabulated only at instrumented nodes, i.e. those for which * {@linkplain Instrumenter#probe(Node) probing} is supported;
  • *
  • Execution calls are tabulated only at nodes present in the AST when originally created; * dynamically added nodes will not be instrumented.
  • *
*

* Failure Log *

* For the benefit of language implementors, the tool maintains a log describing failed attempts to * probe AST nodes. Most failures occur when the type of the wrapper created by * {@link Node#createWrapperNode()} is not assignable to the relevant {@link Child} field in the * node's parent. *

*

* {@linkplain #reset() Resetting} the counts has no effect on the failure log. *

* 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, without effect on the state of the tool. *

* * @see ProbeInstrument * @see SyntaxTag * @see ProbeFailure */ public final class NodeExecCounter extends Instrumenter.Tool { /** * Execution count for AST nodes of a particular type. */ public interface NodeExecutionCount { Class nodeClass(); long executionCount(); } /** * 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 ProbeInstrument}. */ private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() { @Override public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { 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 AtomicLong getCounter(Class nodeClass) { AtomicLong nodeCounter = counters.get(nodeClass); if (nodeCounter == null) { nodeCounter = new AtomicLong(); counters.put(nodeClass, nodeCounter); } return nodeCounter; } }; /** Counting data. */ private final Map, AtomicLong> counters = new HashMap<>(); /** Failure log. */ private final List failures = new ArrayList<>(); /** For disposal. */ private final List instruments = new ArrayList<>(); /** * If non-null, counting is restricted to nodes holding this tag. */ private final SyntaxTag countingTag; /** * Prober used only when instrumenting every node. */ private ASTProber astProber; /** * Listener used only when restricting counting to a specific tag. */ private ProbeListener probeListener; /** * Create a per node-type execution counting tool for all nodes in subsequently created ASTs. */ public NodeExecCounter() { this(null); } /** * Creates a per-type execution counting for nodes tagged as specified in subsequently created * ASTs. */ public NodeExecCounter(SyntaxTag tag) { this.countingTag = tag; } @Override protected boolean internalInstall() { if (countingTag == null) { astProber = new ExecCounterASTProber(); getInstrumenter().registerASTProber(astProber); } else { probeListener = new NodeExecCounterProbeListener(); getInstrumenter().addProbeListener(probeListener); } return true; } @Override protected void internalReset() { counters.clear(); failures.clear(); } @Override protected void internalDispose() { if (astProber != null) { getInstrumenter().unregisterASTProber(astProber); } if (probeListener != null) { 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. */ public NodeExecutionCount[] getCounts() { final Collection, AtomicLong>> entrySet = counters.entrySet(); final NodeExecutionCount[] result = new NodeExecCountImpl[entrySet.size()]; int i = 0; for (Map.Entry, AtomicLong> entry : entrySet) { result[i++] = new NodeExecCountImpl(entry.getKey(), entry.getValue().longValue()); } return result; } /** * Gets a log containing a report of every failed attempt to instrument a node. */ public ProbeFailure[] getFailures() { return failures.toArray(new ProbeFailure[failures.size()]); } /** * A default printer for the current counts, producing lines of the form * " : " in descending order of count. */ public void print(PrintStream out) { print(out, false); } /** * A default printer for the current counts, producing lines of the form * " : " in descending order of count. * * @param out * @param verbose whether to describe nodes on which instrumentation failed */ public void print(PrintStream out, boolean verbose) { final long missedNodes = failures.size(); out.println(); if (countingTag == null) { out.println("Execution counts by node type:"); } else { out.println("\"" + countingTag.name() + "\"-tagged execution counts by node type:"); } final StringBuilder disclaim = new StringBuilder("("); if (missedNodes > 0) { disclaim.append(Long.toString(missedNodes) + " original AST nodes not instrumented, "); } disclaim.append("dynamically added nodes not instrumented)"); out.println(disclaim.toString()); NodeExecutionCount[] execCounts = getCounts(); // Sort in descending order Arrays.sort(execCounts, new Comparator() { public int compare(NodeExecutionCount o1, NodeExecutionCount o2) { return Long.compare(o2.executionCount(), o1.executionCount()); } }); for (NodeExecutionCount nodeCount : execCounts) { out.format("%12d", nodeCount.executionCount()); out.println(" : " + nodeCount.nodeClass().getName()); } if (verbose && missedNodes > 0) { out.println("Instrumentation failures for execution counts:"); for (ProbeFailure failure : failures) { out.println("\t" + failure.getMessage()); } } } /** * A prober that attempts to probe and instrument every node. */ private class ExecCounterASTProber implements ASTProber { public void probeAST(final Instrumenter instrumenter, final RootNode startNode) { startNode.accept(new NodeVisitor() { public boolean visit(Node node) { try { final Probe probe = instrumenter.probe(node); final ProbeInstrument instrument = instrumenter.attach(probe, instrumentListener, "NodeExecCounter"); instruments.add(instrument); } catch (ProbeException ex) { failures.add(ex.getFailure()); } return true; } }); } } /** * A listener that assumes ASTs have been tagged external to this tool, and which instruments * nodes holding a specified tag. */ private class NodeExecCounterProbeListener extends DefaultProbeListener { @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { if (countingTag == tag) { final ProbeInstrument instrument = getInstrumenter().attach(probe, instrumentListener, NodeExecCounter.class.getSimpleName()); instruments.add(instrument); } } } private static class NodeExecCountImpl implements NodeExecutionCount { private final Class nodeClass; private final long count; public NodeExecCountImpl(Class nodeClass, long count) { this.nodeClass = nodeClass; this.count = count; } public Class nodeClass() { return nodeClass; } public long executionCount() { return count; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy