Skip to content

Commit 03bf2fa

Browse files
committed
[lldb-dap] Move requests into their own object/file
1 parent fb25216 commit 03bf2fa

File tree

6 files changed

+365
-0
lines changed

6 files changed

+365
-0
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ add_lldb_tool(lldb-dap
3636
SourceBreakpoint.cpp
3737
Watchpoint.cpp
3838

39+
Request/Request.cpp
40+
Request/AttachRequest.cpp
41+
3942
LINK_LIBS
4043
liblldb
4144
lldbHost
@@ -46,6 +49,8 @@ add_lldb_tool(lldb-dap
4649
Support
4750
)
4851

52+
target_include_directories(lldb-dap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
53+
4954
if(LLDB_DAP_WELCOME_MESSAGE)
5055
target_compile_definitions(lldb-dap
5156
PRIVATE

lldb/tools/lldb-dap/DAP.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "InstructionBreakpoint.h"
1717
#include "OutputRedirector.h"
1818
#include "ProgressEvent.h"
19+
#include "Request/Request.h"
1920
#include "SourceBreakpoint.h"
2021
#include "lldb/API/SBBroadcaster.h"
2122
#include "lldb/API/SBCommandInterpreter.h"
@@ -37,6 +38,7 @@
3738
#include "llvm/Support/JSON.h"
3839
#include "llvm/Support/Threading.h"
3940
#include <map>
41+
#include <memory>
4042
#include <mutex>
4143
#include <optional>
4244
#include <thread>
@@ -184,6 +186,7 @@ struct DAP {
184186
lldb::pid_t restarting_process_id;
185187
bool configuration_done_sent;
186188
std::map<std::string, RequestCallback, std::less<>> request_handlers;
189+
llvm::StringMap<std::unique_ptr<Request>> new_request_handlers;
187190
bool waiting_for_run_in_terminal;
188191
ProgressEventReporter progress_event_reporter;
189192
// Keep track of the last stop thread index IDs as threads won't go away
@@ -330,6 +333,11 @@ struct DAP {
330333
/// IDE.
331334
void RegisterRequestCallback(std::string request, RequestCallback callback);
332335

336+
/// Registers a request handler.
337+
template <typename Request> void RegisterRequest() {
338+
new_request_handlers[Request::getName()] = std::make_unique<Request>(*this);
339+
}
340+
333341
/// Debuggee will continue from stopped state.
334342
void WillContinue() { variables.Clear(); }
335343

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
//===-- AttachRequest.cpp -------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DAP.h"
10+
#include "JSONUtils.h"
11+
#include "Request.h"
12+
#include "lldb/API/SBListener.h"
13+
#include "llvm/Support/FileSystem.h"
14+
15+
namespace lldb_dap {
16+
/// Prints a welcome message on the editor if the preprocessor variable
17+
/// LLDB_DAP_WELCOME_MESSAGE is defined.
18+
static void PrintWelcomeMessage(DAP &dap) {
19+
#ifdef LLDB_DAP_WELCOME_MESSAGE
20+
dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
21+
#endif
22+
}
23+
24+
// "AttachRequest": {
25+
// "allOf": [ { "$ref": "#/definitions/Request" }, {
26+
// "type": "object",
27+
// "description": "Attach request; value of command field is 'attach'.",
28+
// "properties": {
29+
// "command": {
30+
// "type": "string",
31+
// "enum": [ "attach" ]
32+
// },
33+
// "arguments": {
34+
// "$ref": "#/definitions/AttachRequestArguments"
35+
// }
36+
// },
37+
// "required": [ "command", "arguments" ]
38+
// }]
39+
// },
40+
// "AttachRequestArguments": {
41+
// "type": "object",
42+
// "description": "Arguments for 'attach' request.\nThe attach request has no
43+
// standardized attributes."
44+
// },
45+
// "AttachResponse": {
46+
// "allOf": [ { "$ref": "#/definitions/Response" }, {
47+
// "type": "object",
48+
// "description": "Response to 'attach' request. This is just an
49+
// acknowledgement, so no body field is required."
50+
// }]
51+
// }
52+
53+
void AttachRequest::operator()(const llvm::json::Object &request) {
54+
dap.is_attach = true;
55+
dap.last_launch_or_attach_request = request;
56+
llvm::json::Object response;
57+
lldb::SBError error;
58+
FillResponse(request, response);
59+
lldb::SBAttachInfo attach_info;
60+
const int invalid_port = 0;
61+
const auto *arguments = request.getObject("arguments");
62+
const lldb::pid_t pid =
63+
GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
64+
const auto gdb_remote_port =
65+
GetUnsigned(arguments, "gdb-remote-port", invalid_port);
66+
const auto gdb_remote_hostname =
67+
GetString(arguments, "gdb-remote-hostname", "localhost");
68+
if (pid != LLDB_INVALID_PROCESS_ID)
69+
attach_info.SetProcessID(pid);
70+
const auto wait_for = GetBoolean(arguments, "waitFor", false);
71+
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
72+
dap.init_commands = GetStrings(arguments, "initCommands");
73+
dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
74+
dap.stop_commands = GetStrings(arguments, "stopCommands");
75+
dap.exit_commands = GetStrings(arguments, "exitCommands");
76+
dap.terminate_commands = GetStrings(arguments, "terminateCommands");
77+
auto attachCommands = GetStrings(arguments, "attachCommands");
78+
llvm::StringRef core_file = GetString(arguments, "coreFile");
79+
const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
80+
dap.stop_at_entry =
81+
core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
82+
dap.post_run_commands = GetStrings(arguments, "postRunCommands");
83+
const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
84+
dap.enable_auto_variable_summaries =
85+
GetBoolean(arguments, "enableAutoVariableSummaries", false);
86+
dap.enable_synthetic_child_debugging =
87+
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
88+
dap.display_extended_backtrace =
89+
GetBoolean(arguments, "displayExtendedBacktrace", false);
90+
dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`");
91+
dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
92+
dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
93+
94+
PrintWelcomeMessage(dap);
95+
96+
// This is a hack for loading DWARF in .o files on Mac where the .o files
97+
// in the debug map of the main executable have relative paths which require
98+
// the lldb-dap binary to have its working directory set to that relative
99+
// root for the .o files in order to be able to load debug info.
100+
if (!debuggerRoot.empty())
101+
llvm::sys::fs::set_current_path(debuggerRoot);
102+
103+
// Run any initialize LLDB commands the user specified in the launch.json
104+
if (llvm::Error err = dap.RunInitCommands()) {
105+
response["success"] = false;
106+
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
107+
dap.SendJSON(llvm::json::Value(std::move(response)));
108+
return;
109+
}
110+
111+
SetSourceMapFromArguments(*arguments);
112+
113+
lldb::SBError status;
114+
dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
115+
if (status.Fail()) {
116+
response["success"] = llvm::json::Value(false);
117+
EmplaceSafeString(response, "message", status.GetCString());
118+
dap.SendJSON(llvm::json::Value(std::move(response)));
119+
return;
120+
}
121+
122+
// Run any pre run LLDB commands the user specified in the launch.json
123+
if (llvm::Error err = dap.RunPreRunCommands()) {
124+
response["success"] = false;
125+
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
126+
dap.SendJSON(llvm::json::Value(std::move(response)));
127+
return;
128+
}
129+
130+
if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
131+
wait_for) {
132+
char attach_msg[256];
133+
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
134+
"Waiting to attach to \"%s\"...",
135+
dap.target.GetExecutable().GetFilename());
136+
dap.SendOutput(OutputType::Console,
137+
llvm::StringRef(attach_msg, attach_msg_len));
138+
}
139+
if (attachCommands.empty()) {
140+
// No "attachCommands", just attach normally.
141+
// Disable async events so the attach will be successful when we return from
142+
// the launch call and the launch will happen synchronously
143+
dap.debugger.SetAsync(false);
144+
if (core_file.empty()) {
145+
if ((pid != LLDB_INVALID_PROCESS_ID) &&
146+
(gdb_remote_port != invalid_port)) {
147+
// If both pid and port numbers are specified.
148+
error.SetErrorString("The user can't specify both pid and port");
149+
} else if (gdb_remote_port != invalid_port) {
150+
// If port is specified and pid is not.
151+
lldb::SBListener listener = dap.debugger.GetListener();
152+
153+
// If the user hasn't provided the hostname property, default localhost
154+
// being used.
155+
std::string connect_url =
156+
llvm::formatv("connect://{0}:", gdb_remote_hostname);
157+
connect_url += std::to_string(gdb_remote_port);
158+
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
159+
error);
160+
} else {
161+
// Attach by process name or id.
162+
dap.target.Attach(attach_info, error);
163+
}
164+
} else
165+
dap.target.LoadCore(core_file.data(), error);
166+
// Reenable async events
167+
dap.debugger.SetAsync(true);
168+
} else {
169+
// We have "attachCommands" that are a set of commands that are expected
170+
// to execute the commands after which a process should be created. If there
171+
// is no valid process after running these commands, we have failed.
172+
if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
173+
response["success"] = false;
174+
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
175+
dap.SendJSON(llvm::json::Value(std::move(response)));
176+
return;
177+
}
178+
// The custom commands might have created a new target so we should use the
179+
// selected target after these commands are run.
180+
dap.target = dap.debugger.GetSelectedTarget();
181+
182+
// Make sure the process is attached and stopped before proceeding as the
183+
// the launch commands are not run using the synchronous mode.
184+
error = dap.WaitForProcessToStop(timeout_seconds);
185+
}
186+
187+
if (error.Success() && core_file.empty()) {
188+
auto attached_pid = dap.target.GetProcess().GetProcessID();
189+
if (attached_pid == LLDB_INVALID_PROCESS_ID) {
190+
if (attachCommands.empty())
191+
error.SetErrorString("failed to attach to a process");
192+
else
193+
error.SetErrorString("attachCommands failed to attach to a process");
194+
}
195+
}
196+
197+
if (error.Fail()) {
198+
response["success"] = llvm::json::Value(false);
199+
EmplaceSafeString(response, "message", std::string(error.GetCString()));
200+
} else {
201+
dap.RunPostRunCommands();
202+
}
203+
204+
dap.SendJSON(llvm::json::Value(std::move(response)));
205+
if (error.Success()) {
206+
SendProcessEvent(Attach);
207+
dap.SendJSON(CreateEventObject("initialized"));
208+
}
209+
}
210+
211+
} // namespace lldb_dap
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//===-- Request.cpp -------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "Request.h"
10+
#include "DAP.h"
11+
#include "JSONUtils.h"
12+
#include "lldb/API/SBFileSpec.h"
13+
14+
namespace lldb_dap {
15+
16+
void Request::SendProcessEvent(Request::LaunchMethod launch_method) {
17+
lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
18+
char exe_path[PATH_MAX];
19+
exe_fspec.GetPath(exe_path, sizeof(exe_path));
20+
llvm::json::Object event(CreateEventObject("process"));
21+
llvm::json::Object body;
22+
EmplaceSafeString(body, "name", std::string(exe_path));
23+
const auto pid = dap.target.GetProcess().GetProcessID();
24+
body.try_emplace("systemProcessId", (int64_t)pid);
25+
body.try_emplace("isLocalProcess", true);
26+
const char *startMethod = nullptr;
27+
switch (launch_method) {
28+
case Launch:
29+
startMethod = "launch";
30+
break;
31+
case Attach:
32+
startMethod = "attach";
33+
break;
34+
case AttachForSuspendedLaunch:
35+
startMethod = "attachForSuspendedLaunch";
36+
break;
37+
}
38+
body.try_emplace("startMethod", startMethod);
39+
event.try_emplace("body", std::move(body));
40+
dap.SendJSON(llvm::json::Value(std::move(event)));
41+
}
42+
43+
// Both attach and launch take a either a sourcePath or sourceMap
44+
// argument (or neither), from which we need to set the target.source-map.
45+
void Request::SetSourceMapFromArguments(const llvm::json::Object &arguments) {
46+
const char *sourceMapHelp =
47+
"source must be be an array of two-element arrays, "
48+
"each containing a source and replacement path string.\n";
49+
50+
std::string sourceMapCommand;
51+
llvm::raw_string_ostream strm(sourceMapCommand);
52+
strm << "settings set target.source-map ";
53+
const auto sourcePath = GetString(arguments, "sourcePath");
54+
55+
// sourceMap is the new, more general form of sourcePath and overrides it.
56+
constexpr llvm::StringRef sourceMapKey = "sourceMap";
57+
58+
if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) {
59+
for (const auto &value : *sourceMapArray) {
60+
const auto *mapping = value.getAsArray();
61+
if (mapping == nullptr || mapping->size() != 2 ||
62+
(*mapping)[0].kind() != llvm::json::Value::String ||
63+
(*mapping)[1].kind() != llvm::json::Value::String) {
64+
dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
65+
return;
66+
}
67+
const auto mapFrom = GetAsString((*mapping)[0]);
68+
const auto mapTo = GetAsString((*mapping)[1]);
69+
strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
70+
}
71+
} else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) {
72+
for (const auto &[key, value] : *sourceMapObj) {
73+
if (value.kind() == llvm::json::Value::String) {
74+
strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" ";
75+
}
76+
}
77+
} else {
78+
if (ObjectContainsKey(arguments, sourceMapKey)) {
79+
dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
80+
return;
81+
}
82+
if (sourcePath.empty())
83+
return;
84+
// Do any source remapping needed before we create our targets
85+
strm << "\".\" \"" << sourcePath << "\"";
86+
}
87+
if (!sourceMapCommand.empty()) {
88+
dap.RunLLDBCommands("Setting source map:", {sourceMapCommand});
89+
}
90+
}
91+
92+
} // namespace lldb_dap

0 commit comments

Comments
 (0)