symbolsToWrite) {
send(new SymbolMessage(symbolsToWrite));
}
- public void sendTracingData(final ByteBuffer buffer) {
- traceSocket.send(buffer);
- }
-
public void awaitClient() {
assert VmSettings.TRUFFLE_DEBUGGER_ENABLED;
assert clientConnected != null;
@@ -268,6 +271,7 @@ public void awaitClient() {
try {
messageSocket = clientConnected.get();
assert messageSocket != null;
+ messageSocketInitialized.complete(messageSocket);
traceSocket = traceHandler.getConnection().get();
assert traceSocket != null;
@@ -304,8 +308,23 @@ public void sendTracingData() {
}
}
+ public void sendTracingData(final ByteBuffer buffer) {
+ // log("[DEBUGGER] Trace buffers sent: "+buffer);
+ traceSocket.send(buffer);
+ }
+
public void sendProgramInfo() {
- send(ProgramInfoResponse.create(webDebugger.vm.getArguments()));
+ // when the server has really started, i.e. the client has connected, then do the send
+ messageSocketInitialized.thenRun(
+ () -> send(ProgramInfoResponse.create(webDebugger.vm.getArguments())));
+ }
+
+ public void sendPauseActorResponse(final long pausedActorId) {
+ send(PauseActorResponse.create(pausedActorId));
+ }
+
+ public void sendResumeActorResponse(final long actorId) {
+ send(ResumeActorResponse.create(actorId));
}
public void registerOrUpdate(final LineBreakpoint bp) {
@@ -320,7 +339,11 @@ public Suspension getSuspensionForGlobalId(final long globalId) {
return webDebugger.getSuspension(TraceData.getActivityIdFromGlobalValId(globalId));
}
- static void log(final String str) {
+ public Actor getActorById(final long activityId) {
+ return webDebugger.getActorById(activityId);
+ }
+
+ public static void log(final String str) {
// Checkstyle: stop
System.out.println(str);
// Checkstyle: resume
@@ -330,10 +353,19 @@ public void completeConnection(final WebSocket conn) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> closeAllSockets()));
clientConnected.complete(conn);
+
+ // when the server has really started, i.e. the client has connected, then do the send
+ messageSocketInitialized.thenRun(() -> sendInitResponse());
+ }
+
+ private void sendInitResponse() {
+ // log("[DEBUGGER] Message socket initialized "+messageSocketInitialized.isDone());
+
send(InitializationResponse.create(EntityType.values(),
ActivityType.values(), PassiveEntityType.values(),
DynamicScopeType.values(), SendOp.values(), ReceiveOp.values(),
- BreakpointType.values(), SteppingType.values(), Implementation.values()));
+ BreakpointType.values(), SteppingType.values(), Implementation.values(),
+ MessageReception.values()));
}
private void closeAllSockets() {
diff --git a/src/tools/debugger/RuntimeReflectionRegistration.java b/src/tools/debugger/RuntimeReflectionRegistration.java
index d32b04da11..a6a1723d01 100644
--- a/src/tools/debugger/RuntimeReflectionRegistration.java
+++ b/src/tools/debugger/RuntimeReflectionRegistration.java
@@ -13,27 +13,12 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import tools.debugger.message.InitializationResponse;
-import tools.debugger.message.InitializeConnection;
+import tools.debugger.breakpoints.BreakpointInfo;
+import tools.debugger.breakpoints.LineBreakpoint;
+import tools.debugger.breakpoints.SectionBreakpoint;
+import tools.debugger.message.*;
import tools.debugger.message.Message.IncommingMessage;
import tools.debugger.message.Message.OutgoingMessage;
-import tools.debugger.message.ProgramInfoRequest;
-import tools.debugger.message.ProgramInfoResponse;
-import tools.debugger.message.ScopesRequest;
-import tools.debugger.message.ScopesResponse;
-import tools.debugger.message.SourceMessage;
-import tools.debugger.message.StackTraceRequest;
-import tools.debugger.message.StackTraceResponse;
-import tools.debugger.message.StepMessage;
-import tools.debugger.message.StoppedMessage;
-import tools.debugger.message.SymbolMessage;
-import tools.debugger.message.TraceDataRequest;
-import tools.debugger.message.UpdateBreakpoint;
-import tools.debugger.message.VariablesRequest;
-import tools.debugger.message.VariablesResponse;
-import tools.debugger.session.BreakpointInfo;
-import tools.debugger.session.LineBreakpoint;
-import tools.debugger.session.SectionBreakpoint;
/**
@@ -79,6 +64,8 @@ public void register(final String name, final Class> klass) {
outMsgs.register(ScopesResponse.class);
outMsgs.register(VariablesResponse.class);
outMsgs.register(ProgramInfoResponse.class);
+ outMsgs.register("pauseActorResponse", PauseActorResponse.class);
+ outMsgs.register("resumeActorResponse", ResumeActorResponse.class);
ClassGroup inMsgs = new ClassGroup(IncommingMessage.class, "action", true);
inMsgs.register(InitializeConnection.class);
@@ -89,6 +76,8 @@ public void register(final String name, final Class> klass) {
inMsgs.register(VariablesRequest.class);
inMsgs.register(ProgramInfoRequest.class);
inMsgs.register(TraceDataRequest.class);
+ inMsgs.register(EvaluateExpressionRequest.class);
+ inMsgs.register("pauseActorMessageReceiver", PauseActorRequest.class);
ClassGroup bps = new ClassGroup(BreakpointInfo.class, "type", true);
bps.register(LineBreakpoint.class);
diff --git a/src/tools/debugger/Tags.java b/src/tools/debugger/Tags.java
index 69fd083e85..82dcedf6aa 100644
--- a/src/tools/debugger/Tags.java
+++ b/src/tools/debugger/Tags.java
@@ -24,6 +24,8 @@
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.source.SourceSection;
+import som.VM;
+
public abstract class Tags {
private Tags() {}
diff --git a/src/tools/debugger/WebDebugger.java b/src/tools/debugger/WebDebugger.java
index 593e19bf11..4670c45ecc 100644
--- a/src/tools/debugger/WebDebugger.java
+++ b/src/tools/debugger/WebDebugger.java
@@ -25,12 +25,14 @@
import bd.source.SourceCoordinate;
import som.VM;
+import som.interpreter.actors.Actor;
import som.vm.Activity;
import som.vm.Symbols;
import tools.TraceData;
import tools.concurrency.TracingActivityThread;
+import tools.concurrency.TracingActors;
+import tools.debugger.breakpoints.Breakpoints;
import tools.debugger.frontend.Suspension;
-import tools.debugger.session.Breakpoints;
/**
@@ -108,7 +110,7 @@ public void prepareSteppingAfterNextRootNode(final Thread thread) {
breakpoints.prepareSteppingAfterNextRootNode(thread);
}
- Suspension getSuspension(final long activityId) {
+ public Suspension getSuspension(final long activityId) {
return idToSuspension.get(activityId);
}
@@ -178,5 +180,9 @@ public Breakpoints getBreakpoints() {
return breakpoints;
}
+ public Actor getActorById(long actorId) {
+ return TracingActors.TracingActor.getActorById(actorId);
+ }
+
private static Gson jsonProcessor = RuntimeReflectionRegistration.createJsonProcessor();
}
diff --git a/src/tools/debugger/WebSocketHandler.java b/src/tools/debugger/WebSocketHandler.java
index fdce3d7542..d0955ab815 100644
--- a/src/tools/debugger/WebSocketHandler.java
+++ b/src/tools/debugger/WebSocketHandler.java
@@ -34,7 +34,8 @@ public int awaitStartup() throws ExecutionException {
while (true) {
try {
return connectionPort.get();
- } catch (InterruptedException e) { /* Retry on interrupt. */ }
+ } catch (InterruptedException e) {
+ /* Retry on interrupt. */ }
}
}
@@ -64,6 +65,8 @@ public static class MessageHandler extends WebSocketHandler {
public MessageHandler(final int port, final FrontendConnector connector,
final Gson gson) {
super(port);
+ this.setReuseAddr(true);
+ // this.setConnectionLostTimeout(0); // uncomment if timeout fails
this.connector = connector;
this.gson = gson;
}
@@ -86,6 +89,8 @@ public static class TraceHandler extends WebSocketHandler {
public TraceHandler(final int port) {
super(port);
+ this.setReuseAddr(true);
+ // this.setConnectionLostTimeout(0); // uncomment if timeout fails
connection = new CompletableFuture<>();
}
diff --git a/src/tools/debugger/asyncstacktraces/ShadowStackEntry.java b/src/tools/debugger/asyncstacktraces/ShadowStackEntry.java
new file mode 100644
index 0000000000..d06c40711d
--- /dev/null
+++ b/src/tools/debugger/asyncstacktraces/ShadowStackEntry.java
@@ -0,0 +1,170 @@
+package tools.debugger.asyncstacktraces;
+
+import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+
+import som.interpreter.SArguments;
+import som.interpreter.actors.Actor.ActorProcessingThread;
+import som.interpreter.actors.EventualMessage;
+import som.interpreter.actors.SPromise;
+import som.vm.VmSettings;
+
+
+public class ShadowStackEntry {
+
+ protected ShadowStackEntry previous;
+ protected final Node expression;
+ protected final long actorId;
+
+ public static long numberOfAllocations;
+
+ public static final boolean ALLOCATION_COUNT = false;
+
+ public Node getExpression() {
+ return expression;
+ }
+
+ public ShadowStackEntry getPreviousShadowStackEntry() {
+ return previous;
+ }
+
+ public static ShadowStackEntry createTop(final Node expr) {
+ return new ShadowStackEntry(null, expr);
+ }
+
+ public static ShadowStackEntry create(final ShadowStackEntry previous,
+ final Node expr) {
+ assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || previous != null;
+ return new ShadowStackEntry(previous, unwrapNodeIfNecessary(expr));
+ }
+
+ public static ShadowStackEntry createAtAsyncSend(final ShadowStackEntry previous,
+ final Node expr) {
+ assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || previous != null;
+ return new EntryAtMessageSend(previous, unwrapNodeIfNecessary(expr));
+ }
+
+ public static ShadowStackEntry createAtPromiseResolution(final ShadowStackEntry previous,
+ final Node expr, final EntryForPromiseResolution.ResolutionLocation resolutionType,
+ final String resolutionValue, SPromise promiseOrNull) {
+ assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || previous != null;
+ return new EntryForPromiseResolution(previous, unwrapNodeIfNecessary(expr), resolutionType,
+ resolutionValue, promiseOrNull);
+ }
+
+ public static ShadowStackEntry createAtPromiseResolution(final ShadowStackEntry previous,
+ final Node expr, final EntryForPromiseResolution.ResolutionLocation resolutionType,
+ final String resolutionValue){
+ return ShadowStackEntry.createAtPromiseResolution(previous, expr, resolutionType,resolutionValue,null);
+ }
+
+ public static EntryForTaskSpawn createAtTaskSpawn(final ShadowStackEntry previous, final Node currentExpression){
+ ShadowStackEntry methodCallEntry = new ShadowStackEntry(previous,currentExpression);
+ return new EntryForTaskSpawn(methodCallEntry,currentExpression);
+ }
+
+ public static Node unwrapNodeIfNecessary(final Node node) {
+ if (node instanceof WrapperNode) {
+ return ((WrapperNode) node).getDelegateNode();
+ } else {
+ return node;
+ }
+ }
+
+ public ShadowStackEntry(final ShadowStackEntry previous, final Node expr) {
+ assert VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE;
+ this.previous = previous;
+ this.expression = expr;
+ if (ALLOCATION_COUNT) {
+ numberOfAllocations++;
+ }
+ //this.actorId = getCurrentActor();
+ this.actorId = 0;
+ }
+
+ public long getCurrentActor() {
+ Thread t = Thread.currentThread();
+ if (t instanceof ActorProcessingThread) {
+ return EventualMessage.getActorCurrentMessageIsExecutionOn().getId();
+ } else {
+ return -1L;
+ }
+ }
+
+ public RootNode getRootNode() {
+ return expression.getRootNode();
+ }
+
+ public SourceSection getSourceSection() {
+ return expression.getSourceSection();
+ }
+
+ public boolean isAsync() {
+ return false;
+ }
+
+ public void setPreviousShadowStackEntry(final ShadowStackEntry maybeEntry) {
+ previous = maybeEntry;
+ }
+
+ public static final class EntryAtMessageSend extends ShadowStackEntry {
+
+ private EntryAtMessageSend(final ShadowStackEntry previous, final Node expr) {
+ super(previous, expr);
+ }
+ }
+
+ public static final class EntryForPromiseResolution extends ShadowStackEntry {
+ public enum ResolutionLocation {
+ SUCCESSFUL("resolved with a value"), ERROR("resolved with an error"),
+ CHAINED("resolved with a promise"), ON_WHEN_RESOLVED_BLOCK("on whenResolved block"),
+ ON_WHEN_RESOLVED("on whenResolved"), ON_WHEN_RESOLVED_ERROR("on whenResolved error"),
+ ON_RECEIVE_MESSAGE("on receive message"), ON_SCHEDULE_PROMISE("on schedule");
+
+ private final String label;
+
+ ResolutionLocation(final String label) {
+ this.label = label;
+ }
+
+ public String getValue() {
+ return label;
+ }
+ }
+
+ public ResolutionLocation resolutionLocation;
+ public String resolutionValue;
+ public SPromise promiseGroupOrNull = null;
+
+ private EntryForPromiseResolution(final ShadowStackEntry previous,
+ final Node expr, final ResolutionLocation resolutionLocation,
+ final String resolutionValue) {
+ super(previous, expr);
+ this.resolutionLocation = resolutionLocation;
+ this.resolutionValue = resolutionValue;
+ }
+
+ private EntryForPromiseResolution(final ShadowStackEntry previous,
+ final Node expr, final ResolutionLocation resolutionLocation,
+ final String resolutionValue, SPromise promiseGroup) {
+ this(previous,expr,resolutionLocation,resolutionValue);
+ this.promiseGroupOrNull = promiseGroup;
+ }
+
+ public boolean isPromiseGroup() { return promiseGroupOrNull != null; }
+
+ @Override
+ public boolean isAsync() {
+ return true;
+ }
+
+ }
+ public static final class EntryForTaskSpawn extends ShadowStackEntry {
+
+ public EntryForTaskSpawn(ShadowStackEntry previous, Node expr) {
+ super(previous, expr);
+ }
+ }
+}
diff --git a/src/tools/debugger/asyncstacktraces/ShadowStackEntryLoad.java b/src/tools/debugger/asyncstacktraces/ShadowStackEntryLoad.java
new file mode 100644
index 0000000000..f62974e1d9
--- /dev/null
+++ b/src/tools/debugger/asyncstacktraces/ShadowStackEntryLoad.java
@@ -0,0 +1,136 @@
+package tools.debugger.asyncstacktraces;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+
+import som.interpreter.SArguments;
+import som.vm.VmSettings;
+
+
+public abstract class ShadowStackEntryLoad extends Node {
+ public static final int NUM_SHADOW_STACK_ENTRIES = 6;
+
+ public static final boolean ANALYSIS = false;
+ public static int cacheHit = 0;
+ public static int megaCacheHit = 0;
+ public static int megaMiss = 0;
+
+ @CompilerDirectives.TruffleBoundary
+ public static ShadowStackEntryLoad create() {
+ if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) {
+ return new UninitializedShadowStackEntryLoad();
+ } else {
+ return null;
+ }
+ }
+
+ public void loadShadowStackEntry(final Object[] arguments, final Node expression,
+ final VirtualFrame frame, final boolean async) {
+ ShadowStackEntry prevEntry = SArguments.getShadowStackEntry(frame);
+ loadShadowStackEntry(arguments, expression, prevEntry, this, async);
+ }
+
+ protected abstract void loadShadowStackEntry(Object[] arguments,
+ Node expression,
+ ShadowStackEntry prevEntry,
+ ShadowStackEntryLoad firstShadowStackEntryLoad,
+ boolean async);
+
+ public abstract int getCurrentCacheSize();
+
+ protected void setShadowStackEntry(final ShadowStackEntry shadowStackEntry,
+ final Object[] arguments) {
+ SArguments.setShadowStackEntry(arguments, shadowStackEntry);
+ }
+
+ private static final class UninitializedShadowStackEntryLoad extends ShadowStackEntryLoad {
+
+ @CompilerDirectives.TruffleBoundary
+ @Override
+ protected void loadShadowStackEntry(final Object[] arguments,
+ final Node expression,
+ final ShadowStackEntry prevEntry,
+ final ShadowStackEntryLoad firstShadowStackEntryLoad,
+ final boolean async) {
+ if (!VmSettings.ACTOR_ASYNC_STACK_TRACE_INLINE_CACHE) {
+ setShadowStackEntry(
+ SArguments.instantiateShadowStackEntry(prevEntry, expression, async),
+ arguments);
+ return;
+ }
+ ShadowStackEntry newEntry =
+ SArguments.instantiateShadowStackEntry(prevEntry, expression, async);
+ ShadowStackEntryLoad newLoad;
+ if (firstShadowStackEntryLoad.getCurrentCacheSize() > NUM_SHADOW_STACK_ENTRIES) {
+ newLoad = new GenericShadowStackEntryLoad();
+ // firstShadowStackEntryLoad.replace(newLoad);
+ replace(newLoad);
+ } else {
+ newLoad = new CachedShadowStackEntryLoad(prevEntry, newEntry);
+ replace(newLoad);
+ }
+ newLoad.loadShadowStackEntry(arguments, expression, prevEntry,
+ firstShadowStackEntryLoad, async);
+ }
+
+ @Override
+ public int getCurrentCacheSize() {
+ return 0;
+ }
+
+ }
+
+ private static final class CachedShadowStackEntryLoad extends ShadowStackEntryLoad {
+
+ @Child protected ShadowStackEntryLoad nextInCache;
+ protected final ShadowStackEntry expectedShadowStackEntry;
+ protected final ShadowStackEntry cachedShadowStackEntry;
+
+ @CompilerDirectives.TruffleBoundary
+ CachedShadowStackEntryLoad(final ShadowStackEntry prevEntry,
+ final ShadowStackEntry newEntry) {
+ this.expectedShadowStackEntry = prevEntry;
+ this.cachedShadowStackEntry = newEntry;
+ nextInCache = new UninitializedShadowStackEntryLoad();
+ }
+
+ @Override
+ public int getCurrentCacheSize() {
+ return 1 + nextInCache.getCurrentCacheSize();
+ }
+
+ @Override
+ protected void loadShadowStackEntry(final Object[] arguments,
+ final Node expression,
+ final ShadowStackEntry prevEntry, final ShadowStackEntryLoad firstShadowStackEntryLoad,
+ final boolean async) {
+ if (prevEntry == expectedShadowStackEntry) {
+ setShadowStackEntry(cachedShadowStackEntry, arguments);
+ if (ANALYSIS) {
+ cacheHit++;
+ }
+ } else {
+ nextInCache.loadShadowStackEntry(arguments, expression, prevEntry,
+ firstShadowStackEntryLoad, async);
+ }
+ }
+ }
+
+ private static final class GenericShadowStackEntryLoad extends ShadowStackEntryLoad {
+
+ @Override
+ protected void loadShadowStackEntry(final Object[] arguments,
+ final Node expression,
+ final ShadowStackEntry prevEntry, final ShadowStackEntryLoad firstShadowStackEntryLoad,
+ final boolean async) {
+ setShadowStackEntry(SArguments.instantiateShadowStackEntry(prevEntry, expression, async),
+ arguments);
+ }
+
+ @Override
+ public int getCurrentCacheSize() {
+ return 0;
+ }
+ }
+}
diff --git a/src/tools/debugger/asyncstacktraces/StackIterator.java b/src/tools/debugger/asyncstacktraces/StackIterator.java
new file mode 100644
index 0000000000..8f8059bd93
--- /dev/null
+++ b/src/tools/debugger/asyncstacktraces/StackIterator.java
@@ -0,0 +1,501 @@
+package tools.debugger.asyncstacktraces;
+
+import java.util.*;
+
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.debug.DebugStackFrame;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameInstance;
+import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
+import com.oracle.truffle.api.frame.FrameInstanceVisitor;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+
+import som.interpreter.Invokable;
+import som.interpreter.Method;
+import som.interpreter.Primitive;
+import som.interpreter.actors.EventualSendNode;
+import som.interpreter.actors.SPromise;
+import som.interpreter.nodes.dispatch.BackCacheCallNode;
+import som.vm.VmSettings;
+import tools.debugger.asyncstacktraces.ShadowStackEntry.EntryAtMessageSend;
+import tools.debugger.asyncstacktraces.ShadowStackEntry.EntryForPromiseResolution;
+import tools.debugger.asyncstacktraces.StackIterator.ShadowStackIterator.HaltShadowStackIterator;
+import tools.debugger.asyncstacktraces.StackIterator.ShadowStackIterator.SuspensionShadowStackIterator;
+import tools.debugger.frontend.ApplicationThreadStack;
+import tools.debugger.frontend.ApplicationThreadStack.StackFrame;
+
+
+/**
+ * This iterator traverses the run time stack and all available calling context
+ * information.
+ *
+ *
+ * In special cases, a single stack frame/calling context might be
+ * represented as multiple stack frames in the iteration.
+ * We chose to do this to explicitly represent context switches in asynchronous
+ * control flow, as caused for instance by eventual message sends or promise resolution.
+ */
+
+public abstract class StackIterator implements Iterator {
+
+ @Override
+ public abstract boolean hasNext();
+
+ @Override
+ public abstract StackFrame next();
+
+ protected StackFrame createStackFrame(final Frame localFrame, final RootNode rootNode,
+ final String name, final SourceSection location) {
+ return new StackFrame(name, rootNode,
+ location, localFrame, false);
+ }
+
+ protected StackFrame createStackFrame(final Frame localFrame, final RootNode rootNode,
+ final String name, final SourceSection location, final boolean isFromMCOpt) {
+ return new StackFrame(name, rootNode,
+ location, localFrame, false,isFromMCOpt);
+ }
+
+ public static StackIterator createHaltIterator(final SourceSection topNode) {
+ if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) {
+ return new HaltShadowStackIterator(topNode);
+ } else {
+ return new HaltIterator(topNode);
+ }
+ }
+
+ public static StackIterator createSuspensionIterator(
+ final Iterator localStack, final long actorId) {
+ if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) {
+ return new SuspensionShadowStackIterator(localStack, actorId);
+ } else {
+ return new SuspensionIterator(localStack);
+ }
+ }
+
+ public static class SuspensionIterator extends StackIterator {
+
+ protected ArrayList frames;
+ protected int currentIndex = 0;
+
+ public SuspensionIterator(final Iterator localStack) {
+ assert localStack != null;
+ frames = new ArrayList();
+ while (localStack.hasNext()) {
+ frames.add(localStack.next());
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return currentIndex != frames.size();
+ }
+
+ @Override
+ public StackFrame next() {
+ DebugStackFrame next = frames.get(currentIndex);
+ currentIndex++;
+ return createStackFrame(next.getFrame(),
+ next.getRootNode(),
+ next.getRootNode().getName(),
+ next.getSourceSection());
+ }
+ }
+
+ public static class HaltIterator extends StackIterator {
+
+ protected ArrayList stackFrames;
+
+ protected int currentIndex = 0;
+
+ public HaltIterator(final SourceSection topNode) {
+ stackFrames = new ArrayList();
+ boolean[] isFirst = new boolean[] {true};
+
+ Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor