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