Skip to content

Commit 651d4c4

Browse files
committed
Support thread debugging through DAP
1 parent 0fdbf05 commit 651d4c4

13 files changed

+384
-252
lines changed

editor/debugger/debug_adapter/debug_adapter_parser.cpp

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ Dictionary DebugAdapterParser::prepare_error_response(const Dictionary &p_params
108108
error = "missing_device";
109109
error_desc = "There's no connected device with specified id.";
110110
break;
111+
case DAP::ErrorType::MALFORMED_REQUEST:
112+
error = "malformed_request";
113+
error_desc = "The request is malformed ({details}).";
114+
break;
111115
case DAP::ErrorType::UNKNOWN:
112116
default:
113117
error = "unknown";
@@ -124,6 +128,12 @@ Dictionary DebugAdapterParser::prepare_error_response(const Dictionary &p_params
124128
return response;
125129
}
126130

131+
Dictionary DebugAdapterParser::prepare_malformed_error_response(const Dictionary &p_params, const String &p_error) const {
132+
Dictionary variables;
133+
variables["details"] = p_error;
134+
return prepare_error_response(p_params, DAP::ErrorType::MALFORMED_REQUEST, variables);
135+
}
136+
127137
Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const {
128138
Dictionary response = prepare_success_response(p_params);
129139
Dictionary args = p_params["arguments"];
@@ -303,36 +313,39 @@ Dictionary DebugAdapterParser::req_pause(const Dictionary &p_params) const {
303313
EditorRunBar::get_singleton()->get_pause_button()->set_pressed(true);
304314
EditorDebuggerNode::get_singleton()->_paused();
305315

306-
DebugAdapterProtocol::get_singleton()->notify_stopped_paused();
307-
308316
return prepare_success_response(p_params);
309317
}
310318

311319
Dictionary DebugAdapterParser::req_continue(const Dictionary &p_params) const {
312320
EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);
313321
EditorDebuggerNode::get_singleton()->_paused();
314322

315-
DebugAdapterProtocol::get_singleton()->notify_continued();
316-
317323
return prepare_success_response(p_params);
318324
}
319325

320326
Dictionary DebugAdapterParser::req_threads(const Dictionary &p_params) const {
321327
Dictionary response = prepare_success_response(p_params), body;
322328
response["body"] = body;
323329

324-
DAP::Thread thread;
325-
326-
thread.id = 1; // Hardcoded because Godot only supports debugging one thread at the moment
327-
thread.name = "Main";
328-
Array arr = { thread.to_json() };
329-
body["threads"] = arr;
330+
Array threads;
331+
for (const KeyValue<Thread::ID, Ref<DebugAdapterProtocol::ThreadData>> &thread_data : DebugAdapterProtocol::get_singleton()->thread_data_list) {
332+
DAP::Thread thread;
333+
thread.id = thread_data.key;
334+
thread.name = thread_data.value->name;
335+
threads.push_back(thread.to_json());
336+
}
330337

338+
body["threads"] = threads;
331339
return response;
332340
}
333341

334342
Dictionary DebugAdapterParser::req_stackTrace(const Dictionary &p_params) const {
335-
if (DebugAdapterProtocol::get_singleton()->_processing_stackdump) {
343+
Dictionary args = p_params["arguments"];
344+
Thread::ID thread_id = args["threadId"];
345+
ERR_FAIL_COND_V(!DebugAdapterProtocol::get_singleton()->thread_data_list.has(thread_id), prepare_malformed_error_response(p_params, vformat("Unknown thread id: %d", thread_id)));
346+
const Ref<DebugAdapterProtocol::ThreadData> &thread = DebugAdapterProtocol::get_singleton()->thread_data_list[thread_id];
347+
348+
if (thread->processing_stackdump) {
336349
return Dictionary();
337350
}
338351

@@ -343,16 +356,8 @@ Dictionary DebugAdapterParser::req_stackTrace(const Dictionary &p_params) const
343356
bool columns_at_one = DebugAdapterProtocol::get_singleton()->get_current_peer()->columnsStartAt1;
344357

345358
Array arr;
346-
DebugAdapterProtocol *dap = DebugAdapterProtocol::get_singleton();
347-
for (DAP::StackFrame sf : dap->stackframe_list) {
348-
if (!lines_at_one) {
349-
sf.line--;
350-
}
351-
if (!columns_at_one) {
352-
sf.column--;
353-
}
354-
355-
arr.push_back(sf.to_json());
359+
for (const DAP::StackFrame &sf : thread->stackframe_list) {
360+
arr.push_back(sf.to_json(lines_at_one, columns_at_one));
356361
}
357362

358363
body["stackFrames"] = arr;
@@ -415,16 +420,21 @@ Dictionary DebugAdapterParser::req_breakpointLocations(const Dictionary &p_param
415420
}
416421

417422
Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const {
423+
using DAPRemoteID = DebugAdapterProtocol::DAPRemoteID;
424+
Dictionary args = p_params["arguments"];
425+
DAPRemoteID frame_id = args["frameId"];
426+
427+
ERR_FAIL_COND_V(!DebugAdapterProtocol::get_singleton()->thread_remote_data_lookup.has(frame_id), prepare_malformed_error_response(p_params, vformat("Unknown frame id: %d", frame_id)));
428+
const Ref<DebugAdapterProtocol::ThreadData> &thread = DebugAdapterProtocol::get_singleton()->thread_remote_data_lookup[frame_id];
429+
ERR_FAIL_COND_V(!thread->godot_stackframe_ids.has(frame_id), prepare_malformed_error_response(p_params, vformat("Unknown frame id: %d", frame_id)));
430+
418431
Dictionary response = prepare_success_response(p_params), body;
419432
response["body"] = body;
420433

421-
Dictionary args = p_params["arguments"];
422-
int frame_id = args["frameId"];
423434
Array scope_list;
424-
425-
HashMap<DebugAdapterProtocol::DAPStackFrameID, Vector<int>>::Iterator E = DebugAdapterProtocol::get_singleton()->scope_list.find(frame_id);
435+
HashMap<DAPRemoteID, Vector<DAPRemoteID>>::Iterator E = DebugAdapterProtocol::get_singleton()->scope_list.find(frame_id);
426436
if (E) {
427-
const Vector<int> &scope_ids = E->value;
437+
const Vector<DAPRemoteID> &scope_ids = E->value;
428438
ERR_FAIL_COND_V(scope_ids.size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN));
429439
for (int i = 0; i < 3; ++i) {
430440
DAP::Scope scope;
@@ -447,35 +457,38 @@ Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const {
447457
}
448458
}
449459

450-
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_stack_dump(frame_id);
451-
DebugAdapterProtocol::get_singleton()->_current_frame = frame_id;
460+
int godot_frame_id = thread->godot_stackframe_ids[frame_id];
461+
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_stack_dump(godot_frame_id);
462+
thread->current_frame = frame_id;
452463

453464
body["scopes"] = scope_list;
454465
return response;
455466
}
456467

457468
Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const {
469+
Dictionary args = p_params["arguments"];
470+
DebugAdapterProtocol::DAPRemoteID variable_id = args["variablesReference"];
471+
472+
ERR_FAIL_COND_V(!DebugAdapterProtocol::get_singleton()->thread_remote_data_lookup.has(variable_id), prepare_malformed_error_response(p_params, vformat("Unknown variable id: %d", variable_id)));
473+
const Ref<DebugAdapterProtocol::ThreadData> &thread = DebugAdapterProtocol::get_singleton()->thread_remote_data_lookup[variable_id];
474+
458475
// If _remaining_vars > 0, the debuggee is still sending a stack dump to the editor.
459-
if (DebugAdapterProtocol::get_singleton()->_remaining_vars > 0) {
476+
if (thread->remaining_vars > 0) {
460477
return Dictionary();
461478
}
462479

463-
Dictionary args = p_params["arguments"];
464-
int variable_id = args["variablesReference"];
465-
466480
if (HashMap<int, Array>::Iterator E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); E) {
467481
Dictionary response = prepare_success_response(p_params);
468482
Dictionary body;
469483
response["body"] = body;
470484

471485
if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsVariableType) {
472-
for (int i = 0; i < E->value.size(); i++) {
473-
Dictionary variable = E->value[i];
486+
for (Dictionary variable : E->value) {
474487
variable.erase("type");
475488
}
476489
}
477490

478-
body["variables"] = E ? E->value : Array();
491+
body["variables"] = E->value;
479492
return response;
480493
} else {
481494
// If the requested variable is an object, it needs to be requested from the debuggee.
@@ -485,29 +498,55 @@ Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const {
485498
return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN);
486499
}
487500

488-
DebugAdapterProtocol::get_singleton()->request_remote_object(object_id);
501+
DebugAdapterProtocol::get_singleton()->request_remote_object(object_id, *thread);
489502
}
490503
return Dictionary();
491504
}
492505

493506
Dictionary DebugAdapterParser::req_next(const Dictionary &p_params) const {
507+
Dictionary args = p_params["arguments"];
508+
509+
Thread::ID thread_id = args["threadId"];
510+
ERR_FAIL_COND_V(!DebugAdapterProtocol::get_singleton()->thread_data_list.has(thread_id), prepare_malformed_error_response(p_params, vformat("Unknown thread id: %d", thread_id)));
511+
const Ref<DebugAdapterProtocol::ThreadData> &thread = DebugAdapterProtocol::get_singleton()->thread_data_list[thread_id];
512+
513+
EditorDebuggerNode::get_singleton()->get_default_debugger()->debug_select_thread(thread_id);
494514
EditorDebuggerNode::get_singleton()->get_default_debugger()->debug_next();
495-
DebugAdapterProtocol::get_singleton()->_stepping = true;
515+
thread->stepping = true;
496516

497517
return prepare_success_response(p_params);
498518
}
499519

500520
Dictionary DebugAdapterParser::req_stepIn(const Dictionary &p_params) const {
521+
Dictionary args = p_params["arguments"];
522+
523+
Thread::ID thread_id = args["threadId"];
524+
ERR_FAIL_COND_V(!DebugAdapterProtocol::get_singleton()->thread_data_list.has(thread_id), prepare_malformed_error_response(p_params, vformat("Unknown thread id: %d", thread_id)));
525+
const Ref<DebugAdapterProtocol::ThreadData> &thread = DebugAdapterProtocol::get_singleton()->thread_data_list[thread_id];
526+
527+
EditorDebuggerNode::get_singleton()->get_default_debugger()->debug_select_thread(thread_id);
501528
EditorDebuggerNode::get_singleton()->get_default_debugger()->debug_step();
502-
DebugAdapterProtocol::get_singleton()->_stepping = true;
529+
thread->stepping = true;
503530

504531
return prepare_success_response(p_params);
505532
}
506533

507534
Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const {
508535
Dictionary args = p_params["arguments"];
509536
String expression = args["expression"];
510-
int frame_id = args.has("frameId") ? static_cast<int>(args["frameId"]) : DebugAdapterProtocol::get_singleton()->_current_frame;
537+
538+
int godot_frame_id;
539+
if (args.has("frameId")) {
540+
DebugAdapterProtocol::DAPRemoteID frame_id = static_cast<int>(args["frameId"]);
541+
542+
ERR_FAIL_COND_V(!DebugAdapterProtocol::get_singleton()->thread_remote_data_lookup.has(frame_id), prepare_malformed_error_response(p_params, vformat("Unknown frame id: %d", frame_id)));
543+
const Ref<DebugAdapterProtocol::ThreadData> &thread = DebugAdapterProtocol::get_singleton()->thread_remote_data_lookup[frame_id];
544+
ERR_FAIL_COND_V(!thread->godot_stackframe_ids.has(frame_id), prepare_malformed_error_response(p_params, vformat("Unknown frame id: %d", frame_id)));
545+
godot_frame_id = thread->godot_stackframe_ids[frame_id];
546+
} else {
547+
// If no frame is specified, assume it is top frame, which is 0 on Godot.
548+
godot_frame_id = 0;
549+
}
511550

512551
if (HashMap<String, DAP::Variable>::Iterator E = DebugAdapterProtocol::get_singleton()->eval_list.find(expression); E) {
513552
Dictionary response = prepare_success_response(p_params);
@@ -523,7 +562,7 @@ Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const {
523562
DebugAdapterProtocol::get_singleton()->eval_list.erase(E->key);
524563
return response;
525564
} else {
526-
DebugAdapterProtocol::get_singleton()->request_remote_evaluate(expression, frame_id);
565+
DebugAdapterProtocol::get_singleton()->request_remote_evaluate(expression, godot_frame_id);
527566
}
528567
return Dictionary();
529568
}
@@ -564,7 +603,7 @@ Dictionary DebugAdapterParser::ev_terminated() const {
564603
return event;
565604
}
566605

567-
Dictionary DebugAdapterParser::ev_exited(const int &p_exitcode) const {
606+
Dictionary DebugAdapterParser::ev_exited(int p_exitcode) const {
568607
Dictionary event = prepare_base_event(), body;
569608
event["event"] = "exited";
570609
event["body"] = body;
@@ -574,18 +613,18 @@ Dictionary DebugAdapterParser::ev_exited(const int &p_exitcode) const {
574613
return event;
575614
}
576615

577-
Dictionary DebugAdapterParser::ev_stopped() const {
616+
Dictionary DebugAdapterParser::ev_stopped(Thread::ID p_thread_id) const {
578617
Dictionary event = prepare_base_event(), body;
579618
event["event"] = "stopped";
580619
event["body"] = body;
581620

582-
body["threadId"] = 1;
621+
body["threadId"] = p_thread_id;
583622

584623
return event;
585624
}
586625

587-
Dictionary DebugAdapterParser::ev_stopped_paused() const {
588-
Dictionary event = ev_stopped();
626+
Dictionary DebugAdapterParser::ev_stopped_paused(Thread::ID thread_id) const {
627+
Dictionary event = ev_stopped(thread_id);
589628
Dictionary body = event["body"];
590629

591630
body["reason"] = "paused";
@@ -594,8 +633,8 @@ Dictionary DebugAdapterParser::ev_stopped_paused() const {
594633
return event;
595634
}
596635

597-
Dictionary DebugAdapterParser::ev_stopped_exception(const String &p_error) const {
598-
Dictionary event = ev_stopped();
636+
Dictionary DebugAdapterParser::ev_stopped_exception(const String &p_error, Thread::ID p_thread_id) const {
637+
Dictionary event = ev_stopped(p_thread_id);
599638
Dictionary body = event["body"];
600639

601640
body["reason"] = "exception";
@@ -605,8 +644,8 @@ Dictionary DebugAdapterParser::ev_stopped_exception(const String &p_error) const
605644
return event;
606645
}
607646

608-
Dictionary DebugAdapterParser::ev_stopped_breakpoint(const int &p_id) const {
609-
Dictionary event = ev_stopped();
647+
Dictionary DebugAdapterParser::ev_stopped_breakpoint(int p_id, Thread::ID p_thread_id) const {
648+
Dictionary event = ev_stopped(p_thread_id);
610649
Dictionary body = event["body"];
611650

612651
body["reason"] = "breakpoint";
@@ -618,8 +657,8 @@ Dictionary DebugAdapterParser::ev_stopped_breakpoint(const int &p_id) const {
618657
return event;
619658
}
620659

621-
Dictionary DebugAdapterParser::ev_stopped_step() const {
622-
Dictionary event = ev_stopped();
660+
Dictionary DebugAdapterParser::ev_stopped_step(Thread::ID p_thread_id) const {
661+
Dictionary event = ev_stopped(p_thread_id);
623662
Dictionary body = event["body"];
624663

625664
body["reason"] = "step";
@@ -628,12 +667,12 @@ Dictionary DebugAdapterParser::ev_stopped_step() const {
628667
return event;
629668
}
630669

631-
Dictionary DebugAdapterParser::ev_continued() const {
670+
Dictionary DebugAdapterParser::ev_continued(Thread::ID p_thread_id) const {
632671
Dictionary event = prepare_base_event(), body;
633672
event["event"] = "continued";
634673
event["body"] = body;
635674

636-
body["threadId"] = 1;
675+
body["threadId"] = p_thread_id;
637676

638677
return event;
639678
}
@@ -649,7 +688,7 @@ Dictionary DebugAdapterParser::ev_output(const String &p_message, RemoteDebugger
649688
return event;
650689
}
651690

652-
Dictionary DebugAdapterParser::ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const {
691+
Dictionary DebugAdapterParser::ev_breakpoint(const DAP::Breakpoint &p_breakpoint, bool p_enabled) const {
653692
Dictionary event = prepare_base_event(), body;
654693
event["event"] = "breakpoint";
655694
event["body"] = body;

editor/debugger/debug_adapter/debug_adapter_parser.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ class DebugAdapterParser : public Object {
5959
Dictionary prepare_base_event() const;
6060
Dictionary prepare_success_response(const Dictionary &p_params) const;
6161
Dictionary prepare_error_response(const Dictionary &p_params, DAP::ErrorType err_type, const Dictionary &variables = Dictionary()) const;
62+
Dictionary prepare_malformed_error_response(const Dictionary &p_params, const String &p_error) const;
6263

63-
Dictionary ev_stopped() const;
64+
Dictionary ev_stopped(Thread::ID p_thread_id) const;
6465

6566
public:
6667
// Requests
@@ -92,13 +93,13 @@ class DebugAdapterParser : public Object {
9293
Dictionary ev_initialized() const;
9394
Dictionary ev_process(const String &p_command) const;
9495
Dictionary ev_terminated() const;
95-
Dictionary ev_exited(const int &p_exitcode) const;
96-
Dictionary ev_stopped_paused() const;
97-
Dictionary ev_stopped_exception(const String &p_error) const;
98-
Dictionary ev_stopped_breakpoint(const int &p_id) const;
99-
Dictionary ev_stopped_step() const;
100-
Dictionary ev_continued() const;
96+
Dictionary ev_exited(int p_exitcode) const;
97+
Dictionary ev_stopped_paused(Thread::ID p_thread_id) const;
98+
Dictionary ev_stopped_exception(const String &p_error, Thread::ID p_thread_id) const;
99+
Dictionary ev_stopped_breakpoint(int p_id, Thread::ID p_thread_id) const;
100+
Dictionary ev_stopped_step(Thread::ID p_thread_id) const;
101+
Dictionary ev_continued(Thread::ID p_thread_id) const;
101102
Dictionary ev_output(const String &p_message, RemoteDebugger::MessageType p_type) const;
102103
Dictionary ev_custom_data(const String &p_msg, const Array &p_data) const;
103-
Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const;
104+
Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, bool p_enabled) const;
104105
};

0 commit comments

Comments
 (0)