xref: /openbsd-src/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- 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 <chrono>
10be691f3bSpatrick #include <cstdarg>
11061da546Spatrick #include <fstream>
12061da546Spatrick #include <mutex>
13be691f3bSpatrick #include <sstream>
14061da546Spatrick 
15061da546Spatrick #include "LLDBUtils.h"
16061da546Spatrick #include "VSCode.h"
17061da546Spatrick #include "llvm/Support/FormatVariadic.h"
18061da546Spatrick 
19061da546Spatrick #if defined(_WIN32)
20061da546Spatrick #define NOMINMAX
21061da546Spatrick #include <fcntl.h>
22061da546Spatrick #include <io.h>
23be691f3bSpatrick #include <windows.h>
24061da546Spatrick #endif
25061da546Spatrick 
26061da546Spatrick using namespace lldb_vscode;
27061da546Spatrick 
28061da546Spatrick namespace lldb_vscode {
29061da546Spatrick 
30061da546Spatrick VSCode g_vsc;
31061da546Spatrick 
VSCode()32061da546Spatrick VSCode::VSCode()
33*f6aab3d8Srobert     : broadcaster("lldb-vscode"),
34061da546Spatrick       exception_breakpoints(
35061da546Spatrick           {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
36061da546Spatrick            {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
37*f6aab3d8Srobert            {"objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC},
38*f6aab3d8Srobert            {"objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC},
39061da546Spatrick            {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
40061da546Spatrick            {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
41061da546Spatrick       focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
42*f6aab3d8Srobert       stop_at_entry(false), is_attach(false), configuration_done_sent(false),
43*f6aab3d8Srobert       reverse_request_seq(0), waiting_for_run_in_terminal(false),
44be691f3bSpatrick       progress_event_reporter(
__anon5742f8c60102(const ProgressEvent &event) 45be691f3bSpatrick           [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }) {
46061da546Spatrick   const char *log_file_path = getenv("LLDBVSCODE_LOG");
47061da546Spatrick #if defined(_WIN32)
48061da546Spatrick   // Windows opens stdout and stdin in text mode which converts \n to 13,10
49061da546Spatrick   // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
50061da546Spatrick   // fixes this.
51061da546Spatrick   int result = _setmode(fileno(stdout), _O_BINARY);
52061da546Spatrick   assert(result);
53061da546Spatrick   result = _setmode(fileno(stdin), _O_BINARY);
54061da546Spatrick   (void)result;
55061da546Spatrick   assert(result);
56061da546Spatrick #endif
57061da546Spatrick   if (log_file_path)
58061da546Spatrick     log.reset(new std::ofstream(log_file_path));
59061da546Spatrick }
60061da546Spatrick 
61*f6aab3d8Srobert VSCode::~VSCode() = default;
62061da546Spatrick 
GetLineForPC(int64_t sourceReference,lldb::addr_t pc) const63061da546Spatrick int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
64061da546Spatrick   auto pos = source_map.find(sourceReference);
65061da546Spatrick   if (pos != source_map.end())
66061da546Spatrick     return pos->second.GetLineForPC(pc);
67061da546Spatrick   return 0;
68061da546Spatrick }
69061da546Spatrick 
GetExceptionBreakpoint(const std::string & filter)70061da546Spatrick ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) {
71061da546Spatrick   for (auto &bp : exception_breakpoints) {
72061da546Spatrick     if (bp.filter == filter)
73061da546Spatrick       return &bp;
74061da546Spatrick   }
75061da546Spatrick   return nullptr;
76061da546Spatrick }
77061da546Spatrick 
78061da546Spatrick ExceptionBreakpoint *
GetExceptionBreakpoint(const lldb::break_id_t bp_id)79061da546Spatrick VSCode::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
80061da546Spatrick   for (auto &bp : exception_breakpoints) {
81061da546Spatrick     if (bp.bp.GetID() == bp_id)
82061da546Spatrick       return &bp;
83061da546Spatrick   }
84061da546Spatrick   return nullptr;
85061da546Spatrick }
86061da546Spatrick 
87061da546Spatrick // Send the JSON in "json_str" to the "out" stream. Correctly send the
88061da546Spatrick // "Content-Length:" field followed by the length, followed by the raw
89061da546Spatrick // JSON bytes.
SendJSON(const std::string & json_str)90061da546Spatrick void VSCode::SendJSON(const std::string &json_str) {
91061da546Spatrick   output.write_full("Content-Length: ");
92061da546Spatrick   output.write_full(llvm::utostr(json_str.size()));
93061da546Spatrick   output.write_full("\r\n\r\n");
94061da546Spatrick   output.write_full(json_str);
95061da546Spatrick 
96061da546Spatrick   if (log) {
97061da546Spatrick     *log << "<-- " << std::endl
98061da546Spatrick          << "Content-Length: " << json_str.size() << "\r\n\r\n"
99061da546Spatrick          << json_str << std::endl;
100061da546Spatrick   }
101061da546Spatrick }
102061da546Spatrick 
103061da546Spatrick // Serialize the JSON value into a string and send the JSON packet to
104061da546Spatrick // the "out" stream.
SendJSON(const llvm::json::Value & json)105061da546Spatrick void VSCode::SendJSON(const llvm::json::Value &json) {
106061da546Spatrick   std::string s;
107061da546Spatrick   llvm::raw_string_ostream strm(s);
108061da546Spatrick   strm << json;
109061da546Spatrick   static std::mutex mutex;
110061da546Spatrick   std::lock_guard<std::mutex> locker(mutex);
111061da546Spatrick   SendJSON(strm.str());
112061da546Spatrick }
113061da546Spatrick 
114061da546Spatrick // Read a JSON packet from the "in" stream.
ReadJSON()115061da546Spatrick std::string VSCode::ReadJSON() {
116061da546Spatrick   std::string length_str;
117061da546Spatrick   std::string json_str;
118061da546Spatrick   int length;
119061da546Spatrick 
120061da546Spatrick   if (!input.read_expected(log.get(), "Content-Length: "))
121061da546Spatrick     return json_str;
122061da546Spatrick 
123061da546Spatrick   if (!input.read_line(log.get(), length_str))
124061da546Spatrick     return json_str;
125061da546Spatrick 
126061da546Spatrick   if (!llvm::to_integer(length_str, length))
127061da546Spatrick     return json_str;
128061da546Spatrick 
129061da546Spatrick   if (!input.read_expected(log.get(), "\r\n"))
130061da546Spatrick     return json_str;
131061da546Spatrick 
132061da546Spatrick   if (!input.read_full(log.get(), length, json_str))
133061da546Spatrick     return json_str;
134061da546Spatrick 
135dda28197Spatrick   if (log) {
136dda28197Spatrick     *log << "--> " << std::endl
137dda28197Spatrick          << "Content-Length: " << length << "\r\n\r\n"
138dda28197Spatrick          << json_str << std::endl;
139dda28197Spatrick   }
140dda28197Spatrick 
141061da546Spatrick   return json_str;
142061da546Spatrick }
143061da546Spatrick 
144061da546Spatrick // "OutputEvent": {
145061da546Spatrick //   "allOf": [ { "$ref": "#/definitions/Event" }, {
146061da546Spatrick //     "type": "object",
147061da546Spatrick //     "description": "Event message for 'output' event type. The event
148061da546Spatrick //                     indicates that the target has produced some output.",
149061da546Spatrick //     "properties": {
150061da546Spatrick //       "event": {
151061da546Spatrick //         "type": "string",
152061da546Spatrick //         "enum": [ "output" ]
153061da546Spatrick //       },
154061da546Spatrick //       "body": {
155061da546Spatrick //         "type": "object",
156061da546Spatrick //         "properties": {
157061da546Spatrick //           "category": {
158061da546Spatrick //             "type": "string",
159061da546Spatrick //             "description": "The output category. If not specified,
160061da546Spatrick //                             'console' is assumed.",
161061da546Spatrick //             "_enum": [ "console", "stdout", "stderr", "telemetry" ]
162061da546Spatrick //           },
163061da546Spatrick //           "output": {
164061da546Spatrick //             "type": "string",
165061da546Spatrick //             "description": "The output to report."
166061da546Spatrick //           },
167061da546Spatrick //           "variablesReference": {
168061da546Spatrick //             "type": "number",
169061da546Spatrick //             "description": "If an attribute 'variablesReference' exists
170061da546Spatrick //                             and its value is > 0, the output contains
171061da546Spatrick //                             objects which can be retrieved by passing
172061da546Spatrick //                             variablesReference to the VariablesRequest."
173061da546Spatrick //           },
174061da546Spatrick //           "source": {
175061da546Spatrick //             "$ref": "#/definitions/Source",
176061da546Spatrick //             "description": "An optional source location where the output
177061da546Spatrick //                             was produced."
178061da546Spatrick //           },
179061da546Spatrick //           "line": {
180061da546Spatrick //             "type": "integer",
181061da546Spatrick //             "description": "An optional source location line where the
182061da546Spatrick //                             output was produced."
183061da546Spatrick //           },
184061da546Spatrick //           "column": {
185061da546Spatrick //             "type": "integer",
186061da546Spatrick //             "description": "An optional source location column where the
187061da546Spatrick //                             output was produced."
188061da546Spatrick //           },
189061da546Spatrick //           "data": {
190061da546Spatrick //             "type":["array","boolean","integer","null","number","object",
191061da546Spatrick //                     "string"],
192061da546Spatrick //             "description": "Optional data to report. For the 'telemetry'
193061da546Spatrick //                             category the data will be sent to telemetry, for
194061da546Spatrick //                             the other categories the data is shown in JSON
195061da546Spatrick //                             format."
196061da546Spatrick //           }
197061da546Spatrick //         },
198061da546Spatrick //         "required": ["output"]
199061da546Spatrick //       }
200061da546Spatrick //     },
201061da546Spatrick //     "required": [ "event", "body" ]
202061da546Spatrick //   }]
203061da546Spatrick // }
SendOutput(OutputType o,const llvm::StringRef output)204061da546Spatrick void VSCode::SendOutput(OutputType o, const llvm::StringRef output) {
205061da546Spatrick   if (output.empty())
206061da546Spatrick     return;
207061da546Spatrick 
208061da546Spatrick   llvm::json::Object event(CreateEventObject("output"));
209061da546Spatrick   llvm::json::Object body;
210061da546Spatrick   const char *category = nullptr;
211061da546Spatrick   switch (o) {
212061da546Spatrick   case OutputType::Console:
213061da546Spatrick     category = "console";
214061da546Spatrick     break;
215061da546Spatrick   case OutputType::Stdout:
216061da546Spatrick     category = "stdout";
217061da546Spatrick     break;
218061da546Spatrick   case OutputType::Stderr:
219061da546Spatrick     category = "stderr";
220061da546Spatrick     break;
221061da546Spatrick   case OutputType::Telemetry:
222061da546Spatrick     category = "telemetry";
223061da546Spatrick     break;
224061da546Spatrick   }
225061da546Spatrick   body.try_emplace("category", category);
226061da546Spatrick   EmplaceSafeString(body, "output", output.str());
227061da546Spatrick   event.try_emplace("body", std::move(body));
228061da546Spatrick   SendJSON(llvm::json::Value(std::move(event)));
229061da546Spatrick }
230061da546Spatrick 
231be691f3bSpatrick // interface ProgressStartEvent extends Event {
232be691f3bSpatrick //   event: 'progressStart';
233be691f3bSpatrick //
234be691f3bSpatrick //   body: {
235be691f3bSpatrick //     /**
236be691f3bSpatrick //      * An ID that must be used in subsequent 'progressUpdate' and
237be691f3bSpatrick //      'progressEnd'
238be691f3bSpatrick //      * events to make them refer to the same progress reporting.
239be691f3bSpatrick //      * IDs must be unique within a debug session.
240be691f3bSpatrick //      */
241be691f3bSpatrick //     progressId: string;
242be691f3bSpatrick //
243be691f3bSpatrick //     /**
244be691f3bSpatrick //      * Mandatory (short) title of the progress reporting. Shown in the UI to
245be691f3bSpatrick //      * describe the long running operation.
246be691f3bSpatrick //      */
247be691f3bSpatrick //     title: string;
248be691f3bSpatrick //
249be691f3bSpatrick //     /**
250be691f3bSpatrick //      * The request ID that this progress report is related to. If specified a
251be691f3bSpatrick //      * debug adapter is expected to emit
252be691f3bSpatrick //      * progress events for the long running request until the request has
253be691f3bSpatrick //      been
254be691f3bSpatrick //      * either completed or cancelled.
255be691f3bSpatrick //      * If the request ID is omitted, the progress report is assumed to be
256be691f3bSpatrick //      * related to some general activity of the debug adapter.
257be691f3bSpatrick //      */
258be691f3bSpatrick //     requestId?: number;
259be691f3bSpatrick //
260be691f3bSpatrick //     /**
261be691f3bSpatrick //      * If true, the request that reports progress may be canceled with a
262be691f3bSpatrick //      * 'cancel' request.
263be691f3bSpatrick //      * So this property basically controls whether the client should use UX
264be691f3bSpatrick //      that
265be691f3bSpatrick //      * supports cancellation.
266be691f3bSpatrick //      * Clients that don't support cancellation are allowed to ignore the
267be691f3bSpatrick //      * setting.
268be691f3bSpatrick //      */
269be691f3bSpatrick //     cancellable?: boolean;
270be691f3bSpatrick //
271be691f3bSpatrick //     /**
272be691f3bSpatrick //      * Optional, more detailed progress message.
273be691f3bSpatrick //      */
274be691f3bSpatrick //     message?: string;
275be691f3bSpatrick //
276be691f3bSpatrick //     /**
277be691f3bSpatrick //      * Optional progress percentage to display (value range: 0 to 100). If
278be691f3bSpatrick //      * omitted no percentage will be shown.
279be691f3bSpatrick //      */
280be691f3bSpatrick //     percentage?: number;
281be691f3bSpatrick //   };
282be691f3bSpatrick // }
283be691f3bSpatrick //
284be691f3bSpatrick // interface ProgressUpdateEvent extends Event {
285be691f3bSpatrick //   event: 'progressUpdate';
286be691f3bSpatrick //
287be691f3bSpatrick //   body: {
288be691f3bSpatrick //     /**
289be691f3bSpatrick //      * The ID that was introduced in the initial 'progressStart' event.
290be691f3bSpatrick //      */
291be691f3bSpatrick //     progressId: string;
292be691f3bSpatrick //
293be691f3bSpatrick //     /**
294be691f3bSpatrick //      * Optional, more detailed progress message. If omitted, the previous
295be691f3bSpatrick //      * message (if any) is used.
296be691f3bSpatrick //      */
297be691f3bSpatrick //     message?: string;
298be691f3bSpatrick //
299be691f3bSpatrick //     /**
300be691f3bSpatrick //      * Optional progress percentage to display (value range: 0 to 100). If
301be691f3bSpatrick //      * omitted no percentage will be shown.
302be691f3bSpatrick //      */
303be691f3bSpatrick //     percentage?: number;
304be691f3bSpatrick //   };
305be691f3bSpatrick // }
306be691f3bSpatrick //
307be691f3bSpatrick // interface ProgressEndEvent extends Event {
308be691f3bSpatrick //   event: 'progressEnd';
309be691f3bSpatrick //
310be691f3bSpatrick //   body: {
311be691f3bSpatrick //     /**
312be691f3bSpatrick //      * The ID that was introduced in the initial 'ProgressStartEvent'.
313be691f3bSpatrick //      */
314be691f3bSpatrick //     progressId: string;
315be691f3bSpatrick //
316be691f3bSpatrick //     /**
317be691f3bSpatrick //      * Optional, more detailed progress message. If omitted, the previous
318be691f3bSpatrick //      * message (if any) is used.
319be691f3bSpatrick //      */
320be691f3bSpatrick //     message?: string;
321be691f3bSpatrick //   };
322be691f3bSpatrick // }
323be691f3bSpatrick 
SendProgressEvent(uint64_t progress_id,const char * message,uint64_t completed,uint64_t total)324be691f3bSpatrick void VSCode::SendProgressEvent(uint64_t progress_id, const char *message,
325be691f3bSpatrick                                uint64_t completed, uint64_t total) {
326be691f3bSpatrick   progress_event_reporter.Push(progress_id, message, completed, total);
327be691f3bSpatrick }
328be691f3bSpatrick 
329061da546Spatrick void __attribute__((format(printf, 3, 4)))
SendFormattedOutput(OutputType o,const char * format,...)330061da546Spatrick VSCode::SendFormattedOutput(OutputType o, const char *format, ...) {
331061da546Spatrick   char buffer[1024];
332061da546Spatrick   va_list args;
333061da546Spatrick   va_start(args, format);
334061da546Spatrick   int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
335061da546Spatrick   va_end(args);
336be691f3bSpatrick   SendOutput(
337be691f3bSpatrick       o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
338061da546Spatrick }
339061da546Spatrick 
GetNextSourceReference()340061da546Spatrick int64_t VSCode::GetNextSourceReference() {
341061da546Spatrick   static int64_t ref = 0;
342061da546Spatrick   return ++ref;
343061da546Spatrick }
344061da546Spatrick 
345061da546Spatrick ExceptionBreakpoint *
GetExceptionBPFromStopReason(lldb::SBThread & thread)346061da546Spatrick VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
347061da546Spatrick   const auto num = thread.GetStopReasonDataCount();
348061da546Spatrick   // Check to see if have hit an exception breakpoint and change the
349061da546Spatrick   // reason to "exception", but only do so if all breakpoints that were
350061da546Spatrick   // hit are exception breakpoints.
351061da546Spatrick   ExceptionBreakpoint *exc_bp = nullptr;
352061da546Spatrick   for (size_t i = 0; i < num; i += 2) {
353061da546Spatrick     // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
354061da546Spatrick     // thread.GetStopReasonDataAtIndex(i+1) will return the location
355061da546Spatrick     // within that breakpoint. We only care about the bp ID so we can
356061da546Spatrick     // see if this is an exception breakpoint that is getting hit.
357061da546Spatrick     lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
358061da546Spatrick     exc_bp = GetExceptionBreakpoint(bp_id);
359061da546Spatrick     // If any breakpoint is not an exception breakpoint, then stop and
360061da546Spatrick     // report this as a normal breakpoint
361061da546Spatrick     if (exc_bp == nullptr)
362061da546Spatrick       return nullptr;
363061da546Spatrick   }
364061da546Spatrick   return exc_bp;
365061da546Spatrick }
366061da546Spatrick 
GetLLDBThread(const llvm::json::Object & arguments)367061da546Spatrick lldb::SBThread VSCode::GetLLDBThread(const llvm::json::Object &arguments) {
368061da546Spatrick   auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
369061da546Spatrick   return target.GetProcess().GetThreadByID(tid);
370061da546Spatrick }
371061da546Spatrick 
GetLLDBFrame(const llvm::json::Object & arguments)372061da546Spatrick lldb::SBFrame VSCode::GetLLDBFrame(const llvm::json::Object &arguments) {
373061da546Spatrick   const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
374061da546Spatrick   lldb::SBProcess process = target.GetProcess();
375061da546Spatrick   // Upper 32 bits is the thread index ID
376061da546Spatrick   lldb::SBThread thread =
377061da546Spatrick       process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
378061da546Spatrick   // Lower 32 bits is the frame index
379061da546Spatrick   return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
380061da546Spatrick }
381061da546Spatrick 
CreateTopLevelScopes()382061da546Spatrick llvm::json::Value VSCode::CreateTopLevelScopes() {
383061da546Spatrick   llvm::json::Array scopes;
384*f6aab3d8Srobert   scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS,
385*f6aab3d8Srobert                                   g_vsc.variables.locals.GetSize(), false));
386*f6aab3d8Srobert   scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
387*f6aab3d8Srobert                                   g_vsc.variables.globals.GetSize(), false));
388*f6aab3d8Srobert   scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
389*f6aab3d8Srobert                                   g_vsc.variables.registers.GetSize(), false));
390061da546Spatrick   return llvm::json::Value(std::move(scopes));
391061da546Spatrick }
392061da546Spatrick 
RunLLDBCommands(llvm::StringRef prefix,const std::vector<std::string> & commands)393061da546Spatrick void VSCode::RunLLDBCommands(llvm::StringRef prefix,
394061da546Spatrick                              const std::vector<std::string> &commands) {
395061da546Spatrick   SendOutput(OutputType::Console,
396061da546Spatrick              llvm::StringRef(::RunLLDBCommands(prefix, commands)));
397061da546Spatrick }
398061da546Spatrick 
RunInitCommands()399061da546Spatrick void VSCode::RunInitCommands() {
400061da546Spatrick   RunLLDBCommands("Running initCommands:", init_commands);
401061da546Spatrick }
402061da546Spatrick 
RunPreRunCommands()403061da546Spatrick void VSCode::RunPreRunCommands() {
404061da546Spatrick   RunLLDBCommands("Running preRunCommands:", pre_run_commands);
405061da546Spatrick }
406061da546Spatrick 
RunStopCommands()407061da546Spatrick void VSCode::RunStopCommands() {
408061da546Spatrick   RunLLDBCommands("Running stopCommands:", stop_commands);
409061da546Spatrick }
410061da546Spatrick 
RunExitCommands()411061da546Spatrick void VSCode::RunExitCommands() {
412061da546Spatrick   RunLLDBCommands("Running exitCommands:", exit_commands);
413061da546Spatrick }
414061da546Spatrick 
RunTerminateCommands()415dda28197Spatrick void VSCode::RunTerminateCommands() {
416dda28197Spatrick   RunLLDBCommands("Running terminateCommands:", terminate_commands);
417dda28197Spatrick }
418dda28197Spatrick 
419be691f3bSpatrick lldb::SBTarget
CreateTargetFromArguments(const llvm::json::Object & arguments,lldb::SBError & error)420be691f3bSpatrick VSCode::CreateTargetFromArguments(const llvm::json::Object &arguments,
421dda28197Spatrick                                   lldb::SBError &error) {
422dda28197Spatrick   // Grab the name of the program we need to debug and create a target using
423dda28197Spatrick   // the given program as an argument. Executable file can be a source of target
424dda28197Spatrick   // architecture and platform, if they differ from the host. Setting exe path
425dda28197Spatrick   // in launch info is useless because Target.Launch() will not change
426dda28197Spatrick   // architecture and platform, therefore they should be known at the target
427dda28197Spatrick   // creation. We also use target triple and platform from the launch
428dda28197Spatrick   // configuration, if given, since in some cases ELF file doesn't contain
429dda28197Spatrick   // enough information to determine correct arch and platform (or ELF can be
430dda28197Spatrick   // omitted at all), so it is good to leave the user an apportunity to specify
431dda28197Spatrick   // those. Any of those three can be left empty.
432dda28197Spatrick   llvm::StringRef target_triple = GetString(arguments, "targetTriple");
433dda28197Spatrick   llvm::StringRef platform_name = GetString(arguments, "platformName");
434dda28197Spatrick   llvm::StringRef program = GetString(arguments, "program");
435dda28197Spatrick   auto target = this->debugger.CreateTarget(
436be691f3bSpatrick       program.data(), target_triple.data(), platform_name.data(),
437dda28197Spatrick       true, // Add dependent modules.
438be691f3bSpatrick       error);
439dda28197Spatrick 
440dda28197Spatrick   if (error.Fail()) {
441dda28197Spatrick     // Update message if there was an error.
442dda28197Spatrick     error.SetErrorStringWithFormat(
443be691f3bSpatrick         "Could not create a target for a program '%s': %s.", program.data(),
444be691f3bSpatrick         error.GetCString());
445dda28197Spatrick   }
446dda28197Spatrick 
447dda28197Spatrick   return target;
448dda28197Spatrick }
449dda28197Spatrick 
SetTarget(const lldb::SBTarget target)450dda28197Spatrick void VSCode::SetTarget(const lldb::SBTarget target) {
451dda28197Spatrick   this->target = target;
452dda28197Spatrick 
453dda28197Spatrick   if (target.IsValid()) {
454dda28197Spatrick     // Configure breakpoint event listeners for the target.
455dda28197Spatrick     lldb::SBListener listener = this->debugger.GetListener();
456dda28197Spatrick     listener.StartListeningForEvents(
457dda28197Spatrick         this->target.GetBroadcaster(),
458dda28197Spatrick         lldb::SBTarget::eBroadcastBitBreakpointChanged);
459dda28197Spatrick     listener.StartListeningForEvents(this->broadcaster,
460dda28197Spatrick                                      eBroadcastBitStopEventThread);
461dda28197Spatrick   }
462dda28197Spatrick }
463dda28197Spatrick 
GetNextObject(llvm::json::Object & object)464be691f3bSpatrick PacketStatus VSCode::GetNextObject(llvm::json::Object &object) {
465be691f3bSpatrick   std::string json = ReadJSON();
466be691f3bSpatrick   if (json.empty())
467be691f3bSpatrick     return PacketStatus::EndOfFile;
468be691f3bSpatrick 
469be691f3bSpatrick   llvm::StringRef json_sref(json);
470be691f3bSpatrick   llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
471be691f3bSpatrick   if (!json_value) {
472be691f3bSpatrick     auto error = json_value.takeError();
473be691f3bSpatrick     if (log) {
474be691f3bSpatrick       std::string error_str;
475be691f3bSpatrick       llvm::raw_string_ostream strm(error_str);
476be691f3bSpatrick       strm << error;
477be691f3bSpatrick       strm.flush();
478be691f3bSpatrick       *log << "error: failed to parse JSON: " << error_str << std::endl
479be691f3bSpatrick            << json << std::endl;
480be691f3bSpatrick     }
481be691f3bSpatrick     return PacketStatus::JSONMalformed;
482be691f3bSpatrick   }
483be691f3bSpatrick   object = *json_value->getAsObject();
484be691f3bSpatrick   if (!json_value->getAsObject()) {
485be691f3bSpatrick     if (log)
486be691f3bSpatrick       *log << "error: json packet isn't a object" << std::endl;
487be691f3bSpatrick     return PacketStatus::JSONNotObject;
488be691f3bSpatrick   }
489be691f3bSpatrick   return PacketStatus::Success;
490be691f3bSpatrick }
491be691f3bSpatrick 
HandleObject(const llvm::json::Object & object)492be691f3bSpatrick bool VSCode::HandleObject(const llvm::json::Object &object) {
493be691f3bSpatrick   const auto packet_type = GetString(object, "type");
494be691f3bSpatrick   if (packet_type == "request") {
495be691f3bSpatrick     const auto command = GetString(object, "command");
496be691f3bSpatrick     auto handler_pos = request_handlers.find(std::string(command));
497be691f3bSpatrick     if (handler_pos != request_handlers.end()) {
498be691f3bSpatrick       handler_pos->second(object);
499be691f3bSpatrick       return true; // Success
500be691f3bSpatrick     } else {
501be691f3bSpatrick       if (log)
502be691f3bSpatrick         *log << "error: unhandled command \"" << command.data() << std::endl;
503be691f3bSpatrick       return false; // Fail
504be691f3bSpatrick     }
505be691f3bSpatrick   }
506be691f3bSpatrick   return false;
507be691f3bSpatrick }
508be691f3bSpatrick 
SendReverseRequest(llvm::json::Object request,llvm::json::Object & response)509be691f3bSpatrick PacketStatus VSCode::SendReverseRequest(llvm::json::Object request,
510be691f3bSpatrick                                         llvm::json::Object &response) {
511be691f3bSpatrick   request.try_emplace("seq", ++reverse_request_seq);
512be691f3bSpatrick   SendJSON(llvm::json::Value(std::move(request)));
513be691f3bSpatrick   while (true) {
514be691f3bSpatrick     PacketStatus status = GetNextObject(response);
515be691f3bSpatrick     const auto packet_type = GetString(response, "type");
516be691f3bSpatrick     if (packet_type == "response")
517be691f3bSpatrick       return status;
518be691f3bSpatrick     else {
519be691f3bSpatrick       // Not our response, we got another packet
520be691f3bSpatrick       HandleObject(response);
521be691f3bSpatrick     }
522be691f3bSpatrick   }
523be691f3bSpatrick   return PacketStatus::EndOfFile;
524be691f3bSpatrick }
525be691f3bSpatrick 
RegisterRequestCallback(std::string request,RequestCallback callback)526be691f3bSpatrick void VSCode::RegisterRequestCallback(std::string request,
527be691f3bSpatrick                                      RequestCallback callback) {
528be691f3bSpatrick   request_handlers[request] = callback;
529be691f3bSpatrick }
530be691f3bSpatrick 
WaitForProcessToStop(uint32_t seconds)531*f6aab3d8Srobert lldb::SBError VSCode::WaitForProcessToStop(uint32_t seconds) {
532*f6aab3d8Srobert   lldb::SBError error;
533*f6aab3d8Srobert   lldb::SBProcess process = target.GetProcess();
534*f6aab3d8Srobert   if (!process.IsValid()) {
535*f6aab3d8Srobert     error.SetErrorString("invalid process");
536*f6aab3d8Srobert     return error;
537*f6aab3d8Srobert   }
538*f6aab3d8Srobert   auto timeout_time =
539*f6aab3d8Srobert       std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
540*f6aab3d8Srobert   while (std::chrono::steady_clock::now() < timeout_time) {
541*f6aab3d8Srobert     const auto state = process.GetState();
542*f6aab3d8Srobert     switch (state) {
543*f6aab3d8Srobert       case lldb::eStateAttaching:
544*f6aab3d8Srobert       case lldb::eStateConnected:
545*f6aab3d8Srobert       case lldb::eStateInvalid:
546*f6aab3d8Srobert       case lldb::eStateLaunching:
547*f6aab3d8Srobert       case lldb::eStateRunning:
548*f6aab3d8Srobert       case lldb::eStateStepping:
549*f6aab3d8Srobert       case lldb::eStateSuspended:
550*f6aab3d8Srobert         break;
551*f6aab3d8Srobert       case lldb::eStateDetached:
552*f6aab3d8Srobert         error.SetErrorString("process detached during launch or attach");
553*f6aab3d8Srobert         return error;
554*f6aab3d8Srobert       case lldb::eStateExited:
555*f6aab3d8Srobert         error.SetErrorString("process exited during launch or attach");
556*f6aab3d8Srobert         return error;
557*f6aab3d8Srobert       case lldb::eStateUnloaded:
558*f6aab3d8Srobert         error.SetErrorString("process unloaded during launch or attach");
559*f6aab3d8Srobert         return error;
560*f6aab3d8Srobert       case lldb::eStateCrashed:
561*f6aab3d8Srobert       case lldb::eStateStopped:
562*f6aab3d8Srobert         return lldb::SBError(); // Success!
563*f6aab3d8Srobert     }
564*f6aab3d8Srobert     std::this_thread::sleep_for(std::chrono::microseconds(250));
565*f6aab3d8Srobert   }
566*f6aab3d8Srobert   error.SetErrorStringWithFormat("process failed to stop within %u seconds",
567*f6aab3d8Srobert                                  seconds);
568*f6aab3d8Srobert   return error;
569*f6aab3d8Srobert }
570*f6aab3d8Srobert 
Clear()571*f6aab3d8Srobert void Variables::Clear() {
572*f6aab3d8Srobert   locals.Clear();
573*f6aab3d8Srobert   globals.Clear();
574*f6aab3d8Srobert   registers.Clear();
575*f6aab3d8Srobert   expandable_variables.clear();
576*f6aab3d8Srobert }
577*f6aab3d8Srobert 
GetNewVariableRefence(bool is_permanent)578*f6aab3d8Srobert int64_t Variables::GetNewVariableRefence(bool is_permanent) {
579*f6aab3d8Srobert   if (is_permanent)
580*f6aab3d8Srobert     return next_permanent_var_ref++;
581*f6aab3d8Srobert   return next_temporary_var_ref++;
582*f6aab3d8Srobert }
583*f6aab3d8Srobert 
IsPermanentVariableReference(int64_t var_ref)584*f6aab3d8Srobert bool Variables::IsPermanentVariableReference(int64_t var_ref) {
585*f6aab3d8Srobert   return var_ref >= PermanentVariableStartIndex;
586*f6aab3d8Srobert }
587*f6aab3d8Srobert 
GetVariable(int64_t var_ref) const588*f6aab3d8Srobert lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
589*f6aab3d8Srobert   if (IsPermanentVariableReference(var_ref)) {
590*f6aab3d8Srobert     auto pos = expandable_permanent_variables.find(var_ref);
591*f6aab3d8Srobert     if (pos != expandable_permanent_variables.end())
592*f6aab3d8Srobert       return pos->second;
593*f6aab3d8Srobert   } else {
594*f6aab3d8Srobert     auto pos = expandable_variables.find(var_ref);
595*f6aab3d8Srobert     if (pos != expandable_variables.end())
596*f6aab3d8Srobert       return pos->second;
597*f6aab3d8Srobert   }
598*f6aab3d8Srobert   return lldb::SBValue();
599*f6aab3d8Srobert }
600*f6aab3d8Srobert 
InsertExpandableVariable(lldb::SBValue variable,bool is_permanent)601*f6aab3d8Srobert int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
602*f6aab3d8Srobert                                             bool is_permanent) {
603*f6aab3d8Srobert   int64_t var_ref = GetNewVariableRefence(is_permanent);
604*f6aab3d8Srobert   if (is_permanent)
605*f6aab3d8Srobert     expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
606*f6aab3d8Srobert   else
607*f6aab3d8Srobert     expandable_variables.insert(std::make_pair(var_ref, variable));
608*f6aab3d8Srobert   return var_ref;
609*f6aab3d8Srobert }
610*f6aab3d8Srobert 
611061da546Spatrick } // namespace lldb_vscode
612