mirror of
https://github.com/intel/llvm.git
synced 2026-01-17 14:48:27 +08:00
[lldb-dap] Send an Invalidated event on thread stack change. (#163976)
When the user send `thread return <expr>` command this changes the stack length but the UI does not update. Send stack invalidated event to the client to update the stack.
This commit is contained in:
3
lldb/test/API/tools/lldb-dap/invalidated-event/Makefile
Normal file
3
lldb/test/API/tools/lldb-dap/invalidated-event/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
Test lldb-dap recieves invalidated-events when the area such as
|
||||
stack, variables, threads has changes but the client does not
|
||||
know about it.
|
||||
"""
|
||||
|
||||
import lldbdap_testcase
|
||||
from lldbsuite.test.lldbtest import line_number
|
||||
from dap_server import Event
|
||||
|
||||
|
||||
class TestDAP_invalidatedEvent(lldbdap_testcase.DAPTestCaseBase):
|
||||
def verify_top_frame_name(self, frame_name: str):
|
||||
all_frames = self.get_stackFrames()
|
||||
self.assertGreaterEqual(len(all_frames), 1, "Expected at least one frame.")
|
||||
top_frame_name = all_frames[0]["name"]
|
||||
self.assertRegex(top_frame_name, f"{frame_name}.*")
|
||||
|
||||
def test_invalidated_stack_area_event(self):
|
||||
"""
|
||||
Test an invalidated event for the stack area.
|
||||
The event is sent when the command `thread return <expr>` is sent by the user.
|
||||
"""
|
||||
other_source = "other.h"
|
||||
return_bp_line = line_number(other_source, "// thread return breakpoint")
|
||||
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.set_source_breakpoints(other_source, [return_bp_line])
|
||||
self.continue_to_next_stop()
|
||||
|
||||
self.verify_top_frame_name("add")
|
||||
thread_id = self.dap_server.get_thread_id()
|
||||
self.assertIsNotNone(thread_id, "Exepected a thread id.")
|
||||
|
||||
# run thread return
|
||||
thread_command = "thread return 20"
|
||||
eval_resp = self.dap_server.request_evaluate(thread_command, context="repl")
|
||||
self.assertTrue(eval_resp["success"], f"Failed to evaluate `{thread_command}`.")
|
||||
|
||||
# wait for the invalidated stack event.
|
||||
stack_event = self.dap_server.wait_for_event(["invalidated"])
|
||||
self.assertIsNotNone(stack_event, "Expected an invalidated event.")
|
||||
event_body: Event = stack_event["body"]
|
||||
self.assertIn("stacks", event_body["areas"])
|
||||
self.assertIn("threadId", event_body.keys())
|
||||
self.assertEqual(
|
||||
thread_id,
|
||||
event_body["threadId"],
|
||||
f"Expected the event from thread {thread_id}.",
|
||||
)
|
||||
|
||||
# confirm we are back at the main frame.
|
||||
self.verify_top_frame_name("main")
|
||||
self.continue_to_exit()
|
||||
9
lldb/test/API/tools/lldb-dap/invalidated-event/main.cpp
Normal file
9
lldb/test/API/tools/lldb-dap/invalidated-event/main.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "other.h"
|
||||
|
||||
int main() {
|
||||
int first = 5;
|
||||
int second = 10;
|
||||
const int result = add(first, second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
lldb/test/API/tools/lldb-dap/invalidated-event/other.h
Normal file
10
lldb/test/API/tools/lldb-dap/invalidated-event/other.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef OTHER_H
|
||||
#define OTHER_H
|
||||
|
||||
int add(int a, int b) {
|
||||
int first = a;
|
||||
int second = b; // thread return breakpoint
|
||||
int result = first + second;
|
||||
return result;
|
||||
}
|
||||
#endif // OTHER_H
|
||||
@@ -1368,6 +1368,12 @@ void DAP::EventThread() {
|
||||
broadcaster.AddListener(listener, eBroadcastBitStopEventThread);
|
||||
debugger.GetBroadcaster().AddListener(
|
||||
listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
|
||||
|
||||
// listen for thread events.
|
||||
listener.StartListeningForEventClass(
|
||||
debugger, lldb::SBThread::GetBroadcasterClassName(),
|
||||
lldb::SBThread::eBroadcastBitStackChanged);
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
if (listener.WaitForEvent(1, event)) {
|
||||
@@ -1503,6 +1509,9 @@ void DAP::EventThread() {
|
||||
SendJSON(llvm::json::Value(std::move(bp_event)));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (lldb::SBThread::EventIsThreadEvent(event)) {
|
||||
HandleThreadEvent(event);
|
||||
} else if (event_mask & lldb::eBroadcastBitError ||
|
||||
event_mask & lldb::eBroadcastBitWarning) {
|
||||
lldb::SBStructuredData data =
|
||||
@@ -1522,6 +1531,16 @@ void DAP::EventThread() {
|
||||
}
|
||||
}
|
||||
|
||||
void DAP::HandleThreadEvent(const lldb::SBEvent &event) {
|
||||
uint32_t event_type = event.GetType();
|
||||
|
||||
if (event_type & lldb::SBThread::eBroadcastBitStackChanged) {
|
||||
const lldb::SBThread evt_thread = lldb::SBThread::GetThreadFromEvent(event);
|
||||
SendInvalidatedEvent(*this, {InvalidatedEventBody::eAreaStacks},
|
||||
evt_thread.GetThreadID());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(
|
||||
const protocol::Source &source,
|
||||
const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints) {
|
||||
|
||||
@@ -460,6 +460,7 @@ private:
|
||||
/// Event threads.
|
||||
/// @{
|
||||
void EventThread();
|
||||
void HandleThreadEvent(const lldb::SBEvent &event);
|
||||
void ProgressEventThread();
|
||||
|
||||
std::thread event_thread;
|
||||
|
||||
@@ -276,11 +276,16 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
|
||||
}
|
||||
|
||||
void SendInvalidatedEvent(
|
||||
DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas) {
|
||||
DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas,
|
||||
lldb::tid_t tid) {
|
||||
if (!dap.clientFeatures.contains(protocol::eClientFeatureInvalidatedEvent))
|
||||
return;
|
||||
protocol::InvalidatedEventBody body;
|
||||
body.areas = areas;
|
||||
|
||||
if (tid != LLDB_INVALID_THREAD_ID)
|
||||
body.threadId = tid;
|
||||
|
||||
dap.Send(protocol::Event{"invalidated", std::move(body)});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "DAPForward.h"
|
||||
#include "Protocol/ProtocolEvents.h"
|
||||
#include "lldb/lldb-defines.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
@@ -35,7 +37,8 @@ void SendContinuedEvent(DAP &dap);
|
||||
void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process);
|
||||
|
||||
void SendInvalidatedEvent(
|
||||
DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas);
|
||||
DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas,
|
||||
lldb::tid_t tid = LLDB_INVALID_THREAD_ID);
|
||||
|
||||
void SendMemoryEvent(DAP &dap, lldb::SBValue variable);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ llvm::json::Value toJSON(const InvalidatedEventBody::Area &IEBA) {
|
||||
llvm::json::Value toJSON(const InvalidatedEventBody &IEB) {
|
||||
json::Object Result{{"areas", IEB.areas}};
|
||||
if (IEB.threadId)
|
||||
Result.insert({"threadID", IEB.threadId});
|
||||
Result.insert({"threadId", IEB.threadId});
|
||||
if (IEB.stackFrameId)
|
||||
Result.insert({"stackFrameId", IEB.stackFrameId});
|
||||
return Result;
|
||||
|
||||
@@ -1079,14 +1079,17 @@ TEST(ProtocolTypesTest, InvalidatedEventBody) {
|
||||
body.areas = {InvalidatedEventBody::eAreaStacks,
|
||||
InvalidatedEventBody::eAreaThreads};
|
||||
body.stackFrameId = 1;
|
||||
StringRef json = R"({
|
||||
"areas": [
|
||||
"stacks",
|
||||
"threads"
|
||||
],
|
||||
"stackFrameId": 1
|
||||
})";
|
||||
EXPECT_EQ(json, pp(body));
|
||||
body.threadId = 20;
|
||||
Expected<json::Value> expected = json::parse(R"({
|
||||
"areas": [
|
||||
"stacks",
|
||||
"threads"
|
||||
],
|
||||
"stackFrameId": 1,
|
||||
"threadId": 20
|
||||
})");
|
||||
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
|
||||
EXPECT_EQ(pp(*expected), pp(body));
|
||||
}
|
||||
|
||||
TEST(ProtocolTypesTest, MemoryEventBody) {
|
||||
|
||||
Reference in New Issue
Block a user