Skylark debugging protocol: include frames information in ThreadPausedEvents.
This is almost always desirable -- if a thread is paused, the IDE expects
to know the context.
PiperOrigin-RevId: 199865078
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkdebug/proto/skylark_debugging.proto b/src/main/java/com/google/devtools/build/lib/skylarkdebug/proto/skylark_debugging.proto
index ed398e7..649f293 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkdebug/proto/skylark_debugging.proto
+++ b/src/main/java/com/google/devtools/build/lib/skylarkdebug/proto/skylark_debugging.proto
@@ -184,6 +184,11 @@
message ThreadPausedEvent {
// The thread that was paused.
Thread thread = 1;
+
+ // The list of stack frames for the paused thread. The first element in the
+ // list represents the topmost frame (that is, the current innermost
+ // function).
+ repeated Frame frame = 2;
}
// An event indicating that a thread has continued execution after being paused.
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebugEventHelper.java b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebugEventHelper.java
index 0254484..aa2279f 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebugEventHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebugEventHelper.java
@@ -130,9 +130,9 @@
.build();
}
- static DebugEvent threadPausedEvent(Thread thread) {
+ static DebugEvent threadPausedEvent(Thread thread, Collection<Frame> frames) {
return DebugEvent.newBuilder()
- .setThreadPaused(ThreadPausedEvent.newBuilder().setThread(thread))
+ .setThreadPaused(ThreadPausedEvent.newBuilder().setThread(thread).addAllFrame(frames))
.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/ThreadHandler.java b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/ThreadHandler.java
index fd30c64..e1f74f9 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/ThreadHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/ThreadHandler.java
@@ -225,6 +225,11 @@
}
// no need to list frames within the synchronize block: threads can only be resumed in response
// to a client request, and requests are handled serially
+ return listFrames(debuggable, pausedState);
+ }
+
+ private static ImmutableList<SkylarkDebuggingProtos.Frame> listFrames(
+ Debuggable debuggable, PausedThreadState pausedState) {
return debuggable
.listFrames(pausedState.location)
.stream()
@@ -265,6 +270,7 @@
SkylarkDebuggingProtos.Thread threadProto;
PausedThreadState pausedState;
+ Debuggable debuggable;
synchronized (threads) {
ThreadState thread = threads.get(threadId);
if (thread == null) {
@@ -276,13 +282,15 @@
transport.postEvent(DebugEventHelper.threadStartedEvent(threadId, fallbackThreadName));
thread = doRegisterThread(threadId, fallbackThreadName, env);
}
+ debuggable = thread.debuggable;
pausedState = new PausedThreadState(location);
thread.pausedState = pausedState;
// get proto after setting the paused state, so that it's up to date
threadProto = getThreadProto(thread);
}
- transport.postEvent(DebugEventHelper.threadPausedEvent(threadProto));
+ transport.postEvent(
+ DebugEventHelper.threadPausedEvent(threadProto, listFrames(debuggable, pausedState)));
pausedState.semaphore.acquireUninterruptibly();
transport.postEvent(
DebugEventHelper.threadContinuedEvent(
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/SkylarkDebugServerTest.java b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/SkylarkDebugServerTest.java
index d667493..8aea188 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/SkylarkDebugServerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/SkylarkDebugServerTest.java
@@ -169,7 +169,7 @@
Environment env = newEnvironment();
Location breakpoint =
- Location.newBuilder().setLineNumber(2).setPath("/a/build/file/BUILD").build();
+ Location.newBuilder().setLineNumber(1).setPath("/a/build/file/BUILD").build();
setBreakpoints(ImmutableList.of(breakpoint));
Thread evaluationThread = execInWorkerThread(buildFile, env);
@@ -179,6 +179,22 @@
// wait for breakpoint to be hit
client.waitForEvent(DebugEvent::hasThreadPaused, Duration.ofSeconds(5));
+ assertThat(client.unnumberedEvents)
+ .contains(
+ DebugEventHelper.threadPausedEvent(
+ SkylarkDebuggingProtos.Thread.newBuilder()
+ .setName(threadName)
+ .setId(threadId)
+ .setIsPaused(true)
+ .setLocation(breakpoint.toBuilder().setColumnNumber(1))
+ .build(),
+ ImmutableList.of(
+ Frame.newBuilder()
+ .setFunctionName("<top level>")
+ .setLocation(breakpoint.toBuilder().setColumnNumber(1))
+ .addScope(Scope.newBuilder().setName("global"))
+ .build())));
+
assertThat(listThreads().getThreadList())
.containsExactly(
SkylarkDebuggingProtos.Thread.newBuilder()