1061da546Spatrick //===-- lldb-vscode.cpp -----------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9be691f3bSpatrick #include "VSCode.h"
10be691f3bSpatrick
11be691f3bSpatrick #include <cassert>
12be691f3bSpatrick #include <climits>
13be691f3bSpatrick #include <cstdarg>
14be691f3bSpatrick #include <cstdio>
15be691f3bSpatrick #include <cstdlib>
16be691f3bSpatrick #include <cstring>
17061da546Spatrick #include <sys/stat.h>
18061da546Spatrick #include <sys/types.h>
19061da546Spatrick #if defined(_WIN32)
20061da546Spatrick // We need to #define NOMINMAX in order to skip `min()` and `max()` macro
21061da546Spatrick // definitions that conflict with other system headers.
22061da546Spatrick // We also need to #undef GetObject (which is defined to GetObjectW) because
23061da546Spatrick // the JSON code we use also has methods named `GetObject()` and we conflict
24061da546Spatrick // against these.
25061da546Spatrick #define NOMINMAX
26061da546Spatrick #include <windows.h>
27061da546Spatrick #undef GetObject
28061da546Spatrick #include <io.h>
29061da546Spatrick #else
30061da546Spatrick #include <netinet/in.h>
31061da546Spatrick #include <sys/socket.h>
32061da546Spatrick #include <unistd.h>
33061da546Spatrick #endif
34061da546Spatrick
35061da546Spatrick #include <algorithm>
36061da546Spatrick #include <chrono>
37061da546Spatrick #include <fstream>
38061da546Spatrick #include <map>
39061da546Spatrick #include <memory>
40061da546Spatrick #include <mutex>
41061da546Spatrick #include <set>
42061da546Spatrick #include <sstream>
43061da546Spatrick #include <thread>
44dda28197Spatrick #include <vector>
45061da546Spatrick
46061da546Spatrick #include "llvm/ADT/ArrayRef.h"
47be691f3bSpatrick #include "llvm/ADT/DenseMap.h"
48be691f3bSpatrick #include "llvm/ADT/ScopeExit.h"
49dda28197Spatrick #include "llvm/Option/Arg.h"
50dda28197Spatrick #include "llvm/Option/ArgList.h"
51dda28197Spatrick #include "llvm/Option/Option.h"
52061da546Spatrick #include "llvm/Support/Errno.h"
53061da546Spatrick #include "llvm/Support/FileSystem.h"
54be691f3bSpatrick #include "llvm/Support/InitLLVM.h"
55dda28197Spatrick #include "llvm/Support/Path.h"
56be691f3bSpatrick #include "llvm/Support/PrettyStackTrace.h"
57061da546Spatrick #include "llvm/Support/raw_ostream.h"
58061da546Spatrick
59061da546Spatrick #include "JSONUtils.h"
60061da546Spatrick #include "LLDBUtils.h"
61be691f3bSpatrick #include "OutputRedirector.h"
62061da546Spatrick
63061da546Spatrick #if defined(_WIN32)
64061da546Spatrick #ifndef PATH_MAX
65061da546Spatrick #define PATH_MAX MAX_PATH
66061da546Spatrick #endif
67061da546Spatrick typedef int socklen_t;
68061da546Spatrick #endif
69061da546Spatrick
70061da546Spatrick using namespace lldb_vscode;
71061da546Spatrick
72061da546Spatrick namespace {
73dda28197Spatrick enum ID {
74dda28197Spatrick OPT_INVALID = 0, // This is not an option ID.
75dda28197Spatrick #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
76dda28197Spatrick HELPTEXT, METAVAR, VALUES) \
77dda28197Spatrick OPT_##ID,
78dda28197Spatrick #include "Options.inc"
79dda28197Spatrick #undef OPTION
80dda28197Spatrick };
81dda28197Spatrick
82*f6aab3d8Srobert #define PREFIX(NAME, VALUE) \
83*f6aab3d8Srobert static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
84*f6aab3d8Srobert static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
85*f6aab3d8Srobert NAME##_init, std::size(NAME##_init) - 1);
86dda28197Spatrick #include "Options.inc"
87dda28197Spatrick #undef PREFIX
88dda28197Spatrick
89*f6aab3d8Srobert static constexpr llvm::opt::OptTable::Info InfoTable[] = {
90dda28197Spatrick #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
91dda28197Spatrick HELPTEXT, METAVAR, VALUES) \
92dda28197Spatrick {PREFIX, NAME, HELPTEXT, \
93dda28197Spatrick METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
94dda28197Spatrick PARAM, FLAGS, OPT_##GROUP, \
95dda28197Spatrick OPT_##ALIAS, ALIASARGS, VALUES},
96dda28197Spatrick #include "Options.inc"
97dda28197Spatrick #undef OPTION
98dda28197Spatrick };
99*f6aab3d8Srobert class LLDBVSCodeOptTable : public llvm::opt::GenericOptTable {
100dda28197Spatrick public:
LLDBVSCodeOptTable()101*f6aab3d8Srobert LLDBVSCodeOptTable() : llvm::opt::GenericOptTable(InfoTable, true) {}
102dda28197Spatrick };
103061da546Spatrick
104061da546Spatrick typedef void (*RequestCallback)(const llvm::json::Object &command);
105061da546Spatrick
106061da546Spatrick enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
107061da546Spatrick
GetTopLevelScope(int64_t variablesReference)108*f6aab3d8Srobert lldb::SBValueList *GetTopLevelScope(int64_t variablesReference) {
109*f6aab3d8Srobert switch (variablesReference) {
110*f6aab3d8Srobert case VARREF_LOCALS:
111*f6aab3d8Srobert return &g_vsc.variables.locals;
112*f6aab3d8Srobert case VARREF_GLOBALS:
113*f6aab3d8Srobert return &g_vsc.variables.globals;
114*f6aab3d8Srobert case VARREF_REGS:
115*f6aab3d8Srobert return &g_vsc.variables.registers;
116*f6aab3d8Srobert default:
117*f6aab3d8Srobert return nullptr;
118*f6aab3d8Srobert }
119*f6aab3d8Srobert }
120*f6aab3d8Srobert
AcceptConnection(int portno)121061da546Spatrick SOCKET AcceptConnection(int portno) {
122061da546Spatrick // Accept a socket connection from any host on "portno".
123061da546Spatrick SOCKET newsockfd = -1;
124061da546Spatrick struct sockaddr_in serv_addr, cli_addr;
125061da546Spatrick SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
126061da546Spatrick if (sockfd < 0) {
127061da546Spatrick if (g_vsc.log)
128061da546Spatrick *g_vsc.log << "error: opening socket (" << strerror(errno) << ")"
129061da546Spatrick << std::endl;
130061da546Spatrick } else {
131061da546Spatrick memset((char *)&serv_addr, 0, sizeof(serv_addr));
132061da546Spatrick serv_addr.sin_family = AF_INET;
133061da546Spatrick // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
134061da546Spatrick serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
135061da546Spatrick serv_addr.sin_port = htons(portno);
136061da546Spatrick if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
137061da546Spatrick if (g_vsc.log)
138061da546Spatrick *g_vsc.log << "error: binding socket (" << strerror(errno) << ")"
139061da546Spatrick << std::endl;
140061da546Spatrick } else {
141061da546Spatrick listen(sockfd, 5);
142061da546Spatrick socklen_t clilen = sizeof(cli_addr);
143061da546Spatrick newsockfd =
144061da546Spatrick llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
145061da546Spatrick (struct sockaddr *)&cli_addr, &clilen);
146061da546Spatrick if (newsockfd < 0)
147061da546Spatrick if (g_vsc.log)
148061da546Spatrick *g_vsc.log << "error: accept (" << strerror(errno) << ")"
149061da546Spatrick << std::endl;
150061da546Spatrick }
151061da546Spatrick #if defined(_WIN32)
152061da546Spatrick closesocket(sockfd);
153061da546Spatrick #else
154061da546Spatrick close(sockfd);
155061da546Spatrick #endif
156061da546Spatrick }
157061da546Spatrick return newsockfd;
158061da546Spatrick }
159061da546Spatrick
MakeArgv(const llvm::ArrayRef<std::string> & strs)160061da546Spatrick std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
161061da546Spatrick // Create and return an array of "const char *", one for each C string in
162061da546Spatrick // "strs" and terminate the list with a NULL. This can be used for argument
163061da546Spatrick // vectors (argv) or environment vectors (envp) like those passed to the
164061da546Spatrick // "main" function in C programs.
165061da546Spatrick std::vector<const char *> argv;
166061da546Spatrick for (const auto &s : strs)
167061da546Spatrick argv.push_back(s.c_str());
168061da546Spatrick argv.push_back(nullptr);
169061da546Spatrick return argv;
170061da546Spatrick }
171061da546Spatrick
172061da546Spatrick // Send a "exited" event to indicate the process has exited.
SendProcessExitedEvent(lldb::SBProcess & process)173061da546Spatrick void SendProcessExitedEvent(lldb::SBProcess &process) {
174061da546Spatrick llvm::json::Object event(CreateEventObject("exited"));
175061da546Spatrick llvm::json::Object body;
176061da546Spatrick body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
177061da546Spatrick event.try_emplace("body", std::move(body));
178061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(event)));
179061da546Spatrick }
180061da546Spatrick
SendThreadExitedEvent(lldb::tid_t tid)181061da546Spatrick void SendThreadExitedEvent(lldb::tid_t tid) {
182061da546Spatrick llvm::json::Object event(CreateEventObject("thread"));
183061da546Spatrick llvm::json::Object body;
184061da546Spatrick body.try_emplace("reason", "exited");
185061da546Spatrick body.try_emplace("threadId", (int64_t)tid);
186061da546Spatrick event.try_emplace("body", std::move(body));
187061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(event)));
188061da546Spatrick }
189061da546Spatrick
190061da546Spatrick // Send a "terminated" event to indicate the process is done being
191061da546Spatrick // debugged.
SendTerminatedEvent()192061da546Spatrick void SendTerminatedEvent() {
193be691f3bSpatrick // If an inferior exits prior to the processing of a disconnect request, then
194be691f3bSpatrick // the threads executing EventThreadFunction and request_discontinue
195be691f3bSpatrick // respectively may call SendTerminatedEvent simultaneously. Without any
196be691f3bSpatrick // synchronization, the thread executing EventThreadFunction may set
197be691f3bSpatrick // g_vsc.sent_terminated_event before the thread executing
198be691f3bSpatrick // request_discontinue has had a chance to test it, in which case the latter
199be691f3bSpatrick // would move ahead to issue a response to the disconnect request. Said
200be691f3bSpatrick // response may get dispatched ahead of the terminated event compelling the
201be691f3bSpatrick // client to terminate the debug session without consuming any console output
202be691f3bSpatrick // that might've been generated by the execution of terminateCommands. So,
203be691f3bSpatrick // synchronize simultaneous calls to SendTerminatedEvent.
204be691f3bSpatrick static std::mutex mutex;
205be691f3bSpatrick std::lock_guard<std::mutex> locker(mutex);
206061da546Spatrick if (!g_vsc.sent_terminated_event) {
207061da546Spatrick g_vsc.sent_terminated_event = true;
208dda28197Spatrick g_vsc.RunTerminateCommands();
209061da546Spatrick // Send a "terminated" event
210*f6aab3d8Srobert llvm::json::Object event(CreateTerminatedEventObject());
211061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(event)));
212061da546Spatrick }
213061da546Spatrick }
214061da546Spatrick
215061da546Spatrick // Send a thread stopped event for all threads as long as the process
216061da546Spatrick // is stopped.
SendThreadStoppedEvent()217061da546Spatrick void SendThreadStoppedEvent() {
218061da546Spatrick lldb::SBProcess process = g_vsc.target.GetProcess();
219061da546Spatrick if (process.IsValid()) {
220061da546Spatrick auto state = process.GetState();
221061da546Spatrick if (state == lldb::eStateStopped) {
222061da546Spatrick llvm::DenseSet<lldb::tid_t> old_thread_ids;
223061da546Spatrick old_thread_ids.swap(g_vsc.thread_ids);
224061da546Spatrick uint32_t stop_id = process.GetStopID();
225061da546Spatrick const uint32_t num_threads = process.GetNumThreads();
226061da546Spatrick
227061da546Spatrick // First make a pass through the threads to see if the focused thread
228061da546Spatrick // has a stop reason. In case the focus thread doesn't have a stop
229061da546Spatrick // reason, remember the first thread that has a stop reason so we can
230061da546Spatrick // set it as the focus thread if below if needed.
231061da546Spatrick lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
232061da546Spatrick uint32_t num_threads_with_reason = 0;
233*f6aab3d8Srobert bool focus_thread_exists = false;
234061da546Spatrick for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
235061da546Spatrick lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
236061da546Spatrick const lldb::tid_t tid = thread.GetThreadID();
237061da546Spatrick const bool has_reason = ThreadHasStopReason(thread);
238061da546Spatrick // If the focus thread doesn't have a stop reason, clear the thread ID
239*f6aab3d8Srobert if (tid == g_vsc.focus_tid) {
240*f6aab3d8Srobert focus_thread_exists = true;
241*f6aab3d8Srobert if (!has_reason)
242061da546Spatrick g_vsc.focus_tid = LLDB_INVALID_THREAD_ID;
243*f6aab3d8Srobert }
244061da546Spatrick if (has_reason) {
245061da546Spatrick ++num_threads_with_reason;
246061da546Spatrick if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
247061da546Spatrick first_tid_with_reason = tid;
248061da546Spatrick }
249061da546Spatrick }
250061da546Spatrick
251*f6aab3d8Srobert // We will have cleared g_vsc.focus_tid if he focus thread doesn't have
252*f6aab3d8Srobert // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
253*f6aab3d8Srobert // then set the focus thread to the first thread with a stop reason.
254*f6aab3d8Srobert if (!focus_thread_exists || g_vsc.focus_tid == LLDB_INVALID_THREAD_ID)
255061da546Spatrick g_vsc.focus_tid = first_tid_with_reason;
256061da546Spatrick
257061da546Spatrick // If no threads stopped with a reason, then report the first one so
258061da546Spatrick // we at least let the UI know we stopped.
259061da546Spatrick if (num_threads_with_reason == 0) {
260061da546Spatrick lldb::SBThread thread = process.GetThreadAtIndex(0);
261061da546Spatrick g_vsc.SendJSON(CreateThreadStopped(thread, stop_id));
262061da546Spatrick } else {
263061da546Spatrick for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
264061da546Spatrick lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
265061da546Spatrick g_vsc.thread_ids.insert(thread.GetThreadID());
266061da546Spatrick if (ThreadHasStopReason(thread)) {
267061da546Spatrick g_vsc.SendJSON(CreateThreadStopped(thread, stop_id));
268061da546Spatrick }
269061da546Spatrick }
270061da546Spatrick }
271061da546Spatrick
272061da546Spatrick for (auto tid : old_thread_ids) {
273061da546Spatrick auto end = g_vsc.thread_ids.end();
274061da546Spatrick auto pos = g_vsc.thread_ids.find(tid);
275061da546Spatrick if (pos == end)
276061da546Spatrick SendThreadExitedEvent(tid);
277061da546Spatrick }
278061da546Spatrick } else {
279061da546Spatrick if (g_vsc.log)
280061da546Spatrick *g_vsc.log << "error: SendThreadStoppedEvent() when process"
281061da546Spatrick " isn't stopped ("
282061da546Spatrick << lldb::SBDebugger::StateAsCString(state) << ')'
283061da546Spatrick << std::endl;
284061da546Spatrick }
285061da546Spatrick } else {
286061da546Spatrick if (g_vsc.log)
287061da546Spatrick *g_vsc.log << "error: SendThreadStoppedEvent() invalid process"
288061da546Spatrick << std::endl;
289061da546Spatrick }
290061da546Spatrick g_vsc.RunStopCommands();
291061da546Spatrick }
292061da546Spatrick
293061da546Spatrick // "ProcessEvent": {
294061da546Spatrick // "allOf": [
295061da546Spatrick // { "$ref": "#/definitions/Event" },
296061da546Spatrick // {
297061da546Spatrick // "type": "object",
298061da546Spatrick // "description": "Event message for 'process' event type. The event
299061da546Spatrick // indicates that the debugger has begun debugging a
300061da546Spatrick // new process. Either one that it has launched, or one
301061da546Spatrick // that it has attached to.",
302061da546Spatrick // "properties": {
303061da546Spatrick // "event": {
304061da546Spatrick // "type": "string",
305061da546Spatrick // "enum": [ "process" ]
306061da546Spatrick // },
307061da546Spatrick // "body": {
308061da546Spatrick // "type": "object",
309061da546Spatrick // "properties": {
310061da546Spatrick // "name": {
311061da546Spatrick // "type": "string",
312061da546Spatrick // "description": "The logical name of the process. This is
313061da546Spatrick // usually the full path to process's executable
314061da546Spatrick // file. Example: /home/myproj/program.js."
315061da546Spatrick // },
316061da546Spatrick // "systemProcessId": {
317061da546Spatrick // "type": "integer",
318061da546Spatrick // "description": "The system process id of the debugged process.
319061da546Spatrick // This property will be missing for non-system
320061da546Spatrick // processes."
321061da546Spatrick // },
322061da546Spatrick // "isLocalProcess": {
323061da546Spatrick // "type": "boolean",
324061da546Spatrick // "description": "If true, the process is running on the same
325061da546Spatrick // computer as the debug adapter."
326061da546Spatrick // },
327061da546Spatrick // "startMethod": {
328061da546Spatrick // "type": "string",
329061da546Spatrick // "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
330061da546Spatrick // "description": "Describes how the debug engine started
331061da546Spatrick // debugging this process.",
332061da546Spatrick // "enumDescriptions": [
333061da546Spatrick // "Process was launched under the debugger.",
334061da546Spatrick // "Debugger attached to an existing process.",
335061da546Spatrick // "A project launcher component has launched a new process in
336061da546Spatrick // a suspended state and then asked the debugger to attach."
337061da546Spatrick // ]
338061da546Spatrick // }
339061da546Spatrick // },
340061da546Spatrick // "required": [ "name" ]
341061da546Spatrick // }
342061da546Spatrick // },
343061da546Spatrick // "required": [ "event", "body" ]
344061da546Spatrick // }
345061da546Spatrick // ]
346061da546Spatrick // }
SendProcessEvent(LaunchMethod launch_method)347061da546Spatrick void SendProcessEvent(LaunchMethod launch_method) {
348061da546Spatrick lldb::SBFileSpec exe_fspec = g_vsc.target.GetExecutable();
349061da546Spatrick char exe_path[PATH_MAX];
350061da546Spatrick exe_fspec.GetPath(exe_path, sizeof(exe_path));
351061da546Spatrick llvm::json::Object event(CreateEventObject("process"));
352061da546Spatrick llvm::json::Object body;
353061da546Spatrick EmplaceSafeString(body, "name", std::string(exe_path));
354061da546Spatrick const auto pid = g_vsc.target.GetProcess().GetProcessID();
355061da546Spatrick body.try_emplace("systemProcessId", (int64_t)pid);
356061da546Spatrick body.try_emplace("isLocalProcess", true);
357061da546Spatrick const char *startMethod = nullptr;
358061da546Spatrick switch (launch_method) {
359061da546Spatrick case Launch:
360061da546Spatrick startMethod = "launch";
361061da546Spatrick break;
362061da546Spatrick case Attach:
363061da546Spatrick startMethod = "attach";
364061da546Spatrick break;
365061da546Spatrick case AttachForSuspendedLaunch:
366061da546Spatrick startMethod = "attachForSuspendedLaunch";
367061da546Spatrick break;
368061da546Spatrick }
369061da546Spatrick body.try_emplace("startMethod", startMethod);
370061da546Spatrick event.try_emplace("body", std::move(body));
371061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(event)));
372061da546Spatrick }
373061da546Spatrick
374061da546Spatrick // Grab any STDOUT and STDERR from the process and send it up to VS Code
375061da546Spatrick // via an "output" event to the "stdout" and "stderr" categories.
SendStdOutStdErr(lldb::SBProcess & process)376061da546Spatrick void SendStdOutStdErr(lldb::SBProcess &process) {
377061da546Spatrick char buffer[1024];
378061da546Spatrick size_t count;
379061da546Spatrick while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
380061da546Spatrick g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
381061da546Spatrick while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
382061da546Spatrick g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
383061da546Spatrick }
384061da546Spatrick
ProgressEventThreadFunction()385be691f3bSpatrick void ProgressEventThreadFunction() {
386be691f3bSpatrick lldb::SBListener listener("lldb-vscode.progress.listener");
387be691f3bSpatrick g_vsc.debugger.GetBroadcaster().AddListener(
388be691f3bSpatrick listener, lldb::SBDebugger::eBroadcastBitProgress);
389be691f3bSpatrick g_vsc.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
390be691f3bSpatrick lldb::SBEvent event;
391be691f3bSpatrick bool done = false;
392be691f3bSpatrick while (!done) {
393be691f3bSpatrick if (listener.WaitForEvent(1, event)) {
394be691f3bSpatrick const auto event_mask = event.GetType();
395be691f3bSpatrick if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
396be691f3bSpatrick if (event_mask & eBroadcastBitStopProgressThread) {
397be691f3bSpatrick done = true;
398be691f3bSpatrick }
399be691f3bSpatrick } else {
400be691f3bSpatrick uint64_t progress_id = 0;
401be691f3bSpatrick uint64_t completed = 0;
402be691f3bSpatrick uint64_t total = 0;
403be691f3bSpatrick bool is_debugger_specific = false;
404be691f3bSpatrick const char *message = lldb::SBDebugger::GetProgressFromEvent(
405be691f3bSpatrick event, progress_id, completed, total, is_debugger_specific);
406be691f3bSpatrick if (message)
407be691f3bSpatrick g_vsc.SendProgressEvent(progress_id, message, completed, total);
408be691f3bSpatrick }
409be691f3bSpatrick }
410be691f3bSpatrick }
411be691f3bSpatrick }
412be691f3bSpatrick
413061da546Spatrick // All events from the debugger, target, process, thread and frames are
414061da546Spatrick // received in this function that runs in its own thread. We are using a
415061da546Spatrick // "FILE *" to output packets back to VS Code and they have mutexes in them
416061da546Spatrick // them prevent multiple threads from writing simultaneously so no locking
417061da546Spatrick // is required.
EventThreadFunction()418061da546Spatrick void EventThreadFunction() {
419061da546Spatrick lldb::SBEvent event;
420061da546Spatrick lldb::SBListener listener = g_vsc.debugger.GetListener();
421061da546Spatrick bool done = false;
422061da546Spatrick while (!done) {
423061da546Spatrick if (listener.WaitForEvent(1, event)) {
424061da546Spatrick const auto event_mask = event.GetType();
425061da546Spatrick if (lldb::SBProcess::EventIsProcessEvent(event)) {
426061da546Spatrick lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
427061da546Spatrick if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
428061da546Spatrick auto state = lldb::SBProcess::GetStateFromEvent(event);
429061da546Spatrick switch (state) {
430061da546Spatrick case lldb::eStateInvalid:
431061da546Spatrick // Not a state event
432061da546Spatrick break;
433061da546Spatrick case lldb::eStateUnloaded:
434061da546Spatrick break;
435061da546Spatrick case lldb::eStateConnected:
436061da546Spatrick break;
437061da546Spatrick case lldb::eStateAttaching:
438061da546Spatrick break;
439061da546Spatrick case lldb::eStateLaunching:
440061da546Spatrick break;
441061da546Spatrick case lldb::eStateStepping:
442061da546Spatrick break;
443061da546Spatrick case lldb::eStateCrashed:
444061da546Spatrick break;
445061da546Spatrick case lldb::eStateDetached:
446061da546Spatrick break;
447061da546Spatrick case lldb::eStateSuspended:
448061da546Spatrick break;
449061da546Spatrick case lldb::eStateStopped:
450*f6aab3d8Srobert // We launch and attach in synchronous mode then the first stop
451*f6aab3d8Srobert // event will not be delivered. If we use "launchCommands" during a
452*f6aab3d8Srobert // launch or "attachCommands" during an attach we might some process
453*f6aab3d8Srobert // stop events which we do not want to send an event for. We will
454*f6aab3d8Srobert // manually send a stopped event in request_configurationDone(...)
455*f6aab3d8Srobert // so don't send any before then.
456*f6aab3d8Srobert if (g_vsc.configuration_done_sent) {
457061da546Spatrick // Only report a stopped event if the process was not restarted.
458061da546Spatrick if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
459061da546Spatrick SendStdOutStdErr(process);
460061da546Spatrick SendThreadStoppedEvent();
461061da546Spatrick }
462*f6aab3d8Srobert }
463061da546Spatrick break;
464061da546Spatrick case lldb::eStateRunning:
465*f6aab3d8Srobert g_vsc.WillContinue();
466061da546Spatrick break;
467061da546Spatrick case lldb::eStateExited: {
468061da546Spatrick // Run any exit LLDB commands the user specified in the
469061da546Spatrick // launch.json
470061da546Spatrick g_vsc.RunExitCommands();
471061da546Spatrick SendProcessExitedEvent(process);
472061da546Spatrick SendTerminatedEvent();
473061da546Spatrick done = true;
474061da546Spatrick } break;
475061da546Spatrick }
476061da546Spatrick } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
477061da546Spatrick (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
478061da546Spatrick SendStdOutStdErr(process);
479061da546Spatrick }
480061da546Spatrick } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
481061da546Spatrick if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
482061da546Spatrick auto event_type =
483061da546Spatrick lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
484061da546Spatrick auto bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event);
485dda28197Spatrick // If the breakpoint was originated from the IDE, it will have the
486dda28197Spatrick // BreakpointBase::GetBreakpointLabel() label attached. Regardless
487dda28197Spatrick // of wether the locations were added or removed, the breakpoint
488dda28197Spatrick // ins't going away, so we the reason is always "changed".
489dda28197Spatrick if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
490dda28197Spatrick event_type & lldb::eBreakpointEventTypeLocationsRemoved) &&
491dda28197Spatrick bp.MatchesName(BreakpointBase::GetBreakpointLabel())) {
492061da546Spatrick auto bp_event = CreateEventObject("breakpoint");
493061da546Spatrick llvm::json::Object body;
494dda28197Spatrick // As VSCode already knows the path of this breakpoint, we don't
495dda28197Spatrick // need to send it back as part of a "changed" event. This
496dda28197Spatrick // prevent us from sending to VSCode paths that should be source
497dda28197Spatrick // mapped. Note that CreateBreakpoint doesn't apply source mapping.
498dda28197Spatrick // Besides, the current implementation of VSCode ignores the
499dda28197Spatrick // "source" element of breakpoint events.
500dda28197Spatrick llvm::json::Value source_bp = CreateBreakpoint(bp);
501dda28197Spatrick source_bp.getAsObject()->erase("source");
502dda28197Spatrick
503dda28197Spatrick body.try_emplace("breakpoint", source_bp);
504dda28197Spatrick body.try_emplace("reason", "changed");
505061da546Spatrick bp_event.try_emplace("body", std::move(body));
506061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(bp_event)));
507061da546Spatrick }
508061da546Spatrick }
509061da546Spatrick } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
510061da546Spatrick if (event_mask & eBroadcastBitStopEventThread) {
511061da546Spatrick done = true;
512061da546Spatrick }
513061da546Spatrick }
514061da546Spatrick }
515061da546Spatrick }
516061da546Spatrick }
517061da546Spatrick
518061da546Spatrick // Both attach and launch take a either a sourcePath or sourceMap
519061da546Spatrick // argument (or neither), from which we need to set the target.source-map.
SetSourceMapFromArguments(const llvm::json::Object & arguments)520061da546Spatrick void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
521061da546Spatrick const char *sourceMapHelp =
522061da546Spatrick "source must be be an array of two-element arrays, "
523061da546Spatrick "each containing a source and replacement path string.\n";
524061da546Spatrick
525061da546Spatrick std::string sourceMapCommand;
526061da546Spatrick llvm::raw_string_ostream strm(sourceMapCommand);
527061da546Spatrick strm << "settings set target.source-map ";
528061da546Spatrick auto sourcePath = GetString(arguments, "sourcePath");
529061da546Spatrick
530061da546Spatrick // sourceMap is the new, more general form of sourcePath and overrides it.
531061da546Spatrick auto sourceMap = arguments.getArray("sourceMap");
532061da546Spatrick if (sourceMap) {
533061da546Spatrick for (const auto &value : *sourceMap) {
534061da546Spatrick auto mapping = value.getAsArray();
535061da546Spatrick if (mapping == nullptr || mapping->size() != 2 ||
536061da546Spatrick (*mapping)[0].kind() != llvm::json::Value::String ||
537061da546Spatrick (*mapping)[1].kind() != llvm::json::Value::String) {
538061da546Spatrick g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
539061da546Spatrick return;
540061da546Spatrick }
541061da546Spatrick auto mapFrom = GetAsString((*mapping)[0]);
542061da546Spatrick auto mapTo = GetAsString((*mapping)[1]);
543061da546Spatrick strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
544061da546Spatrick }
545061da546Spatrick } else {
546061da546Spatrick if (ObjectContainsKey(arguments, "sourceMap")) {
547061da546Spatrick g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
548061da546Spatrick return;
549061da546Spatrick }
550061da546Spatrick if (sourcePath.empty())
551061da546Spatrick return;
552061da546Spatrick // Do any source remapping needed before we create our targets
553061da546Spatrick strm << "\".\" \"" << sourcePath << "\"";
554061da546Spatrick }
555061da546Spatrick strm.flush();
556061da546Spatrick if (!sourceMapCommand.empty()) {
557061da546Spatrick g_vsc.RunLLDBCommands("Setting source map:", {sourceMapCommand});
558061da546Spatrick }
559061da546Spatrick }
560061da546Spatrick
561061da546Spatrick // "AttachRequest": {
562061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
563061da546Spatrick // "type": "object",
564061da546Spatrick // "description": "Attach request; value of command field is 'attach'.",
565061da546Spatrick // "properties": {
566061da546Spatrick // "command": {
567061da546Spatrick // "type": "string",
568061da546Spatrick // "enum": [ "attach" ]
569061da546Spatrick // },
570061da546Spatrick // "arguments": {
571061da546Spatrick // "$ref": "#/definitions/AttachRequestArguments"
572061da546Spatrick // }
573061da546Spatrick // },
574061da546Spatrick // "required": [ "command", "arguments" ]
575061da546Spatrick // }]
576061da546Spatrick // },
577061da546Spatrick // "AttachRequestArguments": {
578061da546Spatrick // "type": "object",
579061da546Spatrick // "description": "Arguments for 'attach' request.\nThe attach request has no
580061da546Spatrick // standardized attributes."
581061da546Spatrick // },
582061da546Spatrick // "AttachResponse": {
583061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
584061da546Spatrick // "type": "object",
585061da546Spatrick // "description": "Response to 'attach' request. This is just an
586061da546Spatrick // acknowledgement, so no body field is required."
587061da546Spatrick // }]
588061da546Spatrick // }
request_attach(const llvm::json::Object & request)589061da546Spatrick void request_attach(const llvm::json::Object &request) {
590dda28197Spatrick g_vsc.is_attach = true;
591061da546Spatrick llvm::json::Object response;
592061da546Spatrick lldb::SBError error;
593061da546Spatrick FillResponse(request, response);
594dda28197Spatrick lldb::SBAttachInfo attach_info;
595061da546Spatrick auto arguments = request.getObject("arguments");
596061da546Spatrick const lldb::pid_t pid =
597061da546Spatrick GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
598061da546Spatrick if (pid != LLDB_INVALID_PROCESS_ID)
599dda28197Spatrick attach_info.SetProcessID(pid);
600061da546Spatrick const auto wait_for = GetBoolean(arguments, "waitFor", false);
601dda28197Spatrick attach_info.SetWaitForLaunch(wait_for, false /*async*/);
602061da546Spatrick g_vsc.init_commands = GetStrings(arguments, "initCommands");
603061da546Spatrick g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands");
604061da546Spatrick g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
605061da546Spatrick g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
606dda28197Spatrick g_vsc.terminate_commands = GetStrings(arguments, "terminateCommands");
607061da546Spatrick auto attachCommands = GetStrings(arguments, "attachCommands");
608dda28197Spatrick llvm::StringRef core_file = GetString(arguments, "coreFile");
609*f6aab3d8Srobert const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
610dda28197Spatrick g_vsc.stop_at_entry =
611dda28197Spatrick core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
612be691f3bSpatrick std::vector<std::string> postRunCommands =
613be691f3bSpatrick GetStrings(arguments, "postRunCommands");
614dda28197Spatrick const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
615061da546Spatrick
616061da546Spatrick // This is a hack for loading DWARF in .o files on Mac where the .o files
617061da546Spatrick // in the debug map of the main executable have relative paths which require
618061da546Spatrick // the lldb-vscode binary to have its working directory set to that relative
619061da546Spatrick // root for the .o files in order to be able to load debug info.
620dda28197Spatrick if (!debuggerRoot.empty())
621dda28197Spatrick llvm::sys::fs::set_current_path(debuggerRoot);
622061da546Spatrick
623061da546Spatrick // Run any initialize LLDB commands the user specified in the launch.json
624061da546Spatrick g_vsc.RunInitCommands();
625061da546Spatrick
626*f6aab3d8Srobert SetSourceMapFromArguments(*arguments);
627*f6aab3d8Srobert
628dda28197Spatrick lldb::SBError status;
629dda28197Spatrick g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
630dda28197Spatrick if (status.Fail()) {
631061da546Spatrick response["success"] = llvm::json::Value(false);
632dda28197Spatrick EmplaceSafeString(response, "message", status.GetCString());
633061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
634061da546Spatrick return;
635061da546Spatrick }
636061da546Spatrick
637061da546Spatrick // Run any pre run LLDB commands the user specified in the launch.json
638061da546Spatrick g_vsc.RunPreRunCommands();
639061da546Spatrick
640061da546Spatrick if (pid == LLDB_INVALID_PROCESS_ID && wait_for) {
641dda28197Spatrick char attach_msg[256];
642dda28197Spatrick auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
643dda28197Spatrick "Waiting to attach to \"%s\"...",
644dda28197Spatrick g_vsc.target.GetExecutable().GetFilename());
645dda28197Spatrick g_vsc.SendOutput(OutputType::Console,
646dda28197Spatrick llvm::StringRef(attach_msg, attach_msg_len));
647061da546Spatrick }
648061da546Spatrick if (attachCommands.empty()) {
649061da546Spatrick // No "attachCommands", just attach normally.
650061da546Spatrick // Disable async events so the attach will be successful when we return from
651061da546Spatrick // the launch call and the launch will happen synchronously
652061da546Spatrick g_vsc.debugger.SetAsync(false);
653dda28197Spatrick if (core_file.empty())
654dda28197Spatrick g_vsc.target.Attach(attach_info, error);
655dda28197Spatrick else
656dda28197Spatrick g_vsc.target.LoadCore(core_file.data(), error);
657061da546Spatrick // Reenable async events
658061da546Spatrick g_vsc.debugger.SetAsync(true);
659061da546Spatrick } else {
660061da546Spatrick // We have "attachCommands" that are a set of commands that are expected
661061da546Spatrick // to execute the commands after which a process should be created. If there
662061da546Spatrick // is no valid process after running these commands, we have failed.
663061da546Spatrick g_vsc.RunLLDBCommands("Running attachCommands:", attachCommands);
664061da546Spatrick // The custom commands might have created a new target so we should use the
665061da546Spatrick // selected target after these commands are run.
666061da546Spatrick g_vsc.target = g_vsc.debugger.GetSelectedTarget();
667061da546Spatrick
668*f6aab3d8Srobert // Make sure the process is attached and stopped before proceeding as the
669*f6aab3d8Srobert // the launch commands are not run using the synchronous mode.
670*f6aab3d8Srobert error = g_vsc.WaitForProcessToStop(timeout_seconds);
671*f6aab3d8Srobert }
672061da546Spatrick
673dda28197Spatrick if (error.Success() && core_file.empty()) {
674061da546Spatrick auto attached_pid = g_vsc.target.GetProcess().GetProcessID();
675061da546Spatrick if (attached_pid == LLDB_INVALID_PROCESS_ID) {
676061da546Spatrick if (attachCommands.empty())
677061da546Spatrick error.SetErrorString("failed to attach to a process");
678061da546Spatrick else
679061da546Spatrick error.SetErrorString("attachCommands failed to attach to a process");
680061da546Spatrick }
681061da546Spatrick }
682061da546Spatrick
683061da546Spatrick if (error.Fail()) {
684061da546Spatrick response["success"] = llvm::json::Value(false);
685061da546Spatrick EmplaceSafeString(response, "message", std::string(error.GetCString()));
686be691f3bSpatrick } else {
687be691f3bSpatrick g_vsc.RunLLDBCommands("Running postRunCommands:", postRunCommands);
688061da546Spatrick }
689be691f3bSpatrick
690061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
691061da546Spatrick if (error.Success()) {
692061da546Spatrick SendProcessEvent(Attach);
693061da546Spatrick g_vsc.SendJSON(CreateEventObject("initialized"));
694061da546Spatrick }
695061da546Spatrick }
696061da546Spatrick
697061da546Spatrick // "ContinueRequest": {
698061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
699061da546Spatrick // "type": "object",
700061da546Spatrick // "description": "Continue request; value of command field is 'continue'.
701061da546Spatrick // The request starts the debuggee to run again.",
702061da546Spatrick // "properties": {
703061da546Spatrick // "command": {
704061da546Spatrick // "type": "string",
705061da546Spatrick // "enum": [ "continue" ]
706061da546Spatrick // },
707061da546Spatrick // "arguments": {
708061da546Spatrick // "$ref": "#/definitions/ContinueArguments"
709061da546Spatrick // }
710061da546Spatrick // },
711061da546Spatrick // "required": [ "command", "arguments" ]
712061da546Spatrick // }]
713061da546Spatrick // },
714061da546Spatrick // "ContinueArguments": {
715061da546Spatrick // "type": "object",
716061da546Spatrick // "description": "Arguments for 'continue' request.",
717061da546Spatrick // "properties": {
718061da546Spatrick // "threadId": {
719061da546Spatrick // "type": "integer",
720061da546Spatrick // "description": "Continue execution for the specified thread (if
721061da546Spatrick // possible). If the backend cannot continue on a single
722061da546Spatrick // thread but will continue on all threads, it should
723061da546Spatrick // set the allThreadsContinued attribute in the response
724061da546Spatrick // to true."
725061da546Spatrick // }
726061da546Spatrick // },
727061da546Spatrick // "required": [ "threadId" ]
728061da546Spatrick // },
729061da546Spatrick // "ContinueResponse": {
730061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
731061da546Spatrick // "type": "object",
732061da546Spatrick // "description": "Response to 'continue' request.",
733061da546Spatrick // "properties": {
734061da546Spatrick // "body": {
735061da546Spatrick // "type": "object",
736061da546Spatrick // "properties": {
737061da546Spatrick // "allThreadsContinued": {
738061da546Spatrick // "type": "boolean",
739061da546Spatrick // "description": "If true, the continue request has ignored the
740061da546Spatrick // specified thread and continued all threads
741061da546Spatrick // instead. If this attribute is missing a value
742061da546Spatrick // of 'true' is assumed for backward
743061da546Spatrick // compatibility."
744061da546Spatrick // }
745061da546Spatrick // }
746061da546Spatrick // }
747061da546Spatrick // },
748061da546Spatrick // "required": [ "body" ]
749061da546Spatrick // }]
750061da546Spatrick // }
request_continue(const llvm::json::Object & request)751061da546Spatrick void request_continue(const llvm::json::Object &request) {
752061da546Spatrick llvm::json::Object response;
753061da546Spatrick FillResponse(request, response);
754061da546Spatrick lldb::SBProcess process = g_vsc.target.GetProcess();
755061da546Spatrick auto arguments = request.getObject("arguments");
756061da546Spatrick // Remember the thread ID that caused the resume so we can set the
757061da546Spatrick // "threadCausedFocus" boolean value in the "stopped" events.
758061da546Spatrick g_vsc.focus_tid = GetUnsigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
759061da546Spatrick lldb::SBError error = process.Continue();
760061da546Spatrick llvm::json::Object body;
761061da546Spatrick body.try_emplace("allThreadsContinued", true);
762061da546Spatrick response.try_emplace("body", std::move(body));
763061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
764061da546Spatrick }
765061da546Spatrick
766061da546Spatrick // "ConfigurationDoneRequest": {
767061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
768061da546Spatrick // "type": "object",
769061da546Spatrick // "description": "ConfigurationDone request; value of command field
770061da546Spatrick // is 'configurationDone'.\nThe client of the debug protocol must
771061da546Spatrick // send this request at the end of the sequence of configuration
772061da546Spatrick // requests (which was started by the InitializedEvent).",
773061da546Spatrick // "properties": {
774061da546Spatrick // "command": {
775061da546Spatrick // "type": "string",
776061da546Spatrick // "enum": [ "configurationDone" ]
777061da546Spatrick // },
778061da546Spatrick // "arguments": {
779061da546Spatrick // "$ref": "#/definitions/ConfigurationDoneArguments"
780061da546Spatrick // }
781061da546Spatrick // },
782061da546Spatrick // "required": [ "command" ]
783061da546Spatrick // }]
784061da546Spatrick // },
785061da546Spatrick // "ConfigurationDoneArguments": {
786061da546Spatrick // "type": "object",
787061da546Spatrick // "description": "Arguments for 'configurationDone' request.\nThe
788061da546Spatrick // configurationDone request has no standardized attributes."
789061da546Spatrick // },
790061da546Spatrick // "ConfigurationDoneResponse": {
791061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
792061da546Spatrick // "type": "object",
793061da546Spatrick // "description": "Response to 'configurationDone' request. This is
794061da546Spatrick // just an acknowledgement, so no body field is required."
795061da546Spatrick // }]
796061da546Spatrick // },
request_configurationDone(const llvm::json::Object & request)797061da546Spatrick void request_configurationDone(const llvm::json::Object &request) {
798061da546Spatrick llvm::json::Object response;
799061da546Spatrick FillResponse(request, response);
800061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
801*f6aab3d8Srobert g_vsc.configuration_done_sent = true;
802061da546Spatrick if (g_vsc.stop_at_entry)
803061da546Spatrick SendThreadStoppedEvent();
804061da546Spatrick else
805061da546Spatrick g_vsc.target.GetProcess().Continue();
806061da546Spatrick }
807061da546Spatrick
808061da546Spatrick // "DisconnectRequest": {
809061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
810061da546Spatrick // "type": "object",
811061da546Spatrick // "description": "Disconnect request; value of command field is
812061da546Spatrick // 'disconnect'.",
813061da546Spatrick // "properties": {
814061da546Spatrick // "command": {
815061da546Spatrick // "type": "string",
816061da546Spatrick // "enum": [ "disconnect" ]
817061da546Spatrick // },
818061da546Spatrick // "arguments": {
819061da546Spatrick // "$ref": "#/definitions/DisconnectArguments"
820061da546Spatrick // }
821061da546Spatrick // },
822061da546Spatrick // "required": [ "command" ]
823061da546Spatrick // }]
824061da546Spatrick // },
825061da546Spatrick // "DisconnectArguments": {
826061da546Spatrick // "type": "object",
827061da546Spatrick // "description": "Arguments for 'disconnect' request.",
828061da546Spatrick // "properties": {
829061da546Spatrick // "terminateDebuggee": {
830061da546Spatrick // "type": "boolean",
831061da546Spatrick // "description": "Indicates whether the debuggee should be terminated
832061da546Spatrick // when the debugger is disconnected. If unspecified,
833061da546Spatrick // the debug adapter is free to do whatever it thinks
834061da546Spatrick // is best. A client can only rely on this attribute
835061da546Spatrick // being properly honored if a debug adapter returns
836061da546Spatrick // true for the 'supportTerminateDebuggee' capability."
837061da546Spatrick // },
838061da546Spatrick // "restart": {
839061da546Spatrick // "type": "boolean",
840061da546Spatrick // "description": "Indicates whether the debuggee should be restart
841061da546Spatrick // the process."
842061da546Spatrick // }
843061da546Spatrick // }
844061da546Spatrick // },
845061da546Spatrick // "DisconnectResponse": {
846061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
847061da546Spatrick // "type": "object",
848061da546Spatrick // "description": "Response to 'disconnect' request. This is just an
849061da546Spatrick // acknowledgement, so no body field is required."
850061da546Spatrick // }]
851061da546Spatrick // }
request_disconnect(const llvm::json::Object & request)852061da546Spatrick void request_disconnect(const llvm::json::Object &request) {
853061da546Spatrick llvm::json::Object response;
854061da546Spatrick FillResponse(request, response);
855061da546Spatrick auto arguments = request.getObject("arguments");
856061da546Spatrick
857dda28197Spatrick bool defaultTerminateDebuggee = g_vsc.is_attach ? false : true;
858dda28197Spatrick bool terminateDebuggee =
859dda28197Spatrick GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee);
860061da546Spatrick lldb::SBProcess process = g_vsc.target.GetProcess();
861061da546Spatrick auto state = process.GetState();
862061da546Spatrick switch (state) {
863061da546Spatrick case lldb::eStateInvalid:
864061da546Spatrick case lldb::eStateUnloaded:
865061da546Spatrick case lldb::eStateDetached:
866061da546Spatrick case lldb::eStateExited:
867061da546Spatrick break;
868061da546Spatrick case lldb::eStateConnected:
869061da546Spatrick case lldb::eStateAttaching:
870061da546Spatrick case lldb::eStateLaunching:
871061da546Spatrick case lldb::eStateStepping:
872061da546Spatrick case lldb::eStateCrashed:
873061da546Spatrick case lldb::eStateSuspended:
874061da546Spatrick case lldb::eStateStopped:
875061da546Spatrick case lldb::eStateRunning:
876061da546Spatrick g_vsc.debugger.SetAsync(false);
877dda28197Spatrick lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach();
878dda28197Spatrick if (!error.Success())
879dda28197Spatrick response.try_emplace("error", error.GetCString());
880061da546Spatrick g_vsc.debugger.SetAsync(true);
881061da546Spatrick break;
882061da546Spatrick }
883061da546Spatrick SendTerminatedEvent();
884dda28197Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
885061da546Spatrick if (g_vsc.event_thread.joinable()) {
886061da546Spatrick g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
887061da546Spatrick g_vsc.event_thread.join();
888061da546Spatrick }
889be691f3bSpatrick if (g_vsc.progress_event_thread.joinable()) {
890be691f3bSpatrick g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
891be691f3bSpatrick g_vsc.progress_event_thread.join();
892be691f3bSpatrick }
893061da546Spatrick }
894061da546Spatrick
request_exceptionInfo(const llvm::json::Object & request)895061da546Spatrick void request_exceptionInfo(const llvm::json::Object &request) {
896061da546Spatrick llvm::json::Object response;
897061da546Spatrick FillResponse(request, response);
898061da546Spatrick auto arguments = request.getObject("arguments");
899061da546Spatrick llvm::json::Object body;
900061da546Spatrick lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
901061da546Spatrick if (thread.IsValid()) {
902061da546Spatrick auto stopReason = thread.GetStopReason();
903061da546Spatrick if (stopReason == lldb::eStopReasonSignal)
904061da546Spatrick body.try_emplace("exceptionId", "signal");
905061da546Spatrick else if (stopReason == lldb::eStopReasonBreakpoint) {
906061da546Spatrick ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
907061da546Spatrick if (exc_bp) {
908061da546Spatrick EmplaceSafeString(body, "exceptionId", exc_bp->filter);
909061da546Spatrick EmplaceSafeString(body, "description", exc_bp->label);
910061da546Spatrick } else {
911061da546Spatrick body.try_emplace("exceptionId", "exception");
912061da546Spatrick }
913061da546Spatrick } else {
914061da546Spatrick body.try_emplace("exceptionId", "exception");
915061da546Spatrick }
916061da546Spatrick if (!ObjectContainsKey(body, "description")) {
917061da546Spatrick char description[1024];
918061da546Spatrick if (thread.GetStopDescription(description, sizeof(description))) {
919061da546Spatrick EmplaceSafeString(body, "description", std::string(description));
920061da546Spatrick }
921061da546Spatrick }
922061da546Spatrick body.try_emplace("breakMode", "always");
923061da546Spatrick // auto excInfoCount = thread.GetStopReasonDataCount();
924061da546Spatrick // for (auto i=0; i<excInfoCount; ++i) {
925061da546Spatrick // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i);
926061da546Spatrick // }
927061da546Spatrick } else {
928061da546Spatrick response["success"] = llvm::json::Value(false);
929061da546Spatrick }
930061da546Spatrick response.try_emplace("body", std::move(body));
931061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
932061da546Spatrick }
933061da546Spatrick
934061da546Spatrick // "CompletionsRequest": {
935061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
936061da546Spatrick // "type": "object",
937be691f3bSpatrick // "description": "Returns a list of possible completions for a given caret
938be691f3bSpatrick // position and text.\nThe CompletionsRequest may only be called if the
939be691f3bSpatrick // 'supportsCompletionsRequest' capability exists and is true.",
940061da546Spatrick // "properties": {
941061da546Spatrick // "command": {
942061da546Spatrick // "type": "string",
943061da546Spatrick // "enum": [ "completions" ]
944061da546Spatrick // },
945061da546Spatrick // "arguments": {
946061da546Spatrick // "$ref": "#/definitions/CompletionsArguments"
947061da546Spatrick // }
948061da546Spatrick // },
949061da546Spatrick // "required": [ "command", "arguments" ]
950061da546Spatrick // }]
951061da546Spatrick // },
952061da546Spatrick // "CompletionsArguments": {
953061da546Spatrick // "type": "object",
954061da546Spatrick // "description": "Arguments for 'completions' request.",
955061da546Spatrick // "properties": {
956061da546Spatrick // "frameId": {
957061da546Spatrick // "type": "integer",
958be691f3bSpatrick // "description": "Returns completions in the scope of this stack frame.
959be691f3bSpatrick // If not specified, the completions are returned for the global scope."
960061da546Spatrick // },
961061da546Spatrick // "text": {
962061da546Spatrick // "type": "string",
963be691f3bSpatrick // "description": "One or more source lines. Typically this is the text a
964be691f3bSpatrick // user has typed into the debug console before he asked for completion."
965061da546Spatrick // },
966061da546Spatrick // "column": {
967061da546Spatrick // "type": "integer",
968be691f3bSpatrick // "description": "The character position for which to determine the
969be691f3bSpatrick // completion proposals."
970061da546Spatrick // },
971061da546Spatrick // "line": {
972061da546Spatrick // "type": "integer",
973be691f3bSpatrick // "description": "An optional line for which to determine the completion
974be691f3bSpatrick // proposals. If missing the first line of the text is assumed."
975061da546Spatrick // }
976061da546Spatrick // },
977061da546Spatrick // "required": [ "text", "column" ]
978061da546Spatrick // },
979061da546Spatrick // "CompletionsResponse": {
980061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
981061da546Spatrick // "type": "object",
982061da546Spatrick // "description": "Response to 'completions' request.",
983061da546Spatrick // "properties": {
984061da546Spatrick // "body": {
985061da546Spatrick // "type": "object",
986061da546Spatrick // "properties": {
987061da546Spatrick // "targets": {
988061da546Spatrick // "type": "array",
989061da546Spatrick // "items": {
990061da546Spatrick // "$ref": "#/definitions/CompletionItem"
991061da546Spatrick // },
992061da546Spatrick // "description": "The possible completions for ."
993061da546Spatrick // }
994061da546Spatrick // },
995061da546Spatrick // "required": [ "targets" ]
996061da546Spatrick // }
997061da546Spatrick // },
998061da546Spatrick // "required": [ "body" ]
999061da546Spatrick // }]
1000061da546Spatrick // },
1001061da546Spatrick // "CompletionItem": {
1002061da546Spatrick // "type": "object",
1003be691f3bSpatrick // "description": "CompletionItems are the suggestions returned from the
1004be691f3bSpatrick // CompletionsRequest.", "properties": {
1005061da546Spatrick // "label": {
1006061da546Spatrick // "type": "string",
1007be691f3bSpatrick // "description": "The label of this completion item. By default this is
1008be691f3bSpatrick // also the text that is inserted when selecting this completion."
1009061da546Spatrick // },
1010061da546Spatrick // "text": {
1011061da546Spatrick // "type": "string",
1012be691f3bSpatrick // "description": "If text is not falsy then it is inserted instead of the
1013be691f3bSpatrick // label."
1014061da546Spatrick // },
1015061da546Spatrick // "sortText": {
1016061da546Spatrick // "type": "string",
1017be691f3bSpatrick // "description": "A string that should be used when comparing this item
1018be691f3bSpatrick // with other items. When `falsy` the label is used."
1019061da546Spatrick // },
1020061da546Spatrick // "type": {
1021061da546Spatrick // "$ref": "#/definitions/CompletionItemType",
1022be691f3bSpatrick // "description": "The item's type. Typically the client uses this
1023be691f3bSpatrick // information to render the item in the UI with an icon."
1024061da546Spatrick // },
1025061da546Spatrick // "start": {
1026061da546Spatrick // "type": "integer",
1027be691f3bSpatrick // "description": "This value determines the location (in the
1028be691f3bSpatrick // CompletionsRequest's 'text' attribute) where the completion text is
1029be691f3bSpatrick // added.\nIf missing the text is added at the location specified by the
1030be691f3bSpatrick // CompletionsRequest's 'column' attribute."
1031061da546Spatrick // },
1032061da546Spatrick // "length": {
1033061da546Spatrick // "type": "integer",
1034be691f3bSpatrick // "description": "This value determines how many characters are
1035be691f3bSpatrick // overwritten by the completion text.\nIf missing the value 0 is assumed
1036be691f3bSpatrick // which results in the completion text being inserted."
1037061da546Spatrick // }
1038061da546Spatrick // },
1039061da546Spatrick // "required": [ "label" ]
1040061da546Spatrick // },
1041061da546Spatrick // "CompletionItemType": {
1042061da546Spatrick // "type": "string",
1043be691f3bSpatrick // "description": "Some predefined types for the CompletionItem. Please note
1044be691f3bSpatrick // that not all clients have specific icons for all of them.", "enum": [
1045be691f3bSpatrick // "method", "function", "constructor", "field", "variable", "class",
1046be691f3bSpatrick // "interface", "module", "property", "unit", "value", "enum", "keyword",
1047be691f3bSpatrick // "snippet", "text", "color", "file", "reference", "customcolor" ]
1048061da546Spatrick // }
request_completions(const llvm::json::Object & request)1049061da546Spatrick void request_completions(const llvm::json::Object &request) {
1050061da546Spatrick llvm::json::Object response;
1051061da546Spatrick FillResponse(request, response);
1052061da546Spatrick llvm::json::Object body;
1053061da546Spatrick auto arguments = request.getObject("arguments");
1054dda28197Spatrick std::string text = std::string(GetString(arguments, "text"));
1055061da546Spatrick auto original_column = GetSigned(arguments, "column", text.size());
1056061da546Spatrick auto actual_column = original_column - 1;
1057061da546Spatrick llvm::json::Array targets;
1058061da546Spatrick // NOTE: the 'line' argument is not needed, as multiline expressions
1059061da546Spatrick // work well already
1060061da546Spatrick // TODO: support frameID. Currently
1061061da546Spatrick // g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions
1062061da546Spatrick // is frame-unaware.
1063061da546Spatrick
1064061da546Spatrick if (!text.empty() && text[0] == '`') {
1065061da546Spatrick text = text.substr(1);
1066061da546Spatrick actual_column--;
1067061da546Spatrick } else {
1068061da546Spatrick text = "p " + text;
1069061da546Spatrick actual_column += 2;
1070061da546Spatrick }
1071061da546Spatrick lldb::SBStringList matches;
1072061da546Spatrick lldb::SBStringList descriptions;
1073061da546Spatrick g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
1074be691f3bSpatrick text.c_str(), actual_column, 0, -1, matches, descriptions);
1075dda28197Spatrick size_t count = std::min((uint32_t)100, matches.GetSize());
1076061da546Spatrick targets.reserve(count);
1077061da546Spatrick for (size_t i = 0; i < count; i++) {
1078061da546Spatrick std::string match = matches.GetStringAtIndex(i);
1079061da546Spatrick std::string description = descriptions.GetStringAtIndex(i);
1080061da546Spatrick
1081061da546Spatrick llvm::json::Object item;
1082dda28197Spatrick
1083dda28197Spatrick llvm::StringRef match_ref = match;
1084dda28197Spatrick for (llvm::StringRef commit_point : {".", "->"}) {
1085dda28197Spatrick if (match_ref.contains(commit_point)) {
1086dda28197Spatrick match_ref = match_ref.rsplit(commit_point).second;
1087dda28197Spatrick }
1088dda28197Spatrick }
1089dda28197Spatrick EmplaceSafeString(item, "text", match_ref);
1090dda28197Spatrick
1091061da546Spatrick if (description.empty())
1092061da546Spatrick EmplaceSafeString(item, "label", match);
1093061da546Spatrick else
1094061da546Spatrick EmplaceSafeString(item, "label", match + " -- " + description);
1095061da546Spatrick
1096061da546Spatrick targets.emplace_back(std::move(item));
1097061da546Spatrick }
1098061da546Spatrick
1099061da546Spatrick body.try_emplace("targets", std::move(targets));
1100061da546Spatrick response.try_emplace("body", std::move(body));
1101061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1102061da546Spatrick }
1103061da546Spatrick
1104061da546Spatrick // "EvaluateRequest": {
1105061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1106061da546Spatrick // "type": "object",
1107061da546Spatrick // "description": "Evaluate request; value of command field is 'evaluate'.
1108061da546Spatrick // Evaluates the given expression in the context of the
1109061da546Spatrick // top most stack frame. The expression has access to any
1110061da546Spatrick // variables and arguments that are in scope.",
1111061da546Spatrick // "properties": {
1112061da546Spatrick // "command": {
1113061da546Spatrick // "type": "string",
1114061da546Spatrick // "enum": [ "evaluate" ]
1115061da546Spatrick // },
1116061da546Spatrick // "arguments": {
1117061da546Spatrick // "$ref": "#/definitions/EvaluateArguments"
1118061da546Spatrick // }
1119061da546Spatrick // },
1120061da546Spatrick // "required": [ "command", "arguments" ]
1121061da546Spatrick // }]
1122061da546Spatrick // },
1123061da546Spatrick // "EvaluateArguments": {
1124061da546Spatrick // "type": "object",
1125061da546Spatrick // "description": "Arguments for 'evaluate' request.",
1126061da546Spatrick // "properties": {
1127061da546Spatrick // "expression": {
1128061da546Spatrick // "type": "string",
1129061da546Spatrick // "description": "The expression to evaluate."
1130061da546Spatrick // },
1131061da546Spatrick // "frameId": {
1132061da546Spatrick // "type": "integer",
1133061da546Spatrick // "description": "Evaluate the expression in the scope of this stack
1134061da546Spatrick // frame. If not specified, the expression is evaluated
1135061da546Spatrick // in the global scope."
1136061da546Spatrick // },
1137061da546Spatrick // "context": {
1138061da546Spatrick // "type": "string",
1139061da546Spatrick // "_enum": [ "watch", "repl", "hover" ],
1140061da546Spatrick // "enumDescriptions": [
1141061da546Spatrick // "evaluate is run in a watch.",
1142061da546Spatrick // "evaluate is run from REPL console.",
1143061da546Spatrick // "evaluate is run from a data hover."
1144061da546Spatrick // ],
1145061da546Spatrick // "description": "The context in which the evaluate request is run."
1146061da546Spatrick // },
1147061da546Spatrick // "format": {
1148061da546Spatrick // "$ref": "#/definitions/ValueFormat",
1149061da546Spatrick // "description": "Specifies details on how to format the Evaluate
1150061da546Spatrick // result."
1151061da546Spatrick // }
1152061da546Spatrick // },
1153061da546Spatrick // "required": [ "expression" ]
1154061da546Spatrick // },
1155061da546Spatrick // "EvaluateResponse": {
1156061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1157061da546Spatrick // "type": "object",
1158061da546Spatrick // "description": "Response to 'evaluate' request.",
1159061da546Spatrick // "properties": {
1160061da546Spatrick // "body": {
1161061da546Spatrick // "type": "object",
1162061da546Spatrick // "properties": {
1163061da546Spatrick // "result": {
1164061da546Spatrick // "type": "string",
1165061da546Spatrick // "description": "The result of the evaluate request."
1166061da546Spatrick // },
1167061da546Spatrick // "type": {
1168061da546Spatrick // "type": "string",
1169061da546Spatrick // "description": "The optional type of the evaluate result."
1170061da546Spatrick // },
1171061da546Spatrick // "presentationHint": {
1172061da546Spatrick // "$ref": "#/definitions/VariablePresentationHint",
1173061da546Spatrick // "description": "Properties of a evaluate result that can be
1174061da546Spatrick // used to determine how to render the result in
1175061da546Spatrick // the UI."
1176061da546Spatrick // },
1177061da546Spatrick // "variablesReference": {
1178061da546Spatrick // "type": "number",
1179061da546Spatrick // "description": "If variablesReference is > 0, the evaluate
1180061da546Spatrick // result is structured and its children can be
1181061da546Spatrick // retrieved by passing variablesReference to the
1182061da546Spatrick // VariablesRequest."
1183061da546Spatrick // },
1184061da546Spatrick // "namedVariables": {
1185061da546Spatrick // "type": "number",
1186061da546Spatrick // "description": "The number of named child variables. The
1187061da546Spatrick // client can use this optional information to
1188061da546Spatrick // present the variables in a paged UI and fetch
1189061da546Spatrick // them in chunks."
1190061da546Spatrick // },
1191061da546Spatrick // "indexedVariables": {
1192061da546Spatrick // "type": "number",
1193061da546Spatrick // "description": "The number of indexed child variables. The
1194061da546Spatrick // client can use this optional information to
1195061da546Spatrick // present the variables in a paged UI and fetch
1196061da546Spatrick // them in chunks."
1197061da546Spatrick // }
1198061da546Spatrick // },
1199061da546Spatrick // "required": [ "result", "variablesReference" ]
1200061da546Spatrick // }
1201061da546Spatrick // },
1202061da546Spatrick // "required": [ "body" ]
1203061da546Spatrick // }]
1204061da546Spatrick // }
request_evaluate(const llvm::json::Object & request)1205061da546Spatrick void request_evaluate(const llvm::json::Object &request) {
1206061da546Spatrick llvm::json::Object response;
1207061da546Spatrick FillResponse(request, response);
1208061da546Spatrick llvm::json::Object body;
1209061da546Spatrick auto arguments = request.getObject("arguments");
1210061da546Spatrick lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments);
1211061da546Spatrick const auto expression = GetString(arguments, "expression");
1212be691f3bSpatrick llvm::StringRef context = GetString(arguments, "context");
1213061da546Spatrick
1214061da546Spatrick if (!expression.empty() && expression[0] == '`') {
1215dda28197Spatrick auto result =
1216dda28197Spatrick RunLLDBCommands(llvm::StringRef(), {std::string(expression.substr(1))});
1217061da546Spatrick EmplaceSafeString(body, "result", result);
1218061da546Spatrick body.try_emplace("variablesReference", (int64_t)0);
1219061da546Spatrick } else {
1220061da546Spatrick // Always try to get the answer from the local variables if possible. If
1221be691f3bSpatrick // this fails, then if the context is not "hover", actually evaluate an
1222be691f3bSpatrick // expression using the expression parser.
1223be691f3bSpatrick //
1224be691f3bSpatrick // "frame variable" is more reliable than the expression parser in
1225061da546Spatrick // many cases and it is faster.
1226061da546Spatrick lldb::SBValue value = frame.GetValueForVariablePath(
1227061da546Spatrick expression.data(), lldb::eDynamicDontRunTarget);
1228be691f3bSpatrick
1229*f6aab3d8Srobert // Freeze dry the value in case users expand it later in the debug console
1230*f6aab3d8Srobert if (value.GetError().Success() && context == "repl")
1231*f6aab3d8Srobert value = value.Persist();
1232*f6aab3d8Srobert
1233be691f3bSpatrick if (value.GetError().Fail() && context != "hover")
1234061da546Spatrick value = frame.EvaluateExpression(expression.data());
1235be691f3bSpatrick
1236061da546Spatrick if (value.GetError().Fail()) {
1237061da546Spatrick response["success"] = llvm::json::Value(false);
1238061da546Spatrick // This error object must live until we're done with the pointer returned
1239061da546Spatrick // by GetCString().
1240061da546Spatrick lldb::SBError error = value.GetError();
1241061da546Spatrick const char *error_cstr = error.GetCString();
1242061da546Spatrick if (error_cstr && error_cstr[0])
1243061da546Spatrick EmplaceSafeString(response, "message", std::string(error_cstr));
1244061da546Spatrick else
1245061da546Spatrick EmplaceSafeString(response, "message", "evaluate failed");
1246061da546Spatrick } else {
1247061da546Spatrick SetValueForKey(value, body, "result");
1248061da546Spatrick auto value_typename = value.GetType().GetDisplayTypeName();
1249be691f3bSpatrick EmplaceSafeString(body, "type",
1250be691f3bSpatrick value_typename ? value_typename : NO_TYPENAME);
1251061da546Spatrick if (value.MightHaveChildren()) {
1252*f6aab3d8Srobert auto variableReference = g_vsc.variables.InsertExpandableVariable(
1253*f6aab3d8Srobert value, /*is_permanent=*/context == "repl");
1254*f6aab3d8Srobert body.try_emplace("variablesReference", variableReference);
1255061da546Spatrick } else {
1256061da546Spatrick body.try_emplace("variablesReference", (int64_t)0);
1257061da546Spatrick }
1258061da546Spatrick }
1259061da546Spatrick }
1260061da546Spatrick response.try_emplace("body", std::move(body));
1261061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1262061da546Spatrick }
1263061da546Spatrick
1264be691f3bSpatrick // "compileUnitsRequest": {
1265dda28197Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1266dda28197Spatrick // "type": "object",
1267dda28197Spatrick // "description": "Compile Unit request; value of command field is
1268be691f3bSpatrick // 'compileUnits'.",
1269dda28197Spatrick // "properties": {
1270dda28197Spatrick // "command": {
1271dda28197Spatrick // "type": "string",
1272be691f3bSpatrick // "enum": [ "compileUnits" ]
1273dda28197Spatrick // },
1274dda28197Spatrick // "arguments": {
1275be691f3bSpatrick // "$ref": "#/definitions/compileUnitRequestArguments"
1276dda28197Spatrick // }
1277dda28197Spatrick // },
1278dda28197Spatrick // "required": [ "command", "arguments" ]
1279dda28197Spatrick // }]
1280dda28197Spatrick // },
1281be691f3bSpatrick // "compileUnitsRequestArguments": {
1282dda28197Spatrick // "type": "object",
1283be691f3bSpatrick // "description": "Arguments for 'compileUnits' request.",
1284dda28197Spatrick // "properties": {
1285dda28197Spatrick // "moduleId": {
1286dda28197Spatrick // "type": "string",
1287dda28197Spatrick // "description": "The ID of the module."
1288dda28197Spatrick // }
1289dda28197Spatrick // },
1290dda28197Spatrick // "required": [ "moduleId" ]
1291dda28197Spatrick // },
1292be691f3bSpatrick // "compileUnitsResponse": {
1293dda28197Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1294dda28197Spatrick // "type": "object",
1295be691f3bSpatrick // "description": "Response to 'compileUnits' request.",
1296dda28197Spatrick // "properties": {
1297dda28197Spatrick // "body": {
1298be691f3bSpatrick // "description": "Response to 'compileUnits' request. Array of
1299dda28197Spatrick // paths of compile units."
1300dda28197Spatrick // }
1301dda28197Spatrick // }
1302dda28197Spatrick // }]
1303dda28197Spatrick // }
request_compileUnits(const llvm::json::Object & request)1304be691f3bSpatrick void request_compileUnits(const llvm::json::Object &request) {
1305dda28197Spatrick llvm::json::Object response;
1306dda28197Spatrick FillResponse(request, response);
1307dda28197Spatrick llvm::json::Object body;
1308dda28197Spatrick llvm::json::Array units;
1309dda28197Spatrick auto arguments = request.getObject("arguments");
1310dda28197Spatrick std::string module_id = std::string(GetString(arguments, "moduleId"));
1311dda28197Spatrick int num_modules = g_vsc.target.GetNumModules();
1312dda28197Spatrick for (int i = 0; i < num_modules; i++) {
1313dda28197Spatrick auto curr_module = g_vsc.target.GetModuleAtIndex(i);
1314dda28197Spatrick if (module_id == curr_module.GetUUIDString()) {
1315dda28197Spatrick int num_units = curr_module.GetNumCompileUnits();
1316dda28197Spatrick for (int j = 0; j < num_units; j++) {
1317be691f3bSpatrick auto curr_unit = curr_module.GetCompileUnitAtIndex(j);
1318be691f3bSpatrick units.emplace_back(CreateCompileUnit(curr_unit));
1319dda28197Spatrick }
1320dda28197Spatrick body.try_emplace("compileUnits", std::move(units));
1321dda28197Spatrick break;
1322dda28197Spatrick }
1323dda28197Spatrick }
1324dda28197Spatrick response.try_emplace("body", std::move(body));
1325dda28197Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1326dda28197Spatrick }
1327dda28197Spatrick
1328be691f3bSpatrick // "modulesRequest": {
1329be691f3bSpatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1330be691f3bSpatrick // "type": "object",
1331be691f3bSpatrick // "description": "Modules request; value of command field is
1332be691f3bSpatrick // 'modules'.",
1333be691f3bSpatrick // "properties": {
1334be691f3bSpatrick // "command": {
1335be691f3bSpatrick // "type": "string",
1336be691f3bSpatrick // "enum": [ "modules" ]
1337be691f3bSpatrick // },
1338be691f3bSpatrick // },
1339be691f3bSpatrick // "required": [ "command" ]
1340be691f3bSpatrick // }]
1341be691f3bSpatrick // },
1342be691f3bSpatrick // "modulesResponse": {
1343be691f3bSpatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1344be691f3bSpatrick // "type": "object",
1345be691f3bSpatrick // "description": "Response to 'modules' request.",
1346be691f3bSpatrick // "properties": {
1347be691f3bSpatrick // "body": {
1348be691f3bSpatrick // "description": "Response to 'modules' request. Array of
1349be691f3bSpatrick // module objects."
1350be691f3bSpatrick // }
1351be691f3bSpatrick // }
1352be691f3bSpatrick // }]
1353be691f3bSpatrick // }
request_modules(const llvm::json::Object & request)1354be691f3bSpatrick void request_modules(const llvm::json::Object &request) {
1355be691f3bSpatrick llvm::json::Object response;
1356be691f3bSpatrick FillResponse(request, response);
1357be691f3bSpatrick
1358be691f3bSpatrick llvm::json::Array modules;
1359be691f3bSpatrick for (size_t i = 0; i < g_vsc.target.GetNumModules(); i++) {
1360be691f3bSpatrick lldb::SBModule module = g_vsc.target.GetModuleAtIndex(i);
1361be691f3bSpatrick modules.emplace_back(CreateModule(module));
1362be691f3bSpatrick }
1363be691f3bSpatrick
1364be691f3bSpatrick llvm::json::Object body;
1365be691f3bSpatrick body.try_emplace("modules", std::move(modules));
1366be691f3bSpatrick response.try_emplace("body", std::move(body));
1367be691f3bSpatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1368be691f3bSpatrick }
1369be691f3bSpatrick
1370061da546Spatrick // "InitializeRequest": {
1371061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1372061da546Spatrick // "type": "object",
1373061da546Spatrick // "description": "Initialize request; value of command field is
1374061da546Spatrick // 'initialize'.",
1375061da546Spatrick // "properties": {
1376061da546Spatrick // "command": {
1377061da546Spatrick // "type": "string",
1378061da546Spatrick // "enum": [ "initialize" ]
1379061da546Spatrick // },
1380061da546Spatrick // "arguments": {
1381061da546Spatrick // "$ref": "#/definitions/InitializeRequestArguments"
1382061da546Spatrick // }
1383061da546Spatrick // },
1384061da546Spatrick // "required": [ "command", "arguments" ]
1385061da546Spatrick // }]
1386061da546Spatrick // },
1387061da546Spatrick // "InitializeRequestArguments": {
1388061da546Spatrick // "type": "object",
1389061da546Spatrick // "description": "Arguments for 'initialize' request.",
1390061da546Spatrick // "properties": {
1391061da546Spatrick // "clientID": {
1392061da546Spatrick // "type": "string",
1393061da546Spatrick // "description": "The ID of the (frontend) client using this adapter."
1394061da546Spatrick // },
1395061da546Spatrick // "adapterID": {
1396061da546Spatrick // "type": "string",
1397061da546Spatrick // "description": "The ID of the debug adapter."
1398061da546Spatrick // },
1399061da546Spatrick // "locale": {
1400061da546Spatrick // "type": "string",
1401061da546Spatrick // "description": "The ISO-639 locale of the (frontend) client using
1402061da546Spatrick // this adapter, e.g. en-US or de-CH."
1403061da546Spatrick // },
1404061da546Spatrick // "linesStartAt1": {
1405061da546Spatrick // "type": "boolean",
1406061da546Spatrick // "description": "If true all line numbers are 1-based (default)."
1407061da546Spatrick // },
1408061da546Spatrick // "columnsStartAt1": {
1409061da546Spatrick // "type": "boolean",
1410061da546Spatrick // "description": "If true all column numbers are 1-based (default)."
1411061da546Spatrick // },
1412061da546Spatrick // "pathFormat": {
1413061da546Spatrick // "type": "string",
1414061da546Spatrick // "_enum": [ "path", "uri" ],
1415061da546Spatrick // "description": "Determines in what format paths are specified. The
1416061da546Spatrick // default is 'path', which is the native format."
1417061da546Spatrick // },
1418061da546Spatrick // "supportsVariableType": {
1419061da546Spatrick // "type": "boolean",
1420061da546Spatrick // "description": "Client supports the optional type attribute for
1421061da546Spatrick // variables."
1422061da546Spatrick // },
1423061da546Spatrick // "supportsVariablePaging": {
1424061da546Spatrick // "type": "boolean",
1425061da546Spatrick // "description": "Client supports the paging of variables."
1426061da546Spatrick // },
1427061da546Spatrick // "supportsRunInTerminalRequest": {
1428061da546Spatrick // "type": "boolean",
1429061da546Spatrick // "description": "Client supports the runInTerminal request."
1430061da546Spatrick // }
1431061da546Spatrick // },
1432061da546Spatrick // "required": [ "adapterID" ]
1433061da546Spatrick // },
1434061da546Spatrick // "InitializeResponse": {
1435061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1436061da546Spatrick // "type": "object",
1437061da546Spatrick // "description": "Response to 'initialize' request.",
1438061da546Spatrick // "properties": {
1439061da546Spatrick // "body": {
1440061da546Spatrick // "$ref": "#/definitions/Capabilities",
1441061da546Spatrick // "description": "The capabilities of this debug adapter."
1442061da546Spatrick // }
1443061da546Spatrick // }
1444061da546Spatrick // }]
1445061da546Spatrick // }
request_initialize(const llvm::json::Object & request)1446061da546Spatrick void request_initialize(const llvm::json::Object &request) {
1447*f6aab3d8Srobert auto log_cb = [](const char *buf, void *baton) -> void {
1448*f6aab3d8Srobert g_vsc.SendOutput(OutputType::Console, llvm::StringRef{buf});
1449*f6aab3d8Srobert };
1450be691f3bSpatrick
1451*f6aab3d8Srobert auto arguments = request.getObject("arguments");
1452*f6aab3d8Srobert // sourceInitFile option is not from formal DAP specification. It is only
1453*f6aab3d8Srobert // used by unit tests to prevent sourcing .lldbinit files from environment
1454*f6aab3d8Srobert // which may affect the outcome of tests.
1455*f6aab3d8Srobert bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
1456*f6aab3d8Srobert
1457*f6aab3d8Srobert g_vsc.debugger =
1458*f6aab3d8Srobert lldb::SBDebugger::Create(source_init_file, log_cb, nullptr);
1459*f6aab3d8Srobert g_vsc.progress_event_thread = std::thread(ProgressEventThreadFunction);
1460061da546Spatrick
1461061da546Spatrick // Start our event thread so we can receive events from the debugger, target,
1462061da546Spatrick // process and more.
1463061da546Spatrick g_vsc.event_thread = std::thread(EventThreadFunction);
1464061da546Spatrick
1465061da546Spatrick llvm::json::Object response;
1466061da546Spatrick FillResponse(request, response);
1467061da546Spatrick llvm::json::Object body;
1468061da546Spatrick // The debug adapter supports the configurationDoneRequest.
1469061da546Spatrick body.try_emplace("supportsConfigurationDoneRequest", true);
1470061da546Spatrick // The debug adapter supports function breakpoints.
1471061da546Spatrick body.try_emplace("supportsFunctionBreakpoints", true);
1472061da546Spatrick // The debug adapter supports conditional breakpoints.
1473061da546Spatrick body.try_emplace("supportsConditionalBreakpoints", true);
1474061da546Spatrick // The debug adapter supports breakpoints that break execution after a
1475061da546Spatrick // specified number of hits.
1476061da546Spatrick body.try_emplace("supportsHitConditionalBreakpoints", true);
1477061da546Spatrick // The debug adapter supports a (side effect free) evaluate request for
1478061da546Spatrick // data hovers.
1479061da546Spatrick body.try_emplace("supportsEvaluateForHovers", true);
1480061da546Spatrick // Available filters or options for the setExceptionBreakpoints request.
1481061da546Spatrick llvm::json::Array filters;
1482061da546Spatrick for (const auto &exc_bp : g_vsc.exception_breakpoints) {
1483061da546Spatrick filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
1484061da546Spatrick }
1485061da546Spatrick body.try_emplace("exceptionBreakpointFilters", std::move(filters));
1486be691f3bSpatrick // The debug adapter supports launching a debugee in intergrated VSCode
1487be691f3bSpatrick // terminal.
1488be691f3bSpatrick body.try_emplace("supportsRunInTerminalRequest", true);
1489061da546Spatrick // The debug adapter supports stepping back via the stepBack and
1490061da546Spatrick // reverseContinue requests.
1491061da546Spatrick body.try_emplace("supportsStepBack", false);
1492061da546Spatrick // The debug adapter supports setting a variable to a value.
1493061da546Spatrick body.try_emplace("supportsSetVariable", true);
1494061da546Spatrick // The debug adapter supports restarting a frame.
1495061da546Spatrick body.try_emplace("supportsRestartFrame", false);
1496061da546Spatrick // The debug adapter supports the gotoTargetsRequest.
1497061da546Spatrick body.try_emplace("supportsGotoTargetsRequest", false);
1498061da546Spatrick // The debug adapter supports the stepInTargetsRequest.
1499061da546Spatrick body.try_emplace("supportsStepInTargetsRequest", false);
1500dda28197Spatrick // We need to improve the current implementation of completions in order to
1501dda28197Spatrick // enable it again. For some context, this is how VSCode works:
1502dda28197Spatrick // - VSCode sends a completion request whenever chars are added, the user
1503dda28197Spatrick // triggers completion manually via CTRL-space or similar mechanisms, but
1504dda28197Spatrick // not when there's a deletion. Besides, VSCode doesn't let us know which
1505dda28197Spatrick // of these events we are handling. What is more, the use can paste or cut
1506dda28197Spatrick // sections of the text arbitrarily.
1507dda28197Spatrick // https://github.com/microsoft/vscode/issues/89531 tracks part of the
1508dda28197Spatrick // issue just mentioned.
1509dda28197Spatrick // This behavior causes many problems with the current way completion is
1510dda28197Spatrick // implemented in lldb-vscode, as these requests could be really expensive,
1511dda28197Spatrick // blocking the debugger, and there could be many concurrent requests unless
1512dda28197Spatrick // the user types very slowly... We need to address this specific issue, or
1513dda28197Spatrick // at least trigger completion only when the user explicitly wants it, which
1514dda28197Spatrick // is the behavior of LLDB CLI, that expects a TAB.
1515dda28197Spatrick body.try_emplace("supportsCompletionsRequest", false);
1516061da546Spatrick // The debug adapter supports the modules request.
1517*f6aab3d8Srobert body.try_emplace("supportsModulesRequest", true);
1518061da546Spatrick // The set of additional module information exposed by the debug adapter.
1519061da546Spatrick // body.try_emplace("additionalModuleColumns"] = ColumnDescriptor
1520061da546Spatrick // Checksum algorithms supported by the debug adapter.
1521061da546Spatrick // body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm
1522061da546Spatrick // The debug adapter supports the RestartRequest. In this case a client
1523061da546Spatrick // should not implement 'restart' by terminating and relaunching the adapter
1524061da546Spatrick // but by calling the RestartRequest.
1525061da546Spatrick body.try_emplace("supportsRestartRequest", false);
1526061da546Spatrick // The debug adapter supports 'exceptionOptions' on the
1527061da546Spatrick // setExceptionBreakpoints request.
1528061da546Spatrick body.try_emplace("supportsExceptionOptions", true);
1529061da546Spatrick // The debug adapter supports a 'format' attribute on the stackTraceRequest,
1530061da546Spatrick // variablesRequest, and evaluateRequest.
1531061da546Spatrick body.try_emplace("supportsValueFormattingOptions", true);
1532061da546Spatrick // The debug adapter supports the exceptionInfo request.
1533061da546Spatrick body.try_emplace("supportsExceptionInfoRequest", true);
1534061da546Spatrick // The debug adapter supports the 'terminateDebuggee' attribute on the
1535061da546Spatrick // 'disconnect' request.
1536061da546Spatrick body.try_emplace("supportTerminateDebuggee", true);
1537061da546Spatrick // The debug adapter supports the delayed loading of parts of the stack,
1538061da546Spatrick // which requires that both the 'startFrame' and 'levels' arguments and the
1539061da546Spatrick // 'totalFrames' result of the 'StackTrace' request are supported.
1540061da546Spatrick body.try_emplace("supportsDelayedStackTraceLoading", true);
1541061da546Spatrick // The debug adapter supports the 'loadedSources' request.
1542061da546Spatrick body.try_emplace("supportsLoadedSourcesRequest", false);
1543be691f3bSpatrick // The debug adapter supports sending progress reporting events.
1544be691f3bSpatrick body.try_emplace("supportsProgressReporting", true);
1545*f6aab3d8Srobert // The debug adapter supports 'logMessage' in breakpoint.
1546*f6aab3d8Srobert body.try_emplace("supportsLogPoints", true);
1547061da546Spatrick
1548061da546Spatrick response.try_emplace("body", std::move(body));
1549061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1550061da546Spatrick }
1551061da546Spatrick
request_runInTerminal(const llvm::json::Object & launch_request)1552be691f3bSpatrick llvm::Error request_runInTerminal(const llvm::json::Object &launch_request) {
1553be691f3bSpatrick g_vsc.is_attach = true;
1554be691f3bSpatrick lldb::SBAttachInfo attach_info;
1555be691f3bSpatrick
1556be691f3bSpatrick llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =
1557be691f3bSpatrick CreateRunInTerminalCommFile();
1558be691f3bSpatrick if (!comm_file_or_err)
1559be691f3bSpatrick return comm_file_or_err.takeError();
1560be691f3bSpatrick FifoFile &comm_file = *comm_file_or_err.get();
1561be691f3bSpatrick
1562be691f3bSpatrick RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
1563be691f3bSpatrick
1564be691f3bSpatrick llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
1565be691f3bSpatrick launch_request, g_vsc.debug_adaptor_path, comm_file.m_path);
1566be691f3bSpatrick llvm::json::Object reverse_response;
1567be691f3bSpatrick lldb_vscode::PacketStatus status =
1568be691f3bSpatrick g_vsc.SendReverseRequest(reverse_request, reverse_response);
1569be691f3bSpatrick if (status != lldb_vscode::PacketStatus::Success)
1570be691f3bSpatrick return llvm::createStringError(llvm::inconvertibleErrorCode(),
1571be691f3bSpatrick "Process cannot be launched by the IDE. %s",
1572be691f3bSpatrick comm_channel.GetLauncherError().c_str());
1573be691f3bSpatrick
1574be691f3bSpatrick if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
1575be691f3bSpatrick attach_info.SetProcessID(*pid);
1576be691f3bSpatrick else
1577be691f3bSpatrick return pid.takeError();
1578be691f3bSpatrick
1579be691f3bSpatrick g_vsc.debugger.SetAsync(false);
1580be691f3bSpatrick lldb::SBError error;
1581be691f3bSpatrick g_vsc.target.Attach(attach_info, error);
1582be691f3bSpatrick
1583be691f3bSpatrick if (error.Fail())
1584be691f3bSpatrick return llvm::createStringError(llvm::inconvertibleErrorCode(),
1585be691f3bSpatrick "Failed to attach to the target process. %s",
1586be691f3bSpatrick comm_channel.GetLauncherError().c_str());
1587be691f3bSpatrick // This will notify the runInTerminal launcher that we attached.
1588be691f3bSpatrick // We have to make this async, as the function won't return until the launcher
1589be691f3bSpatrick // resumes and reads the data.
1590be691f3bSpatrick std::future<lldb::SBError> did_attach_message_success =
1591be691f3bSpatrick comm_channel.NotifyDidAttach();
1592be691f3bSpatrick
1593be691f3bSpatrick // We just attached to the runInTerminal launcher, which was waiting to be
1594be691f3bSpatrick // attached. We now resume it, so it can receive the didAttach notification
1595be691f3bSpatrick // and then perform the exec. Upon continuing, the debugger will stop the
1596be691f3bSpatrick // process right in the middle of the exec. To the user, what we are doing is
1597be691f3bSpatrick // transparent, as they will only be able to see the process since the exec,
1598be691f3bSpatrick // completely unaware of the preparatory work.
1599be691f3bSpatrick g_vsc.target.GetProcess().Continue();
1600be691f3bSpatrick
1601be691f3bSpatrick // Now that the actual target is just starting (i.e. exec was just invoked),
1602be691f3bSpatrick // we return the debugger to its async state.
1603be691f3bSpatrick g_vsc.debugger.SetAsync(true);
1604be691f3bSpatrick
1605be691f3bSpatrick // If sending the notification failed, the launcher should be dead by now and
1606be691f3bSpatrick // the async didAttach notification should have an error message, so we
1607be691f3bSpatrick // return it. Otherwise, everything was a success.
1608be691f3bSpatrick did_attach_message_success.wait();
1609be691f3bSpatrick error = did_attach_message_success.get();
1610be691f3bSpatrick if (error.Success())
1611be691f3bSpatrick return llvm::Error::success();
1612be691f3bSpatrick return llvm::createStringError(llvm::inconvertibleErrorCode(),
1613be691f3bSpatrick error.GetCString());
1614be691f3bSpatrick }
1615be691f3bSpatrick
1616061da546Spatrick // "LaunchRequest": {
1617061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1618061da546Spatrick // "type": "object",
1619061da546Spatrick // "description": "Launch request; value of command field is 'launch'.",
1620061da546Spatrick // "properties": {
1621061da546Spatrick // "command": {
1622061da546Spatrick // "type": "string",
1623061da546Spatrick // "enum": [ "launch" ]
1624061da546Spatrick // },
1625061da546Spatrick // "arguments": {
1626061da546Spatrick // "$ref": "#/definitions/LaunchRequestArguments"
1627061da546Spatrick // }
1628061da546Spatrick // },
1629061da546Spatrick // "required": [ "command", "arguments" ]
1630061da546Spatrick // }]
1631061da546Spatrick // },
1632061da546Spatrick // "LaunchRequestArguments": {
1633061da546Spatrick // "type": "object",
1634061da546Spatrick // "description": "Arguments for 'launch' request.",
1635061da546Spatrick // "properties": {
1636061da546Spatrick // "noDebug": {
1637061da546Spatrick // "type": "boolean",
1638061da546Spatrick // "description": "If noDebug is true the launch request should launch
1639061da546Spatrick // the program without enabling debugging."
1640061da546Spatrick // }
1641061da546Spatrick // }
1642061da546Spatrick // },
1643061da546Spatrick // "LaunchResponse": {
1644061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1645061da546Spatrick // "type": "object",
1646061da546Spatrick // "description": "Response to 'launch' request. This is just an
1647061da546Spatrick // acknowledgement, so no body field is required."
1648061da546Spatrick // }]
1649061da546Spatrick // }
request_launch(const llvm::json::Object & request)1650061da546Spatrick void request_launch(const llvm::json::Object &request) {
1651dda28197Spatrick g_vsc.is_attach = false;
1652061da546Spatrick llvm::json::Object response;
1653061da546Spatrick lldb::SBError error;
1654061da546Spatrick FillResponse(request, response);
1655061da546Spatrick auto arguments = request.getObject("arguments");
1656061da546Spatrick g_vsc.init_commands = GetStrings(arguments, "initCommands");
1657061da546Spatrick g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands");
1658061da546Spatrick g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
1659061da546Spatrick g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
1660dda28197Spatrick g_vsc.terminate_commands = GetStrings(arguments, "terminateCommands");
1661061da546Spatrick auto launchCommands = GetStrings(arguments, "launchCommands");
1662be691f3bSpatrick std::vector<std::string> postRunCommands =
1663be691f3bSpatrick GetStrings(arguments, "postRunCommands");
1664061da546Spatrick g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
1665dda28197Spatrick const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
1666*f6aab3d8Srobert const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
1667061da546Spatrick
1668061da546Spatrick // This is a hack for loading DWARF in .o files on Mac where the .o files
1669061da546Spatrick // in the debug map of the main executable have relative paths which require
1670061da546Spatrick // the lldb-vscode binary to have its working directory set to that relative
1671061da546Spatrick // root for the .o files in order to be able to load debug info.
1672dda28197Spatrick if (!debuggerRoot.empty())
1673dda28197Spatrick llvm::sys::fs::set_current_path(debuggerRoot);
1674dda28197Spatrick
1675dda28197Spatrick // Run any initialize LLDB commands the user specified in the launch.json.
1676dda28197Spatrick // This is run before target is created, so commands can't do anything with
1677dda28197Spatrick // the targets - preRunCommands are run with the target.
1678dda28197Spatrick g_vsc.RunInitCommands();
1679061da546Spatrick
1680061da546Spatrick SetSourceMapFromArguments(*arguments);
1681061da546Spatrick
1682dda28197Spatrick lldb::SBError status;
1683dda28197Spatrick g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
1684dda28197Spatrick if (status.Fail()) {
1685dda28197Spatrick response["success"] = llvm::json::Value(false);
1686dda28197Spatrick EmplaceSafeString(response, "message", status.GetCString());
1687dda28197Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1688dda28197Spatrick return;
1689dda28197Spatrick }
1690dda28197Spatrick
1691dda28197Spatrick // Instantiate a launch info instance for the target.
1692dda28197Spatrick auto launch_info = g_vsc.target.GetLaunchInfo();
1693061da546Spatrick
1694061da546Spatrick // Grab the current working directory if there is one and set it in the
1695061da546Spatrick // launch info.
1696061da546Spatrick const auto cwd = GetString(arguments, "cwd");
1697061da546Spatrick if (!cwd.empty())
1698dda28197Spatrick launch_info.SetWorkingDirectory(cwd.data());
1699061da546Spatrick
1700061da546Spatrick // Extract any extra arguments and append them to our program arguments for
1701061da546Spatrick // when we launch
1702061da546Spatrick auto args = GetStrings(arguments, "args");
1703061da546Spatrick if (!args.empty())
1704dda28197Spatrick launch_info.SetArguments(MakeArgv(args).data(), true);
1705061da546Spatrick
1706061da546Spatrick // Pass any environment variables along that the user specified.
1707061da546Spatrick auto envs = GetStrings(arguments, "env");
1708061da546Spatrick if (!envs.empty())
1709dda28197Spatrick launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true);
1710061da546Spatrick
1711dda28197Spatrick auto flags = launch_info.GetLaunchFlags();
1712061da546Spatrick
1713061da546Spatrick if (GetBoolean(arguments, "disableASLR", true))
1714061da546Spatrick flags |= lldb::eLaunchFlagDisableASLR;
1715061da546Spatrick if (GetBoolean(arguments, "disableSTDIO", false))
1716061da546Spatrick flags |= lldb::eLaunchFlagDisableSTDIO;
1717061da546Spatrick if (GetBoolean(arguments, "shellExpandArguments", false))
1718061da546Spatrick flags |= lldb::eLaunchFlagShellExpandArguments;
1719061da546Spatrick const bool detatchOnError = GetBoolean(arguments, "detachOnError", false);
1720dda28197Spatrick launch_info.SetDetachOnError(detatchOnError);
1721dda28197Spatrick launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
1722061da546Spatrick lldb::eLaunchFlagStopAtEntry);
1723061da546Spatrick
1724061da546Spatrick // Run any pre run LLDB commands the user specified in the launch.json
1725061da546Spatrick g_vsc.RunPreRunCommands();
1726be691f3bSpatrick
1727be691f3bSpatrick if (GetBoolean(arguments, "runInTerminal", false)) {
1728be691f3bSpatrick if (llvm::Error err = request_runInTerminal(request))
1729be691f3bSpatrick error.SetErrorString(llvm::toString(std::move(err)).c_str());
1730be691f3bSpatrick } else if (launchCommands.empty()) {
1731061da546Spatrick // Disable async events so the launch will be successful when we return from
1732061da546Spatrick // the launch call and the launch will happen synchronously
1733061da546Spatrick g_vsc.debugger.SetAsync(false);
1734dda28197Spatrick g_vsc.target.Launch(launch_info, error);
1735061da546Spatrick g_vsc.debugger.SetAsync(true);
1736061da546Spatrick } else {
1737061da546Spatrick g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands);
1738061da546Spatrick // The custom commands might have created a new target so we should use the
1739061da546Spatrick // selected target after these commands are run.
1740061da546Spatrick g_vsc.target = g_vsc.debugger.GetSelectedTarget();
1741*f6aab3d8Srobert // Make sure the process is launched and stopped at the entry point before
1742*f6aab3d8Srobert // proceeding as the the launch commands are not run using the synchronous
1743*f6aab3d8Srobert // mode.
1744*f6aab3d8Srobert error = g_vsc.WaitForProcessToStop(timeout_seconds);
1745061da546Spatrick }
1746061da546Spatrick
1747061da546Spatrick if (error.Fail()) {
1748061da546Spatrick response["success"] = llvm::json::Value(false);
1749061da546Spatrick EmplaceSafeString(response, "message", std::string(error.GetCString()));
1750be691f3bSpatrick } else {
1751be691f3bSpatrick g_vsc.RunLLDBCommands("Running postRunCommands:", postRunCommands);
1752061da546Spatrick }
1753be691f3bSpatrick
1754061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1755061da546Spatrick
1756be691f3bSpatrick if (g_vsc.is_attach)
1757be691f3bSpatrick SendProcessEvent(Attach); // this happens when doing runInTerminal
1758be691f3bSpatrick else
1759061da546Spatrick SendProcessEvent(Launch);
1760061da546Spatrick g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized")));
1761061da546Spatrick }
1762061da546Spatrick
1763061da546Spatrick // "NextRequest": {
1764061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1765061da546Spatrick // "type": "object",
1766061da546Spatrick // "description": "Next request; value of command field is 'next'. The
1767061da546Spatrick // request starts the debuggee to run again for one step.
1768061da546Spatrick // The debug adapter first sends the NextResponse and then
1769061da546Spatrick // a StoppedEvent (event type 'step') after the step has
1770061da546Spatrick // completed.",
1771061da546Spatrick // "properties": {
1772061da546Spatrick // "command": {
1773061da546Spatrick // "type": "string",
1774061da546Spatrick // "enum": [ "next" ]
1775061da546Spatrick // },
1776061da546Spatrick // "arguments": {
1777061da546Spatrick // "$ref": "#/definitions/NextArguments"
1778061da546Spatrick // }
1779061da546Spatrick // },
1780061da546Spatrick // "required": [ "command", "arguments" ]
1781061da546Spatrick // }]
1782061da546Spatrick // },
1783061da546Spatrick // "NextArguments": {
1784061da546Spatrick // "type": "object",
1785061da546Spatrick // "description": "Arguments for 'next' request.",
1786061da546Spatrick // "properties": {
1787061da546Spatrick // "threadId": {
1788061da546Spatrick // "type": "integer",
1789061da546Spatrick // "description": "Execute 'next' for this thread."
1790061da546Spatrick // }
1791061da546Spatrick // },
1792061da546Spatrick // "required": [ "threadId" ]
1793061da546Spatrick // },
1794061da546Spatrick // "NextResponse": {
1795061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1796061da546Spatrick // "type": "object",
1797061da546Spatrick // "description": "Response to 'next' request. This is just an
1798061da546Spatrick // acknowledgement, so no body field is required."
1799061da546Spatrick // }]
1800061da546Spatrick // }
request_next(const llvm::json::Object & request)1801061da546Spatrick void request_next(const llvm::json::Object &request) {
1802061da546Spatrick llvm::json::Object response;
1803061da546Spatrick FillResponse(request, response);
1804061da546Spatrick auto arguments = request.getObject("arguments");
1805061da546Spatrick lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
1806061da546Spatrick if (thread.IsValid()) {
1807061da546Spatrick // Remember the thread ID that caused the resume so we can set the
1808061da546Spatrick // "threadCausedFocus" boolean value in the "stopped" events.
1809061da546Spatrick g_vsc.focus_tid = thread.GetThreadID();
1810061da546Spatrick thread.StepOver();
1811061da546Spatrick } else {
1812061da546Spatrick response["success"] = llvm::json::Value(false);
1813061da546Spatrick }
1814061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1815061da546Spatrick }
1816061da546Spatrick
1817061da546Spatrick // "PauseRequest": {
1818061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1819061da546Spatrick // "type": "object",
1820061da546Spatrick // "description": "Pause request; value of command field is 'pause'. The
1821061da546Spatrick // request suspenses the debuggee. The debug adapter first sends the
1822061da546Spatrick // PauseResponse and then a StoppedEvent (event type 'pause') after the
1823061da546Spatrick // thread has been paused successfully.", "properties": {
1824061da546Spatrick // "command": {
1825061da546Spatrick // "type": "string",
1826061da546Spatrick // "enum": [ "pause" ]
1827061da546Spatrick // },
1828061da546Spatrick // "arguments": {
1829061da546Spatrick // "$ref": "#/definitions/PauseArguments"
1830061da546Spatrick // }
1831061da546Spatrick // },
1832061da546Spatrick // "required": [ "command", "arguments" ]
1833061da546Spatrick // }]
1834061da546Spatrick // },
1835061da546Spatrick // "PauseArguments": {
1836061da546Spatrick // "type": "object",
1837061da546Spatrick // "description": "Arguments for 'pause' request.",
1838061da546Spatrick // "properties": {
1839061da546Spatrick // "threadId": {
1840061da546Spatrick // "type": "integer",
1841061da546Spatrick // "description": "Pause execution for this thread."
1842061da546Spatrick // }
1843061da546Spatrick // },
1844061da546Spatrick // "required": [ "threadId" ]
1845061da546Spatrick // },
1846061da546Spatrick // "PauseResponse": {
1847061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1848061da546Spatrick // "type": "object",
1849061da546Spatrick // "description": "Response to 'pause' request. This is just an
1850061da546Spatrick // acknowledgement, so no body field is required."
1851061da546Spatrick // }]
1852061da546Spatrick // }
request_pause(const llvm::json::Object & request)1853061da546Spatrick void request_pause(const llvm::json::Object &request) {
1854061da546Spatrick llvm::json::Object response;
1855061da546Spatrick FillResponse(request, response);
1856061da546Spatrick lldb::SBProcess process = g_vsc.target.GetProcess();
1857061da546Spatrick lldb::SBError error = process.Stop();
1858061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1859061da546Spatrick }
1860061da546Spatrick
1861061da546Spatrick // "ScopesRequest": {
1862061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1863061da546Spatrick // "type": "object",
1864061da546Spatrick // "description": "Scopes request; value of command field is 'scopes'. The
1865061da546Spatrick // request returns the variable scopes for a given stackframe ID.",
1866061da546Spatrick // "properties": {
1867061da546Spatrick // "command": {
1868061da546Spatrick // "type": "string",
1869061da546Spatrick // "enum": [ "scopes" ]
1870061da546Spatrick // },
1871061da546Spatrick // "arguments": {
1872061da546Spatrick // "$ref": "#/definitions/ScopesArguments"
1873061da546Spatrick // }
1874061da546Spatrick // },
1875061da546Spatrick // "required": [ "command", "arguments" ]
1876061da546Spatrick // }]
1877061da546Spatrick // },
1878061da546Spatrick // "ScopesArguments": {
1879061da546Spatrick // "type": "object",
1880061da546Spatrick // "description": "Arguments for 'scopes' request.",
1881061da546Spatrick // "properties": {
1882061da546Spatrick // "frameId": {
1883061da546Spatrick // "type": "integer",
1884061da546Spatrick // "description": "Retrieve the scopes for this stackframe."
1885061da546Spatrick // }
1886061da546Spatrick // },
1887061da546Spatrick // "required": [ "frameId" ]
1888061da546Spatrick // },
1889061da546Spatrick // "ScopesResponse": {
1890061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
1891061da546Spatrick // "type": "object",
1892061da546Spatrick // "description": "Response to 'scopes' request.",
1893061da546Spatrick // "properties": {
1894061da546Spatrick // "body": {
1895061da546Spatrick // "type": "object",
1896061da546Spatrick // "properties": {
1897061da546Spatrick // "scopes": {
1898061da546Spatrick // "type": "array",
1899061da546Spatrick // "items": {
1900061da546Spatrick // "$ref": "#/definitions/Scope"
1901061da546Spatrick // },
1902061da546Spatrick // "description": "The scopes of the stackframe. If the array has
1903061da546Spatrick // length zero, there are no scopes available."
1904061da546Spatrick // }
1905061da546Spatrick // },
1906061da546Spatrick // "required": [ "scopes" ]
1907061da546Spatrick // }
1908061da546Spatrick // },
1909061da546Spatrick // "required": [ "body" ]
1910061da546Spatrick // }]
1911061da546Spatrick // }
request_scopes(const llvm::json::Object & request)1912061da546Spatrick void request_scopes(const llvm::json::Object &request) {
1913061da546Spatrick llvm::json::Object response;
1914061da546Spatrick FillResponse(request, response);
1915061da546Spatrick llvm::json::Object body;
1916061da546Spatrick auto arguments = request.getObject("arguments");
1917061da546Spatrick lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments);
1918dda28197Spatrick // As the user selects different stack frames in the GUI, a "scopes" request
1919dda28197Spatrick // will be sent to the DAP. This is the only way we know that the user has
1920dda28197Spatrick // selected a frame in a thread. There are no other notifications that are
1921dda28197Spatrick // sent and VS code doesn't allow multiple frames to show variables
1922dda28197Spatrick // concurrently. If we select the thread and frame as the "scopes" requests
1923dda28197Spatrick // are sent, this allows users to type commands in the debugger console
1924dda28197Spatrick // with a backtick character to run lldb commands and these lldb commands
1925dda28197Spatrick // will now have the right context selected as they are run. If the user
1926dda28197Spatrick // types "`bt" into the debugger console and we had another thread selected
1927dda28197Spatrick // in the LLDB library, we would show the wrong thing to the user. If the
1928dda28197Spatrick // users switches threads with a lldb command like "`thread select 14", the
1929dda28197Spatrick // GUI will not update as there are no "event" notification packets that
1930dda28197Spatrick // allow us to change the currently selected thread or frame in the GUI that
1931dda28197Spatrick // I am aware of.
1932dda28197Spatrick if (frame.IsValid()) {
1933dda28197Spatrick frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
1934dda28197Spatrick frame.GetThread().SetSelectedFrame(frame.GetFrameID());
1935dda28197Spatrick }
1936*f6aab3d8Srobert g_vsc.variables.locals = frame.GetVariables(/*arguments=*/true,
1937*f6aab3d8Srobert /*locals=*/true,
1938*f6aab3d8Srobert /*statics=*/false,
1939*f6aab3d8Srobert /*in_scope_only=*/true);
1940*f6aab3d8Srobert g_vsc.variables.globals = frame.GetVariables(/*arguments=*/false,
1941*f6aab3d8Srobert /*locals=*/false,
1942*f6aab3d8Srobert /*statics=*/true,
1943*f6aab3d8Srobert /*in_scope_only=*/true);
1944*f6aab3d8Srobert g_vsc.variables.registers = frame.GetRegisters();
1945061da546Spatrick body.try_emplace("scopes", g_vsc.CreateTopLevelScopes());
1946061da546Spatrick response.try_emplace("body", std::move(body));
1947061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1948061da546Spatrick }
1949061da546Spatrick
1950061da546Spatrick // "SetBreakpointsRequest": {
1951061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
1952061da546Spatrick // "type": "object",
1953061da546Spatrick // "description": "SetBreakpoints request; value of command field is
1954061da546Spatrick // 'setBreakpoints'. Sets multiple breakpoints for a single source and
1955061da546Spatrick // clears all previous breakpoints in that source. To clear all breakpoint
1956061da546Spatrick // for a source, specify an empty array. When a breakpoint is hit, a
1957061da546Spatrick // StoppedEvent (event type 'breakpoint') is generated.", "properties": {
1958061da546Spatrick // "command": {
1959061da546Spatrick // "type": "string",
1960061da546Spatrick // "enum": [ "setBreakpoints" ]
1961061da546Spatrick // },
1962061da546Spatrick // "arguments": {
1963061da546Spatrick // "$ref": "#/definitions/SetBreakpointsArguments"
1964061da546Spatrick // }
1965061da546Spatrick // },
1966061da546Spatrick // "required": [ "command", "arguments" ]
1967061da546Spatrick // }]
1968061da546Spatrick // },
1969061da546Spatrick // "SetBreakpointsArguments": {
1970061da546Spatrick // "type": "object",
1971061da546Spatrick // "description": "Arguments for 'setBreakpoints' request.",
1972061da546Spatrick // "properties": {
1973061da546Spatrick // "source": {
1974061da546Spatrick // "$ref": "#/definitions/Source",
1975061da546Spatrick // "description": "The source location of the breakpoints; either
1976061da546Spatrick // source.path or source.reference must be specified."
1977061da546Spatrick // },
1978061da546Spatrick // "breakpoints": {
1979061da546Spatrick // "type": "array",
1980061da546Spatrick // "items": {
1981061da546Spatrick // "$ref": "#/definitions/SourceBreakpoint"
1982061da546Spatrick // },
1983061da546Spatrick // "description": "The code locations of the breakpoints."
1984061da546Spatrick // },
1985061da546Spatrick // "lines": {
1986061da546Spatrick // "type": "array",
1987061da546Spatrick // "items": {
1988061da546Spatrick // "type": "integer"
1989061da546Spatrick // },
1990061da546Spatrick // "description": "Deprecated: The code locations of the breakpoints."
1991061da546Spatrick // },
1992061da546Spatrick // "sourceModified": {
1993061da546Spatrick // "type": "boolean",
1994061da546Spatrick // "description": "A value of true indicates that the underlying source
1995061da546Spatrick // has been modified which results in new breakpoint locations."
1996061da546Spatrick // }
1997061da546Spatrick // },
1998061da546Spatrick // "required": [ "source" ]
1999061da546Spatrick // },
2000061da546Spatrick // "SetBreakpointsResponse": {
2001061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2002061da546Spatrick // "type": "object",
2003061da546Spatrick // "description": "Response to 'setBreakpoints' request. Returned is
2004061da546Spatrick // information about each breakpoint created by this request. This includes
2005061da546Spatrick // the actual code location and whether the breakpoint could be verified.
2006061da546Spatrick // The breakpoints returned are in the same order as the elements of the
2007061da546Spatrick // 'breakpoints' (or the deprecated 'lines') in the
2008061da546Spatrick // SetBreakpointsArguments.", "properties": {
2009061da546Spatrick // "body": {
2010061da546Spatrick // "type": "object",
2011061da546Spatrick // "properties": {
2012061da546Spatrick // "breakpoints": {
2013061da546Spatrick // "type": "array",
2014061da546Spatrick // "items": {
2015061da546Spatrick // "$ref": "#/definitions/Breakpoint"
2016061da546Spatrick // },
2017061da546Spatrick // "description": "Information about the breakpoints. The array
2018061da546Spatrick // elements are in the same order as the elements of the
2019061da546Spatrick // 'breakpoints' (or the deprecated 'lines') in the
2020061da546Spatrick // SetBreakpointsArguments."
2021061da546Spatrick // }
2022061da546Spatrick // },
2023061da546Spatrick // "required": [ "breakpoints" ]
2024061da546Spatrick // }
2025061da546Spatrick // },
2026061da546Spatrick // "required": [ "body" ]
2027061da546Spatrick // }]
2028061da546Spatrick // },
2029061da546Spatrick // "SourceBreakpoint": {
2030061da546Spatrick // "type": "object",
2031061da546Spatrick // "description": "Properties of a breakpoint or logpoint passed to the
2032061da546Spatrick // setBreakpoints request.", "properties": {
2033061da546Spatrick // "line": {
2034061da546Spatrick // "type": "integer",
2035061da546Spatrick // "description": "The source line of the breakpoint or logpoint."
2036061da546Spatrick // },
2037061da546Spatrick // "column": {
2038061da546Spatrick // "type": "integer",
2039061da546Spatrick // "description": "An optional source column of the breakpoint."
2040061da546Spatrick // },
2041061da546Spatrick // "condition": {
2042061da546Spatrick // "type": "string",
2043061da546Spatrick // "description": "An optional expression for conditional breakpoints."
2044061da546Spatrick // },
2045061da546Spatrick // "hitCondition": {
2046061da546Spatrick // "type": "string",
2047061da546Spatrick // "description": "An optional expression that controls how many hits of
2048061da546Spatrick // the breakpoint are ignored. The backend is expected to interpret the
2049061da546Spatrick // expression as needed."
2050061da546Spatrick // },
2051061da546Spatrick // "logMessage": {
2052061da546Spatrick // "type": "string",
2053061da546Spatrick // "description": "If this attribute exists and is non-empty, the backend
2054061da546Spatrick // must not 'break' (stop) but log the message instead. Expressions within
2055061da546Spatrick // {} are interpolated."
2056061da546Spatrick // }
2057061da546Spatrick // },
2058061da546Spatrick // "required": [ "line" ]
2059061da546Spatrick // }
request_setBreakpoints(const llvm::json::Object & request)2060061da546Spatrick void request_setBreakpoints(const llvm::json::Object &request) {
2061061da546Spatrick llvm::json::Object response;
2062061da546Spatrick lldb::SBError error;
2063061da546Spatrick FillResponse(request, response);
2064061da546Spatrick auto arguments = request.getObject("arguments");
2065061da546Spatrick auto source = arguments->getObject("source");
2066061da546Spatrick const auto path = GetString(source, "path");
2067061da546Spatrick auto breakpoints = arguments->getArray("breakpoints");
2068061da546Spatrick llvm::json::Array response_breakpoints;
2069dda28197Spatrick
2070061da546Spatrick // Decode the source breakpoint infos for this "setBreakpoints" request
2071061da546Spatrick SourceBreakpointMap request_bps;
2072be691f3bSpatrick // "breakpoints" may be unset, in which case we treat it the same as being set
2073be691f3bSpatrick // to an empty array.
2074be691f3bSpatrick if (breakpoints) {
2075061da546Spatrick for (const auto &bp : *breakpoints) {
2076061da546Spatrick auto bp_obj = bp.getAsObject();
2077061da546Spatrick if (bp_obj) {
2078061da546Spatrick SourceBreakpoint src_bp(*bp_obj);
2079dda28197Spatrick request_bps[src_bp.line] = src_bp;
2080dda28197Spatrick
2081dda28197Spatrick // We check if this breakpoint already exists to update it
2082dda28197Spatrick auto existing_source_bps = g_vsc.source_breakpoints.find(path);
2083dda28197Spatrick if (existing_source_bps != g_vsc.source_breakpoints.end()) {
2084be691f3bSpatrick const auto &existing_bp =
2085be691f3bSpatrick existing_source_bps->second.find(src_bp.line);
2086dda28197Spatrick if (existing_bp != existing_source_bps->second.end()) {
2087dda28197Spatrick existing_bp->second.UpdateBreakpoint(src_bp);
2088dda28197Spatrick AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path,
2089dda28197Spatrick src_bp.line);
2090dda28197Spatrick continue;
2091061da546Spatrick }
2092061da546Spatrick }
2093dda28197Spatrick // At this point the breakpoint is new
2094*f6aab3d8Srobert g_vsc.source_breakpoints[path][src_bp.line] = src_bp;
2095*f6aab3d8Srobert SourceBreakpoint &new_bp = g_vsc.source_breakpoints[path][src_bp.line];
2096*f6aab3d8Srobert new_bp.SetBreakpoint(path.data());
2097*f6aab3d8Srobert AppendBreakpoint(new_bp.bp, response_breakpoints, path, new_bp.line);
2098dda28197Spatrick }
2099dda28197Spatrick }
2100be691f3bSpatrick }
2101061da546Spatrick
2102061da546Spatrick // Delete any breakpoints in this source file that aren't in the
2103061da546Spatrick // request_bps set. There is no call to remove breakpoints other than
2104061da546Spatrick // calling this function with a smaller or empty "breakpoints" list.
2105dda28197Spatrick auto old_src_bp_pos = g_vsc.source_breakpoints.find(path);
2106dda28197Spatrick if (old_src_bp_pos != g_vsc.source_breakpoints.end()) {
2107dda28197Spatrick for (auto &old_bp : old_src_bp_pos->second) {
2108dda28197Spatrick auto request_pos = request_bps.find(old_bp.first);
2109061da546Spatrick if (request_pos == request_bps.end()) {
2110061da546Spatrick // This breakpoint no longer exists in this source file, delete it
2111dda28197Spatrick g_vsc.target.BreakpointDelete(old_bp.second.bp.GetID());
2112dda28197Spatrick old_src_bp_pos->second.erase(old_bp.first);
2113061da546Spatrick }
2114061da546Spatrick }
2115061da546Spatrick }
2116061da546Spatrick
2117061da546Spatrick llvm::json::Object body;
2118061da546Spatrick body.try_emplace("breakpoints", std::move(response_breakpoints));
2119061da546Spatrick response.try_emplace("body", std::move(body));
2120061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2121061da546Spatrick }
2122061da546Spatrick
2123061da546Spatrick // "SetExceptionBreakpointsRequest": {
2124061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2125061da546Spatrick // "type": "object",
2126061da546Spatrick // "description": "SetExceptionBreakpoints request; value of command field
2127061da546Spatrick // is 'setExceptionBreakpoints'. The request configures the debuggers
2128061da546Spatrick // response to thrown exceptions. If an exception is configured to break, a
2129061da546Spatrick // StoppedEvent is fired (event type 'exception').", "properties": {
2130061da546Spatrick // "command": {
2131061da546Spatrick // "type": "string",
2132061da546Spatrick // "enum": [ "setExceptionBreakpoints" ]
2133061da546Spatrick // },
2134061da546Spatrick // "arguments": {
2135061da546Spatrick // "$ref": "#/definitions/SetExceptionBreakpointsArguments"
2136061da546Spatrick // }
2137061da546Spatrick // },
2138061da546Spatrick // "required": [ "command", "arguments" ]
2139061da546Spatrick // }]
2140061da546Spatrick // },
2141061da546Spatrick // "SetExceptionBreakpointsArguments": {
2142061da546Spatrick // "type": "object",
2143061da546Spatrick // "description": "Arguments for 'setExceptionBreakpoints' request.",
2144061da546Spatrick // "properties": {
2145061da546Spatrick // "filters": {
2146061da546Spatrick // "type": "array",
2147061da546Spatrick // "items": {
2148061da546Spatrick // "type": "string"
2149061da546Spatrick // },
2150061da546Spatrick // "description": "IDs of checked exception options. The set of IDs is
2151061da546Spatrick // returned via the 'exceptionBreakpointFilters' capability."
2152061da546Spatrick // },
2153061da546Spatrick // "exceptionOptions": {
2154061da546Spatrick // "type": "array",
2155061da546Spatrick // "items": {
2156061da546Spatrick // "$ref": "#/definitions/ExceptionOptions"
2157061da546Spatrick // },
2158061da546Spatrick // "description": "Configuration options for selected exceptions."
2159061da546Spatrick // }
2160061da546Spatrick // },
2161061da546Spatrick // "required": [ "filters" ]
2162061da546Spatrick // },
2163061da546Spatrick // "SetExceptionBreakpointsResponse": {
2164061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2165061da546Spatrick // "type": "object",
2166061da546Spatrick // "description": "Response to 'setExceptionBreakpoints' request. This is
2167061da546Spatrick // just an acknowledgement, so no body field is required."
2168061da546Spatrick // }]
2169061da546Spatrick // }
request_setExceptionBreakpoints(const llvm::json::Object & request)2170061da546Spatrick void request_setExceptionBreakpoints(const llvm::json::Object &request) {
2171061da546Spatrick llvm::json::Object response;
2172061da546Spatrick lldb::SBError error;
2173061da546Spatrick FillResponse(request, response);
2174061da546Spatrick auto arguments = request.getObject("arguments");
2175061da546Spatrick auto filters = arguments->getArray("filters");
2176061da546Spatrick // Keep a list of any exception breakpoint filter names that weren't set
2177061da546Spatrick // so we can clear any exception breakpoints if needed.
2178061da546Spatrick std::set<std::string> unset_filters;
2179061da546Spatrick for (const auto &bp : g_vsc.exception_breakpoints)
2180061da546Spatrick unset_filters.insert(bp.filter);
2181061da546Spatrick
2182061da546Spatrick for (const auto &value : *filters) {
2183061da546Spatrick const auto filter = GetAsString(value);
2184dda28197Spatrick auto exc_bp = g_vsc.GetExceptionBreakpoint(std::string(filter));
2185061da546Spatrick if (exc_bp) {
2186061da546Spatrick exc_bp->SetBreakpoint();
2187dda28197Spatrick unset_filters.erase(std::string(filter));
2188061da546Spatrick }
2189061da546Spatrick }
2190061da546Spatrick for (const auto &filter : unset_filters) {
2191061da546Spatrick auto exc_bp = g_vsc.GetExceptionBreakpoint(filter);
2192061da546Spatrick if (exc_bp)
2193061da546Spatrick exc_bp->ClearBreakpoint();
2194061da546Spatrick }
2195061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2196061da546Spatrick }
2197061da546Spatrick
2198061da546Spatrick // "SetFunctionBreakpointsRequest": {
2199061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2200061da546Spatrick // "type": "object",
2201061da546Spatrick // "description": "SetFunctionBreakpoints request; value of command field is
2202061da546Spatrick // 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
2203061da546Spatrick // all previous function breakpoints. To clear all function breakpoint,
2204061da546Spatrick // specify an empty array. When a function breakpoint is hit, a StoppedEvent
2205061da546Spatrick // (event type 'function breakpoint') is generated.", "properties": {
2206061da546Spatrick // "command": {
2207061da546Spatrick // "type": "string",
2208061da546Spatrick // "enum": [ "setFunctionBreakpoints" ]
2209061da546Spatrick // },
2210061da546Spatrick // "arguments": {
2211061da546Spatrick // "$ref": "#/definitions/SetFunctionBreakpointsArguments"
2212061da546Spatrick // }
2213061da546Spatrick // },
2214061da546Spatrick // "required": [ "command", "arguments" ]
2215061da546Spatrick // }]
2216061da546Spatrick // },
2217061da546Spatrick // "SetFunctionBreakpointsArguments": {
2218061da546Spatrick // "type": "object",
2219061da546Spatrick // "description": "Arguments for 'setFunctionBreakpoints' request.",
2220061da546Spatrick // "properties": {
2221061da546Spatrick // "breakpoints": {
2222061da546Spatrick // "type": "array",
2223061da546Spatrick // "items": {
2224061da546Spatrick // "$ref": "#/definitions/FunctionBreakpoint"
2225061da546Spatrick // },
2226061da546Spatrick // "description": "The function names of the breakpoints."
2227061da546Spatrick // }
2228061da546Spatrick // },
2229061da546Spatrick // "required": [ "breakpoints" ]
2230061da546Spatrick // },
2231061da546Spatrick // "FunctionBreakpoint": {
2232061da546Spatrick // "type": "object",
2233061da546Spatrick // "description": "Properties of a breakpoint passed to the
2234061da546Spatrick // setFunctionBreakpoints request.", "properties": {
2235061da546Spatrick // "name": {
2236061da546Spatrick // "type": "string",
2237061da546Spatrick // "description": "The name of the function."
2238061da546Spatrick // },
2239061da546Spatrick // "condition": {
2240061da546Spatrick // "type": "string",
2241061da546Spatrick // "description": "An optional expression for conditional breakpoints."
2242061da546Spatrick // },
2243061da546Spatrick // "hitCondition": {
2244061da546Spatrick // "type": "string",
2245061da546Spatrick // "description": "An optional expression that controls how many hits of
2246061da546Spatrick // the breakpoint are ignored. The backend is expected to interpret the
2247061da546Spatrick // expression as needed."
2248061da546Spatrick // }
2249061da546Spatrick // },
2250061da546Spatrick // "required": [ "name" ]
2251061da546Spatrick // },
2252061da546Spatrick // "SetFunctionBreakpointsResponse": {
2253061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2254061da546Spatrick // "type": "object",
2255061da546Spatrick // "description": "Response to 'setFunctionBreakpoints' request. Returned is
2256061da546Spatrick // information about each breakpoint created by this request.",
2257061da546Spatrick // "properties": {
2258061da546Spatrick // "body": {
2259061da546Spatrick // "type": "object",
2260061da546Spatrick // "properties": {
2261061da546Spatrick // "breakpoints": {
2262061da546Spatrick // "type": "array",
2263061da546Spatrick // "items": {
2264061da546Spatrick // "$ref": "#/definitions/Breakpoint"
2265061da546Spatrick // },
2266061da546Spatrick // "description": "Information about the breakpoints. The array
2267061da546Spatrick // elements correspond to the elements of the 'breakpoints' array."
2268061da546Spatrick // }
2269061da546Spatrick // },
2270061da546Spatrick // "required": [ "breakpoints" ]
2271061da546Spatrick // }
2272061da546Spatrick // },
2273061da546Spatrick // "required": [ "body" ]
2274061da546Spatrick // }]
2275061da546Spatrick // }
request_setFunctionBreakpoints(const llvm::json::Object & request)2276061da546Spatrick void request_setFunctionBreakpoints(const llvm::json::Object &request) {
2277061da546Spatrick llvm::json::Object response;
2278061da546Spatrick lldb::SBError error;
2279061da546Spatrick FillResponse(request, response);
2280061da546Spatrick auto arguments = request.getObject("arguments");
2281061da546Spatrick auto breakpoints = arguments->getArray("breakpoints");
2282061da546Spatrick FunctionBreakpointMap request_bps;
2283061da546Spatrick llvm::json::Array response_breakpoints;
2284061da546Spatrick for (const auto &value : *breakpoints) {
2285061da546Spatrick auto bp_obj = value.getAsObject();
2286061da546Spatrick if (bp_obj == nullptr)
2287061da546Spatrick continue;
2288061da546Spatrick FunctionBreakpoint func_bp(*bp_obj);
2289061da546Spatrick request_bps[func_bp.functionName] = std::move(func_bp);
2290061da546Spatrick }
2291061da546Spatrick
2292061da546Spatrick std::vector<llvm::StringRef> remove_names;
2293061da546Spatrick // Disable any function breakpoints that aren't in the request_bps.
2294061da546Spatrick // There is no call to remove function breakpoints other than calling this
2295061da546Spatrick // function with a smaller or empty "breakpoints" list.
2296061da546Spatrick for (auto &pair : g_vsc.function_breakpoints) {
2297061da546Spatrick auto request_pos = request_bps.find(pair.first());
2298061da546Spatrick if (request_pos == request_bps.end()) {
2299061da546Spatrick // This function breakpoint no longer exists delete it from LLDB
2300061da546Spatrick g_vsc.target.BreakpointDelete(pair.second.bp.GetID());
2301061da546Spatrick remove_names.push_back(pair.first());
2302061da546Spatrick } else {
2303061da546Spatrick // Update the existing breakpoint as any setting withing the function
2304061da546Spatrick // breakpoint might have changed.
2305061da546Spatrick pair.second.UpdateBreakpoint(request_pos->second);
2306061da546Spatrick // Remove this breakpoint from the request breakpoints since we have
2307061da546Spatrick // handled it here and we don't need to set a new breakpoint below.
2308061da546Spatrick request_bps.erase(request_pos);
2309061da546Spatrick // Add this breakpoint info to the response
2310061da546Spatrick AppendBreakpoint(pair.second.bp, response_breakpoints);
2311061da546Spatrick }
2312061da546Spatrick }
2313061da546Spatrick // Remove any breakpoints that are no longer in our list
2314061da546Spatrick for (const auto &name : remove_names)
2315061da546Spatrick g_vsc.function_breakpoints.erase(name);
2316061da546Spatrick
2317061da546Spatrick // Any breakpoints that are left in "request_bps" are breakpoints that
2318061da546Spatrick // need to be set.
2319061da546Spatrick for (auto &pair : request_bps) {
2320061da546Spatrick // Add this breakpoint info to the response
2321061da546Spatrick g_vsc.function_breakpoints[pair.first()] = std::move(pair.second);
2322*f6aab3d8Srobert FunctionBreakpoint &new_bp = g_vsc.function_breakpoints[pair.first()];
2323*f6aab3d8Srobert new_bp.SetBreakpoint();
2324*f6aab3d8Srobert AppendBreakpoint(new_bp.bp, response_breakpoints);
2325061da546Spatrick }
2326061da546Spatrick
2327061da546Spatrick llvm::json::Object body;
2328061da546Spatrick body.try_emplace("breakpoints", std::move(response_breakpoints));
2329061da546Spatrick response.try_emplace("body", std::move(body));
2330061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2331061da546Spatrick }
2332061da546Spatrick
2333061da546Spatrick // "SourceRequest": {
2334061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2335061da546Spatrick // "type": "object",
2336061da546Spatrick // "description": "Source request; value of command field is 'source'. The
2337061da546Spatrick // request retrieves the source code for a given source reference.",
2338061da546Spatrick // "properties": {
2339061da546Spatrick // "command": {
2340061da546Spatrick // "type": "string",
2341061da546Spatrick // "enum": [ "source" ]
2342061da546Spatrick // },
2343061da546Spatrick // "arguments": {
2344061da546Spatrick // "$ref": "#/definitions/SourceArguments"
2345061da546Spatrick // }
2346061da546Spatrick // },
2347061da546Spatrick // "required": [ "command", "arguments" ]
2348061da546Spatrick // }]
2349061da546Spatrick // },
2350061da546Spatrick // "SourceArguments": {
2351061da546Spatrick // "type": "object",
2352061da546Spatrick // "description": "Arguments for 'source' request.",
2353061da546Spatrick // "properties": {
2354061da546Spatrick // "source": {
2355061da546Spatrick // "$ref": "#/definitions/Source",
2356061da546Spatrick // "description": "Specifies the source content to load. Either
2357061da546Spatrick // source.path or source.sourceReference must be specified."
2358061da546Spatrick // },
2359061da546Spatrick // "sourceReference": {
2360061da546Spatrick // "type": "integer",
2361061da546Spatrick // "description": "The reference to the source. This is the same as
2362061da546Spatrick // source.sourceReference. This is provided for backward compatibility
2363061da546Spatrick // since old backends do not understand the 'source' attribute."
2364061da546Spatrick // }
2365061da546Spatrick // },
2366061da546Spatrick // "required": [ "sourceReference" ]
2367061da546Spatrick // },
2368061da546Spatrick // "SourceResponse": {
2369061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2370061da546Spatrick // "type": "object",
2371061da546Spatrick // "description": "Response to 'source' request.",
2372061da546Spatrick // "properties": {
2373061da546Spatrick // "body": {
2374061da546Spatrick // "type": "object",
2375061da546Spatrick // "properties": {
2376061da546Spatrick // "content": {
2377061da546Spatrick // "type": "string",
2378061da546Spatrick // "description": "Content of the source reference."
2379061da546Spatrick // },
2380061da546Spatrick // "mimeType": {
2381061da546Spatrick // "type": "string",
2382061da546Spatrick // "description": "Optional content type (mime type) of the source."
2383061da546Spatrick // }
2384061da546Spatrick // },
2385061da546Spatrick // "required": [ "content" ]
2386061da546Spatrick // }
2387061da546Spatrick // },
2388061da546Spatrick // "required": [ "body" ]
2389061da546Spatrick // }]
2390061da546Spatrick // }
request_source(const llvm::json::Object & request)2391061da546Spatrick void request_source(const llvm::json::Object &request) {
2392061da546Spatrick llvm::json::Object response;
2393061da546Spatrick FillResponse(request, response);
2394061da546Spatrick llvm::json::Object body;
2395061da546Spatrick
2396061da546Spatrick auto arguments = request.getObject("arguments");
2397061da546Spatrick auto source = arguments->getObject("source");
2398061da546Spatrick auto sourceReference = GetSigned(source, "sourceReference", -1);
2399061da546Spatrick auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference);
2400061da546Spatrick if (pos != g_vsc.source_map.end()) {
2401061da546Spatrick EmplaceSafeString(body, "content", pos->second.content);
2402061da546Spatrick } else {
2403061da546Spatrick response["success"] = llvm::json::Value(false);
2404061da546Spatrick }
2405be691f3bSpatrick EmplaceSafeString(body, "mimeType", "text/x-lldb.disassembly");
2406061da546Spatrick response.try_emplace("body", std::move(body));
2407061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2408061da546Spatrick }
2409061da546Spatrick
2410061da546Spatrick // "StackTraceRequest": {
2411061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2412061da546Spatrick // "type": "object",
2413061da546Spatrick // "description": "StackTrace request; value of command field is
2414061da546Spatrick // 'stackTrace'. The request returns a stacktrace from the current execution
2415061da546Spatrick // state.", "properties": {
2416061da546Spatrick // "command": {
2417061da546Spatrick // "type": "string",
2418061da546Spatrick // "enum": [ "stackTrace" ]
2419061da546Spatrick // },
2420061da546Spatrick // "arguments": {
2421061da546Spatrick // "$ref": "#/definitions/StackTraceArguments"
2422061da546Spatrick // }
2423061da546Spatrick // },
2424061da546Spatrick // "required": [ "command", "arguments" ]
2425061da546Spatrick // }]
2426061da546Spatrick // },
2427061da546Spatrick // "StackTraceArguments": {
2428061da546Spatrick // "type": "object",
2429061da546Spatrick // "description": "Arguments for 'stackTrace' request.",
2430061da546Spatrick // "properties": {
2431061da546Spatrick // "threadId": {
2432061da546Spatrick // "type": "integer",
2433061da546Spatrick // "description": "Retrieve the stacktrace for this thread."
2434061da546Spatrick // },
2435061da546Spatrick // "startFrame": {
2436061da546Spatrick // "type": "integer",
2437061da546Spatrick // "description": "The index of the first frame to return; if omitted
2438061da546Spatrick // frames start at 0."
2439061da546Spatrick // },
2440061da546Spatrick // "levels": {
2441061da546Spatrick // "type": "integer",
2442061da546Spatrick // "description": "The maximum number of frames to return. If levels is
2443061da546Spatrick // not specified or 0, all frames are returned."
2444061da546Spatrick // },
2445061da546Spatrick // "format": {
2446061da546Spatrick // "$ref": "#/definitions/StackFrameFormat",
2447061da546Spatrick // "description": "Specifies details on how to format the stack frames."
2448061da546Spatrick // }
2449061da546Spatrick // },
2450061da546Spatrick // "required": [ "threadId" ]
2451061da546Spatrick // },
2452061da546Spatrick // "StackTraceResponse": {
2453061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2454061da546Spatrick // "type": "object",
2455061da546Spatrick // "description": "Response to 'stackTrace' request.",
2456061da546Spatrick // "properties": {
2457061da546Spatrick // "body": {
2458061da546Spatrick // "type": "object",
2459061da546Spatrick // "properties": {
2460061da546Spatrick // "stackFrames": {
2461061da546Spatrick // "type": "array",
2462061da546Spatrick // "items": {
2463061da546Spatrick // "$ref": "#/definitions/StackFrame"
2464061da546Spatrick // },
2465061da546Spatrick // "description": "The frames of the stackframe. If the array has
2466061da546Spatrick // length zero, there are no stackframes available. This means that
2467061da546Spatrick // there is no location information available."
2468061da546Spatrick // },
2469061da546Spatrick // "totalFrames": {
2470061da546Spatrick // "type": "integer",
2471061da546Spatrick // "description": "The total number of frames available."
2472061da546Spatrick // }
2473061da546Spatrick // },
2474061da546Spatrick // "required": [ "stackFrames" ]
2475061da546Spatrick // }
2476061da546Spatrick // },
2477061da546Spatrick // "required": [ "body" ]
2478061da546Spatrick // }]
2479061da546Spatrick // }
request_stackTrace(const llvm::json::Object & request)2480061da546Spatrick void request_stackTrace(const llvm::json::Object &request) {
2481061da546Spatrick llvm::json::Object response;
2482061da546Spatrick FillResponse(request, response);
2483061da546Spatrick lldb::SBError error;
2484061da546Spatrick auto arguments = request.getObject("arguments");
2485061da546Spatrick lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
2486061da546Spatrick llvm::json::Array stackFrames;
2487061da546Spatrick llvm::json::Object body;
2488061da546Spatrick
2489061da546Spatrick if (thread.IsValid()) {
2490061da546Spatrick const auto startFrame = GetUnsigned(arguments, "startFrame", 0);
2491061da546Spatrick const auto levels = GetUnsigned(arguments, "levels", 0);
2492061da546Spatrick const auto endFrame = (levels == 0) ? INT64_MAX : (startFrame + levels);
2493061da546Spatrick for (uint32_t i = startFrame; i < endFrame; ++i) {
2494061da546Spatrick auto frame = thread.GetFrameAtIndex(i);
2495061da546Spatrick if (!frame.IsValid())
2496061da546Spatrick break;
2497061da546Spatrick stackFrames.emplace_back(CreateStackFrame(frame));
2498061da546Spatrick }
2499061da546Spatrick const auto totalFrames = thread.GetNumFrames();
2500061da546Spatrick body.try_emplace("totalFrames", totalFrames);
2501061da546Spatrick }
2502061da546Spatrick body.try_emplace("stackFrames", std::move(stackFrames));
2503061da546Spatrick response.try_emplace("body", std::move(body));
2504061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2505061da546Spatrick }
2506061da546Spatrick
2507061da546Spatrick // "StepInRequest": {
2508061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2509061da546Spatrick // "type": "object",
2510061da546Spatrick // "description": "StepIn request; value of command field is 'stepIn'. The
2511061da546Spatrick // request starts the debuggee to step into a function/method if possible.
2512061da546Spatrick // If it cannot step into a target, 'stepIn' behaves like 'next'. The debug
2513061da546Spatrick // adapter first sends the StepInResponse and then a StoppedEvent (event
2514061da546Spatrick // type 'step') after the step has completed. If there are multiple
2515061da546Spatrick // function/method calls (or other targets) on the source line, the optional
2516061da546Spatrick // argument 'targetId' can be used to control into which target the 'stepIn'
2517061da546Spatrick // should occur. The list of possible targets for a given source line can be
2518061da546Spatrick // retrieved via the 'stepInTargets' request.", "properties": {
2519061da546Spatrick // "command": {
2520061da546Spatrick // "type": "string",
2521061da546Spatrick // "enum": [ "stepIn" ]
2522061da546Spatrick // },
2523061da546Spatrick // "arguments": {
2524061da546Spatrick // "$ref": "#/definitions/StepInArguments"
2525061da546Spatrick // }
2526061da546Spatrick // },
2527061da546Spatrick // "required": [ "command", "arguments" ]
2528061da546Spatrick // }]
2529061da546Spatrick // },
2530061da546Spatrick // "StepInArguments": {
2531061da546Spatrick // "type": "object",
2532061da546Spatrick // "description": "Arguments for 'stepIn' request.",
2533061da546Spatrick // "properties": {
2534061da546Spatrick // "threadId": {
2535061da546Spatrick // "type": "integer",
2536061da546Spatrick // "description": "Execute 'stepIn' for this thread."
2537061da546Spatrick // },
2538061da546Spatrick // "targetId": {
2539061da546Spatrick // "type": "integer",
2540061da546Spatrick // "description": "Optional id of the target to step into."
2541061da546Spatrick // }
2542061da546Spatrick // },
2543061da546Spatrick // "required": [ "threadId" ]
2544061da546Spatrick // },
2545061da546Spatrick // "StepInResponse": {
2546061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2547061da546Spatrick // "type": "object",
2548061da546Spatrick // "description": "Response to 'stepIn' request. This is just an
2549061da546Spatrick // acknowledgement, so no body field is required."
2550061da546Spatrick // }]
2551061da546Spatrick // }
request_stepIn(const llvm::json::Object & request)2552061da546Spatrick void request_stepIn(const llvm::json::Object &request) {
2553061da546Spatrick llvm::json::Object response;
2554061da546Spatrick FillResponse(request, response);
2555061da546Spatrick auto arguments = request.getObject("arguments");
2556061da546Spatrick lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
2557061da546Spatrick if (thread.IsValid()) {
2558061da546Spatrick // Remember the thread ID that caused the resume so we can set the
2559061da546Spatrick // "threadCausedFocus" boolean value in the "stopped" events.
2560061da546Spatrick g_vsc.focus_tid = thread.GetThreadID();
2561061da546Spatrick thread.StepInto();
2562061da546Spatrick } else {
2563061da546Spatrick response["success"] = llvm::json::Value(false);
2564061da546Spatrick }
2565061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2566061da546Spatrick }
2567061da546Spatrick
2568061da546Spatrick // "StepOutRequest": {
2569061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2570061da546Spatrick // "type": "object",
2571061da546Spatrick // "description": "StepOut request; value of command field is 'stepOut'. The
2572061da546Spatrick // request starts the debuggee to run again for one step. The debug adapter
2573061da546Spatrick // first sends the StepOutResponse and then a StoppedEvent (event type
2574061da546Spatrick // 'step') after the step has completed.", "properties": {
2575061da546Spatrick // "command": {
2576061da546Spatrick // "type": "string",
2577061da546Spatrick // "enum": [ "stepOut" ]
2578061da546Spatrick // },
2579061da546Spatrick // "arguments": {
2580061da546Spatrick // "$ref": "#/definitions/StepOutArguments"
2581061da546Spatrick // }
2582061da546Spatrick // },
2583061da546Spatrick // "required": [ "command", "arguments" ]
2584061da546Spatrick // }]
2585061da546Spatrick // },
2586061da546Spatrick // "StepOutArguments": {
2587061da546Spatrick // "type": "object",
2588061da546Spatrick // "description": "Arguments for 'stepOut' request.",
2589061da546Spatrick // "properties": {
2590061da546Spatrick // "threadId": {
2591061da546Spatrick // "type": "integer",
2592061da546Spatrick // "description": "Execute 'stepOut' for this thread."
2593061da546Spatrick // }
2594061da546Spatrick // },
2595061da546Spatrick // "required": [ "threadId" ]
2596061da546Spatrick // },
2597061da546Spatrick // "StepOutResponse": {
2598061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2599061da546Spatrick // "type": "object",
2600061da546Spatrick // "description": "Response to 'stepOut' request. This is just an
2601061da546Spatrick // acknowledgement, so no body field is required."
2602061da546Spatrick // }]
2603061da546Spatrick // }
request_stepOut(const llvm::json::Object & request)2604061da546Spatrick void request_stepOut(const llvm::json::Object &request) {
2605061da546Spatrick llvm::json::Object response;
2606061da546Spatrick FillResponse(request, response);
2607061da546Spatrick auto arguments = request.getObject("arguments");
2608061da546Spatrick lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
2609061da546Spatrick if (thread.IsValid()) {
2610061da546Spatrick // Remember the thread ID that caused the resume so we can set the
2611061da546Spatrick // "threadCausedFocus" boolean value in the "stopped" events.
2612061da546Spatrick g_vsc.focus_tid = thread.GetThreadID();
2613061da546Spatrick thread.StepOut();
2614061da546Spatrick } else {
2615061da546Spatrick response["success"] = llvm::json::Value(false);
2616061da546Spatrick }
2617061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2618061da546Spatrick }
2619061da546Spatrick
2620061da546Spatrick // "ThreadsRequest": {
2621061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2622061da546Spatrick // "type": "object",
2623061da546Spatrick // "description": "Thread request; value of command field is 'threads'. The
2624061da546Spatrick // request retrieves a list of all threads.", "properties": {
2625061da546Spatrick // "command": {
2626061da546Spatrick // "type": "string",
2627061da546Spatrick // "enum": [ "threads" ]
2628061da546Spatrick // }
2629061da546Spatrick // },
2630061da546Spatrick // "required": [ "command" ]
2631061da546Spatrick // }]
2632061da546Spatrick // },
2633061da546Spatrick // "ThreadsResponse": {
2634061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2635061da546Spatrick // "type": "object",
2636061da546Spatrick // "description": "Response to 'threads' request.",
2637061da546Spatrick // "properties": {
2638061da546Spatrick // "body": {
2639061da546Spatrick // "type": "object",
2640061da546Spatrick // "properties": {
2641061da546Spatrick // "threads": {
2642061da546Spatrick // "type": "array",
2643061da546Spatrick // "items": {
2644061da546Spatrick // "$ref": "#/definitions/Thread"
2645061da546Spatrick // },
2646061da546Spatrick // "description": "All threads."
2647061da546Spatrick // }
2648061da546Spatrick // },
2649061da546Spatrick // "required": [ "threads" ]
2650061da546Spatrick // }
2651061da546Spatrick // },
2652061da546Spatrick // "required": [ "body" ]
2653061da546Spatrick // }]
2654061da546Spatrick // }
request_threads(const llvm::json::Object & request)2655061da546Spatrick void request_threads(const llvm::json::Object &request) {
2656061da546Spatrick
2657061da546Spatrick lldb::SBProcess process = g_vsc.target.GetProcess();
2658061da546Spatrick llvm::json::Object response;
2659061da546Spatrick FillResponse(request, response);
2660061da546Spatrick
2661061da546Spatrick const uint32_t num_threads = process.GetNumThreads();
2662061da546Spatrick llvm::json::Array threads;
2663061da546Spatrick for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
2664061da546Spatrick lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
2665061da546Spatrick threads.emplace_back(CreateThread(thread));
2666061da546Spatrick }
2667061da546Spatrick if (threads.size() == 0) {
2668061da546Spatrick response["success"] = llvm::json::Value(false);
2669061da546Spatrick }
2670061da546Spatrick llvm::json::Object body;
2671061da546Spatrick body.try_emplace("threads", std::move(threads));
2672061da546Spatrick response.try_emplace("body", std::move(body));
2673061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2674061da546Spatrick }
2675061da546Spatrick
2676061da546Spatrick // "SetVariableRequest": {
2677061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2678061da546Spatrick // "type": "object",
2679061da546Spatrick // "description": "setVariable request; value of command field is
2680061da546Spatrick // 'setVariable'. Set the variable with the given name in the variable
2681061da546Spatrick // container to a new value.", "properties": {
2682061da546Spatrick // "command": {
2683061da546Spatrick // "type": "string",
2684061da546Spatrick // "enum": [ "setVariable" ]
2685061da546Spatrick // },
2686061da546Spatrick // "arguments": {
2687061da546Spatrick // "$ref": "#/definitions/SetVariableArguments"
2688061da546Spatrick // }
2689061da546Spatrick // },
2690061da546Spatrick // "required": [ "command", "arguments" ]
2691061da546Spatrick // }]
2692061da546Spatrick // },
2693061da546Spatrick // "SetVariableArguments": {
2694061da546Spatrick // "type": "object",
2695061da546Spatrick // "description": "Arguments for 'setVariable' request.",
2696061da546Spatrick // "properties": {
2697061da546Spatrick // "variablesReference": {
2698061da546Spatrick // "type": "integer",
2699061da546Spatrick // "description": "The reference of the variable container."
2700061da546Spatrick // },
2701061da546Spatrick // "name": {
2702061da546Spatrick // "type": "string",
2703061da546Spatrick // "description": "The name of the variable."
2704061da546Spatrick // },
2705061da546Spatrick // "value": {
2706061da546Spatrick // "type": "string",
2707061da546Spatrick // "description": "The value of the variable."
2708061da546Spatrick // },
2709061da546Spatrick // "format": {
2710061da546Spatrick // "$ref": "#/definitions/ValueFormat",
2711061da546Spatrick // "description": "Specifies details on how to format the response value."
2712061da546Spatrick // }
2713061da546Spatrick // },
2714061da546Spatrick // "required": [ "variablesReference", "name", "value" ]
2715061da546Spatrick // },
2716061da546Spatrick // "SetVariableResponse": {
2717061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2718061da546Spatrick // "type": "object",
2719061da546Spatrick // "description": "Response to 'setVariable' request.",
2720061da546Spatrick // "properties": {
2721061da546Spatrick // "body": {
2722061da546Spatrick // "type": "object",
2723061da546Spatrick // "properties": {
2724061da546Spatrick // "value": {
2725061da546Spatrick // "type": "string",
2726061da546Spatrick // "description": "The new value of the variable."
2727061da546Spatrick // },
2728061da546Spatrick // "type": {
2729061da546Spatrick // "type": "string",
2730061da546Spatrick // "description": "The type of the new value. Typically shown in the
2731061da546Spatrick // UI when hovering over the value."
2732061da546Spatrick // },
2733061da546Spatrick // "variablesReference": {
2734061da546Spatrick // "type": "number",
2735061da546Spatrick // "description": "If variablesReference is > 0, the new value is
2736061da546Spatrick // structured and its children can be retrieved by passing
2737061da546Spatrick // variablesReference to the VariablesRequest."
2738061da546Spatrick // },
2739061da546Spatrick // "namedVariables": {
2740061da546Spatrick // "type": "number",
2741061da546Spatrick // "description": "The number of named child variables. The client
2742061da546Spatrick // can use this optional information to present the variables in a
2743061da546Spatrick // paged UI and fetch them in chunks."
2744061da546Spatrick // },
2745061da546Spatrick // "indexedVariables": {
2746061da546Spatrick // "type": "number",
2747061da546Spatrick // "description": "The number of indexed child variables. The client
2748061da546Spatrick // can use this optional information to present the variables in a
2749061da546Spatrick // paged UI and fetch them in chunks."
2750061da546Spatrick // }
2751061da546Spatrick // },
2752061da546Spatrick // "required": [ "value" ]
2753061da546Spatrick // }
2754061da546Spatrick // },
2755061da546Spatrick // "required": [ "body" ]
2756061da546Spatrick // }]
2757061da546Spatrick // }
request_setVariable(const llvm::json::Object & request)2758061da546Spatrick void request_setVariable(const llvm::json::Object &request) {
2759061da546Spatrick llvm::json::Object response;
2760061da546Spatrick FillResponse(request, response);
2761061da546Spatrick llvm::json::Array variables;
2762061da546Spatrick llvm::json::Object body;
2763061da546Spatrick auto arguments = request.getObject("arguments");
2764061da546Spatrick // This is a reference to the containing variable/scope
2765061da546Spatrick const auto variablesReference =
2766061da546Spatrick GetUnsigned(arguments, "variablesReference", 0);
2767be691f3bSpatrick llvm::StringRef name = GetString(arguments, "name");
2768*f6aab3d8Srobert bool is_duplicated_variable_name = name.contains(" @");
2769be691f3bSpatrick
2770061da546Spatrick const auto value = GetString(arguments, "value");
2771061da546Spatrick // Set success to false just in case we don't find the variable by name
2772061da546Spatrick response.try_emplace("success", false);
2773061da546Spatrick
2774061da546Spatrick lldb::SBValue variable;
2775061da546Spatrick int64_t newVariablesReference = 0;
2776061da546Spatrick
2777061da546Spatrick // The "id" is the unique integer ID that is unique within the enclosing
2778061da546Spatrick // variablesReference. It is optionally added to any "interface Variable"
2779061da546Spatrick // objects to uniquely identify a variable within an enclosing
2780061da546Spatrick // variablesReference. It helps to disambiguate between two variables that
2781061da546Spatrick // have the same name within the same scope since the "setVariables" request
2782061da546Spatrick // only specifies the variable reference of the enclosing scope/variable, and
2783061da546Spatrick // the name of the variable. We could have two shadowed variables with the
2784061da546Spatrick // same name in "Locals" or "Globals". In our case the "id" absolute index
2785061da546Spatrick // of the variable within the g_vsc.variables list.
2786061da546Spatrick const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
2787061da546Spatrick if (id_value != UINT64_MAX) {
2788*f6aab3d8Srobert variable = g_vsc.variables.GetVariable(id_value);
2789*f6aab3d8Srobert } else if (lldb::SBValueList *top_scope =
2790*f6aab3d8Srobert GetTopLevelScope(variablesReference)) {
2791061da546Spatrick // variablesReference is one of our scopes, not an actual variable it is
2792061da546Spatrick // asking for a variable in locals or globals or registers
2793*f6aab3d8Srobert int64_t end_idx = top_scope->GetSize();
2794*f6aab3d8Srobert // Searching backward so that we choose the variable in closest scope
2795*f6aab3d8Srobert // among variables of the same name.
2796*f6aab3d8Srobert for (int64_t i = end_idx - 1; i >= 0; --i) {
2797*f6aab3d8Srobert lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
2798be691f3bSpatrick std::string variable_name = CreateUniqueVariableNameForDisplay(
2799be691f3bSpatrick curr_variable, is_duplicated_variable_name);
2800061da546Spatrick if (variable_name == name) {
2801061da546Spatrick variable = curr_variable;
2802061da546Spatrick break;
2803061da546Spatrick }
2804061da546Spatrick }
2805061da546Spatrick } else {
2806be691f3bSpatrick // This is not under the globals or locals scope, so there are no duplicated
2807be691f3bSpatrick // names.
2808be691f3bSpatrick
2809061da546Spatrick // We have a named item within an actual variable so we need to find it
2810061da546Spatrick // withing the container variable by name.
2811*f6aab3d8Srobert lldb::SBValue container = g_vsc.variables.GetVariable(variablesReference);
2812061da546Spatrick variable = container.GetChildMemberWithName(name.data());
2813061da546Spatrick if (!variable.IsValid()) {
2814061da546Spatrick if (name.startswith("[")) {
2815061da546Spatrick llvm::StringRef index_str(name.drop_front(1));
2816061da546Spatrick uint64_t index = 0;
2817061da546Spatrick if (!index_str.consumeInteger(0, index)) {
2818061da546Spatrick if (index_str == "]")
2819061da546Spatrick variable = container.GetChildAtIndex(index);
2820061da546Spatrick }
2821061da546Spatrick }
2822061da546Spatrick }
2823061da546Spatrick }
2824061da546Spatrick
2825061da546Spatrick if (variable.IsValid()) {
2826061da546Spatrick lldb::SBError error;
2827061da546Spatrick bool success = variable.SetValueFromCString(value.data(), error);
2828061da546Spatrick if (success) {
2829061da546Spatrick SetValueForKey(variable, body, "value");
2830061da546Spatrick EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName());
2831*f6aab3d8Srobert
2832*f6aab3d8Srobert // We don't know the index of the variable in our g_vsc.variables
2833*f6aab3d8Srobert // so always insert a new one to get its variablesReference.
2834*f6aab3d8Srobert // is_permanent is false because debug console does not support
2835*f6aab3d8Srobert // setVariable request.
2836*f6aab3d8Srobert if (variable.MightHaveChildren())
2837*f6aab3d8Srobert newVariablesReference = g_vsc.variables.InsertExpandableVariable(
2838*f6aab3d8Srobert variable, /*is_permanent=*/false);
2839*f6aab3d8Srobert
2840061da546Spatrick body.try_emplace("variablesReference", newVariablesReference);
2841061da546Spatrick } else {
2842061da546Spatrick EmplaceSafeString(body, "message", std::string(error.GetCString()));
2843061da546Spatrick }
2844061da546Spatrick response["success"] = llvm::json::Value(success);
2845be691f3bSpatrick } else {
2846be691f3bSpatrick response["success"] = llvm::json::Value(false);
2847061da546Spatrick }
2848061da546Spatrick
2849061da546Spatrick response.try_emplace("body", std::move(body));
2850061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2851061da546Spatrick }
2852061da546Spatrick
2853061da546Spatrick // "VariablesRequest": {
2854061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Request" }, {
2855061da546Spatrick // "type": "object",
2856061da546Spatrick // "description": "Variables request; value of command field is 'variables'.
2857061da546Spatrick // Retrieves all child variables for the given variable reference. An
2858061da546Spatrick // optional filter can be used to limit the fetched children to either named
2859061da546Spatrick // or indexed children.", "properties": {
2860061da546Spatrick // "command": {
2861061da546Spatrick // "type": "string",
2862061da546Spatrick // "enum": [ "variables" ]
2863061da546Spatrick // },
2864061da546Spatrick // "arguments": {
2865061da546Spatrick // "$ref": "#/definitions/VariablesArguments"
2866061da546Spatrick // }
2867061da546Spatrick // },
2868061da546Spatrick // "required": [ "command", "arguments" ]
2869061da546Spatrick // }]
2870061da546Spatrick // },
2871061da546Spatrick // "VariablesArguments": {
2872061da546Spatrick // "type": "object",
2873061da546Spatrick // "description": "Arguments for 'variables' request.",
2874061da546Spatrick // "properties": {
2875061da546Spatrick // "variablesReference": {
2876061da546Spatrick // "type": "integer",
2877061da546Spatrick // "description": "The Variable reference."
2878061da546Spatrick // },
2879061da546Spatrick // "filter": {
2880061da546Spatrick // "type": "string",
2881061da546Spatrick // "enum": [ "indexed", "named" ],
2882061da546Spatrick // "description": "Optional filter to limit the child variables to either
2883061da546Spatrick // named or indexed. If ommited, both types are fetched."
2884061da546Spatrick // },
2885061da546Spatrick // "start": {
2886061da546Spatrick // "type": "integer",
2887061da546Spatrick // "description": "The index of the first variable to return; if omitted
2888061da546Spatrick // children start at 0."
2889061da546Spatrick // },
2890061da546Spatrick // "count": {
2891061da546Spatrick // "type": "integer",
2892061da546Spatrick // "description": "The number of variables to return. If count is missing
2893061da546Spatrick // or 0, all variables are returned."
2894061da546Spatrick // },
2895061da546Spatrick // "format": {
2896061da546Spatrick // "$ref": "#/definitions/ValueFormat",
2897061da546Spatrick // "description": "Specifies details on how to format the Variable
2898061da546Spatrick // values."
2899061da546Spatrick // }
2900061da546Spatrick // },
2901061da546Spatrick // "required": [ "variablesReference" ]
2902061da546Spatrick // },
2903061da546Spatrick // "VariablesResponse": {
2904061da546Spatrick // "allOf": [ { "$ref": "#/definitions/Response" }, {
2905061da546Spatrick // "type": "object",
2906061da546Spatrick // "description": "Response to 'variables' request.",
2907061da546Spatrick // "properties": {
2908061da546Spatrick // "body": {
2909061da546Spatrick // "type": "object",
2910061da546Spatrick // "properties": {
2911061da546Spatrick // "variables": {
2912061da546Spatrick // "type": "array",
2913061da546Spatrick // "items": {
2914061da546Spatrick // "$ref": "#/definitions/Variable"
2915061da546Spatrick // },
2916061da546Spatrick // "description": "All (or a range) of variables for the given
2917061da546Spatrick // variable reference."
2918061da546Spatrick // }
2919061da546Spatrick // },
2920061da546Spatrick // "required": [ "variables" ]
2921061da546Spatrick // }
2922061da546Spatrick // },
2923061da546Spatrick // "required": [ "body" ]
2924061da546Spatrick // }]
2925061da546Spatrick // }
request_variables(const llvm::json::Object & request)2926061da546Spatrick void request_variables(const llvm::json::Object &request) {
2927061da546Spatrick llvm::json::Object response;
2928061da546Spatrick FillResponse(request, response);
2929061da546Spatrick llvm::json::Array variables;
2930061da546Spatrick auto arguments = request.getObject("arguments");
2931061da546Spatrick const auto variablesReference =
2932061da546Spatrick GetUnsigned(arguments, "variablesReference", 0);
2933061da546Spatrick const int64_t start = GetSigned(arguments, "start", 0);
2934061da546Spatrick const int64_t count = GetSigned(arguments, "count", 0);
2935061da546Spatrick bool hex = false;
2936061da546Spatrick auto format = arguments->getObject("format");
2937061da546Spatrick if (format)
2938061da546Spatrick hex = GetBoolean(format, "hex", false);
2939061da546Spatrick
2940*f6aab3d8Srobert if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
2941061da546Spatrick // variablesReference is one of our scopes, not an actual variable it is
2942061da546Spatrick // asking for the list of args, locals or globals.
2943061da546Spatrick int64_t start_idx = 0;
2944061da546Spatrick int64_t num_children = 0;
2945*f6aab3d8Srobert
2946*f6aab3d8Srobert if (variablesReference == VARREF_REGS) {
2947*f6aab3d8Srobert // Change the default format of any pointer sized registers in the first
2948*f6aab3d8Srobert // register set to be the lldb::eFormatAddressInfo so we show the pointer
2949*f6aab3d8Srobert // and resolve what the pointer resolves to. Only change the format if the
2950*f6aab3d8Srobert // format was set to the default format or if it was hex as some registers
2951*f6aab3d8Srobert // have formats set for them.
2952*f6aab3d8Srobert const uint32_t addr_size = g_vsc.target.GetProcess().GetAddressByteSize();
2953*f6aab3d8Srobert lldb::SBValue reg_set = g_vsc.variables.registers.GetValueAtIndex(0);
2954*f6aab3d8Srobert const uint32_t num_regs = reg_set.GetNumChildren();
2955*f6aab3d8Srobert for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
2956*f6aab3d8Srobert lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx);
2957*f6aab3d8Srobert const lldb::Format format = reg.GetFormat();
2958*f6aab3d8Srobert if (format == lldb::eFormatDefault || format == lldb::eFormatHex) {
2959*f6aab3d8Srobert if (reg.GetByteSize() == addr_size)
2960*f6aab3d8Srobert reg.SetFormat(lldb::eFormatAddressInfo);
2961*f6aab3d8Srobert }
2962*f6aab3d8Srobert }
2963*f6aab3d8Srobert }
2964*f6aab3d8Srobert
2965*f6aab3d8Srobert num_children = top_scope->GetSize();
2966*f6aab3d8Srobert if (num_children == 0 && variablesReference == VARREF_LOCALS) {
2967*f6aab3d8Srobert // Check for an error in the SBValueList that might explain why we don't
2968*f6aab3d8Srobert // have locals. If we have an error display it as the sole value in the
2969*f6aab3d8Srobert // the locals.
2970*f6aab3d8Srobert
2971*f6aab3d8Srobert // "error" owns the error string so we must keep it alive as long as we
2972*f6aab3d8Srobert // want to use the returns "const char *"
2973*f6aab3d8Srobert lldb::SBError error = top_scope->GetError();
2974*f6aab3d8Srobert const char *var_err = error.GetCString();
2975*f6aab3d8Srobert if (var_err) {
2976*f6aab3d8Srobert // Create a fake variable named "error" to explain why variables were
2977*f6aab3d8Srobert // not available. This new error will help let users know when there was
2978*f6aab3d8Srobert // a problem that kept variables from being available for display and
2979*f6aab3d8Srobert // allow users to fix this issue instead of seeing no variables. The
2980*f6aab3d8Srobert // errors are only set when there is a problem that the user could
2981*f6aab3d8Srobert // fix, so no error will show up when you have no debug info, only when
2982*f6aab3d8Srobert // we do have debug info and something that is fixable can be done.
2983*f6aab3d8Srobert llvm::json::Object object;
2984*f6aab3d8Srobert EmplaceSafeString(object, "name", "<error>");
2985*f6aab3d8Srobert EmplaceSafeString(object, "type", "const char *");
2986*f6aab3d8Srobert EmplaceSafeString(object, "value", var_err);
2987*f6aab3d8Srobert object.try_emplace("variablesReference", (int64_t)0);
2988*f6aab3d8Srobert variables.emplace_back(std::move(object));
2989*f6aab3d8Srobert }
2990061da546Spatrick }
2991061da546Spatrick const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
2992be691f3bSpatrick
2993be691f3bSpatrick // We first find out which variable names are duplicated
2994be691f3bSpatrick std::map<std::string, int> variable_name_counts;
2995061da546Spatrick for (auto i = start_idx; i < end_idx; ++i) {
2996*f6aab3d8Srobert lldb::SBValue variable = top_scope->GetValueAtIndex(i);
2997061da546Spatrick if (!variable.IsValid())
2998061da546Spatrick break;
2999be691f3bSpatrick variable_name_counts[GetNonNullVariableName(variable)]++;
3000be691f3bSpatrick }
3001be691f3bSpatrick
3002be691f3bSpatrick // Now we construct the result with unique display variable names
3003be691f3bSpatrick for (auto i = start_idx; i < end_idx; ++i) {
3004*f6aab3d8Srobert lldb::SBValue variable = top_scope->GetValueAtIndex(i);
3005be691f3bSpatrick
3006be691f3bSpatrick if (!variable.IsValid())
3007be691f3bSpatrick break;
3008*f6aab3d8Srobert
3009*f6aab3d8Srobert int64_t var_ref = 0;
3010*f6aab3d8Srobert if (variable.MightHaveChildren()) {
3011*f6aab3d8Srobert var_ref = g_vsc.variables.InsertExpandableVariable(
3012*f6aab3d8Srobert variable, /*is_permanent=*/false);
3013*f6aab3d8Srobert }
3014*f6aab3d8Srobert variables.emplace_back(CreateVariable(
3015*f6aab3d8Srobert variable, var_ref, var_ref != 0 ? var_ref : UINT64_MAX, hex,
3016be691f3bSpatrick variable_name_counts[GetNonNullVariableName(variable)] > 1));
3017061da546Spatrick }
3018061da546Spatrick } else {
3019061da546Spatrick // We are expanding a variable that has children, so we will return its
3020061da546Spatrick // children.
3021*f6aab3d8Srobert lldb::SBValue variable = g_vsc.variables.GetVariable(variablesReference);
3022061da546Spatrick if (variable.IsValid()) {
3023061da546Spatrick const auto num_children = variable.GetNumChildren();
3024061da546Spatrick const int64_t end_idx = start + ((count == 0) ? num_children : count);
3025061da546Spatrick for (auto i = start; i < end_idx; ++i) {
3026061da546Spatrick lldb::SBValue child = variable.GetChildAtIndex(i);
3027061da546Spatrick if (!child.IsValid())
3028061da546Spatrick break;
3029061da546Spatrick if (child.MightHaveChildren()) {
3030*f6aab3d8Srobert auto is_permanent =
3031*f6aab3d8Srobert g_vsc.variables.IsPermanentVariableReference(variablesReference);
3032*f6aab3d8Srobert auto childVariablesReferences =
3033*f6aab3d8Srobert g_vsc.variables.InsertExpandableVariable(child, is_permanent);
3034*f6aab3d8Srobert variables.emplace_back(CreateVariable(child, childVariablesReferences,
3035*f6aab3d8Srobert childVariablesReferences, hex));
3036061da546Spatrick } else {
3037061da546Spatrick variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex));
3038061da546Spatrick }
3039061da546Spatrick }
3040061da546Spatrick }
3041061da546Spatrick }
3042061da546Spatrick llvm::json::Object body;
3043061da546Spatrick body.try_emplace("variables", std::move(variables));
3044061da546Spatrick response.try_emplace("body", std::move(body));
3045061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
3046061da546Spatrick }
3047061da546Spatrick
3048061da546Spatrick // A request used in testing to get the details on all breakpoints that are
3049061da546Spatrick // currently set in the target. This helps us to test "setBreakpoints" and
3050061da546Spatrick // "setFunctionBreakpoints" requests to verify we have the correct set of
3051061da546Spatrick // breakpoints currently set in LLDB.
request__testGetTargetBreakpoints(const llvm::json::Object & request)3052061da546Spatrick void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
3053061da546Spatrick llvm::json::Object response;
3054061da546Spatrick FillResponse(request, response);
3055061da546Spatrick llvm::json::Array response_breakpoints;
3056061da546Spatrick for (uint32_t i = 0; g_vsc.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
3057061da546Spatrick auto bp = g_vsc.target.GetBreakpointAtIndex(i);
3058061da546Spatrick AppendBreakpoint(bp, response_breakpoints);
3059061da546Spatrick }
3060061da546Spatrick llvm::json::Object body;
3061061da546Spatrick body.try_emplace("breakpoints", std::move(response_breakpoints));
3062061da546Spatrick response.try_emplace("body", std::move(body));
3063061da546Spatrick g_vsc.SendJSON(llvm::json::Value(std::move(response)));
3064061da546Spatrick }
3065061da546Spatrick
RegisterRequestCallbacks()3066be691f3bSpatrick void RegisterRequestCallbacks() {
3067be691f3bSpatrick g_vsc.RegisterRequestCallback("attach", request_attach);
3068be691f3bSpatrick g_vsc.RegisterRequestCallback("completions", request_completions);
3069be691f3bSpatrick g_vsc.RegisterRequestCallback("continue", request_continue);
3070be691f3bSpatrick g_vsc.RegisterRequestCallback("configurationDone", request_configurationDone);
3071be691f3bSpatrick g_vsc.RegisterRequestCallback("disconnect", request_disconnect);
3072be691f3bSpatrick g_vsc.RegisterRequestCallback("evaluate", request_evaluate);
3073be691f3bSpatrick g_vsc.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
3074be691f3bSpatrick g_vsc.RegisterRequestCallback("initialize", request_initialize);
3075be691f3bSpatrick g_vsc.RegisterRequestCallback("launch", request_launch);
3076be691f3bSpatrick g_vsc.RegisterRequestCallback("next", request_next);
3077be691f3bSpatrick g_vsc.RegisterRequestCallback("pause", request_pause);
3078be691f3bSpatrick g_vsc.RegisterRequestCallback("scopes", request_scopes);
3079be691f3bSpatrick g_vsc.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
3080be691f3bSpatrick g_vsc.RegisterRequestCallback("setExceptionBreakpoints",
3081be691f3bSpatrick request_setExceptionBreakpoints);
3082be691f3bSpatrick g_vsc.RegisterRequestCallback("setFunctionBreakpoints",
3083be691f3bSpatrick request_setFunctionBreakpoints);
3084be691f3bSpatrick g_vsc.RegisterRequestCallback("setVariable", request_setVariable);
3085be691f3bSpatrick g_vsc.RegisterRequestCallback("source", request_source);
3086be691f3bSpatrick g_vsc.RegisterRequestCallback("stackTrace", request_stackTrace);
3087be691f3bSpatrick g_vsc.RegisterRequestCallback("stepIn", request_stepIn);
3088be691f3bSpatrick g_vsc.RegisterRequestCallback("stepOut", request_stepOut);
3089be691f3bSpatrick g_vsc.RegisterRequestCallback("threads", request_threads);
3090be691f3bSpatrick g_vsc.RegisterRequestCallback("variables", request_variables);
3091be691f3bSpatrick // Custom requests
3092be691f3bSpatrick g_vsc.RegisterRequestCallback("compileUnits", request_compileUnits);
3093be691f3bSpatrick g_vsc.RegisterRequestCallback("modules", request_modules);
3094061da546Spatrick // Testing requests
3095be691f3bSpatrick g_vsc.RegisterRequestCallback("_testGetTargetBreakpoints",
3096be691f3bSpatrick request__testGetTargetBreakpoints);
3097061da546Spatrick }
3098061da546Spatrick
3099061da546Spatrick } // anonymous namespace
3100061da546Spatrick
printHelp(LLDBVSCodeOptTable & table,llvm::StringRef tool_name)3101dda28197Spatrick static void printHelp(LLDBVSCodeOptTable &table, llvm::StringRef tool_name) {
3102dda28197Spatrick std::string usage_str = tool_name.str() + " options";
3103be691f3bSpatrick table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB VSCode", false);
3104dda28197Spatrick
3105dda28197Spatrick std::string examples = R"___(
3106dda28197Spatrick EXAMPLES:
3107dda28197Spatrick The debug adapter can be started in two modes.
3108dda28197Spatrick
3109dda28197Spatrick Running lldb-vscode without any arguments will start communicating with the
3110dda28197Spatrick parent over stdio. Passing a port number causes lldb-vscode to start listening
3111dda28197Spatrick for connections on that port.
3112dda28197Spatrick
3113dda28197Spatrick lldb-vscode -p <port>
3114dda28197Spatrick
3115dda28197Spatrick Passing --wait-for-debugger will pause the process at startup and wait for a
3116dda28197Spatrick debugger to attach to the process.
3117dda28197Spatrick
3118dda28197Spatrick lldb-vscode -g
3119dda28197Spatrick )___";
3120dda28197Spatrick llvm::outs() << examples;
3121dda28197Spatrick }
3122dda28197Spatrick
3123be691f3bSpatrick // If --launch-target is provided, this instance of lldb-vscode becomes a
3124be691f3bSpatrick // runInTerminal launcher. It will ultimately launch the program specified in
3125be691f3bSpatrick // the --launch-target argument, which is the original program the user wanted
3126be691f3bSpatrick // to debug. This is done in such a way that the actual debug adaptor can
3127be691f3bSpatrick // place breakpoints at the beginning of the program.
3128be691f3bSpatrick //
3129be691f3bSpatrick // The launcher will communicate with the debug adaptor using a fifo file in the
3130be691f3bSpatrick // directory specified in the --comm-file argument.
3131be691f3bSpatrick //
3132be691f3bSpatrick // Regarding the actual flow, this launcher will first notify the debug adaptor
3133be691f3bSpatrick // of its pid. Then, the launcher will be in a pending state waiting to be
3134be691f3bSpatrick // attached by the adaptor.
3135be691f3bSpatrick //
3136be691f3bSpatrick // Once attached and resumed, the launcher will exec and become the program
3137be691f3bSpatrick // specified by --launch-target, which is the original target the
3138be691f3bSpatrick // user wanted to run.
3139be691f3bSpatrick //
3140be691f3bSpatrick // In case of errors launching the target, a suitable error message will be
3141be691f3bSpatrick // emitted to the debug adaptor.
LaunchRunInTerminalTarget(llvm::opt::Arg & target_arg,llvm::StringRef comm_file,char * argv[])3142be691f3bSpatrick void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
3143be691f3bSpatrick llvm::StringRef comm_file, char *argv[]) {
3144be691f3bSpatrick #if defined(_WIN32)
3145be691f3bSpatrick llvm::errs() << "runInTerminal is only supported on POSIX systems\n";
3146be691f3bSpatrick exit(EXIT_FAILURE);
3147be691f3bSpatrick #else
3148be691f3bSpatrick RunInTerminalLauncherCommChannel comm_channel(comm_file);
3149be691f3bSpatrick if (llvm::Error err = comm_channel.NotifyPid()) {
3150be691f3bSpatrick llvm::errs() << llvm::toString(std::move(err)) << "\n";
3151be691f3bSpatrick exit(EXIT_FAILURE);
3152be691f3bSpatrick }
3153be691f3bSpatrick
3154be691f3bSpatrick // We will wait to be attached with a timeout. We don't wait indefinitely
3155be691f3bSpatrick // using a signal to prevent being paused forever.
3156be691f3bSpatrick
3157be691f3bSpatrick // This env var should be used only for tests.
3158be691f3bSpatrick const char *timeout_env_var = getenv("LLDB_VSCODE_RIT_TIMEOUT_IN_MS");
3159be691f3bSpatrick int timeout_in_ms =
3160be691f3bSpatrick timeout_env_var != nullptr ? atoi(timeout_env_var) : 20000;
3161be691f3bSpatrick if (llvm::Error err = comm_channel.WaitUntilDebugAdaptorAttaches(
3162be691f3bSpatrick std::chrono::milliseconds(timeout_in_ms))) {
3163be691f3bSpatrick llvm::errs() << llvm::toString(std::move(err)) << "\n";
3164be691f3bSpatrick exit(EXIT_FAILURE);
3165be691f3bSpatrick }
3166be691f3bSpatrick
3167be691f3bSpatrick const char *target = target_arg.getValue();
3168be691f3bSpatrick execvp(target, argv);
3169be691f3bSpatrick
3170be691f3bSpatrick std::string error = std::strerror(errno);
3171be691f3bSpatrick comm_channel.NotifyError(error);
3172be691f3bSpatrick llvm::errs() << error << "\n";
3173be691f3bSpatrick exit(EXIT_FAILURE);
3174be691f3bSpatrick #endif
3175be691f3bSpatrick }
3176be691f3bSpatrick
3177be691f3bSpatrick /// used only by TestVSCode_redirection_to_console.py
redirection_test()3178be691f3bSpatrick void redirection_test() {
3179be691f3bSpatrick printf("stdout message\n");
3180be691f3bSpatrick fprintf(stderr, "stderr message\n");
3181be691f3bSpatrick fflush(stdout);
3182be691f3bSpatrick fflush(stderr);
3183be691f3bSpatrick }
3184be691f3bSpatrick
3185be691f3bSpatrick /// Redirect stdout and stderr fo the IDE's console output.
3186be691f3bSpatrick ///
3187be691f3bSpatrick /// Errors in this operation will be printed to the log file and the IDE's
3188be691f3bSpatrick /// console output as well.
3189be691f3bSpatrick ///
3190be691f3bSpatrick /// \return
3191be691f3bSpatrick /// A fd pointing to the original stdout.
SetupStdoutStderrRedirection()3192be691f3bSpatrick int SetupStdoutStderrRedirection() {
3193*f6aab3d8Srobert int stdoutfd = fileno(stdout);
3194*f6aab3d8Srobert int new_stdout_fd = dup(stdoutfd);
3195*f6aab3d8Srobert auto output_callback_stderr = [](llvm::StringRef data) {
3196*f6aab3d8Srobert g_vsc.SendOutput(OutputType::Stderr, data);
3197be691f3bSpatrick };
3198*f6aab3d8Srobert auto output_callback_stdout = [](llvm::StringRef data) {
3199*f6aab3d8Srobert g_vsc.SendOutput(OutputType::Stdout, data);
3200*f6aab3d8Srobert };
3201*f6aab3d8Srobert if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) {
3202be691f3bSpatrick std::string error_message = llvm::toString(std::move(err));
3203be691f3bSpatrick if (g_vsc.log)
3204be691f3bSpatrick *g_vsc.log << error_message << std::endl;
3205*f6aab3d8Srobert output_callback_stderr(error_message);
3206be691f3bSpatrick }
3207*f6aab3d8Srobert if (llvm::Error err = RedirectFd(fileno(stderr), output_callback_stderr)) {
3208*f6aab3d8Srobert std::string error_message = llvm::toString(std::move(err));
3209*f6aab3d8Srobert if (g_vsc.log)
3210*f6aab3d8Srobert *g_vsc.log << error_message << std::endl;
3211*f6aab3d8Srobert output_callback_stderr(error_message);
3212be691f3bSpatrick }
3213be691f3bSpatrick
3214be691f3bSpatrick /// used only by TestVSCode_redirection_to_console.py
3215be691f3bSpatrick if (getenv("LLDB_VSCODE_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
3216be691f3bSpatrick redirection_test();
3217be691f3bSpatrick return new_stdout_fd;
3218be691f3bSpatrick }
3219be691f3bSpatrick
main(int argc,char * argv[])3220061da546Spatrick int main(int argc, char *argv[]) {
3221be691f3bSpatrick llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
3222be691f3bSpatrick llvm::PrettyStackTraceProgram X(argc, argv);
3223061da546Spatrick
3224be691f3bSpatrick llvm::SmallString<256> program_path(argv[0]);
3225be691f3bSpatrick llvm::sys::fs::make_absolute(program_path);
3226be691f3bSpatrick g_vsc.debug_adaptor_path = program_path.str().str();
3227dda28197Spatrick
3228dda28197Spatrick LLDBVSCodeOptTable T;
3229dda28197Spatrick unsigned MAI, MAC;
3230*f6aab3d8Srobert llvm::ArrayRef<const char *> ArgsArr = llvm::ArrayRef(argv + 1, argc);
3231dda28197Spatrick llvm::opt::InputArgList input_args = T.ParseArgs(ArgsArr, MAI, MAC);
3232dda28197Spatrick
3233dda28197Spatrick if (input_args.hasArg(OPT_help)) {
3234dda28197Spatrick printHelp(T, llvm::sys::path::filename(argv[0]));
3235be691f3bSpatrick return EXIT_SUCCESS;
3236dda28197Spatrick }
3237dda28197Spatrick
3238be691f3bSpatrick if (llvm::opt::Arg *target_arg = input_args.getLastArg(OPT_launch_target)) {
3239be691f3bSpatrick if (llvm::opt::Arg *comm_file = input_args.getLastArg(OPT_comm_file)) {
3240be691f3bSpatrick int target_args_pos = argc;
3241be691f3bSpatrick for (int i = 0; i < argc; i++)
3242be691f3bSpatrick if (strcmp(argv[i], "--launch-target") == 0) {
3243be691f3bSpatrick target_args_pos = i + 1;
3244be691f3bSpatrick break;
3245be691f3bSpatrick }
3246be691f3bSpatrick LaunchRunInTerminalTarget(*target_arg, comm_file->getValue(),
3247be691f3bSpatrick argv + target_args_pos);
3248be691f3bSpatrick } else {
3249be691f3bSpatrick llvm::errs() << "\"--launch-target\" requires \"--comm-file\" to be "
3250be691f3bSpatrick "specified\n";
3251be691f3bSpatrick return EXIT_FAILURE;
3252be691f3bSpatrick }
3253be691f3bSpatrick }
3254be691f3bSpatrick
3255be691f3bSpatrick // stdout/stderr redirection to the IDE's console
3256be691f3bSpatrick int new_stdout_fd = SetupStdoutStderrRedirection();
3257be691f3bSpatrick
3258be691f3bSpatrick // Initialize LLDB first before we do anything.
3259be691f3bSpatrick lldb::SBDebugger::Initialize();
3260be691f3bSpatrick
3261be691f3bSpatrick // Terminate the debugger before the C++ destructor chain kicks in.
3262be691f3bSpatrick auto terminate_debugger =
3263be691f3bSpatrick llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
3264be691f3bSpatrick
3265be691f3bSpatrick RegisterRequestCallbacks();
3266be691f3bSpatrick
3267be691f3bSpatrick int portno = -1;
3268be691f3bSpatrick
3269dda28197Spatrick if (auto *arg = input_args.getLastArg(OPT_port)) {
3270dda28197Spatrick auto optarg = arg->getValue();
3271dda28197Spatrick char *remainder;
3272dda28197Spatrick portno = strtol(optarg, &remainder, 0);
3273dda28197Spatrick if (remainder == optarg || *remainder != '\0') {
3274dda28197Spatrick fprintf(stderr, "'%s' is not a valid port number.\n", optarg);
3275be691f3bSpatrick return EXIT_FAILURE;
3276dda28197Spatrick }
3277dda28197Spatrick }
3278dda28197Spatrick
3279061da546Spatrick #if !defined(_WIN32)
3280dda28197Spatrick if (input_args.hasArg(OPT_wait_for_debugger)) {
3281061da546Spatrick printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());
3282061da546Spatrick pause();
3283dda28197Spatrick }
3284061da546Spatrick #endif
3285dda28197Spatrick if (portno != -1) {
3286061da546Spatrick printf("Listening on port %i...\n", portno);
3287061da546Spatrick SOCKET socket_fd = AcceptConnection(portno);
3288061da546Spatrick if (socket_fd >= 0) {
3289061da546Spatrick g_vsc.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
3290dda28197Spatrick g_vsc.output.descriptor = StreamDescriptor::from_socket(socket_fd, false);
3291061da546Spatrick } else {
3292be691f3bSpatrick return EXIT_FAILURE;
3293061da546Spatrick }
3294061da546Spatrick } else {
3295061da546Spatrick g_vsc.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
3296be691f3bSpatrick g_vsc.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
3297061da546Spatrick }
3298be691f3bSpatrick
3299dda28197Spatrick while (!g_vsc.sent_terminated_event) {
3300be691f3bSpatrick llvm::json::Object object;
3301be691f3bSpatrick lldb_vscode::PacketStatus status = g_vsc.GetNextObject(object);
3302be691f3bSpatrick if (status == lldb_vscode::PacketStatus::EndOfFile)
3303061da546Spatrick break;
3304be691f3bSpatrick if (status != lldb_vscode::PacketStatus::Success)
3305be691f3bSpatrick return 1; // Fatal error
3306061da546Spatrick
3307be691f3bSpatrick if (!g_vsc.HandleObject(object))
3308061da546Spatrick return 1;
3309061da546Spatrick }
3310061da546Spatrick
3311be691f3bSpatrick return EXIT_SUCCESS;
3312061da546Spatrick }
3313