Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/test/API/tools/lldb-dap/invalidated-event/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions lldb/test/API/tools/lldb-dap/invalidated-event/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "other.h"

int main() {
int first = 5;
int second = 10;
const int result = add(first, second);

return 0;
}
10 changes: 10 additions & 0 deletions lldb/test/API/tools/lldb-dap/invalidated-event/other.h
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 =
Expand All @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions lldb/tools/lldb-dap/DAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ struct DAP final : public DAPTransport::MessageHandler {
/// Event threads.
/// @{
void EventThread();
void HandleThreadEvent(const lldb::SBEvent &event);
void ProgressEventThread();

std::thread event_thread;
Expand Down
7 changes: 6 additions & 1 deletion lldb/tools/lldb-dap/EventHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)});
}

Expand Down
5 changes: 4 additions & 1 deletion lldb/tools/lldb-dap/EventHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 11 additions & 8 deletions lldb/unittests/DAP/ProtocolTypesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading