|
9 | 9 | #include "DAP.h" |
10 | 10 | #include "EventHelper.h" |
11 | 11 | #include "JSONUtils.h" |
| 12 | +#include "LLDBUtils.h" |
| 13 | +#include "Protocol/ProtocolRequests.h" |
12 | 14 | #include "RequestHandler.h" |
13 | 15 | #include "lldb/API/SBListener.h" |
| 16 | +#include "lldb/lldb-defines.h" |
| 17 | +#include "llvm/Support/Error.h" |
14 | 18 | #include "llvm/Support/FileSystem.h" |
15 | 19 |
|
| 20 | +using namespace llvm; |
| 21 | +using namespace lldb_dap::protocol; |
| 22 | + |
16 | 23 | namespace lldb_dap { |
17 | 24 |
|
18 | | -// "AttachRequest": { |
19 | | -// "allOf": [ { "$ref": "#/definitions/Request" }, { |
20 | | -// "type": "object", |
21 | | -// "description": "Attach request; value of command field is 'attach'.", |
22 | | -// "properties": { |
23 | | -// "command": { |
24 | | -// "type": "string", |
25 | | -// "enum": [ "attach" ] |
26 | | -// }, |
27 | | -// "arguments": { |
28 | | -// "$ref": "#/definitions/AttachRequestArguments" |
29 | | -// } |
30 | | -// }, |
31 | | -// "required": [ "command", "arguments" ] |
32 | | -// }] |
33 | | -// }, |
34 | | -// "AttachRequestArguments": { |
35 | | -// "type": "object", |
36 | | -// "description": "Arguments for 'attach' request.\nThe attach request has no |
37 | | -// standardized attributes." |
38 | | -// }, |
39 | | -// "AttachResponse": { |
40 | | -// "allOf": [ { "$ref": "#/definitions/Response" }, { |
41 | | -// "type": "object", |
42 | | -// "description": "Response to 'attach' request. This is just an |
43 | | -// acknowledgement, so no body field is required." |
44 | | -// }] |
45 | | -// } |
46 | | -void AttachRequestHandler::operator()(const llvm::json::Object &request) const { |
47 | | - dap.is_attach = true; |
48 | | - llvm::json::Object response; |
49 | | - lldb::SBError error; |
50 | | - FillResponse(request, response); |
51 | | - lldb::SBAttachInfo attach_info; |
52 | | - const int invalid_port = 0; |
53 | | - const auto *arguments = request.getObject("arguments"); |
54 | | - const lldb::pid_t pid = |
55 | | - GetInteger<uint64_t>(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID); |
56 | | - const auto gdb_remote_port = |
57 | | - GetInteger<uint64_t>(arguments, "gdb-remote-port").value_or(invalid_port); |
58 | | - const auto gdb_remote_hostname = |
59 | | - GetString(arguments, "gdb-remote-hostname").value_or("localhost"); |
60 | | - if (pid != LLDB_INVALID_PROCESS_ID) |
61 | | - attach_info.SetProcessID(pid); |
62 | | - const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); |
63 | | - attach_info.SetWaitForLaunch(wait_for, false /*async*/); |
64 | | - dap.configuration.initCommands = GetStrings(arguments, "initCommands"); |
65 | | - dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); |
66 | | - dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); |
67 | | - dap.configuration.stopCommands = GetStrings(arguments, "stopCommands"); |
68 | | - dap.configuration.exitCommands = GetStrings(arguments, "exitCommands"); |
69 | | - dap.configuration.terminateCommands = |
70 | | - GetStrings(arguments, "terminateCommands"); |
71 | | - auto attachCommands = GetStrings(arguments, "attachCommands"); |
72 | | - llvm::StringRef core_file = GetString(arguments, "coreFile").value_or(""); |
73 | | - const uint64_t timeout_seconds = |
74 | | - GetInteger<uint64_t>(arguments, "timeout").value_or(30); |
75 | | - dap.stop_at_entry = core_file.empty() |
76 | | - ? GetBoolean(arguments, "stopOnEntry").value_or(false) |
77 | | - : true; |
78 | | - const llvm::StringRef debuggerRoot = |
79 | | - GetString(arguments, "debuggerRoot").value_or(""); |
80 | | - dap.configuration.enableAutoVariableSummaries = |
81 | | - GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false); |
82 | | - dap.configuration.enableSyntheticChildDebugging = |
83 | | - GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false); |
84 | | - dap.configuration.displayExtendedBacktrace = |
85 | | - GetBoolean(arguments, "displayExtendedBacktrace").value_or(false); |
86 | | - dap.configuration.commandEscapePrefix = |
87 | | - GetString(arguments, "commandEscapePrefix").value_or("`"); |
88 | | - dap.configuration.program = GetString(arguments, "program"); |
89 | | - dap.configuration.targetTriple = GetString(arguments, "targetTriple"); |
90 | | - dap.configuration.platformName = GetString(arguments, "platformName"); |
91 | | - dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or("")); |
92 | | - dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or("")); |
| 25 | +/// The `attach` request is sent from the client to the debug adapter to attach |
| 26 | +/// to a debuggee that is already running. |
| 27 | +/// |
| 28 | +/// Since attaching is debugger/runtime specific, the arguments for this request |
| 29 | +/// are not part of this specification. |
| 30 | +Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { |
| 31 | + dap.SetConfiguration(args.configuration, true); |
| 32 | + if (!args.coreFile.empty()) |
| 33 | + dap.stop_at_entry = true; |
| 34 | + |
| 35 | + // If both pid and port numbers are specified. |
| 36 | + if ((args.pid != LLDB_INVALID_PROCESS_ID) && |
| 37 | + (args.gdbRemotePort != LLDB_DAP_INVALID_PORT)) |
| 38 | + return make_error<DAPError>( |
| 39 | + "pid and gdb-remote-port are mutually exclusive"); |
93 | 40 |
|
94 | 41 | PrintWelcomeMessage(); |
95 | 42 |
|
96 | 43 | // 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); |
| 44 | + // in the debug map of the main executable have relative paths which |
| 45 | + // require the lldb-dap binary to have its working directory set to that |
| 46 | + // relative root for the .o files in order to be able to load debug info. |
| 47 | + if (!dap.configuration.debuggerRoot.empty()) |
| 48 | + sys::fs::set_current_path(dap.configuration.debuggerRoot); |
102 | 49 |
|
103 | 50 | // 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 | | - } |
| 51 | + if (llvm::Error err = dap.RunInitCommands()) |
| 52 | + return err; |
110 | 53 |
|
111 | | - SetSourceMapFromArguments(*arguments); |
| 54 | + dap.ConfigureSourceMaps(); |
112 | 55 |
|
113 | | - lldb::SBError status; |
114 | | - dap.SetTarget(dap.CreateTarget(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 | | - } |
| 56 | + lldb::SBError error; |
| 57 | + lldb::SBTarget target = dap.CreateTarget(error); |
| 58 | + if (error.Fail()) |
| 59 | + return ToError(error); |
| 60 | + |
| 61 | + dap.SetTarget(target); |
121 | 62 |
|
122 | 63 | // 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 | | - } |
| 64 | + if (Error err = dap.RunPreRunCommands()) |
| 65 | + return err; |
129 | 66 |
|
130 | | - if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && |
131 | | - wait_for) { |
| 67 | + if ((args.pid == LLDB_INVALID_PROCESS_ID || |
| 68 | + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) && |
| 69 | + args.waitFor) { |
132 | 70 | char attach_msg[256]; |
133 | 71 | auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), |
134 | 72 | "Waiting to attach to \"%s\"...", |
135 | 73 | dap.target.GetExecutable().GetFilename()); |
136 | | - dap.SendOutput(OutputType::Console, |
137 | | - llvm::StringRef(attach_msg, attach_msg_len)); |
| 74 | + dap.SendOutput(OutputType::Console, StringRef(attach_msg, attach_msg_len)); |
138 | 75 | } |
139 | | - if (attachCommands.empty()) { |
| 76 | + |
| 77 | + if (args.attachCommands.empty()) { |
140 | 78 | // No "attachCommands", just attach normally. |
141 | 79 | // Disable async events so the attach will be successful when we return from |
142 | 80 | // the launch call and the launch will happen synchronously |
143 | 81 | 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) { |
| 82 | + if (args.coreFile.empty()) { |
| 83 | + if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) { |
150 | 84 | // If port is specified and pid is not. |
151 | 85 | lldb::SBListener listener = dap.debugger.GetListener(); |
152 | 86 |
|
153 | 87 | // If the user hasn't provided the hostname property, default localhost |
154 | 88 | // being used. |
155 | 89 | 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", |
| 90 | + llvm::formatv("connect://{0}:", args.gdbRemoteHostname); |
| 91 | + connect_url += std::to_string(args.gdbRemotePort); |
| 92 | + dap.target.ConnectRemote(listener, connect_url.data(), "gdb-remote", |
159 | 93 | error); |
160 | 94 | } else { |
161 | 95 | // Attach by process name or id. |
| 96 | + lldb::SBAttachInfo attach_info; |
| 97 | + if (!args.configuration.program.empty()) |
| 98 | + attach_info.SetExecutable(args.configuration.program.data()); |
| 99 | + if (args.pid != LLDB_INVALID_PROCESS_ID) |
| 100 | + attach_info.SetProcessID(args.pid); |
| 101 | + attach_info.SetWaitForLaunch(args.waitFor, false /*async*/); |
162 | 102 | dap.target.Attach(attach_info, error); |
163 | 103 | } |
164 | 104 | } else |
165 | | - dap.target.LoadCore(core_file.data(), error); |
| 105 | + dap.target.LoadCore(args.coreFile.data(), error); |
166 | 106 | // Reenable async events |
167 | 107 | dap.debugger.SetAsync(true); |
168 | 108 | } else { |
169 | 109 | // We have "attachCommands" that are a set of commands that are expected |
170 | 110 | // to execute the commands after which a process should be created. If there |
171 | 111 | // 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 | | - } |
| 112 | + if (llvm::Error err = dap.RunAttachCommands(args.attachCommands)) |
| 113 | + return err; |
| 114 | + |
178 | 115 | // The custom commands might have created a new target so we should use the |
179 | 116 | // selected target after these commands are run. |
180 | 117 | dap.target = dap.debugger.GetSelectedTarget(); |
181 | 118 |
|
182 | 119 | // Make sure the process is attached and stopped before proceeding as the |
183 | 120 | // the launch commands are not run using the synchronous mode. |
184 | | - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); |
| 121 | + error = dap.WaitForProcessToStop(dap.configuration.timeout); |
185 | 122 | } |
186 | 123 |
|
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 | | - } |
| 124 | + if (error.Fail()) |
| 125 | + return ToError(error); |
196 | 126 |
|
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 | | - } |
| 127 | + if (args.coreFile.empty() && !dap.target.GetProcess().IsValid()) |
| 128 | + return make_error<DAPError>("failed to attach to process"); |
203 | 129 |
|
204 | | - dap.SendJSON(llvm::json::Value(std::move(response))); |
205 | | - if (error.Success()) { |
| 130 | + dap.RunPostRunCommands(); |
| 131 | + |
| 132 | + return Error::success(); |
| 133 | +} |
| 134 | + |
| 135 | +void AttachRequestHandler::PostRun() const { |
| 136 | + if (dap.target.GetProcess().IsValid()) { |
206 | 137 | SendProcessEvent(dap, Attach); |
207 | | - dap.SendJSON(CreateEventObject("initialized")); |
208 | 138 | } |
| 139 | + |
| 140 | + dap.SendJSON(CreateEventObject("initialized")); |
209 | 141 | } |
210 | 142 |
|
211 | 143 | } // namespace lldb_dap |
0 commit comments