
com.oracle.truffle.api.debug.Debugger 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.api.debug;
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
import com.oracle.truffle.api.impl.Accessor;
import com.oracle.truffle.api.instrument.Instrumenter;
import com.oracle.truffle.api.instrument.KillException;
import com.oracle.truffle.api.instrument.Probe;
import com.oracle.truffle.api.instrument.StandardAfterInstrumentListener;
import com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener;
import com.oracle.truffle.api.instrument.StandardSyntaxTag;
import com.oracle.truffle.api.instrument.SyntaxTag;
import com.oracle.truffle.api.instrument.TagInstrument;
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;
/**
* Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}.
* Instance of this class is delivered via {@link SuspendedEvent#getDebugger()} and
* {@link ExecutionEvent#getDebugger()} events, once {@link com.oracle.truffle.api.debug debugging
* is turned on}.
*/
@SuppressWarnings("javadoc")
public final class Debugger {
private static final boolean TRACE = false;
private static final String TRACE_PREFIX = "Debugger: ";
private static final PrintStream OUT = System.out;
private static final SyntaxTag STEPPING_TAG = StandardSyntaxTag.STATEMENT;
private static final SyntaxTag CALL_TAG = StandardSyntaxTag.CALL;
private static void trace(String format, Object... args) {
if (TRACE) {
OUT.println(TRACE_PREFIX + String.format(format, args));
}
}
private final Instrumenter instrumenter;
private final Object vm;
private Source lastSource;
interface BreakpointCallback {
/**
* Passes control to the debugger with execution suspended.
*/
void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason);
}
interface WarningLog {
/**
* Logs a warning that is kept until the start of the next execution.
*/
void addWarning(String warning);
}
private final BreakpointCallback breakpointCallback;
private final WarningLog warningLog;
/**
* Implementation of line-oriented breakpoints.
*/
private final LineBreakpointFactory lineBreaks;
/**
* Implementation of tag-oriented breakpoints.
*/
private final TagBreakpointFactory tagBreaks;
/**
* Head of the stack of executions.
*/
private DebugExecutionContext debugContext;
Debugger(Object vm, Instrumenter instrumenter) {
this.vm = vm;
this.instrumenter = instrumenter;
Source.setFileCaching(true);
// Initialize execution context stack
debugContext = new DebugExecutionContext(null, null, 0);
debugContext.setStrategy(0, new Continue());
debugContext.contextTrace("START EXEC DEFAULT");
breakpointCallback = new BreakpointCallback() {
@TruffleBoundary
public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) {
debugContext.halt(astNode, mFrame, true, haltReason);
}
};
warningLog = new WarningLog() {
public void addWarning(String warning) {
assert debugContext != null;
debugContext.logWarning(warning);
}
};
this.lineBreaks = new LineBreakpointFactory(this, breakpointCallback, warningLog);
this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog);
}
/**
* Sets a breakpoint to halt at a source line.
*
* @param ignoreCount number of hits to ignore before halting
* @param lineLocation where to set the breakpoint (source, line number)
* @param oneShot breakpoint disposes itself after fist hit, if {@code true}
* @return a new breakpoint, initially enabled
* @throws IOException if the breakpoint can not be set.
*/
@TruffleBoundary
public Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException {
return lineBreaks.create(ignoreCount, lineLocation, oneShot);
}
/**
* Sets a breakpoint to halt at any node holding a specified {@link SyntaxTag}.
*
* @param ignoreCount number of hits to ignore before halting
* @param oneShot if {@code true} breakpoint removes it self after a hit
* @return a new breakpoint, initially enabled
* @throws IOException if the breakpoint already set
*/
@TruffleBoundary
public Breakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException {
return tagBreaks.create(ignoreCount, tag, oneShot);
}
/**
* Gets all existing breakpoints, whatever their status, in natural sorted order. Modification
* save.
*/
@TruffleBoundary
public Collection getBreakpoints() {
final Collection result = new ArrayList<>();
result.addAll(lineBreaks.getAll());
result.addAll(tagBreaks.getAll());
return result;
}
/**
* Prepare to execute in Continue mode when guest language program execution resumes. In this
* mode:
*
* - Execution will continue until either:
*
* - execution arrives at a node to which an enabled breakpoint is attached,
* or:
* - execution completes.
*
*
*/
@TruffleBoundary
void prepareContinue(int depth) {
debugContext.setStrategy(depth, new Continue());
}
/**
* Prepare to execute in StepInto mode when guest language program execution resumes. In this
* mode:
*
* - User breakpoints are disabled.
* - Execution will continue until either:
*
* - execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT
* STATMENT}, or:
* - execution completes.
*
* - StepInto mode persists only through one resumption (i.e. {@code stepIntoCount} steps),
* and reverts by default to Continue mode.
*
*
* @param stepCount the number of times to perform StepInto before halting
* @throws IllegalArgumentException if the specified number is {@code <= 0}
*/
@TruffleBoundary
void prepareStepInto(int stepCount) {
if (stepCount <= 0) {
throw new IllegalArgumentException();
}
debugContext.setStrategy(new StepInto(stepCount));
}
/**
* Prepare to execute in StepOut mode when guest language program execution resumes. In this
* mode:
*
* - User breakpoints are enabled.
* - Execution will continue until either:
*
* - execution arrives at the nearest enclosing call site on the stack, or
* - execution completes.
*
* - StepOut mode persists only through one resumption, and reverts by default to Continue
* mode.
*
*/
@TruffleBoundary
void prepareStepOut() {
debugContext.setStrategy(new StepOut());
}
/**
* Prepare to execute in StepOver mode when guest language program execution resumes. In this
* mode:
*
* - Execution will continue until either:
*
* - execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT
* STATEMENT} when not nested in one or more function/method calls, or:
* - execution arrives at a node to which a breakpoint is attached and when nested in one or
* more function/method calls, or:
* - execution completes.
*
* - StepOver mode persists only through one resumption (i.e. {@code stepOverCount} steps),
* and reverts by default to Continue mode.
*
*
* @param stepCount the number of times to perform StepInto before halting
* @throws IllegalArgumentException if the specified number is {@code <= 0}
*/
@TruffleBoundary
void prepareStepOver(int stepCount) {
if (stepCount <= 0) {
throw new IllegalArgumentException();
}
debugContext.setStrategy(new StepOver(stepCount));
}
Instrumenter getInstrumenter() {
return instrumenter;
}
/**
* A mode of user navigation from a current code location to another, e.g "step in" vs.
* "step over".
*/
private abstract class StepStrategy {
private DebugExecutionContext context;
protected final String strategyName;
protected StepStrategy() {
this.strategyName = getClass().getSimpleName();
}
final String getName() {
return strategyName;
}
/**
* Reconfigure the debugger so that when execution continues the program will halt at the
* location specified by this strategy.
*/
final void enable(DebugExecutionContext c, int stackDepth) {
this.context = c;
setStrategy(stackDepth);
}
/**
* Return the debugger to the default navigation mode.
*/
final void disable() {
unsetStrategy();
}
@TruffleBoundary
final void halt(Node astNode, MaterializedFrame mFrame, boolean before) {
context.halt(astNode, mFrame, before, this.getClass().getSimpleName());
}
@TruffleBoundary
final void replaceStrategy(StepStrategy newStrategy) {
context.setStrategy(newStrategy);
}
@TruffleBoundary
protected final void strategyTrace(String action, String format, Object... args) {
if (TRACE) {
context.contextTrace("%s (%s) %s", action, strategyName, String.format(format, args));
}
}
@TruffleBoundary
protected final void suspendUserBreakpoints() {
lineBreaks.setActive(false);
tagBreaks.setActive(false);
}
@SuppressWarnings("unused")
protected final void restoreUserBreakpoints() {
lineBreaks.setActive(true);
tagBreaks.setActive(true);
}
/**
* Reconfigure the debugger so that when execution continues, it will do so using this mode
* of navigation.
*/
protected abstract void setStrategy(int stackDepth);
/**
* Return to the debugger to the default mode of navigation.
*/
protected abstract void unsetStrategy();
}
/**
* Strategy: the null stepping strategy.
*
* - User breakpoints are enabled.
* - Execution continues until either:
*
* - execution arrives at a node with attached user breakpoint, or:
* - execution completes.
*
*
*/
private final class Continue extends StepStrategy {
@Override
protected void setStrategy(int stackDepth) {
}
@Override
protected void unsetStrategy() {
}
}
/**
* Strategy: per-statement stepping.
*
* - User breakpoints are enabled.
* - Execution continues until either:
*
* - execution arrives at a STATEMENT node, or:
* - execution returns to a CALL node and the call stack is smaller then when
* execution started, or:
* - execution completes.
*
*
*
* @see Debugger#prepareStepInto(int)
*/
private final class StepInto extends StepStrategy {
private TagInstrument beforeTagInstrument;
private TagInstrument afterTagInstrument;
private int unfinishedStepCount;
StepInto(int stepCount) {
super();
this.unfinishedStepCount = stepCount;
}
@Override
protected void setStrategy(final int stackDepth) {
beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() {
@TruffleBoundary
@Override
public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
// HALT: just before statement
--unfinishedStepCount;
strategyTrace("HALT BEFORE", "unfinished steps=%d", unfinishedStepCount);
// Should run in fast path
if (unfinishedStepCount <= 0) {
halt(node, vFrame.materialize(), true);
}
strategyTrace("RESUME BEFORE", "");
}
}, "Debugger StepInto");
afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() {
public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
doHalt(node, vFrame.materialize());
}
public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
doHalt(node, vFrame.materialize());
}
public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Throwable exception) {
doHalt(node, vFrame.materialize());
}
@TruffleBoundary
private void doHalt(Node node, MaterializedFrame mFrame) {
--unfinishedStepCount;
strategyTrace(null, "HALT AFTER unfinished steps=%d", unfinishedStepCount);
if (currentStackDepth() < stackDepth) {
// HALT: just "stepped out"
if (unfinishedStepCount <= 0) {
halt(node, mFrame, false);
}
}
strategyTrace("RESUME AFTER", "");
}
}, "Debugger StepInto");
}
@Override
protected void unsetStrategy() {
beforeTagInstrument.dispose();
afterTagInstrument.dispose();
}
}
/**
* Strategy: execution to nearest enclosing call site.
*
* - User breakpoints are enabled.
* - Execution continues until either:
*
* - execution arrives at a node with attached user breakpoint, or:
* - execution returns to a CALL node and the call stack is smaller than when
* execution started, or:
* - execution completes.
*
*
*
* @see Debugger#prepareStepOut()
*/
private final class StepOut extends StepStrategy {
private TagInstrument afterTagInstrument;
@Override
protected void setStrategy(final int stackDepth) {
afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() {
public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
doHalt(node, vFrame.materialize());
}
public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
doHalt(node, vFrame.materialize());
}
public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Throwable exception) {
doHalt(node, vFrame.materialize());
}
@TruffleBoundary
private void doHalt(Node node, MaterializedFrame mFrame) {
// HALT:
final int currentStackDepth = currentStackDepth();
strategyTrace("HALT AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth);
if (currentStackDepth < stackDepth) {
halt(node, mFrame, false);
}
strategyTrace("RESUME AFTER", "");
}
}, "Debugger StepOut");
}
@Override
protected void unsetStrategy() {
afterTagInstrument.dispose();
}
}
/**
* Strategy: per-statement stepping, so long as not nested in method calls (i.e. at original
* stack depth).
*
* - User breakpoints are enabled.
* - Execution continues until either:
*
* - execution arrives at a STATEMENT node with stack depth no more than when started
* or:
* - the program completes.
*
*
*/
private final class StepOver extends StepStrategy {
private TagInstrument beforeTagInstrument;
private TagInstrument afterTagInstrument;
private int unfinishedStepCount;
StepOver(int stepCount) {
this.unfinishedStepCount = stepCount;
}
@Override
protected void setStrategy(final int stackDepth) {
beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() {
@TruffleBoundary
@Override
public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
final int currentStackDepth = currentStackDepth();
if (currentStackDepth <= stackDepth) {
// HALT: stack depth unchanged or smaller; treat like StepInto
--unfinishedStepCount;
if (TRACE) {
strategyTrace("HALT BEFORE", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
}
// Test should run in fast path
if (unfinishedStepCount <= 0) {
halt(node, vFrame.materialize(), true);
}
} else {
// CONTINUE: Stack depth increased; don't count as a step
strategyTrace("STEP INTO", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
// Stop treating like StepInto, start treating like StepOut
replaceStrategy(new StepOverNested(unfinishedStepCount, stackDepth));
}
strategyTrace("RESUME BEFORE", "");
}
}, "Debugger StepOver");
afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() {
public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
doHalt(node, vFrame.materialize());
}
public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
doHalt(node, vFrame.materialize());
}
public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Throwable exception) {
doHalt(node, vFrame.materialize());
}
@TruffleBoundary
private void doHalt(Node node, MaterializedFrame mFrame) {
final int currentStackDepth = currentStackDepth();
if (currentStackDepth < stackDepth) {
// HALT: just "stepped out"
--unfinishedStepCount;
strategyTrace("HALT AFTER", "unfinished steps=%d stackDepth: start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
// Should run in fast path
if (unfinishedStepCount <= 0) {
halt(node, mFrame, false);
}
strategyTrace("RESUME AFTER", "");
}
}
}, "Debugger StepOver");
}
@Override
protected void unsetStrategy() {
beforeTagInstrument.dispose();
afterTagInstrument.dispose();
}
}
/**
* Strategy: per-statement stepping, not into method calls, in effect while at increased stack
* depth
*
* - User breakpoints are enabled.
* - Execution continues until either:
*
* - execution arrives at a STATEMENT node with stack depth no more than when started
* or:
* - the program completes or:
*
*
*/
private final class StepOverNested extends StepStrategy {
private TagInstrument beforeTagInstrument;
private int unfinishedStepCount;
private final int startStackDepth;
StepOverNested(int stepCount, int startStackDepth) {
this.unfinishedStepCount = stepCount;
this.startStackDepth = startStackDepth;
}
@Override
protected void setStrategy(final int stackDepth) {
beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() {
@TruffleBoundary
@Override
public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
final int currentStackDepth = currentStackDepth();
if (currentStackDepth <= startStackDepth) {
// At original step depth (or smaller) after being nested
--unfinishedStepCount;
strategyTrace("HALT AFTER", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
if (unfinishedStepCount <= 0) {
halt(node, vFrame.materialize(), false);
}
// TODO (mlvdv) fixme for multiple steps
strategyTrace("RESUME BEFORE", "");
}
}
}, "Debuger StepOverNested");
}
@Override
protected void unsetStrategy() {
beforeTagInstrument.dispose();
}
}
/**
* Information and debugging state for a single Truffle execution (which make take place over
* one or more suspended executions). This holds interaction state, for example what is
* executing (e.g. some {@link Source}), what the execution mode is ("stepping" or
* "continuing"). When not running, this holds a cache of the Truffle stack for this particular
* execution, effectively hiding the Truffle stack for any currently suspended executions (down
* the stack).
*/
private final class DebugExecutionContext {
// Previous halted context in stack
private final DebugExecutionContext predecessor;
// The current execution level; first is 0.
private final int level; // Number of contexts suspended below
private final Source source;
private final int contextStackBase; // Where the stack for this execution starts
private final List warnings = new ArrayList<>();
private boolean running;
/**
* The stepping strategy currently configured in the debugger.
*/
private StepStrategy strategy;
/**
* Where halted; null if running.
*/
private Node haltedNode;
/**
* Where halted; null if running.
*/
private MaterializedFrame haltedFrame;
/**
* Subset of the Truffle stack corresponding to the current execution, not including the
* current frame.
*/
private List contextStack;
private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) {
this(executionSource, previousContext, -1);
}
private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext, int depth) {
this.source = executionSource;
this.predecessor = previousContext;
this.level = previousContext == null ? 0 : previousContext.level + 1;
// "Base" is the number of stack frames for all nested (halted) executions.
this.contextStackBase = depth == -1 ? currentStackDepth() : depth;
this.running = true;
contextTrace("NEW CONTEXT");
}
/**
* Sets up a strategy for the next resumption of execution.
*
* @param stepStrategy
*/
void setStrategy(StepStrategy stepStrategy) {
setStrategy(currentStackDepth(), stepStrategy);
}
void setStrategy(int depth, StepStrategy stepStrategy) {
if (this.strategy == null) {
this.strategy = stepStrategy;
this.strategy.enable(this, depth);
if (TRACE) {
contextTrace("SET MODE -->" + stepStrategy.getName());
}
} else {
strategy.disable();
strategy = stepStrategy;
strategy.enable(this, currentStackDepth());
contextTrace("SWITCH MODE %s-->%s", strategy.getName(), stepStrategy.getName());
}
}
void clearStrategy() {
if (strategy != null) {
final StepStrategy oldStrategy = strategy;
strategy.disable();
strategy = null;
contextTrace("CLEAR MODE %s-->", oldStrategy.getName());
}
}
/**
* Handle a program halt, caused by a breakpoint, stepping strategy, or other cause.
*
* @param astNode the guest language node at which execution is halted
* @param mFrame the current execution frame where execution is halted
* @param before {@code true} if halted before the node, else after.
*/
@TruffleBoundary
void halt(Node astNode, MaterializedFrame mFrame, boolean before, String haltReason) {
assert running;
assert haltedNode == null;
assert haltedFrame == null;
haltedNode = astNode;
haltedFrame = mFrame;
running = false;
clearStrategy();
// Clean up, just in cased the one-shot breakpoints got confused
lineBreaks.disposeOneShots();
// Includes the "caller" frame (not iterated)
final int contextStackDepth = (currentStackDepth() - contextStackBase) + 1;
final List recentWarnings = new ArrayList<>(warnings);
warnings.clear();
final List frames = new ArrayList<>();
// Map the Truffle stack for this execution, ignore nested executions
// Ignore frames for which no CallNode is available.
// The top/current/0 frame is not produced by the iterator; reported separately
Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() {
int stackIndex = 1;
@Override
public FrameInstance visitFrame(FrameInstance frameInstance) {
if (stackIndex < contextStackDepth) {
final Node callNode = frameInstance.getCallNode();
if (callNode != null) {
final SourceSection sourceSection = callNode.getEncapsulatingSourceSection();
if (sourceSection != null && !sourceSection.getIdentifier().equals("")) {
frames.add(frameInstance);
} else if (TRACE) {
if (callNode != null) {
contextTrace("HIDDEN frame added: " + callNode);
} else {
contextTrace("HIDDEN frame added");
}
frames.add(frameInstance);
}
} else if (TRACE) {
if (callNode != null) {
contextTrace("HIDDEN frame added: " + callNode);
} else {
contextTrace("HIDDEN frame added");
}
frames.add(frameInstance);
}
stackIndex++;
return null;
}
return frameInstance;
}
});
contextStack = Collections.unmodifiableList(frames);
if (TRACE) {
final String reason = haltReason == null ? "" : haltReason + "";
final String where = before ? "BEFORE" : "AFTER";
contextTrace("HALT %s : (%s) stack base=%d", where, reason, contextStackBase);
contextTrace("CURRENT STACK:");
// printStack(OUT);
}
try {
// Pass control to the debug client with current execution suspended
ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, haltedNode, haltedFrame, contextStack, recentWarnings));
// Debug client finished normally, execution resumes
// Presume that the client has set a new strategy (or default to Continue)
running = true;
} catch (KillException e) {
contextTrace("KILL");
throw e;
} finally {
haltedNode = null;
haltedFrame = null;
}
}
void logWarning(String warning) {
warnings.add(warning);
}
/*
* private void printStack(PrintStream stream) { getFrames(); if (frames == null) {
* stream.println(""); } else { final Visualizer visualizer =
* provider.getVisualizer(); for (FrameDebugDescription frameDesc : frames) { final
* StringBuilder sb = new StringBuilder(" frame " + Integer.toString(frameDesc.index()));
* sb.append(":at " + visualizer.displaySourceLocation(frameDesc.node())); sb.append(":in '"
* + visualizer.displayMethodName(frameDesc.node()) + "'"); stream.println(sb.toString()); }
* } }
*/
void contextTrace(String format, Object... args) {
if (TRACE) {
final String srcName = (source != null) ? source.getName() : "no source";
Debugger.trace("<%d> %s (%s)", level, String.format(format, args), srcName);
}
}
}
// TODO (mlvdv) wish there were fast-path access to stack depth
/**
* Depth of current Truffle stack, including nested executions. Includes the top/current frame,
* which the standard iterator does not count: {@code 0} if no executions.
*/
@TruffleBoundary
private static int currentStackDepth() {
final int[] count = {0};
Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() {
@Override
public Void visitFrame(FrameInstance frameInstance) {
count[0] = count[0] + 1;
return null;
}
});
return count[0] == 0 ? 0 : count[0] + 1;
}
void executionStarted(int depth, Source source) {
Source execSource = source;
if (execSource == null) {
execSource = lastSource;
} else {
lastSource = execSource;
}
// Push a new execution context onto stack
debugContext = new DebugExecutionContext(execSource, debugContext, depth);
prepareContinue(depth);
debugContext.contextTrace("START EXEC ");
ACCESSOR.dispatchEvent(vm, new ExecutionEvent(this));
}
void executionEnded() {
lineBreaks.disposeOneShots();
tagBreaks.disposeOneShots();
debugContext.clearStrategy();
debugContext.contextTrace("END EXEC ");
// Pop the stack of execution contexts.
debugContext = debugContext.predecessor;
}
/**
* Evaluates a snippet of code in a halted execution context.
*
* @param ev
* @param code
* @param frameInstance
* @return
* @throws IOException
*/
Object evalInContext(SuspendedEvent ev, String code, FrameInstance frameInstance) throws IOException {
if (frameInstance == null) {
return ACCESSOR.evalInContext(vm, ev, code, debugContext.haltedNode, debugContext.haltedFrame);
} else {
return ACCESSOR.evalInContext(vm, ev, code, frameInstance.getCallNode(), frameInstance.getFrame(FrameAccess.MATERIALIZE, true).materialize());
}
}
@SuppressWarnings("rawtypes")
static final class AccessorDebug extends Accessor {
@Override
protected Closeable executionStart(Object vm, int currentDepth, final Debugger debugger, Source s) {
debugger.executionStarted(currentDepth, s);
return new Closeable() {
@Override
public void close() throws IOException {
debugger.executionEnded();
}
};
}
@Override
protected Debugger createDebugger(Object vm, Instrumenter instrumenter) {
return new Debugger(vm, instrumenter);
}
@Override
protected Class extends TruffleLanguage> findLanguage(Probe probe) {
return super.findLanguage(probe);
}
@Override
protected void dispatchEvent(Object vm, Object event) {
super.dispatchEvent(vm, event);
}
@Override
protected Object evalInContext(Object vm, SuspendedEvent ev, String code, Node node, MaterializedFrame frame) throws IOException {
return super.evalInContext(vm, ev, code, node, frame);
}
}
// registers into Accessor.DEBUG
static final AccessorDebug ACCESSOR = new AccessorDebug();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy