Skip to content

Commit 52075f0

Browse files
ashgtida-viperJDevlieghere
authored
[lldb-dap] Migrating 'threads' request to structured types. (#142510)
Moving `threads` request to structured types. Adding helper types for this and moving helpers from JSONUtils to ProtocolUtils. --------- Co-authored-by: Ebuka Ezike <[email protected]> Co-authored-by: Jonas Devlieghere <[email protected]>
1 parent c66b72f commit 52075f0

18 files changed

+368
-324
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,6 @@ def _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool:
308308
return keepGoing
309309

310310
def _process_continued(self, all_threads_continued: bool):
311-
self.threads = None
312311
self.frame_scopes = {}
313312
if all_threads_continued:
314313
self.thread_stop_reasons = {}
@@ -1180,6 +1179,9 @@ def request_threads(self):
11801179
with information about all threads"""
11811180
command_dict = {"command": "threads", "type": "request", "arguments": {}}
11821181
response = self.send_recv(command_dict)
1182+
if not response["success"]:
1183+
self.threads = None
1184+
return response
11831185
body = response["body"]
11841186
# Fill in "self.threads" correctly so that clients that call
11851187
# self.get_threads() or self.get_thread_id(...) can get information

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,10 @@ void DAP::EventThread() {
12401240
// automatically restarted.
12411241
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
12421242
SendStdOutStdErr(*this, process);
1243-
SendThreadStoppedEvent(*this);
1243+
if (llvm::Error err = SendThreadStoppedEvent(*this))
1244+
DAP_LOG_ERROR(log, std::move(err),
1245+
"({1}) reporting thread stopped: {0}",
1246+
transport.GetClientName());
12441247
}
12451248
break;
12461249
case lldb::eStateRunning:

lldb/tools/lldb-dap/DAP.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ struct DAP {
152152
llvm::DenseSet<ClientFeature> clientFeatures;
153153

154154
/// The initial thread list upon attaching.
155-
std::optional<llvm::json::Array> initial_thread_list;
155+
std::vector<protocol::Thread> initial_thread_list;
156156

157157
/// Keep track of all the modules our client knows about: either through the
158158
/// modules request or the module events.

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
#include "EventHelper.h"
1010
#include "DAP.h"
11-
#include "DAPLog.h"
11+
#include "DAPError.h"
1212
#include "JSONUtils.h"
1313
#include "LLDBUtils.h"
1414
#include "lldb/API/SBFileSpec.h"
15+
#include "llvm/Support/Error.h"
1516

1617
#if defined(_WIN32)
1718
#define NOMINMAX
@@ -22,6 +23,8 @@
2223
#endif
2324
#endif
2425

26+
using namespace llvm;
27+
2528
namespace lldb_dap {
2629

2730
static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {
@@ -116,78 +119,78 @@ void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {
116119

117120
// Send a thread stopped event for all threads as long as the process
118121
// is stopped.
119-
void SendThreadStoppedEvent(DAP &dap) {
122+
llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry) {
123+
lldb::SBMutex lock = dap.GetAPIMutex();
124+
std::lock_guard<lldb::SBMutex> guard(lock);
125+
120126
lldb::SBProcess process = dap.target.GetProcess();
121-
if (process.IsValid()) {
122-
auto state = process.GetState();
123-
if (state == lldb::eStateStopped) {
124-
llvm::DenseSet<lldb::tid_t> old_thread_ids;
125-
old_thread_ids.swap(dap.thread_ids);
126-
uint32_t stop_id = process.GetStopID();
127-
const uint32_t num_threads = process.GetNumThreads();
128-
129-
// First make a pass through the threads to see if the focused thread
130-
// has a stop reason. In case the focus thread doesn't have a stop
131-
// reason, remember the first thread that has a stop reason so we can
132-
// set it as the focus thread if below if needed.
133-
lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
134-
uint32_t num_threads_with_reason = 0;
135-
bool focus_thread_exists = false;
136-
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
137-
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
138-
const lldb::tid_t tid = thread.GetThreadID();
139-
const bool has_reason = ThreadHasStopReason(thread);
140-
// If the focus thread doesn't have a stop reason, clear the thread ID
141-
if (tid == dap.focus_tid) {
142-
focus_thread_exists = true;
143-
if (!has_reason)
144-
dap.focus_tid = LLDB_INVALID_THREAD_ID;
145-
}
146-
if (has_reason) {
147-
++num_threads_with_reason;
148-
if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
149-
first_tid_with_reason = tid;
150-
}
151-
}
127+
if (!process.IsValid())
128+
return make_error<DAPError>("invalid process");
129+
130+
lldb::StateType state = process.GetState();
131+
if (!lldb::SBDebugger::StateIsStoppedState(state))
132+
return make_error<NotStoppedError>();
133+
134+
llvm::DenseSet<lldb::tid_t> old_thread_ids;
135+
old_thread_ids.swap(dap.thread_ids);
136+
uint32_t stop_id = process.GetStopID();
137+
const uint32_t num_threads = process.GetNumThreads();
138+
139+
// First make a pass through the threads to see if the focused thread
140+
// has a stop reason. In case the focus thread doesn't have a stop
141+
// reason, remember the first thread that has a stop reason so we can
142+
// set it as the focus thread if below if needed.
143+
lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
144+
uint32_t num_threads_with_reason = 0;
145+
bool focus_thread_exists = false;
146+
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
147+
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
148+
const lldb::tid_t tid = thread.GetThreadID();
149+
const bool has_reason = ThreadHasStopReason(thread);
150+
// If the focus thread doesn't have a stop reason, clear the thread ID
151+
if (tid == dap.focus_tid) {
152+
focus_thread_exists = true;
153+
if (!has_reason)
154+
dap.focus_tid = LLDB_INVALID_THREAD_ID;
155+
}
156+
if (has_reason) {
157+
++num_threads_with_reason;
158+
if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
159+
first_tid_with_reason = tid;
160+
}
161+
}
152162

153-
// We will have cleared dap.focus_tid if the focus thread doesn't have
154-
// a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
155-
// then set the focus thread to the first thread with a stop reason.
156-
if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)
157-
dap.focus_tid = first_tid_with_reason;
158-
159-
// If no threads stopped with a reason, then report the first one so
160-
// we at least let the UI know we stopped.
161-
if (num_threads_with_reason == 0) {
162-
lldb::SBThread thread = process.GetThreadAtIndex(0);
163-
dap.focus_tid = thread.GetThreadID();
163+
// We will have cleared dap.focus_tid if the focus thread doesn't have
164+
// a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
165+
// then set the focus thread to the first thread with a stop reason.
166+
if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)
167+
dap.focus_tid = first_tid_with_reason;
168+
169+
// If no threads stopped with a reason, then report the first one so
170+
// we at least let the UI know we stopped.
171+
if (num_threads_with_reason == 0) {
172+
lldb::SBThread thread = process.GetThreadAtIndex(0);
173+
dap.focus_tid = thread.GetThreadID();
174+
dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
175+
} else {
176+
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
177+
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
178+
dap.thread_ids.insert(thread.GetThreadID());
179+
if (ThreadHasStopReason(thread)) {
164180
dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
165-
} else {
166-
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
167-
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
168-
dap.thread_ids.insert(thread.GetThreadID());
169-
if (ThreadHasStopReason(thread)) {
170-
dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
171-
}
172-
}
173181
}
174-
175-
for (auto tid : old_thread_ids) {
176-
auto end = dap.thread_ids.end();
177-
auto pos = dap.thread_ids.find(tid);
178-
if (pos == end)
179-
SendThreadExitedEvent(dap, tid);
180-
}
181-
} else {
182-
DAP_LOG(
183-
dap.log,
184-
"error: SendThreadStoppedEvent() when process isn't stopped ({0})",
185-
lldb::SBDebugger::StateAsCString(state));
186182
}
187-
} else {
188-
DAP_LOG(dap.log, "error: SendThreadStoppedEvent() invalid process");
189183
}
184+
185+
for (const auto &tid : old_thread_ids) {
186+
auto end = dap.thread_ids.end();
187+
auto pos = dap.thread_ids.find(tid);
188+
if (pos == end)
189+
SendThreadExitedEvent(dap, tid);
190+
}
191+
190192
dap.RunStopCommands();
193+
return Error::success();
191194
}
192195

193196
// Send a "terminated" event to indicate the process is done being

lldb/tools/lldb-dap/EventHelper.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLDB_TOOLS_LLDB_DAP_EVENTHELPER_H
1111

1212
#include "DAPForward.h"
13+
#include "llvm/Support/Error.h"
1314

1415
namespace lldb_dap {
1516
struct DAP;
@@ -18,7 +19,7 @@ enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
1819

1920
void SendProcessEvent(DAP &dap, LaunchMethod launch_method);
2021

21-
void SendThreadStoppedEvent(DAP &dap);
22+
llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry = false);
2223

2324
void SendTerminatedEvent(DAP &dap);
2425

lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
#include "DAP.h"
1010
#include "EventHelper.h"
11-
#include "JSONUtils.h"
11+
#include "LLDBUtils.h"
1212
#include "Protocol/ProtocolRequests.h"
13+
#include "ProtocolUtils.h"
1314
#include "RequestHandler.h"
1415
#include "lldb/API/SBDebugger.h"
1516

@@ -51,11 +52,9 @@ ConfigurationDoneRequestHandler::Run(const ConfigurationDoneArguments &) const {
5152
SendProcessEvent(dap, dap.is_attach ? Attach : Launch);
5253

5354
if (dap.stop_at_entry)
54-
SendThreadStoppedEvent(dap);
55-
else
56-
process.Continue();
55+
return SendThreadStoppedEvent(dap, /*on_entry=*/true);
5756

58-
return Error::success();
57+
return ToError(process.Continue());
5958
}
6059

6160
} // namespace lldb_dap

lldb/tools/lldb-dap/Handler/RequestHandler.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,11 +522,14 @@ class StackTraceRequestHandler : public LegacyRequestHandler {
522522
}
523523
};
524524

525-
class ThreadsRequestHandler : public LegacyRequestHandler {
525+
class ThreadsRequestHandler
526+
: public RequestHandler<protocol::ThreadsArguments,
527+
llvm::Expected<protocol::ThreadsResponseBody>> {
526528
public:
527-
using LegacyRequestHandler::LegacyRequestHandler;
529+
using RequestHandler::RequestHandler;
528530
static llvm::StringLiteral GetCommand() { return "threads"; }
529-
void operator()(const llvm::json::Object &request) const override;
531+
llvm::Expected<protocol::ThreadsResponseBody>
532+
Run(const protocol::ThreadsArguments &) const override;
530533
};
531534

532535
class VariablesRequestHandler : public LegacyRequestHandler {

lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,11 @@ void RestartRequestHandler::operator()(
145145
// Because we're restarting, configuration has already happened so we can
146146
// continue the process right away.
147147
if (dap.stop_at_entry) {
148-
SendThreadStoppedEvent(dap);
148+
if (llvm::Error err = SendThreadStoppedEvent(dap, /*on_entry=*/true)) {
149+
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
150+
dap.SendJSON(llvm::json::Value(std::move(response)));
151+
return;
152+
}
149153
} else {
150154
dap.target.GetProcess().Continue();
151155
}

lldb/tools/lldb-dap/Handler/ThreadsRequestHandler.cpp

Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,45 @@
88

99
#include "DAP.h"
1010
#include "EventHelper.h"
11-
#include "JSONUtils.h"
11+
#include "Protocol/ProtocolRequests.h"
12+
#include "ProtocolUtils.h"
1213
#include "RequestHandler.h"
14+
#include "lldb/API/SBDebugger.h"
15+
#include "lldb/API/SBDefines.h"
16+
#include "llvm/Support/Error.h"
17+
#include "llvm/Support/raw_ostream.h"
18+
19+
using namespace llvm;
20+
using namespace lldb_dap::protocol;
1321

1422
namespace lldb_dap {
1523

16-
// "ThreadsRequest": {
17-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
18-
// "type": "object",
19-
// "description": "Thread request; value of command field is 'threads'. The
20-
// request retrieves a list of all threads.", "properties": {
21-
// "command": {
22-
// "type": "string",
23-
// "enum": [ "threads" ]
24-
// }
25-
// },
26-
// "required": [ "command" ]
27-
// }]
28-
// },
29-
// "ThreadsResponse": {
30-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
31-
// "type": "object",
32-
// "description": "Response to 'threads' request.",
33-
// "properties": {
34-
// "body": {
35-
// "type": "object",
36-
// "properties": {
37-
// "threads": {
38-
// "type": "array",
39-
// "items": {
40-
// "$ref": "#/definitions/Thread"
41-
// },
42-
// "description": "All threads."
43-
// }
44-
// },
45-
// "required": [ "threads" ]
46-
// }
47-
// },
48-
// "required": [ "body" ]
49-
// }]
50-
// }
51-
void ThreadsRequestHandler::operator()(
52-
const llvm::json::Object &request) const {
53-
llvm::json::Object response;
54-
FillResponse(request, response);
24+
/// The request retrieves a list of all threads.
25+
Expected<ThreadsResponseBody>
26+
ThreadsRequestHandler::Run(const ThreadsArguments &) const {
27+
lldb::SBProcess process = dap.target.GetProcess();
28+
std::vector<Thread> threads;
5529

56-
llvm::json::Array threads;
5730
// Client requests the baseline of currently existing threads after
5831
// a successful launch or attach by sending a 'threads' request
5932
// right after receiving the configurationDone response.
6033
// If no thread has reported to the client, it prevents something
6134
// like the pause request from working in the running state.
6235
// Return the cache of initial threads as the process might have resumed
63-
if (dap.initial_thread_list) {
64-
threads = dap.initial_thread_list.value();
65-
dap.initial_thread_list.reset();
36+
if (!dap.initial_thread_list.empty()) {
37+
threads = dap.initial_thread_list;
38+
dap.initial_thread_list.clear();
6639
} else {
67-
threads = GetThreads(dap.target.GetProcess(), dap.thread_format);
68-
}
40+
if (!lldb::SBDebugger::StateIsStoppedState(process.GetState()))
41+
return make_error<NotStoppedError>();
6942

70-
if (threads.size() == 0) {
71-
response["success"] = llvm::json::Value(false);
43+
threads = GetThreads(process, dap.thread_format);
7244
}
73-
llvm::json::Object body;
74-
body.try_emplace("threads", std::move(threads));
75-
response.try_emplace("body", std::move(body));
76-
dap.SendJSON(llvm::json::Value(std::move(response)));
45+
46+
if (threads.size() == 0)
47+
return make_error<DAPError>("failed to retrieve threads from process");
48+
49+
return ThreadsResponseBody{threads};
7750
}
7851

7952
} // namespace lldb_dap

0 commit comments

Comments
 (0)