1//===-- RequestHandler.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 "Handler/RequestHandler.h"
10#include "DAP.h"
11#include "EventHelper.h"
12#include "Handler/ResponseHandler.h"
13#include "JSONUtils.h"
14#include "LLDBUtils.h"
15#include "Protocol/ProtocolBase.h"
16#include "Protocol/ProtocolRequests.h"
17#include "RunInTerminal.h"
18#include "lldb/API/SBDefines.h"
19#include "lldb/API/SBEnvironment.h"
20#include "llvm/Support/Error.h"
21#include <mutex>
22
23#if !defined(_WIN32)
24#include <unistd.h>
25#endif
26
27using namespace lldb_dap::protocol;
28
29namespace lldb_dap {
30
31static std::vector<const char *>
32MakeArgv(const llvm::ArrayRef<std::string> &strs) {
33 // Create and return an array of "const char *", one for each C string in
34 // "strs" and terminate the list with a NULL. This can be used for argument
35 // vectors (argv) or environment vectors (envp) like those passed to the
36 // "main" function in C programs.
37 std::vector<const char *> argv;
38 for (const auto &s : strs)
39 argv.push_back(x: s.c_str());
40 argv.push_back(x: nullptr);
41 return argv;
42}
43
44static uint32_t SetLaunchFlag(uint32_t flags, bool flag,
45 lldb::LaunchFlags mask) {
46 if (flag)
47 flags |= mask;
48 else
49 flags &= ~mask;
50
51 return flags;
52}
53
54static llvm::Error
55RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
56 if (!dap.clientFeatures.contains(
57 V: protocol::eClientFeatureRunInTerminalRequest))
58 return llvm::make_error<DAPError>(Args: "Cannot use runInTerminal, feature is "
59 "not supported by the connected client");
60
61 if (arguments.configuration.program.empty())
62 return llvm::make_error<DAPError>(
63 Args: "program must be set to when using runInTerminal");
64
65 dap.is_attach = true;
66 lldb::SBAttachInfo attach_info;
67
68 llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =
69 CreateRunInTerminalCommFile();
70 if (!comm_file_or_err)
71 return comm_file_or_err.takeError();
72 FifoFile &comm_file = *comm_file_or_err.get();
73
74 RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
75
76 lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;
77#if !defined(_WIN32)
78 debugger_pid = getpid();
79#endif
80
81 llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
82 program: arguments.configuration.program, args: arguments.args, env: arguments.env,
83 cwd: arguments.cwd, comm_file: comm_file.m_path, debugger_pid,
84 external: arguments.console == protocol::eConsoleExternalTerminal);
85 dap.SendReverseRequest<LogFailureResponseHandler>(command: "runInTerminal",
86 arguments: std::move(reverse_request));
87
88 if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
89 attach_info.SetProcessID(*pid);
90 else
91 return pid.takeError();
92
93 std::optional<ScopeSyncMode> scope_sync_mode(dap.debugger);
94 lldb::SBError error;
95 dap.target.Attach(attach_info, error);
96
97 if (error.Fail())
98 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
99 Fmt: "Failed to attach to the target process. %s",
100 Vals: comm_channel.GetLauncherError().c_str());
101 // This will notify the runInTerminal launcher that we attached.
102 // We have to make this async, as the function won't return until the launcher
103 // resumes and reads the data.
104 std::future<lldb::SBError> did_attach_message_success =
105 comm_channel.NotifyDidAttach();
106
107 // We just attached to the runInTerminal launcher, which was waiting to be
108 // attached. We now resume it, so it can receive the didAttach notification
109 // and then perform the exec. Upon continuing, the debugger will stop the
110 // process right in the middle of the exec. To the user, what we are doing is
111 // transparent, as they will only be able to see the process since the exec,
112 // completely unaware of the preparatory work.
113 dap.target.GetProcess().Continue();
114
115 // Now that the actual target is just starting (i.e. exec was just invoked),
116 // we return the debugger to its sync state.
117 scope_sync_mode.reset();
118
119 // If sending the notification failed, the launcher should be dead by now and
120 // the async didAttach notification should have an error message, so we
121 // return it. Otherwise, everything was a success.
122 did_attach_message_success.wait();
123 error = did_attach_message_success.get();
124 if (error.Success())
125 return llvm::Error::success();
126 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
127 S: error.GetCString());
128}
129
130void BaseRequestHandler::Run(const Request &request) {
131 // If this request was cancelled, send a cancelled response.
132 if (dap.IsCancelled(request)) {
133 Response cancelled{/*request_seq=*/request.seq,
134 /*command=*/request.command,
135 /*success=*/false,
136 /*message=*/eResponseMessageCancelled,
137 /*body=*/std::nullopt};
138 dap.Send(message: cancelled);
139 return;
140 }
141
142 lldb::SBMutex lock = dap.GetAPIMutex();
143 std::lock_guard<lldb::SBMutex> guard(lock);
144
145 // FIXME: After all the requests have migrated from LegacyRequestHandler >
146 // RequestHandler<> we should be able to move this into
147 // RequestHandler<>::operator().
148 operator()(request);
149
150 // FIXME: After all the requests have migrated from LegacyRequestHandler >
151 // RequestHandler<> we should be able to check `debugger.InterruptRequest` and
152 // mark the response as cancelled.
153}
154
155llvm::Error BaseRequestHandler::LaunchProcess(
156 const protocol::LaunchRequestArguments &arguments) const {
157 auto launchCommands = arguments.launchCommands;
158
159 // Instantiate a launch info instance for the target.
160 auto launch_info = dap.target.GetLaunchInfo();
161
162 // Grab the current working directory if there is one and set it in the
163 // launch info.
164 if (!arguments.cwd.empty())
165 launch_info.SetWorkingDirectory(arguments.cwd.data());
166
167 // Extract any extra arguments and append them to our program arguments for
168 // when we launch
169 if (!arguments.args.empty())
170 launch_info.SetArguments(argv: MakeArgv(strs: arguments.args).data(), append: true);
171
172 // Pass any environment variables along that the user specified.
173 if (!arguments.env.empty()) {
174 lldb::SBEnvironment env;
175 for (const auto &kv : arguments.env)
176 env.Set(name: kv.first().data(), value: kv.second.c_str(), overwrite: true);
177 launch_info.SetEnvironment(env, append: true);
178 }
179
180 launch_info.SetDetachOnError(arguments.detachOnError);
181 launch_info.SetShellExpandArguments(arguments.shellExpandArguments);
182
183 auto flags = launch_info.GetLaunchFlags();
184 flags =
185 SetLaunchFlag(flags, flag: arguments.disableASLR, mask: lldb::eLaunchFlagDisableASLR);
186 flags = SetLaunchFlag(flags, flag: arguments.disableSTDIO,
187 mask: lldb::eLaunchFlagDisableSTDIO);
188 launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
189 lldb::eLaunchFlagStopAtEntry);
190
191 {
192 // Perform the launch in synchronous mode so that we don't have to worry
193 // about process state changes during the launch.
194 ScopeSyncMode scope_sync_mode(dap.debugger);
195
196 if (arguments.console != protocol::eConsoleInternal) {
197 if (llvm::Error err = RunInTerminal(dap, arguments))
198 return err;
199 } else if (launchCommands.empty()) {
200 lldb::SBError error;
201 dap.target.Launch(launch_info, error);
202 if (error.Fail())
203 return ToError(error);
204 } else {
205 // Set the launch info so that run commands can access the configured
206 // launch details.
207 dap.target.SetLaunchInfo(launch_info);
208 if (llvm::Error err = dap.RunLaunchCommands(launch_commands: launchCommands))
209 return err;
210
211 // The custom commands might have created a new target so we should use
212 // the selected target after these commands are run.
213 dap.target = dap.debugger.GetSelectedTarget();
214 }
215 }
216
217 // Make sure the process is launched and stopped at the entry point before
218 // proceeding.
219 lldb::SBError error =
220 dap.WaitForProcessToStop(seconds: arguments.configuration.timeout);
221 if (error.Fail())
222 return ToError(error);
223
224 return llvm::Error::success();
225}
226
227void BaseRequestHandler::PrintWelcomeMessage() const {
228#ifdef LLDB_DAP_WELCOME_MESSAGE
229 dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
230#endif
231}
232
233bool BaseRequestHandler::HasInstructionGranularity(
234 const llvm::json::Object &arguments) const {
235 if (std::optional<llvm::StringRef> value = arguments.getString(K: "granularity"))
236 return value == "instruction";
237 return false;
238}
239
240} // namespace lldb_dap
241

source code of lldb/tools/lldb-dap/Handler/RequestHandler.cpp