12f5cf851SGreg Clayton //===-- VSCode.cpp ----------------------------------------------*- C++ -*-===//
22f5cf851SGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62f5cf851SGreg Clayton //
72f5cf851SGreg Clayton //===----------------------------------------------------------------------===//
82f5cf851SGreg Clayton 
9e122877fSGreg Clayton #include <chrono>
1076e47d48SRaphael Isemann #include <cstdarg>
112f5cf851SGreg Clayton #include <fstream>
122f5cf851SGreg Clayton #include <mutex>
13e122877fSGreg Clayton #include <sstream>
142f5cf851SGreg Clayton 
152f5cf851SGreg Clayton #include "LLDBUtils.h"
1629e87541SZachary Turner #include "VSCode.h"
1729e87541SZachary Turner #include "llvm/Support/FormatVariadic.h"
182f5cf851SGreg Clayton 
1936496cc2SWalter Erquinigo #if defined(_WIN32)
2029e87541SZachary Turner #define NOMINMAX
21c61ee1bdSNathan Lanza #include <fcntl.h>
2229e87541SZachary Turner #include <io.h>
2399614d41SWalter Erquinigo #include <windows.h>
24c61ee1bdSNathan Lanza #endif
25c61ee1bdSNathan Lanza 
262f5cf851SGreg Clayton using namespace lldb_vscode;
272f5cf851SGreg Clayton 
282f5cf851SGreg Clayton namespace lldb_vscode {
292f5cf851SGreg Clayton 
302f5cf851SGreg Clayton VSCode g_vsc;
312f5cf851SGreg Clayton 
VSCode()322f5cf851SGreg Clayton VSCode::VSCode()
33b9139acbSJeffrey Tan     : broadcaster("lldb-vscode"),
3429e87541SZachary Turner       exception_breakpoints(
352f5cf851SGreg Clayton           {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
362f5cf851SGreg Clayton            {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
370ff29057SStephane Moore            {"objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC},
380ff29057SStephane Moore            {"objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC},
392f5cf851SGreg Clayton            {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
402f5cf851SGreg Clayton            {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
412f5cf851SGreg Clayton       focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
4263e51210SGreg Clayton       stop_at_entry(false), is_attach(false), configuration_done_sent(false),
4363e51210SGreg Clayton       reverse_request_seq(0), waiting_for_run_in_terminal(false),
44bff2b9adSWalter Erquinigo       progress_event_reporter(
__anon33c0155f0102(const ProgressEvent &event) 45cc88d301SWalter Erquinigo           [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }) {
462f5cf851SGreg Clayton   const char *log_file_path = getenv("LLDBVSCODE_LOG");
4736496cc2SWalter Erquinigo #if defined(_WIN32)
48c61ee1bdSNathan Lanza   // Windows opens stdout and stdin in text mode which converts \n to 13,10
49c61ee1bdSNathan Lanza   // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
50c61ee1bdSNathan Lanza   // fixes this.
512dbcfad3SSquallATF   int result = _setmode(fileno(stdout), _O_BINARY);
522dbcfad3SSquallATF   assert(result);
532dbcfad3SSquallATF   result = _setmode(fileno(stdin), _O_BINARY);
542d1a0dfeSPavel Labath   (void)result;
552dbcfad3SSquallATF   assert(result);
56c61ee1bdSNathan Lanza #endif
572f5cf851SGreg Clayton   if (log_file_path)
582f5cf851SGreg Clayton     log.reset(new std::ofstream(log_file_path));
592f5cf851SGreg Clayton }
602f5cf851SGreg Clayton 
61*24f9a2f5SShafik Yaghmour VSCode::~VSCode() = default;
622f5cf851SGreg Clayton 
GetLineForPC(int64_t sourceReference,lldb::addr_t pc) const632f5cf851SGreg Clayton int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
642f5cf851SGreg Clayton   auto pos = source_map.find(sourceReference);
652f5cf851SGreg Clayton   if (pos != source_map.end())
662f5cf851SGreg Clayton     return pos->second.GetLineForPC(pc);
672f5cf851SGreg Clayton   return 0;
682f5cf851SGreg Clayton }
692f5cf851SGreg Clayton 
GetExceptionBreakpoint(const std::string & filter)702f5cf851SGreg Clayton ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) {
712f5cf851SGreg Clayton   for (auto &bp : exception_breakpoints) {
722f5cf851SGreg Clayton     if (bp.filter == filter)
732f5cf851SGreg Clayton       return &bp;
742f5cf851SGreg Clayton   }
752f5cf851SGreg Clayton   return nullptr;
762f5cf851SGreg Clayton }
772f5cf851SGreg Clayton 
782f5cf851SGreg Clayton ExceptionBreakpoint *
GetExceptionBreakpoint(const lldb::break_id_t bp_id)792f5cf851SGreg Clayton VSCode::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
802f5cf851SGreg Clayton   for (auto &bp : exception_breakpoints) {
812f5cf851SGreg Clayton     if (bp.bp.GetID() == bp_id)
822f5cf851SGreg Clayton       return &bp;
832f5cf851SGreg Clayton   }
842f5cf851SGreg Clayton   return nullptr;
852f5cf851SGreg Clayton }
862f5cf851SGreg Clayton 
872f5cf851SGreg Clayton // Send the JSON in "json_str" to the "out" stream. Correctly send the
882f5cf851SGreg Clayton // "Content-Length:" field followed by the length, followed by the raw
892f5cf851SGreg Clayton // JSON bytes.
SendJSON(const std::string & json_str)902f5cf851SGreg Clayton void VSCode::SendJSON(const std::string &json_str) {
9129e87541SZachary Turner   output.write_full("Content-Length: ");
9229e87541SZachary Turner   output.write_full(llvm::utostr(json_str.size()));
9329e87541SZachary Turner   output.write_full("\r\n\r\n");
9429e87541SZachary Turner   output.write_full(json_str);
9529e87541SZachary Turner 
962f5cf851SGreg Clayton   if (log) {
972f5cf851SGreg Clayton     *log << "<-- " << std::endl
982f5cf851SGreg Clayton          << "Content-Length: " << json_str.size() << "\r\n\r\n"
992f5cf851SGreg Clayton          << json_str << std::endl;
1002f5cf851SGreg Clayton   }
1012f5cf851SGreg Clayton }
1022f5cf851SGreg Clayton 
1032f5cf851SGreg Clayton // Serialize the JSON value into a string and send the JSON packet to
1042f5cf851SGreg Clayton // the "out" stream.
SendJSON(const llvm::json::Value & json)1052f5cf851SGreg Clayton void VSCode::SendJSON(const llvm::json::Value &json) {
1062f5cf851SGreg Clayton   std::string s;
1072f5cf851SGreg Clayton   llvm::raw_string_ostream strm(s);
1082f5cf851SGreg Clayton   strm << json;
1092f5cf851SGreg Clayton   static std::mutex mutex;
1102f5cf851SGreg Clayton   std::lock_guard<std::mutex> locker(mutex);
1112f5cf851SGreg Clayton   SendJSON(strm.str());
1122f5cf851SGreg Clayton }
1132f5cf851SGreg Clayton 
1142f5cf851SGreg Clayton // Read a JSON packet from the "in" stream.
ReadJSON()1152f5cf851SGreg Clayton std::string VSCode::ReadJSON() {
11629e87541SZachary Turner   std::string length_str;
1172f5cf851SGreg Clayton   std::string json_str;
11829e87541SZachary Turner   int length;
1192f5cf851SGreg Clayton 
12029e87541SZachary Turner   if (!input.read_expected(log.get(), "Content-Length: "))
12129e87541SZachary Turner     return json_str;
12229e87541SZachary Turner 
12329e87541SZachary Turner   if (!input.read_line(log.get(), length_str))
12429e87541SZachary Turner     return json_str;
12529e87541SZachary Turner 
12629e87541SZachary Turner   if (!llvm::to_integer(length_str, length))
12729e87541SZachary Turner     return json_str;
12829e87541SZachary Turner 
12929e87541SZachary Turner   if (!input.read_expected(log.get(), "\r\n"))
13029e87541SZachary Turner     return json_str;
13129e87541SZachary Turner 
13229e87541SZachary Turner   if (!input.read_full(log.get(), length, json_str))
13329e87541SZachary Turner     return json_str;
13429e87541SZachary Turner 
1357202d1c2SGreg Clayton   if (log) {
1367202d1c2SGreg Clayton     *log << "--> " << std::endl
1377202d1c2SGreg Clayton          << "Content-Length: " << length << "\r\n\r\n"
1387202d1c2SGreg Clayton          << json_str << std::endl;
1397202d1c2SGreg Clayton   }
1407202d1c2SGreg Clayton 
1412f5cf851SGreg Clayton   return json_str;
1422f5cf851SGreg Clayton }
1432f5cf851SGreg Clayton 
1442f5cf851SGreg Clayton // "OutputEvent": {
1452f5cf851SGreg Clayton //   "allOf": [ { "$ref": "#/definitions/Event" }, {
1462f5cf851SGreg Clayton //     "type": "object",
1472f5cf851SGreg Clayton //     "description": "Event message for 'output' event type. The event
1482f5cf851SGreg Clayton //                     indicates that the target has produced some output.",
1492f5cf851SGreg Clayton //     "properties": {
1502f5cf851SGreg Clayton //       "event": {
1512f5cf851SGreg Clayton //         "type": "string",
1522f5cf851SGreg Clayton //         "enum": [ "output" ]
1532f5cf851SGreg Clayton //       },
1542f5cf851SGreg Clayton //       "body": {
1552f5cf851SGreg Clayton //         "type": "object",
1562f5cf851SGreg Clayton //         "properties": {
1572f5cf851SGreg Clayton //           "category": {
1582f5cf851SGreg Clayton //             "type": "string",
1592f5cf851SGreg Clayton //             "description": "The output category. If not specified,
1602f5cf851SGreg Clayton //                             'console' is assumed.",
1612f5cf851SGreg Clayton //             "_enum": [ "console", "stdout", "stderr", "telemetry" ]
1622f5cf851SGreg Clayton //           },
1632f5cf851SGreg Clayton //           "output": {
1642f5cf851SGreg Clayton //             "type": "string",
1652f5cf851SGreg Clayton //             "description": "The output to report."
1662f5cf851SGreg Clayton //           },
1672f5cf851SGreg Clayton //           "variablesReference": {
1682f5cf851SGreg Clayton //             "type": "number",
1692f5cf851SGreg Clayton //             "description": "If an attribute 'variablesReference' exists
1702f5cf851SGreg Clayton //                             and its value is > 0, the output contains
1712f5cf851SGreg Clayton //                             objects which can be retrieved by passing
1722f5cf851SGreg Clayton //                             variablesReference to the VariablesRequest."
1732f5cf851SGreg Clayton //           },
1742f5cf851SGreg Clayton //           "source": {
1752f5cf851SGreg Clayton //             "$ref": "#/definitions/Source",
1762f5cf851SGreg Clayton //             "description": "An optional source location where the output
1772f5cf851SGreg Clayton //                             was produced."
1782f5cf851SGreg Clayton //           },
1792f5cf851SGreg Clayton //           "line": {
1802f5cf851SGreg Clayton //             "type": "integer",
1812f5cf851SGreg Clayton //             "description": "An optional source location line where the
1822f5cf851SGreg Clayton //                             output was produced."
1832f5cf851SGreg Clayton //           },
1842f5cf851SGreg Clayton //           "column": {
1852f5cf851SGreg Clayton //             "type": "integer",
1862f5cf851SGreg Clayton //             "description": "An optional source location column where the
1872f5cf851SGreg Clayton //                             output was produced."
1882f5cf851SGreg Clayton //           },
1892f5cf851SGreg Clayton //           "data": {
1902f5cf851SGreg Clayton //             "type":["array","boolean","integer","null","number","object",
1912f5cf851SGreg Clayton //                     "string"],
1922f5cf851SGreg Clayton //             "description": "Optional data to report. For the 'telemetry'
1932f5cf851SGreg Clayton //                             category the data will be sent to telemetry, for
1942f5cf851SGreg Clayton //                             the other categories the data is shown in JSON
1952f5cf851SGreg Clayton //                             format."
1962f5cf851SGreg Clayton //           }
1972f5cf851SGreg Clayton //         },
1982f5cf851SGreg Clayton //         "required": ["output"]
1992f5cf851SGreg Clayton //       }
2002f5cf851SGreg Clayton //     },
2012f5cf851SGreg Clayton //     "required": [ "event", "body" ]
2022f5cf851SGreg Clayton //   }]
2032f5cf851SGreg Clayton // }
SendOutput(OutputType o,const llvm::StringRef output)2042f5cf851SGreg Clayton void VSCode::SendOutput(OutputType o, const llvm::StringRef output) {
2052f5cf851SGreg Clayton   if (output.empty())
2062f5cf851SGreg Clayton     return;
2072f5cf851SGreg Clayton 
2088b5e6991SReid Kleckner   llvm::json::Object event(CreateEventObject("output"));
2092f5cf851SGreg Clayton   llvm::json::Object body;
2102f5cf851SGreg Clayton   const char *category = nullptr;
2112f5cf851SGreg Clayton   switch (o) {
2122f5cf851SGreg Clayton   case OutputType::Console:
2132f5cf851SGreg Clayton     category = "console";
2142f5cf851SGreg Clayton     break;
2152f5cf851SGreg Clayton   case OutputType::Stdout:
2162f5cf851SGreg Clayton     category = "stdout";
2172f5cf851SGreg Clayton     break;
2182f5cf851SGreg Clayton   case OutputType::Stderr:
2192f5cf851SGreg Clayton     category = "stderr";
2202f5cf851SGreg Clayton     break;
2212f5cf851SGreg Clayton   case OutputType::Telemetry:
2222f5cf851SGreg Clayton     category = "telemetry";
2232f5cf851SGreg Clayton     break;
2242f5cf851SGreg Clayton   }
2252f5cf851SGreg Clayton   body.try_emplace("category", category);
226a0d52cbdSNathan Lanza   EmplaceSafeString(body, "output", output.str());
2272f5cf851SGreg Clayton   event.try_emplace("body", std::move(body));
2282f5cf851SGreg Clayton   SendJSON(llvm::json::Value(std::move(event)));
2292f5cf851SGreg Clayton }
2302f5cf851SGreg Clayton 
231e122877fSGreg Clayton // interface ProgressStartEvent extends Event {
232e122877fSGreg Clayton //   event: 'progressStart';
233e122877fSGreg Clayton //
234e122877fSGreg Clayton //   body: {
235e122877fSGreg Clayton //     /**
236e122877fSGreg Clayton //      * An ID that must be used in subsequent 'progressUpdate' and
237e122877fSGreg Clayton //      'progressEnd'
238e122877fSGreg Clayton //      * events to make them refer to the same progress reporting.
239e122877fSGreg Clayton //      * IDs must be unique within a debug session.
240e122877fSGreg Clayton //      */
241e122877fSGreg Clayton //     progressId: string;
242e122877fSGreg Clayton //
243e122877fSGreg Clayton //     /**
244e122877fSGreg Clayton //      * Mandatory (short) title of the progress reporting. Shown in the UI to
245e122877fSGreg Clayton //      * describe the long running operation.
246e122877fSGreg Clayton //      */
247e122877fSGreg Clayton //     title: string;
248e122877fSGreg Clayton //
249e122877fSGreg Clayton //     /**
250e122877fSGreg Clayton //      * The request ID that this progress report is related to. If specified a
251e122877fSGreg Clayton //      * debug adapter is expected to emit
252e122877fSGreg Clayton //      * progress events for the long running request until the request has
253e122877fSGreg Clayton //      been
254e122877fSGreg Clayton //      * either completed or cancelled.
255e122877fSGreg Clayton //      * If the request ID is omitted, the progress report is assumed to be
256e122877fSGreg Clayton //      * related to some general activity of the debug adapter.
257e122877fSGreg Clayton //      */
258e122877fSGreg Clayton //     requestId?: number;
259e122877fSGreg Clayton //
260e122877fSGreg Clayton //     /**
261e122877fSGreg Clayton //      * If true, the request that reports progress may be canceled with a
262e122877fSGreg Clayton //      * 'cancel' request.
263e122877fSGreg Clayton //      * So this property basically controls whether the client should use UX
264e122877fSGreg Clayton //      that
265e122877fSGreg Clayton //      * supports cancellation.
266e122877fSGreg Clayton //      * Clients that don't support cancellation are allowed to ignore the
267e122877fSGreg Clayton //      * setting.
268e122877fSGreg Clayton //      */
269e122877fSGreg Clayton //     cancellable?: boolean;
270e122877fSGreg Clayton //
271e122877fSGreg Clayton //     /**
272e122877fSGreg Clayton //      * Optional, more detailed progress message.
273e122877fSGreg Clayton //      */
274e122877fSGreg Clayton //     message?: string;
275e122877fSGreg Clayton //
276e122877fSGreg Clayton //     /**
277e122877fSGreg Clayton //      * Optional progress percentage to display (value range: 0 to 100). If
278e122877fSGreg Clayton //      * omitted no percentage will be shown.
279e122877fSGreg Clayton //      */
280e122877fSGreg Clayton //     percentage?: number;
281e122877fSGreg Clayton //   };
282e122877fSGreg Clayton // }
283e122877fSGreg Clayton //
284e122877fSGreg Clayton // interface ProgressUpdateEvent extends Event {
285e122877fSGreg Clayton //   event: 'progressUpdate';
286e122877fSGreg Clayton //
287e122877fSGreg Clayton //   body: {
288e122877fSGreg Clayton //     /**
289e122877fSGreg Clayton //      * The ID that was introduced in the initial 'progressStart' event.
290e122877fSGreg Clayton //      */
291e122877fSGreg Clayton //     progressId: string;
292e122877fSGreg Clayton //
293e122877fSGreg Clayton //     /**
294e122877fSGreg Clayton //      * Optional, more detailed progress message. If omitted, the previous
295e122877fSGreg Clayton //      * message (if any) is used.
296e122877fSGreg Clayton //      */
297e122877fSGreg Clayton //     message?: string;
298e122877fSGreg Clayton //
299e122877fSGreg Clayton //     /**
300e122877fSGreg Clayton //      * Optional progress percentage to display (value range: 0 to 100). If
301e122877fSGreg Clayton //      * omitted no percentage will be shown.
302e122877fSGreg Clayton //      */
303e122877fSGreg Clayton //     percentage?: number;
304e122877fSGreg Clayton //   };
305e122877fSGreg Clayton // }
306e122877fSGreg Clayton //
307e122877fSGreg Clayton // interface ProgressEndEvent extends Event {
308e122877fSGreg Clayton //   event: 'progressEnd';
309e122877fSGreg Clayton //
310e122877fSGreg Clayton //   body: {
311e122877fSGreg Clayton //     /**
312e122877fSGreg Clayton //      * The ID that was introduced in the initial 'ProgressStartEvent'.
313e122877fSGreg Clayton //      */
314e122877fSGreg Clayton //     progressId: string;
315e122877fSGreg Clayton //
316e122877fSGreg Clayton //     /**
317e122877fSGreg Clayton //      * Optional, more detailed progress message. If omitted, the previous
318e122877fSGreg Clayton //      * message (if any) is used.
319e122877fSGreg Clayton //      */
320e122877fSGreg Clayton //     message?: string;
321e122877fSGreg Clayton //   };
322e122877fSGreg Clayton // }
323e122877fSGreg Clayton 
SendProgressEvent(uint64_t progress_id,const char * message,uint64_t completed,uint64_t total)324bff2b9adSWalter Erquinigo void VSCode::SendProgressEvent(uint64_t progress_id, const char *message,
325bff2b9adSWalter Erquinigo                                uint64_t completed, uint64_t total) {
326bff2b9adSWalter Erquinigo   progress_event_reporter.Push(progress_id, message, completed, total);
327e122877fSGreg Clayton }
328e122877fSGreg Clayton 
3292f5cf851SGreg Clayton void __attribute__((format(printf, 3, 4)))
SendFormattedOutput(OutputType o,const char * format,...)3302f5cf851SGreg Clayton VSCode::SendFormattedOutput(OutputType o, const char *format, ...) {
3312f5cf851SGreg Clayton   char buffer[1024];
3322f5cf851SGreg Clayton   va_list args;
3332f5cf851SGreg Clayton   va_start(args, format);
3342f5cf851SGreg Clayton   int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
3352f5cf851SGreg Clayton   va_end(args);
33699614d41SWalter Erquinigo   SendOutput(
33799614d41SWalter Erquinigo       o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
3382f5cf851SGreg Clayton }
3392f5cf851SGreg Clayton 
GetNextSourceReference()3402f5cf851SGreg Clayton int64_t VSCode::GetNextSourceReference() {
3412f5cf851SGreg Clayton   static int64_t ref = 0;
3422f5cf851SGreg Clayton   return ++ref;
3432f5cf851SGreg Clayton }
3442f5cf851SGreg Clayton 
3452f5cf851SGreg Clayton ExceptionBreakpoint *
GetExceptionBPFromStopReason(lldb::SBThread & thread)3462f5cf851SGreg Clayton VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
3472f5cf851SGreg Clayton   const auto num = thread.GetStopReasonDataCount();
3482f5cf851SGreg Clayton   // Check to see if have hit an exception breakpoint and change the
3492f5cf851SGreg Clayton   // reason to "exception", but only do so if all breakpoints that were
3502f5cf851SGreg Clayton   // hit are exception breakpoints.
3512f5cf851SGreg Clayton   ExceptionBreakpoint *exc_bp = nullptr;
3522f5cf851SGreg Clayton   for (size_t i = 0; i < num; i += 2) {
3532f5cf851SGreg Clayton     // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
3542f5cf851SGreg Clayton     // thread.GetStopReasonDataAtIndex(i+1) will return the location
3552f5cf851SGreg Clayton     // within that breakpoint. We only care about the bp ID so we can
3562f5cf851SGreg Clayton     // see if this is an exception breakpoint that is getting hit.
3572f5cf851SGreg Clayton     lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
3582f5cf851SGreg Clayton     exc_bp = GetExceptionBreakpoint(bp_id);
3592f5cf851SGreg Clayton     // If any breakpoint is not an exception breakpoint, then stop and
3602f5cf851SGreg Clayton     // report this as a normal breakpoint
3612f5cf851SGreg Clayton     if (exc_bp == nullptr)
3622f5cf851SGreg Clayton       return nullptr;
3632f5cf851SGreg Clayton   }
3642f5cf851SGreg Clayton   return exc_bp;
3652f5cf851SGreg Clayton }
3662f5cf851SGreg Clayton 
GetLLDBThread(const llvm::json::Object & arguments)3672f5cf851SGreg Clayton lldb::SBThread VSCode::GetLLDBThread(const llvm::json::Object &arguments) {
3682f5cf851SGreg Clayton   auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
3692f5cf851SGreg Clayton   return target.GetProcess().GetThreadByID(tid);
3702f5cf851SGreg Clayton }
3712f5cf851SGreg Clayton 
GetLLDBFrame(const llvm::json::Object & arguments)3722f5cf851SGreg Clayton lldb::SBFrame VSCode::GetLLDBFrame(const llvm::json::Object &arguments) {
3732f5cf851SGreg Clayton   const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
3742f5cf851SGreg Clayton   lldb::SBProcess process = target.GetProcess();
3752f5cf851SGreg Clayton   // Upper 32 bits is the thread index ID
376067cc509SNathan Lanza   lldb::SBThread thread =
377067cc509SNathan Lanza       process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
3782f5cf851SGreg Clayton   // Lower 32 bits is the frame index
379067cc509SNathan Lanza   return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
3802f5cf851SGreg Clayton }
3812f5cf851SGreg Clayton 
CreateTopLevelScopes()3822f5cf851SGreg Clayton llvm::json::Value VSCode::CreateTopLevelScopes() {
3832f5cf851SGreg Clayton   llvm::json::Array scopes;
384b9139acbSJeffrey Tan   scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS,
385b9139acbSJeffrey Tan                                   g_vsc.variables.locals.GetSize(), false));
386b9139acbSJeffrey Tan   scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
387b9139acbSJeffrey Tan                                   g_vsc.variables.globals.GetSize(), false));
388b9139acbSJeffrey Tan   scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
389b9139acbSJeffrey Tan                                   g_vsc.variables.registers.GetSize(), false));
3902f5cf851SGreg Clayton   return llvm::json::Value(std::move(scopes));
3912f5cf851SGreg Clayton }
3922f5cf851SGreg Clayton 
RunLLDBCommands(llvm::StringRef prefix,const std::vector<std::string> & commands)3932f5cf851SGreg Clayton void VSCode::RunLLDBCommands(llvm::StringRef prefix,
3942f5cf851SGreg Clayton                              const std::vector<std::string> &commands) {
3952f5cf851SGreg Clayton   SendOutput(OutputType::Console,
3962f5cf851SGreg Clayton              llvm::StringRef(::RunLLDBCommands(prefix, commands)));
3972f5cf851SGreg Clayton }
3982f5cf851SGreg Clayton 
RunInitCommands()3992f5cf851SGreg Clayton void VSCode::RunInitCommands() {
4002f5cf851SGreg Clayton   RunLLDBCommands("Running initCommands:", init_commands);
4012f5cf851SGreg Clayton }
4022f5cf851SGreg Clayton 
RunPreRunCommands()4032f5cf851SGreg Clayton void VSCode::RunPreRunCommands() {
4042f5cf851SGreg Clayton   RunLLDBCommands("Running preRunCommands:", pre_run_commands);
4052f5cf851SGreg Clayton }
4062f5cf851SGreg Clayton 
RunStopCommands()4072f5cf851SGreg Clayton void VSCode::RunStopCommands() {
4082f5cf851SGreg Clayton   RunLLDBCommands("Running stopCommands:", stop_commands);
4092f5cf851SGreg Clayton }
4102f5cf851SGreg Clayton 
RunExitCommands()4112f5cf851SGreg Clayton void VSCode::RunExitCommands() {
4122f5cf851SGreg Clayton   RunLLDBCommands("Running exitCommands:", exit_commands);
4132f5cf851SGreg Clayton }
4142f5cf851SGreg Clayton 
RunTerminateCommands()41574ab1da0SWalter Erquinigo void VSCode::RunTerminateCommands() {
41674ab1da0SWalter Erquinigo   RunLLDBCommands("Running terminateCommands:", terminate_commands);
41774ab1da0SWalter Erquinigo }
41874ab1da0SWalter Erquinigo 
41999614d41SWalter Erquinigo lldb::SBTarget
CreateTargetFromArguments(const llvm::json::Object & arguments,lldb::SBError & error)42099614d41SWalter Erquinigo VSCode::CreateTargetFromArguments(const llvm::json::Object &arguments,
42121d09ccfSTatyana Krasnukha                                   lldb::SBError &error) {
42221d09ccfSTatyana Krasnukha   // Grab the name of the program we need to debug and create a target using
42321d09ccfSTatyana Krasnukha   // the given program as an argument. Executable file can be a source of target
42421d09ccfSTatyana Krasnukha   // architecture and platform, if they differ from the host. Setting exe path
42521d09ccfSTatyana Krasnukha   // in launch info is useless because Target.Launch() will not change
42621d09ccfSTatyana Krasnukha   // architecture and platform, therefore they should be known at the target
42721d09ccfSTatyana Krasnukha   // creation. We also use target triple and platform from the launch
42821d09ccfSTatyana Krasnukha   // configuration, if given, since in some cases ELF file doesn't contain
42921d09ccfSTatyana Krasnukha   // enough information to determine correct arch and platform (or ELF can be
43021d09ccfSTatyana Krasnukha   // omitted at all), so it is good to leave the user an apportunity to specify
43121d09ccfSTatyana Krasnukha   // those. Any of those three can be left empty.
43221d09ccfSTatyana Krasnukha   llvm::StringRef target_triple = GetString(arguments, "targetTriple");
43321d09ccfSTatyana Krasnukha   llvm::StringRef platform_name = GetString(arguments, "platformName");
43421d09ccfSTatyana Krasnukha   llvm::StringRef program = GetString(arguments, "program");
43521d09ccfSTatyana Krasnukha   auto target = this->debugger.CreateTarget(
43699614d41SWalter Erquinigo       program.data(), target_triple.data(), platform_name.data(),
43721d09ccfSTatyana Krasnukha       true, // Add dependent modules.
43899614d41SWalter Erquinigo       error);
43921d09ccfSTatyana Krasnukha 
44021d09ccfSTatyana Krasnukha   if (error.Fail()) {
44121d09ccfSTatyana Krasnukha     // Update message if there was an error.
44221d09ccfSTatyana Krasnukha     error.SetErrorStringWithFormat(
44399614d41SWalter Erquinigo         "Could not create a target for a program '%s': %s.", program.data(),
44499614d41SWalter Erquinigo         error.GetCString());
44521d09ccfSTatyana Krasnukha   }
44621d09ccfSTatyana Krasnukha 
44721d09ccfSTatyana Krasnukha   return target;
44821d09ccfSTatyana Krasnukha }
44921d09ccfSTatyana Krasnukha 
SetTarget(const lldb::SBTarget target)45021d09ccfSTatyana Krasnukha void VSCode::SetTarget(const lldb::SBTarget target) {
45121d09ccfSTatyana Krasnukha   this->target = target;
45221d09ccfSTatyana Krasnukha 
45321d09ccfSTatyana Krasnukha   if (target.IsValid()) {
45421d09ccfSTatyana Krasnukha     // Configure breakpoint event listeners for the target.
45521d09ccfSTatyana Krasnukha     lldb::SBListener listener = this->debugger.GetListener();
45621d09ccfSTatyana Krasnukha     listener.StartListeningForEvents(
45721d09ccfSTatyana Krasnukha         this->target.GetBroadcaster(),
45821d09ccfSTatyana Krasnukha         lldb::SBTarget::eBroadcastBitBreakpointChanged);
45921d09ccfSTatyana Krasnukha     listener.StartListeningForEvents(this->broadcaster,
46021d09ccfSTatyana Krasnukha                                      eBroadcastBitStopEventThread);
46121d09ccfSTatyana Krasnukha   }
46221d09ccfSTatyana Krasnukha }
46321d09ccfSTatyana Krasnukha 
GetNextObject(llvm::json::Object & object)464132e57bcSWalter Erquinigo PacketStatus VSCode::GetNextObject(llvm::json::Object &object) {
465132e57bcSWalter Erquinigo   std::string json = ReadJSON();
466132e57bcSWalter Erquinigo   if (json.empty())
467132e57bcSWalter Erquinigo     return PacketStatus::EndOfFile;
468132e57bcSWalter Erquinigo 
469132e57bcSWalter Erquinigo   llvm::StringRef json_sref(json);
470132e57bcSWalter Erquinigo   llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
471132e57bcSWalter Erquinigo   if (!json_value) {
472132e57bcSWalter Erquinigo     auto error = json_value.takeError();
473132e57bcSWalter Erquinigo     if (log) {
474132e57bcSWalter Erquinigo       std::string error_str;
475132e57bcSWalter Erquinigo       llvm::raw_string_ostream strm(error_str);
476132e57bcSWalter Erquinigo       strm << error;
477132e57bcSWalter Erquinigo       strm.flush();
478132e57bcSWalter Erquinigo       *log << "error: failed to parse JSON: " << error_str << std::endl
479132e57bcSWalter Erquinigo            << json << std::endl;
480132e57bcSWalter Erquinigo     }
481132e57bcSWalter Erquinigo     return PacketStatus::JSONMalformed;
482132e57bcSWalter Erquinigo   }
483132e57bcSWalter Erquinigo   object = *json_value->getAsObject();
484132e57bcSWalter Erquinigo   if (!json_value->getAsObject()) {
485132e57bcSWalter Erquinigo     if (log)
486132e57bcSWalter Erquinigo       *log << "error: json packet isn't a object" << std::endl;
487132e57bcSWalter Erquinigo     return PacketStatus::JSONNotObject;
488132e57bcSWalter Erquinigo   }
489132e57bcSWalter Erquinigo   return PacketStatus::Success;
490132e57bcSWalter Erquinigo }
491132e57bcSWalter Erquinigo 
HandleObject(const llvm::json::Object & object)492132e57bcSWalter Erquinigo bool VSCode::HandleObject(const llvm::json::Object &object) {
493132e57bcSWalter Erquinigo   const auto packet_type = GetString(object, "type");
494132e57bcSWalter Erquinigo   if (packet_type == "request") {
495132e57bcSWalter Erquinigo     const auto command = GetString(object, "command");
496132e57bcSWalter Erquinigo     auto handler_pos = request_handlers.find(std::string(command));
497132e57bcSWalter Erquinigo     if (handler_pos != request_handlers.end()) {
498132e57bcSWalter Erquinigo       handler_pos->second(object);
499132e57bcSWalter Erquinigo       return true; // Success
500132e57bcSWalter Erquinigo     } else {
501132e57bcSWalter Erquinigo       if (log)
502132e57bcSWalter Erquinigo         *log << "error: unhandled command \"" << command.data() << std::endl;
503132e57bcSWalter Erquinigo       return false; // Fail
504132e57bcSWalter Erquinigo     }
505132e57bcSWalter Erquinigo   }
506132e57bcSWalter Erquinigo   return false;
507132e57bcSWalter Erquinigo }
508132e57bcSWalter Erquinigo 
SendReverseRequest(llvm::json::Object request,llvm::json::Object & response)509132e57bcSWalter Erquinigo PacketStatus VSCode::SendReverseRequest(llvm::json::Object request,
510132e57bcSWalter Erquinigo                                         llvm::json::Object &response) {
511132e57bcSWalter Erquinigo   request.try_emplace("seq", ++reverse_request_seq);
512132e57bcSWalter Erquinigo   SendJSON(llvm::json::Value(std::move(request)));
513132e57bcSWalter Erquinigo   while (true) {
514132e57bcSWalter Erquinigo     PacketStatus status = GetNextObject(response);
515132e57bcSWalter Erquinigo     const auto packet_type = GetString(response, "type");
516132e57bcSWalter Erquinigo     if (packet_type == "response")
517132e57bcSWalter Erquinigo       return status;
518132e57bcSWalter Erquinigo     else {
519132e57bcSWalter Erquinigo       // Not our response, we got another packet
520132e57bcSWalter Erquinigo       HandleObject(response);
521132e57bcSWalter Erquinigo     }
522132e57bcSWalter Erquinigo   }
523132e57bcSWalter Erquinigo   return PacketStatus::EndOfFile;
524132e57bcSWalter Erquinigo }
525132e57bcSWalter Erquinigo 
RegisterRequestCallback(std::string request,RequestCallback callback)526132e57bcSWalter Erquinigo void VSCode::RegisterRequestCallback(std::string request,
527132e57bcSWalter Erquinigo                                      RequestCallback callback) {
528132e57bcSWalter Erquinigo   request_handlers[request] = callback;
529132e57bcSWalter Erquinigo }
530132e57bcSWalter Erquinigo 
WaitForProcessToStop(uint32_t seconds)53163e51210SGreg Clayton lldb::SBError VSCode::WaitForProcessToStop(uint32_t seconds) {
53263e51210SGreg Clayton   lldb::SBError error;
53363e51210SGreg Clayton   lldb::SBProcess process = target.GetProcess();
53463e51210SGreg Clayton   if (!process.IsValid()) {
53563e51210SGreg Clayton     error.SetErrorString("invalid process");
53663e51210SGreg Clayton     return error;
53763e51210SGreg Clayton   }
53863e51210SGreg Clayton   auto timeout_time =
53963e51210SGreg Clayton       std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
54063e51210SGreg Clayton   while (std::chrono::steady_clock::now() < timeout_time) {
54163e51210SGreg Clayton     const auto state = process.GetState();
54263e51210SGreg Clayton     switch (state) {
54363e51210SGreg Clayton       case lldb::eStateAttaching:
54463e51210SGreg Clayton       case lldb::eStateConnected:
54563e51210SGreg Clayton       case lldb::eStateInvalid:
54663e51210SGreg Clayton       case lldb::eStateLaunching:
54763e51210SGreg Clayton       case lldb::eStateRunning:
54863e51210SGreg Clayton       case lldb::eStateStepping:
54963e51210SGreg Clayton       case lldb::eStateSuspended:
55063e51210SGreg Clayton         break;
55163e51210SGreg Clayton       case lldb::eStateDetached:
55263e51210SGreg Clayton         error.SetErrorString("process detached during launch or attach");
55363e51210SGreg Clayton         return error;
55463e51210SGreg Clayton       case lldb::eStateExited:
55563e51210SGreg Clayton         error.SetErrorString("process exited during launch or attach");
55663e51210SGreg Clayton         return error;
55763e51210SGreg Clayton       case lldb::eStateUnloaded:
55863e51210SGreg Clayton         error.SetErrorString("process unloaded during launch or attach");
55963e51210SGreg Clayton         return error;
56063e51210SGreg Clayton       case lldb::eStateCrashed:
56163e51210SGreg Clayton       case lldb::eStateStopped:
56263e51210SGreg Clayton         return lldb::SBError(); // Success!
56363e51210SGreg Clayton     }
56463e51210SGreg Clayton     std::this_thread::sleep_for(std::chrono::microseconds(250));
56563e51210SGreg Clayton   }
56663e51210SGreg Clayton   error.SetErrorStringWithFormat("process failed to stop within %u seconds",
56763e51210SGreg Clayton                                  seconds);
56863e51210SGreg Clayton   return error;
56963e51210SGreg Clayton }
57063e51210SGreg Clayton 
Clear()571b9139acbSJeffrey Tan void Variables::Clear() {
572b9139acbSJeffrey Tan   locals.Clear();
573b9139acbSJeffrey Tan   globals.Clear();
574b9139acbSJeffrey Tan   registers.Clear();
575b9139acbSJeffrey Tan   expandable_variables.clear();
576b9139acbSJeffrey Tan }
577b9139acbSJeffrey Tan 
GetNewVariableRefence(bool is_permanent)578b9139acbSJeffrey Tan int64_t Variables::GetNewVariableRefence(bool is_permanent) {
579b9139acbSJeffrey Tan   if (is_permanent)
580b9139acbSJeffrey Tan     return next_permanent_var_ref++;
581b9139acbSJeffrey Tan   return next_temporary_var_ref++;
582b9139acbSJeffrey Tan }
583b9139acbSJeffrey Tan 
IsPermanentVariableReference(int64_t var_ref)584b9139acbSJeffrey Tan bool Variables::IsPermanentVariableReference(int64_t var_ref) {
585b9139acbSJeffrey Tan   return var_ref >= PermanentVariableStartIndex;
586b9139acbSJeffrey Tan }
587b9139acbSJeffrey Tan 
GetVariable(int64_t var_ref) const588b9139acbSJeffrey Tan lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
589b9139acbSJeffrey Tan   if (IsPermanentVariableReference(var_ref)) {
590b9139acbSJeffrey Tan     auto pos = expandable_permanent_variables.find(var_ref);
591b9139acbSJeffrey Tan     if (pos != expandable_permanent_variables.end())
592b9139acbSJeffrey Tan       return pos->second;
593b9139acbSJeffrey Tan   } else {
594b9139acbSJeffrey Tan     auto pos = expandable_variables.find(var_ref);
595b9139acbSJeffrey Tan     if (pos != expandable_variables.end())
596b9139acbSJeffrey Tan       return pos->second;
597b9139acbSJeffrey Tan   }
598b9139acbSJeffrey Tan   return lldb::SBValue();
599b9139acbSJeffrey Tan }
600b9139acbSJeffrey Tan 
InsertExpandableVariable(lldb::SBValue variable,bool is_permanent)601b9139acbSJeffrey Tan int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
602b9139acbSJeffrey Tan                                             bool is_permanent) {
603b9139acbSJeffrey Tan   int64_t var_ref = GetNewVariableRefence(is_permanent);
604b9139acbSJeffrey Tan   if (is_permanent)
605b9139acbSJeffrey Tan     expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
606b9139acbSJeffrey Tan   else
607b9139acbSJeffrey Tan     expandable_variables.insert(std::make_pair(var_ref, variable));
608b9139acbSJeffrey Tan   return var_ref;
609b9139acbSJeffrey Tan }
610b9139acbSJeffrey Tan 
6112f5cf851SGreg Clayton } // namespace lldb_vscode
612