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 dap.SendReverseRequest<LogFailureResponseHandler>(command: "runInTerminal",
85 arguments: std::move(reverse_request));
86
87 if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
88 attach_info.SetProcessID(*pid);
89 else
90 return pid.takeError();
91
92 std::optional<ScopeSyncMode> scope_sync_mode(dap.debugger);
93 lldb::SBError error;
94 dap.target.Attach(attach_info, error);
95
96 if (error.Fail())
97 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
98 Fmt: "Failed to attach to the target process. %s",
99 Vals: comm_channel.GetLauncherError().c_str());
100 // This will notify the runInTerminal launcher that we attached.
101 // We have to make this async, as the function won't return until the launcher
102 // resumes and reads the data.
103 std::future<lldb::SBError> did_attach_message_success =
104 comm_channel.NotifyDidAttach();
105
106 // We just attached to the runInTerminal launcher, which was waiting to be
107 // attached. We now resume it, so it can receive the didAttach notification
108 // and then perform the exec. Upon continuing, the debugger will stop the
109 // process right in the middle of the exec. To the user, what we are doing is
110 // transparent, as they will only be able to see the process since the exec,
111 // completely unaware of the preparatory work.
112 dap.target.GetProcess().Continue();
113
114 // Now that the actual target is just starting (i.e. exec was just invoked),
115 // we return the debugger to its sync state.
116 scope_sync_mode.reset();
117
118 // If sending the notification failed, the launcher should be dead by now and
119 // the async didAttach notification should have an error message, so we
120 // return it. Otherwise, everything was a success.
121 did_attach_message_success.wait();
122 error = did_attach_message_success.get();
123 if (error.Success())
124 return llvm::Error::success();
125 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
126 S: error.GetCString());
127}
128
129void BaseRequestHandler::Run(const Request &request) {
130 // If this request was cancelled, send a cancelled response.
131 if (dap.IsCancelled(request)) {
132 Response cancelled{/*request_seq=*/request.seq,
133 /*command=*/request.command,
134 /*success=*/false,
135 /*message=*/eResponseMessageCancelled,
136 /*body=*/std::nullopt};
137 dap.Send(message: cancelled);
138 return;
139 }
140
141 lldb::SBMutex lock = dap.GetAPIMutex();
142 std::lock_guard<lldb::SBMutex> guard(lock);
143
144 // FIXME: After all the requests have migrated from LegacyRequestHandler >
145 // RequestHandler<> we should be able to move this into
146 // RequestHandler<>::operator().
147 operator()(request);
148
149 // FIXME: After all the requests have migrated from LegacyRequestHandler >
150 // RequestHandler<> we should be able to check `debugger.InterruptRequest` and
151 // mark the response as cancelled.
152}
153
154llvm::Error BaseRequestHandler::LaunchProcess(
155 const protocol::LaunchRequestArguments &arguments) const {
156 auto launchCommands = arguments.launchCommands;
157
158 // Instantiate a launch info instance for the target.
159 auto launch_info = dap.target.GetLaunchInfo();
160
161 // Grab the current working directory if there is one and set it in the
162 // launch info.
163 if (!arguments.cwd.empty())
164 launch_info.SetWorkingDirectory(arguments.cwd.data());
165
166 // Extract any extra arguments and append them to our program arguments for
167 // when we launch
168 if (!arguments.args.empty())
169 launch_info.SetArguments(argv: MakeArgv(strs: arguments.args).data(), append: true);
170
171 // Pass any environment variables along that the user specified.
172 if (!arguments.env.empty()) {
173 lldb::SBEnvironment env;
174 for (const auto &kv : arguments.env)
175 env.Set(name: kv.first().data(), value: kv.second.c_str(), overwrite: true);
176 launch_info.SetEnvironment(env, append: true);
177 }
178
179 launch_info.SetDetachOnError(arguments.detachOnError);
180 launch_info.SetShellExpandArguments(arguments.shellExpandArguments);
181
182 auto flags = launch_info.GetLaunchFlags();
183 flags =
184 SetLaunchFlag(flags, flag: arguments.disableASLR, mask: lldb::eLaunchFlagDisableASLR);
185 flags = SetLaunchFlag(flags, flag: arguments.disableSTDIO,
186 mask: lldb::eLaunchFlagDisableSTDIO);
187 launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
188 lldb::eLaunchFlagStopAtEntry);
189
190 {
191 // Perform the launch in synchronous mode so that we don't have to worry
192 // about process state changes during the launch.
193 ScopeSyncMode scope_sync_mode(dap.debugger);
194
195 if (arguments.runInTerminal) {
196 if (llvm::Error err = RunInTerminal(dap, arguments))
197 return err;
198 } else if (launchCommands.empty()) {
199 lldb::SBError error;
200 dap.target.Launch(launch_info, error);
201 if (error.Fail())
202 return ToError(error);
203 } else {
204 // Set the launch info so that run commands can access the configured
205 // launch details.
206 dap.target.SetLaunchInfo(launch_info);
207 if (llvm::Error err = dap.RunLaunchCommands(launch_commands: launchCommands))
208 return err;
209
210 // The custom commands might have created a new target so we should use
211 // the selected target after these commands are run.
212 dap.target = dap.debugger.GetSelectedTarget();
213 }
214 }
215
216 // Make sure the process is launched and stopped at the entry point before
217 // proceeding.
218 lldb::SBError error =
219 dap.WaitForProcessToStop(seconds: arguments.configuration.timeout);
220 if (error.Fail())
221 return ToError(error);
222
223 return llvm::Error::success();
224}
225
226void BaseRequestHandler::PrintWelcomeMessage() const {
227#ifdef LLDB_DAP_WELCOME_MESSAGE
228 dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
229#endif
230}
231
232bool BaseRequestHandler::HasInstructionGranularity(
233 const llvm::json::Object &arguments) const {
234 if (std::optional<llvm::StringRef> value = arguments.getString(K: "granularity"))
235 return value == "instruction";
236 return false;
237}
238
239} // namespace lldb_dap
240

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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