12f5cf851SGreg Clayton //===-- JSONUtils.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 
98b73fa61SNathan Lanza #include <algorithm>
102c1bea88SYifan Shen #include <iomanip>
112c1bea88SYifan Shen #include <sstream>
1291d5bfdbSGreg Clayton #include <string.h>
138b73fa61SNathan Lanza 
14e796c77bSWalter Erquinigo #include "llvm/ADT/Optional.h"
152f5cf851SGreg Clayton #include "llvm/Support/FormatAdapters.h"
16e796c77bSWalter Erquinigo #include "llvm/Support/Path.h"
175cee8ddcSPavel Labath #include "llvm/Support/ScopedPrinter.h"
182f5cf851SGreg Clayton 
192f5cf851SGreg Clayton #include "lldb/API/SBBreakpoint.h"
202f5cf851SGreg Clayton #include "lldb/API/SBBreakpointLocation.h"
21c9a0754bSWalter Erquinigo #include "lldb/API/SBDeclaration.h"
222f5cf851SGreg Clayton #include "lldb/API/SBValue.h"
238b5e6991SReid Kleckner #include "lldb/Host/PosixApi.h"
242f5cf851SGreg Clayton 
252f5cf851SGreg Clayton #include "ExceptionBreakpoint.h"
262f5cf851SGreg Clayton #include "JSONUtils.h"
272f5cf851SGreg Clayton #include "LLDBUtils.h"
282f5cf851SGreg Clayton #include "VSCode.h"
292f5cf851SGreg Clayton 
302f5cf851SGreg Clayton namespace lldb_vscode {
312f5cf851SGreg Clayton 
EmplaceSafeString(llvm::json::Object & obj,llvm::StringRef key,llvm::StringRef str)32a0d52cbdSNathan Lanza void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
33a0d52cbdSNathan Lanza                        llvm::StringRef str) {
34a0d52cbdSNathan Lanza   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
35a0d52cbdSNathan Lanza     obj.try_emplace(key, str.str());
36a0d52cbdSNathan Lanza   else
37a0d52cbdSNathan Lanza     obj.try_emplace(key, llvm::json::fixUTF8(str));
38a0d52cbdSNathan Lanza }
39a0d52cbdSNathan Lanza 
GetAsString(const llvm::json::Value & value)402f5cf851SGreg Clayton llvm::StringRef GetAsString(const llvm::json::Value &value) {
412f5cf851SGreg Clayton   if (auto s = value.getAsString())
422f5cf851SGreg Clayton     return *s;
432f5cf851SGreg Clayton   return llvm::StringRef();
442f5cf851SGreg Clayton }
452f5cf851SGreg Clayton 
462f5cf851SGreg Clayton // Gets a string from a JSON object using the key, or returns an empty string.
GetString(const llvm::json::Object & obj,llvm::StringRef key)472f5cf851SGreg Clayton llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
485cee8ddcSPavel Labath   if (llvm::Optional<llvm::StringRef> value = obj.getString(key))
495cee8ddcSPavel Labath     return *value;
502f5cf851SGreg Clayton   return llvm::StringRef();
512f5cf851SGreg Clayton }
522f5cf851SGreg Clayton 
GetString(const llvm::json::Object * obj,llvm::StringRef key)532f5cf851SGreg Clayton llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
542f5cf851SGreg Clayton   if (obj == nullptr)
552f5cf851SGreg Clayton     return llvm::StringRef();
562f5cf851SGreg Clayton   return GetString(*obj, key);
572f5cf851SGreg Clayton }
582f5cf851SGreg Clayton 
592f5cf851SGreg Clayton // Gets an unsigned integer from a JSON object using the key, or returns the
602f5cf851SGreg Clayton // specified fail value.
GetUnsigned(const llvm::json::Object & obj,llvm::StringRef key,uint64_t fail_value)612f5cf851SGreg Clayton uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
622f5cf851SGreg Clayton                      uint64_t fail_value) {
632f5cf851SGreg Clayton   if (auto value = obj.getInteger(key))
642f5cf851SGreg Clayton     return (uint64_t)*value;
652f5cf851SGreg Clayton   return fail_value;
662f5cf851SGreg Clayton }
672f5cf851SGreg Clayton 
GetUnsigned(const llvm::json::Object * obj,llvm::StringRef key,uint64_t fail_value)682f5cf851SGreg Clayton uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
692f5cf851SGreg Clayton                      uint64_t fail_value) {
702f5cf851SGreg Clayton   if (obj == nullptr)
712f5cf851SGreg Clayton     return fail_value;
722f5cf851SGreg Clayton   return GetUnsigned(*obj, key, fail_value);
732f5cf851SGreg Clayton }
742f5cf851SGreg Clayton 
GetBoolean(const llvm::json::Object & obj,llvm::StringRef key,bool fail_value)752f5cf851SGreg Clayton bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
762f5cf851SGreg Clayton                 bool fail_value) {
772f5cf851SGreg Clayton   if (auto value = obj.getBoolean(key))
782f5cf851SGreg Clayton     return *value;
792f5cf851SGreg Clayton   if (auto value = obj.getInteger(key))
802f5cf851SGreg Clayton     return *value != 0;
812f5cf851SGreg Clayton   return fail_value;
822f5cf851SGreg Clayton }
832f5cf851SGreg Clayton 
GetBoolean(const llvm::json::Object * obj,llvm::StringRef key,bool fail_value)842f5cf851SGreg Clayton bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
852f5cf851SGreg Clayton                 bool fail_value) {
862f5cf851SGreg Clayton   if (obj == nullptr)
872f5cf851SGreg Clayton     return fail_value;
882f5cf851SGreg Clayton   return GetBoolean(*obj, key, fail_value);
892f5cf851SGreg Clayton }
902f5cf851SGreg Clayton 
GetSigned(const llvm::json::Object & obj,llvm::StringRef key,int64_t fail_value)912f5cf851SGreg Clayton int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
922f5cf851SGreg Clayton                   int64_t fail_value) {
932f5cf851SGreg Clayton   if (auto value = obj.getInteger(key))
942f5cf851SGreg Clayton     return *value;
952f5cf851SGreg Clayton   return fail_value;
962f5cf851SGreg Clayton }
972f5cf851SGreg Clayton 
GetSigned(const llvm::json::Object * obj,llvm::StringRef key,int64_t fail_value)982f5cf851SGreg Clayton int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
992f5cf851SGreg Clayton                   int64_t fail_value) {
1002f5cf851SGreg Clayton   if (obj == nullptr)
1012f5cf851SGreg Clayton     return fail_value;
1022f5cf851SGreg Clayton   return GetSigned(*obj, key, fail_value);
1032f5cf851SGreg Clayton }
1042f5cf851SGreg Clayton 
ObjectContainsKey(const llvm::json::Object & obj,llvm::StringRef key)1052f5cf851SGreg Clayton bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
106a6682a41SJonas Devlieghere   return obj.find(key) != obj.end();
1072f5cf851SGreg Clayton }
1082f5cf851SGreg Clayton 
GetStrings(const llvm::json::Object * obj,llvm::StringRef key)1092f5cf851SGreg Clayton std::vector<std::string> GetStrings(const llvm::json::Object *obj,
1102f5cf851SGreg Clayton                                     llvm::StringRef key) {
1112f5cf851SGreg Clayton   std::vector<std::string> strs;
1122f5cf851SGreg Clayton   auto json_array = obj->getArray(key);
1132f5cf851SGreg Clayton   if (!json_array)
1142f5cf851SGreg Clayton     return strs;
1152f5cf851SGreg Clayton   for (const auto &value : *json_array) {
1162f5cf851SGreg Clayton     switch (value.kind()) {
1172f5cf851SGreg Clayton     case llvm::json::Value::String:
1182f5cf851SGreg Clayton       strs.push_back(value.getAsString()->str());
1192f5cf851SGreg Clayton       break;
1202f5cf851SGreg Clayton     case llvm::json::Value::Number:
1215cee8ddcSPavel Labath     case llvm::json::Value::Boolean:
1225cee8ddcSPavel Labath       strs.push_back(llvm::to_string(value));
1232f5cf851SGreg Clayton       break;
1242f5cf851SGreg Clayton     case llvm::json::Value::Null:
1252f5cf851SGreg Clayton     case llvm::json::Value::Object:
1262f5cf851SGreg Clayton     case llvm::json::Value::Array:
1272f5cf851SGreg Clayton       break;
1282f5cf851SGreg Clayton     }
1292f5cf851SGreg Clayton   }
1302f5cf851SGreg Clayton   return strs;
1312f5cf851SGreg Clayton }
1322f5cf851SGreg Clayton 
SetValueForKey(lldb::SBValue & v,llvm::json::Object & object,llvm::StringRef key)1332f5cf851SGreg Clayton void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
1342f5cf851SGreg Clayton                     llvm::StringRef key) {
1352f5cf851SGreg Clayton 
1362f5cf851SGreg Clayton   llvm::StringRef value = v.GetValue();
1372f5cf851SGreg Clayton   llvm::StringRef summary = v.GetSummary();
1382f5cf851SGreg Clayton   llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
139*0fe220a3SJeffrey Tan   lldb::SBError error = v.GetError();
1402f5cf851SGreg Clayton 
1412f5cf851SGreg Clayton   std::string result;
1422f5cf851SGreg Clayton   llvm::raw_string_ostream strm(result);
143*0fe220a3SJeffrey Tan   if (!error.Success()) {
144*0fe220a3SJeffrey Tan     strm << "<error: " << error.GetCString() << ">";
145*0fe220a3SJeffrey Tan   } else if (!value.empty()) {
1462f5cf851SGreg Clayton     strm << value;
1472f5cf851SGreg Clayton     if (!summary.empty())
1482f5cf851SGreg Clayton       strm << ' ' << summary;
1492f5cf851SGreg Clayton   } else if (!summary.empty()) {
1502f5cf851SGreg Clayton     strm << ' ' << summary;
1512f5cf851SGreg Clayton   } else if (!type_name.empty()) {
1522f5cf851SGreg Clayton     strm << type_name;
1532f5cf851SGreg Clayton     lldb::addr_t address = v.GetLoadAddress();
1542f5cf851SGreg Clayton     if (address != LLDB_INVALID_ADDRESS)
1552f5cf851SGreg Clayton       strm << " @ " << llvm::format_hex(address, 0);
1562f5cf851SGreg Clayton   }
1572f5cf851SGreg Clayton   strm.flush();
158a0d52cbdSNathan Lanza   EmplaceSafeString(object, key, result);
1592f5cf851SGreg Clayton }
1602f5cf851SGreg Clayton 
FillResponse(const llvm::json::Object & request,llvm::json::Object & response)1612f5cf851SGreg Clayton void FillResponse(const llvm::json::Object &request,
1622f5cf851SGreg Clayton                   llvm::json::Object &response) {
1632f5cf851SGreg Clayton   // Fill in all of the needed response fields to a "request" and set "success"
1642f5cf851SGreg Clayton   // to true by default.
1652f5cf851SGreg Clayton   response.try_emplace("type", "response");
1662f5cf851SGreg Clayton   response.try_emplace("seq", (int64_t)0);
167a0d52cbdSNathan Lanza   EmplaceSafeString(response, "command", GetString(request, "command"));
1682f5cf851SGreg Clayton   const int64_t seq = GetSigned(request, "seq", 0);
1692f5cf851SGreg Clayton   response.try_emplace("request_seq", seq);
1702f5cf851SGreg Clayton   response.try_emplace("success", true);
1712f5cf851SGreg Clayton }
1722f5cf851SGreg Clayton 
1732f5cf851SGreg Clayton // "Scope": {
1742f5cf851SGreg Clayton //   "type": "object",
1752f5cf851SGreg Clayton //   "description": "A Scope is a named container for variables. Optionally
1762f5cf851SGreg Clayton //                   a scope can map to a source or a range within a source.",
1772f5cf851SGreg Clayton //   "properties": {
1782f5cf851SGreg Clayton //     "name": {
1792f5cf851SGreg Clayton //       "type": "string",
1802f5cf851SGreg Clayton //       "description": "Name of the scope such as 'Arguments', 'Locals'."
1812f5cf851SGreg Clayton //     },
182dc8f0035SAndy Yankovsky //     "presentationHint": {
183dc8f0035SAndy Yankovsky //       "type": "string",
184dc8f0035SAndy Yankovsky //       "description": "An optional hint for how to present this scope in the
185dc8f0035SAndy Yankovsky //                       UI. If this attribute is missing, the scope is shown
186dc8f0035SAndy Yankovsky //                       with a generic UI.",
187dc8f0035SAndy Yankovsky //       "_enum": [ "arguments", "locals", "registers" ],
188dc8f0035SAndy Yankovsky //     },
1892f5cf851SGreg Clayton //     "variablesReference": {
1902f5cf851SGreg Clayton //       "type": "integer",
1912f5cf851SGreg Clayton //       "description": "The variables of this scope can be retrieved by
1922f5cf851SGreg Clayton //                       passing the value of variablesReference to the
1932f5cf851SGreg Clayton //                       VariablesRequest."
1942f5cf851SGreg Clayton //     },
1952f5cf851SGreg Clayton //     "namedVariables": {
1962f5cf851SGreg Clayton //       "type": "integer",
1972f5cf851SGreg Clayton //       "description": "The number of named variables in this scope. The
1982f5cf851SGreg Clayton //                       client can use this optional information to present
1992f5cf851SGreg Clayton //                       the variables in a paged UI and fetch them in chunks."
2002f5cf851SGreg Clayton //     },
2012f5cf851SGreg Clayton //     "indexedVariables": {
2022f5cf851SGreg Clayton //       "type": "integer",
2032f5cf851SGreg Clayton //       "description": "The number of indexed variables in this scope. The
2042f5cf851SGreg Clayton //                       client can use this optional information to present
2052f5cf851SGreg Clayton //                       the variables in a paged UI and fetch them in chunks."
2062f5cf851SGreg Clayton //     },
2072f5cf851SGreg Clayton //     "expensive": {
2082f5cf851SGreg Clayton //       "type": "boolean",
2092f5cf851SGreg Clayton //       "description": "If true, the number of variables in this scope is
2102f5cf851SGreg Clayton //                       large or expensive to retrieve."
2112f5cf851SGreg Clayton //     },
2122f5cf851SGreg Clayton //     "source": {
2132f5cf851SGreg Clayton //       "$ref": "#/definitions/Source",
2142f5cf851SGreg Clayton //       "description": "Optional source for this scope."
2152f5cf851SGreg Clayton //     },
2162f5cf851SGreg Clayton //     "line": {
2172f5cf851SGreg Clayton //       "type": "integer",
2182f5cf851SGreg Clayton //       "description": "Optional start line of the range covered by this
2192f5cf851SGreg Clayton //                       scope."
2202f5cf851SGreg Clayton //     },
2212f5cf851SGreg Clayton //     "column": {
2222f5cf851SGreg Clayton //       "type": "integer",
2232f5cf851SGreg Clayton //       "description": "Optional start column of the range covered by this
2242f5cf851SGreg Clayton //                       scope."
2252f5cf851SGreg Clayton //     },
2262f5cf851SGreg Clayton //     "endLine": {
2272f5cf851SGreg Clayton //       "type": "integer",
2282f5cf851SGreg Clayton //       "description": "Optional end line of the range covered by this scope."
2292f5cf851SGreg Clayton //     },
2302f5cf851SGreg Clayton //     "endColumn": {
2312f5cf851SGreg Clayton //       "type": "integer",
2322f5cf851SGreg Clayton //       "description": "Optional end column of the range covered by this
2332f5cf851SGreg Clayton //                       scope."
2342f5cf851SGreg Clayton //     }
2352f5cf851SGreg Clayton //   },
2362f5cf851SGreg Clayton //   "required": [ "name", "variablesReference", "expensive" ]
2372f5cf851SGreg Clayton // }
CreateScope(const llvm::StringRef name,int64_t variablesReference,int64_t namedVariables,bool expensive)2382f5cf851SGreg Clayton llvm::json::Value CreateScope(const llvm::StringRef name,
2392f5cf851SGreg Clayton                               int64_t variablesReference,
2402f5cf851SGreg Clayton                               int64_t namedVariables, bool expensive) {
2412f5cf851SGreg Clayton   llvm::json::Object object;
242a0d52cbdSNathan Lanza   EmplaceSafeString(object, "name", name.str());
243dc8f0035SAndy Yankovsky 
244dc8f0035SAndy Yankovsky   // TODO: Support "arguments" scope. At the moment lldb-vscode includes the
245dc8f0035SAndy Yankovsky   // arguments into the "locals" scope.
246dc8f0035SAndy Yankovsky   if (variablesReference == VARREF_LOCALS) {
247dc8f0035SAndy Yankovsky     object.try_emplace("presentationHint", "locals");
248dc8f0035SAndy Yankovsky   } else if (variablesReference == VARREF_REGS) {
249dc8f0035SAndy Yankovsky     object.try_emplace("presentationHint", "registers");
250dc8f0035SAndy Yankovsky   }
251dc8f0035SAndy Yankovsky 
2522f5cf851SGreg Clayton   object.try_emplace("variablesReference", variablesReference);
2532f5cf851SGreg Clayton   object.try_emplace("expensive", expensive);
2542f5cf851SGreg Clayton   object.try_emplace("namedVariables", namedVariables);
2552f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
2562f5cf851SGreg Clayton }
2572f5cf851SGreg Clayton 
2582f5cf851SGreg Clayton // "Breakpoint": {
2592f5cf851SGreg Clayton //   "type": "object",
2602f5cf851SGreg Clayton //   "description": "Information about a Breakpoint created in setBreakpoints
2612f5cf851SGreg Clayton //                   or setFunctionBreakpoints.",
2622f5cf851SGreg Clayton //   "properties": {
2632f5cf851SGreg Clayton //     "id": {
2642f5cf851SGreg Clayton //       "type": "integer",
2652f5cf851SGreg Clayton //       "description": "An optional unique identifier for the breakpoint."
2662f5cf851SGreg Clayton //     },
2672f5cf851SGreg Clayton //     "verified": {
2682f5cf851SGreg Clayton //       "type": "boolean",
2692f5cf851SGreg Clayton //       "description": "If true breakpoint could be set (but not necessarily
2702f5cf851SGreg Clayton //                       at the desired location)."
2712f5cf851SGreg Clayton //     },
2722f5cf851SGreg Clayton //     "message": {
2732f5cf851SGreg Clayton //       "type": "string",
2742f5cf851SGreg Clayton //       "description": "An optional message about the state of the breakpoint.
2752f5cf851SGreg Clayton //                       This is shown to the user and can be used to explain
2762f5cf851SGreg Clayton //                       why a breakpoint could not be verified."
2772f5cf851SGreg Clayton //     },
2782f5cf851SGreg Clayton //     "source": {
2792f5cf851SGreg Clayton //       "$ref": "#/definitions/Source",
2802f5cf851SGreg Clayton //       "description": "The source where the breakpoint is located."
2812f5cf851SGreg Clayton //     },
2822f5cf851SGreg Clayton //     "line": {
2832f5cf851SGreg Clayton //       "type": "integer",
2842f5cf851SGreg Clayton //       "description": "The start line of the actual range covered by the
2852f5cf851SGreg Clayton //                       breakpoint."
2862f5cf851SGreg Clayton //     },
2872f5cf851SGreg Clayton //     "column": {
2882f5cf851SGreg Clayton //       "type": "integer",
2892f5cf851SGreg Clayton //       "description": "An optional start column of the actual range covered
2902f5cf851SGreg Clayton //                       by the breakpoint."
2912f5cf851SGreg Clayton //     },
2922f5cf851SGreg Clayton //     "endLine": {
2932f5cf851SGreg Clayton //       "type": "integer",
2942f5cf851SGreg Clayton //       "description": "An optional end line of the actual range covered by
2952f5cf851SGreg Clayton //                       the breakpoint."
2962f5cf851SGreg Clayton //     },
2972f5cf851SGreg Clayton //     "endColumn": {
2982f5cf851SGreg Clayton //       "type": "integer",
2992f5cf851SGreg Clayton //       "description": "An optional end column of the actual range covered by
3002f5cf851SGreg Clayton //                       the breakpoint. If no end line is given, then the end
3012f5cf851SGreg Clayton //                       column is assumed to be in the start line."
3022f5cf851SGreg Clayton //     }
3032f5cf851SGreg Clayton //   },
3042f5cf851SGreg Clayton //   "required": [ "verified" ]
3052f5cf851SGreg Clayton // }
CreateBreakpoint(lldb::SBBreakpoint & bp,llvm::Optional<llvm::StringRef> request_path,llvm::Optional<uint32_t> request_line)306e796c77bSWalter Erquinigo llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
307e796c77bSWalter Erquinigo                                    llvm::Optional<llvm::StringRef> request_path,
308e796c77bSWalter Erquinigo                                    llvm::Optional<uint32_t> request_line) {
3092f5cf851SGreg Clayton   // Each breakpoint location is treated as a separate breakpoint for VS code.
3102f5cf851SGreg Clayton   // They don't have the notion of a single breakpoint with multiple locations.
3112f5cf851SGreg Clayton   llvm::json::Object object;
3129cb227f5SGreg Clayton   if (!bp.IsValid())
3132f5cf851SGreg Clayton     return llvm::json::Value(std::move(object));
3142f5cf851SGreg Clayton 
3159cb227f5SGreg Clayton   object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
3169cb227f5SGreg Clayton   object.try_emplace("id", bp.GetID());
3179cb227f5SGreg Clayton   // VS Code DAP doesn't currently allow one breakpoint to have multiple
3189cb227f5SGreg Clayton   // locations so we just report the first one. If we report all locations
3199cb227f5SGreg Clayton   // then the IDE starts showing the wrong line numbers and locations for
3209cb227f5SGreg Clayton   // other source file and line breakpoints in the same file.
3219cb227f5SGreg Clayton 
3229cb227f5SGreg Clayton   // Below we search for the first resolved location in a breakpoint and report
3239cb227f5SGreg Clayton   // this as the breakpoint location since it will have a complete location
3249cb227f5SGreg Clayton   // that is at least loaded in the current process.
3259cb227f5SGreg Clayton   lldb::SBBreakpointLocation bp_loc;
3269cb227f5SGreg Clayton   const auto num_locs = bp.GetNumLocations();
3279cb227f5SGreg Clayton   for (size_t i = 0; i < num_locs; ++i) {
3289cb227f5SGreg Clayton     bp_loc = bp.GetLocationAtIndex(i);
3299cb227f5SGreg Clayton     if (bp_loc.IsResolved())
3309cb227f5SGreg Clayton       break;
3319cb227f5SGreg Clayton   }
3329cb227f5SGreg Clayton   // If not locations are resolved, use the first location.
3339cb227f5SGreg Clayton   if (!bp_loc.IsResolved())
3349cb227f5SGreg Clayton     bp_loc = bp.GetLocationAtIndex(0);
3352f5cf851SGreg Clayton   auto bp_addr = bp_loc.GetAddress();
336e796c77bSWalter Erquinigo 
337e796c77bSWalter Erquinigo   if (request_path)
338e796c77bSWalter Erquinigo     object.try_emplace("source", CreateSource(*request_path));
339e796c77bSWalter Erquinigo 
3402f5cf851SGreg Clayton   if (bp_addr.IsValid()) {
3412f5cf851SGreg Clayton     auto line_entry = bp_addr.GetLineEntry();
3422f5cf851SGreg Clayton     const auto line = line_entry.GetLine();
3432f5cf851SGreg Clayton     if (line != UINT32_MAX)
3442f5cf851SGreg Clayton       object.try_emplace("line", line);
3452f5cf851SGreg Clayton     object.try_emplace("source", CreateSource(line_entry));
3462f5cf851SGreg Clayton   }
347e796c77bSWalter Erquinigo   // We try to add request_line as a fallback
348e796c77bSWalter Erquinigo   if (request_line)
349e796c77bSWalter Erquinigo     object.try_emplace("line", *request_line);
3502f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
3512f5cf851SGreg Clayton }
3522f5cf851SGreg Clayton 
GetDebugInfoSizeInSection(lldb::SBSection section)3532c1bea88SYifan Shen static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
3542c1bea88SYifan Shen   uint64_t debug_info_size = 0;
3552c1bea88SYifan Shen   llvm::StringRef section_name(section.GetName());
3562c1bea88SYifan Shen   if (section_name.startswith(".debug") || section_name.startswith("__debug") ||
3572c1bea88SYifan Shen       section_name.startswith(".apple") || section_name.startswith("__apple"))
3582c1bea88SYifan Shen     debug_info_size += section.GetFileByteSize();
3592c1bea88SYifan Shen   size_t num_sub_sections = section.GetNumSubSections();
3602c1bea88SYifan Shen   for (size_t i = 0; i < num_sub_sections; i++) {
3612c1bea88SYifan Shen     debug_info_size +=
3622c1bea88SYifan Shen         GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
3632c1bea88SYifan Shen   }
3642c1bea88SYifan Shen   return debug_info_size;
3652c1bea88SYifan Shen }
3662c1bea88SYifan Shen 
GetDebugInfoSize(lldb::SBModule module)3672c1bea88SYifan Shen static uint64_t GetDebugInfoSize(lldb::SBModule module) {
3682c1bea88SYifan Shen   uint64_t debug_info_size = 0;
3692c1bea88SYifan Shen   size_t num_sections = module.GetNumSections();
3702c1bea88SYifan Shen   for (size_t i = 0; i < num_sections; i++) {
3712c1bea88SYifan Shen     debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
3722c1bea88SYifan Shen   }
3732c1bea88SYifan Shen   return debug_info_size;
3742c1bea88SYifan Shen }
3752c1bea88SYifan Shen 
ConvertDebugInfoSizeToString(uint64_t debug_info)3762c1bea88SYifan Shen static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
3772c1bea88SYifan Shen   std::ostringstream oss;
3782c1bea88SYifan Shen   oss << std::fixed << std::setprecision(1);
3792c1bea88SYifan Shen   if (debug_info < 1024) {
3802c1bea88SYifan Shen     oss << debug_info << "B";
3812c1bea88SYifan Shen   } else if (debug_info < 1024 * 1024) {
3822c1bea88SYifan Shen     double kb = double(debug_info) / 1024.0;
3832c1bea88SYifan Shen     oss << kb << "KB";
3842c1bea88SYifan Shen   } else if (debug_info < 1024 * 1024 * 1024) {
3852c1bea88SYifan Shen     double mb = double(debug_info) / (1024.0 * 1024.0);
3862c1bea88SYifan Shen     oss << mb << "MB";
3872c1bea88SYifan Shen   } else {
3882c1bea88SYifan Shen     double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
3892c1bea88SYifan Shen     oss << gb << "GB";
3902c1bea88SYifan Shen   }
3912c1bea88SYifan Shen   return oss.str();
3922c1bea88SYifan Shen }
CreateModule(lldb::SBModule & module)39377c9aafcSWalter Erquinigo llvm::json::Value CreateModule(lldb::SBModule &module) {
39477c9aafcSWalter Erquinigo   llvm::json::Object object;
39577c9aafcSWalter Erquinigo   if (!module.IsValid())
39677c9aafcSWalter Erquinigo     return llvm::json::Value(std::move(object));
3979a9ae01fSWalter Erquinigo   const char *uuid = module.GetUUIDString();
3989a9ae01fSWalter Erquinigo   object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
39977c9aafcSWalter Erquinigo   object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
40077c9aafcSWalter Erquinigo   char module_path_arr[PATH_MAX];
40177c9aafcSWalter Erquinigo   module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
40277c9aafcSWalter Erquinigo   std::string module_path(module_path_arr);
40377c9aafcSWalter Erquinigo   object.try_emplace("path", module_path);
40477c9aafcSWalter Erquinigo   if (module.GetNumCompileUnits() > 0) {
4052c1bea88SYifan Shen     std::string symbol_str = "Symbols loaded.";
40682139b87SYifan Shen     std::string debug_info_size;
4072c1bea88SYifan Shen     uint64_t debug_info = GetDebugInfoSize(module);
4082c1bea88SYifan Shen     if (debug_info > 0) {
40982139b87SYifan Shen       debug_info_size = ConvertDebugInfoSizeToString(debug_info);
4102c1bea88SYifan Shen     }
4112c1bea88SYifan Shen     object.try_emplace("symbolStatus", symbol_str);
41282139b87SYifan Shen     object.try_emplace("debugInfoSize", debug_info_size);
41377c9aafcSWalter Erquinigo     char symbol_path_arr[PATH_MAX];
4142c1bea88SYifan Shen     module.GetSymbolFileSpec().GetPath(symbol_path_arr,
4152c1bea88SYifan Shen                                        sizeof(symbol_path_arr));
41677c9aafcSWalter Erquinigo     std::string symbol_path(symbol_path_arr);
41777c9aafcSWalter Erquinigo     object.try_emplace("symbolFilePath", symbol_path);
41877c9aafcSWalter Erquinigo   } else {
41977c9aafcSWalter Erquinigo     object.try_emplace("symbolStatus", "Symbols not found.");
42077c9aafcSWalter Erquinigo   }
42177c9aafcSWalter Erquinigo   std::string loaded_addr = std::to_string(
42277c9aafcSWalter Erquinigo       module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target));
42377c9aafcSWalter Erquinigo   object.try_emplace("addressRange", loaded_addr);
42477c9aafcSWalter Erquinigo   std::string version_str;
42577c9aafcSWalter Erquinigo   uint32_t version_nums[3];
4262c1bea88SYifan Shen   uint32_t num_versions =
4272c1bea88SYifan Shen       module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
42877c9aafcSWalter Erquinigo   for (uint32_t i = 0; i < num_versions; ++i) {
42977c9aafcSWalter Erquinigo     if (!version_str.empty())
43077c9aafcSWalter Erquinigo       version_str += ".";
43177c9aafcSWalter Erquinigo     version_str += std::to_string(version_nums[i]);
43277c9aafcSWalter Erquinigo   }
43377c9aafcSWalter Erquinigo   if (!version_str.empty())
43477c9aafcSWalter Erquinigo     object.try_emplace("version", version_str);
43577c9aafcSWalter Erquinigo   return llvm::json::Value(std::move(object));
43677c9aafcSWalter Erquinigo }
43777c9aafcSWalter Erquinigo 
AppendBreakpoint(lldb::SBBreakpoint & bp,llvm::json::Array & breakpoints,llvm::Optional<llvm::StringRef> request_path,llvm::Optional<uint32_t> request_line)438e796c77bSWalter Erquinigo void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
439e796c77bSWalter Erquinigo                       llvm::Optional<llvm::StringRef> request_path,
440e796c77bSWalter Erquinigo                       llvm::Optional<uint32_t> request_line) {
441e796c77bSWalter Erquinigo   breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
4422f5cf851SGreg Clayton }
4432f5cf851SGreg Clayton 
4442f5cf851SGreg Clayton // "Event": {
4452f5cf851SGreg Clayton //   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
4462f5cf851SGreg Clayton //     "type": "object",
4472f5cf851SGreg Clayton //     "description": "Server-initiated event.",
4482f5cf851SGreg Clayton //     "properties": {
4492f5cf851SGreg Clayton //       "type": {
4502f5cf851SGreg Clayton //         "type": "string",
4512f5cf851SGreg Clayton //         "enum": [ "event" ]
4522f5cf851SGreg Clayton //       },
4532f5cf851SGreg Clayton //       "event": {
4542f5cf851SGreg Clayton //         "type": "string",
4552f5cf851SGreg Clayton //         "description": "Type of event."
4562f5cf851SGreg Clayton //       },
4572f5cf851SGreg Clayton //       "body": {
4582f5cf851SGreg Clayton //         "type": [ "array", "boolean", "integer", "null", "number" ,
4592f5cf851SGreg Clayton //                   "object", "string" ],
4602f5cf851SGreg Clayton //         "description": "Event-specific information."
4612f5cf851SGreg Clayton //       }
4622f5cf851SGreg Clayton //     },
4632f5cf851SGreg Clayton //     "required": [ "type", "event" ]
4642f5cf851SGreg Clayton //   }]
4652f5cf851SGreg Clayton // },
4662f5cf851SGreg Clayton // "ProtocolMessage": {
4672f5cf851SGreg Clayton //   "type": "object",
4682f5cf851SGreg Clayton //   "description": "Base class of requests, responses, and events.",
4692f5cf851SGreg Clayton //   "properties": {
4702f5cf851SGreg Clayton //         "seq": {
4712f5cf851SGreg Clayton //           "type": "integer",
4722f5cf851SGreg Clayton //           "description": "Sequence number."
4732f5cf851SGreg Clayton //         },
4742f5cf851SGreg Clayton //         "type": {
4752f5cf851SGreg Clayton //           "type": "string",
4762f5cf851SGreg Clayton //           "description": "Message type.",
4772f5cf851SGreg Clayton //           "_enum": [ "request", "response", "event" ]
4782f5cf851SGreg Clayton //         }
4792f5cf851SGreg Clayton //   },
4802f5cf851SGreg Clayton //   "required": [ "seq", "type" ]
4812f5cf851SGreg Clayton // }
CreateEventObject(const llvm::StringRef event_name)4828b5e6991SReid Kleckner llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
4832f5cf851SGreg Clayton   llvm::json::Object event;
4842f5cf851SGreg Clayton   event.try_emplace("seq", 0);
4852f5cf851SGreg Clayton   event.try_emplace("type", "event");
486a0d52cbdSNathan Lanza   EmplaceSafeString(event, "event", event_name);
4872f5cf851SGreg Clayton   return event;
4882f5cf851SGreg Clayton }
4892f5cf851SGreg Clayton 
4902f5cf851SGreg Clayton // "ExceptionBreakpointsFilter": {
4912f5cf851SGreg Clayton //   "type": "object",
4922f5cf851SGreg Clayton //   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
4932f5cf851SGreg Clayton //                   option for configuring how exceptions are dealt with.",
4942f5cf851SGreg Clayton //   "properties": {
4952f5cf851SGreg Clayton //     "filter": {
4962f5cf851SGreg Clayton //       "type": "string",
4972f5cf851SGreg Clayton //       "description": "The internal ID of the filter. This value is passed
4982f5cf851SGreg Clayton //                       to the setExceptionBreakpoints request."
4992f5cf851SGreg Clayton //     },
5002f5cf851SGreg Clayton //     "label": {
5012f5cf851SGreg Clayton //       "type": "string",
5022f5cf851SGreg Clayton //       "description": "The name of the filter. This will be shown in the UI."
5032f5cf851SGreg Clayton //     },
5042f5cf851SGreg Clayton //     "default": {
5052f5cf851SGreg Clayton //       "type": "boolean",
5062f5cf851SGreg Clayton //       "description": "Initial value of the filter. If not specified a value
5072f5cf851SGreg Clayton //                       'false' is assumed."
5082f5cf851SGreg Clayton //     }
5092f5cf851SGreg Clayton //   },
5102f5cf851SGreg Clayton //   "required": [ "filter", "label" ]
5112f5cf851SGreg Clayton // }
5122f5cf851SGreg Clayton llvm::json::Value
CreateExceptionBreakpointFilter(const ExceptionBreakpoint & bp)5132f5cf851SGreg Clayton CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
5142f5cf851SGreg Clayton   llvm::json::Object object;
515a0d52cbdSNathan Lanza   EmplaceSafeString(object, "filter", bp.filter);
516a0d52cbdSNathan Lanza   EmplaceSafeString(object, "label", bp.label);
5172f5cf851SGreg Clayton   object.try_emplace("default", bp.default_value);
5182f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
5192f5cf851SGreg Clayton }
5202f5cf851SGreg Clayton 
5212f5cf851SGreg Clayton // "Source": {
5222f5cf851SGreg Clayton //   "type": "object",
5232f5cf851SGreg Clayton //   "description": "A Source is a descriptor for source code. It is returned
5242f5cf851SGreg Clayton //                   from the debug adapter as part of a StackFrame and it is
5252f5cf851SGreg Clayton //                   used by clients when specifying breakpoints.",
5262f5cf851SGreg Clayton //   "properties": {
5272f5cf851SGreg Clayton //     "name": {
5282f5cf851SGreg Clayton //       "type": "string",
5292f5cf851SGreg Clayton //       "description": "The short name of the source. Every source returned
5302f5cf851SGreg Clayton //                       from the debug adapter has a name. When sending a
5312f5cf851SGreg Clayton //                       source to the debug adapter this name is optional."
5322f5cf851SGreg Clayton //     },
5332f5cf851SGreg Clayton //     "path": {
5342f5cf851SGreg Clayton //       "type": "string",
5352f5cf851SGreg Clayton //       "description": "The path of the source to be shown in the UI. It is
5362f5cf851SGreg Clayton //                       only used to locate and load the content of the
5372f5cf851SGreg Clayton //                       source if no sourceReference is specified (or its
5382f5cf851SGreg Clayton //                       value is 0)."
5392f5cf851SGreg Clayton //     },
5402f5cf851SGreg Clayton //     "sourceReference": {
5412f5cf851SGreg Clayton //       "type": "number",
5422f5cf851SGreg Clayton //       "description": "If sourceReference > 0 the contents of the source must
5432f5cf851SGreg Clayton //                       be retrieved through the SourceRequest (even if a path
5442f5cf851SGreg Clayton //                       is specified). A sourceReference is only valid for a
5452f5cf851SGreg Clayton //                       session, so it must not be used to persist a source."
5462f5cf851SGreg Clayton //     },
5472f5cf851SGreg Clayton //     "presentationHint": {
5482f5cf851SGreg Clayton //       "type": "string",
5492f5cf851SGreg Clayton //       "description": "An optional hint for how to present the source in the
5502f5cf851SGreg Clayton //                       UI. A value of 'deemphasize' can be used to indicate
5512f5cf851SGreg Clayton //                       that the source is not available or that it is
5522f5cf851SGreg Clayton //                       skipped on stepping.",
5532f5cf851SGreg Clayton //       "enum": [ "normal", "emphasize", "deemphasize" ]
5542f5cf851SGreg Clayton //     },
5552f5cf851SGreg Clayton //     "origin": {
5562f5cf851SGreg Clayton //       "type": "string",
5572f5cf851SGreg Clayton //       "description": "The (optional) origin of this source: possible values
5582f5cf851SGreg Clayton //                       'internal module', 'inlined content from source map',
5592f5cf851SGreg Clayton //                       etc."
5602f5cf851SGreg Clayton //     },
5612f5cf851SGreg Clayton //     "sources": {
5622f5cf851SGreg Clayton //       "type": "array",
5632f5cf851SGreg Clayton //       "items": {
5642f5cf851SGreg Clayton //         "$ref": "#/definitions/Source"
5652f5cf851SGreg Clayton //       },
5662f5cf851SGreg Clayton //       "description": "An optional list of sources that are related to this
5672f5cf851SGreg Clayton //                       source. These may be the source that generated this
5682f5cf851SGreg Clayton //                       source."
5692f5cf851SGreg Clayton //     },
5702f5cf851SGreg Clayton //     "adapterData": {
5712f5cf851SGreg Clayton //       "type":["array","boolean","integer","null","number","object","string"],
5722f5cf851SGreg Clayton //       "description": "Optional data that a debug adapter might want to loop
5732f5cf851SGreg Clayton //                       through the client. The client should leave the data
5742f5cf851SGreg Clayton //                       intact and persist it across sessions. The client
5752f5cf851SGreg Clayton //                       should not interpret the data."
5762f5cf851SGreg Clayton //     },
5772f5cf851SGreg Clayton //     "checksums": {
5782f5cf851SGreg Clayton //       "type": "array",
5792f5cf851SGreg Clayton //       "items": {
5802f5cf851SGreg Clayton //         "$ref": "#/definitions/Checksum"
5812f5cf851SGreg Clayton //       },
5822f5cf851SGreg Clayton //       "description": "The checksums associated with this file."
5832f5cf851SGreg Clayton //     }
5842f5cf851SGreg Clayton //   }
5852f5cf851SGreg Clayton // }
CreateSource(lldb::SBLineEntry & line_entry)5862f5cf851SGreg Clayton llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
5872f5cf851SGreg Clayton   llvm::json::Object object;
5882f5cf851SGreg Clayton   lldb::SBFileSpec file = line_entry.GetFileSpec();
5892f5cf851SGreg Clayton   if (file.IsValid()) {
5902f5cf851SGreg Clayton     const char *name = file.GetFilename();
5912f5cf851SGreg Clayton     if (name)
592a0d52cbdSNathan Lanza       EmplaceSafeString(object, "name", name);
5932f5cf851SGreg Clayton     char path[PATH_MAX] = "";
5942f5cf851SGreg Clayton     file.GetPath(path, sizeof(path));
5952f5cf851SGreg Clayton     if (path[0]) {
596a0d52cbdSNathan Lanza       EmplaceSafeString(object, "path", std::string(path));
5972f5cf851SGreg Clayton     }
5982f5cf851SGreg Clayton   }
5992f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
6002f5cf851SGreg Clayton }
6012f5cf851SGreg Clayton 
CreateSource(llvm::StringRef source_path)602e796c77bSWalter Erquinigo llvm::json::Value CreateSource(llvm::StringRef source_path) {
603e796c77bSWalter Erquinigo   llvm::json::Object source;
604e796c77bSWalter Erquinigo   llvm::StringRef name = llvm::sys::path::filename(source_path);
605e796c77bSWalter Erquinigo   EmplaceSafeString(source, "name", name);
606e796c77bSWalter Erquinigo   EmplaceSafeString(source, "path", source_path);
607e796c77bSWalter Erquinigo   return llvm::json::Value(std::move(source));
608e796c77bSWalter Erquinigo }
609e796c77bSWalter Erquinigo 
CreateSource(lldb::SBFrame & frame,int64_t & disasm_line)6102f5cf851SGreg Clayton llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
6112f5cf851SGreg Clayton   disasm_line = 0;
6122f5cf851SGreg Clayton   auto line_entry = frame.GetLineEntry();
6132f5cf851SGreg Clayton   if (line_entry.GetFileSpec().IsValid())
6142f5cf851SGreg Clayton     return CreateSource(line_entry);
6152f5cf851SGreg Clayton 
6162f5cf851SGreg Clayton   llvm::json::Object object;
6172f5cf851SGreg Clayton   const auto pc = frame.GetPC();
6182f5cf851SGreg Clayton 
6192f5cf851SGreg Clayton   lldb::SBInstructionList insts;
6202f5cf851SGreg Clayton   lldb::SBFunction function = frame.GetFunction();
6212f5cf851SGreg Clayton   lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
6222f5cf851SGreg Clayton   if (function.IsValid()) {
6232f5cf851SGreg Clayton     low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
6242f5cf851SGreg Clayton     auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
6252f5cf851SGreg Clayton     if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
6262f5cf851SGreg Clayton       // We have this disassembly cached already, return the existing
6272f5cf851SGreg Clayton       // sourceReference
6282f5cf851SGreg Clayton       object.try_emplace("sourceReference", addr_srcref->second);
6292f5cf851SGreg Clayton       disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
6302f5cf851SGreg Clayton     } else {
6312f5cf851SGreg Clayton       insts = function.GetInstructions(g_vsc.target);
6322f5cf851SGreg Clayton     }
6332f5cf851SGreg Clayton   } else {
6342f5cf851SGreg Clayton     lldb::SBSymbol symbol = frame.GetSymbol();
6352f5cf851SGreg Clayton     if (symbol.IsValid()) {
6362f5cf851SGreg Clayton       low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
6372f5cf851SGreg Clayton       auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
6382f5cf851SGreg Clayton       if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
6392f5cf851SGreg Clayton         // We have this disassembly cached already, return the existing
6402f5cf851SGreg Clayton         // sourceReference
6412f5cf851SGreg Clayton         object.try_emplace("sourceReference", addr_srcref->second);
6422f5cf851SGreg Clayton         disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
6432f5cf851SGreg Clayton       } else {
6442f5cf851SGreg Clayton         insts = symbol.GetInstructions(g_vsc.target);
6452f5cf851SGreg Clayton       }
6462f5cf851SGreg Clayton     }
6472f5cf851SGreg Clayton   }
6482f5cf851SGreg Clayton   const auto num_insts = insts.GetSize();
6492f5cf851SGreg Clayton   if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
650a0d52cbdSNathan Lanza     EmplaceSafeString(object, "name", frame.GetFunctionName());
6512f5cf851SGreg Clayton     SourceReference source;
6522f5cf851SGreg Clayton     llvm::raw_string_ostream src_strm(source.content);
6532f5cf851SGreg Clayton     std::string line;
6542f5cf851SGreg Clayton     for (size_t i = 0; i < num_insts; ++i) {
6552f5cf851SGreg Clayton       lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
6562f5cf851SGreg Clayton       const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
6572f5cf851SGreg Clayton       const char *m = inst.GetMnemonic(g_vsc.target);
6582f5cf851SGreg Clayton       const char *o = inst.GetOperands(g_vsc.target);
6592f5cf851SGreg Clayton       const char *c = inst.GetComment(g_vsc.target);
6602f5cf851SGreg Clayton       if (pc == inst_addr)
6612f5cf851SGreg Clayton         disasm_line = i + 1;
6622f5cf851SGreg Clayton       const auto inst_offset = inst_addr - low_pc;
6632f5cf851SGreg Clayton       int spaces = 0;
6642f5cf851SGreg Clayton       if (inst_offset < 10)
6652f5cf851SGreg Clayton         spaces = 3;
6662f5cf851SGreg Clayton       else if (inst_offset < 100)
6672f5cf851SGreg Clayton         spaces = 2;
6682f5cf851SGreg Clayton       else if (inst_offset < 1000)
6692f5cf851SGreg Clayton         spaces = 1;
6702f5cf851SGreg Clayton       line.clear();
6712f5cf851SGreg Clayton       llvm::raw_string_ostream line_strm(line);
6722f5cf851SGreg Clayton       line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
673a0d52cbdSNathan Lanza                                  inst_offset, llvm::fmt_repeat(' ', spaces), m,
674a0d52cbdSNathan Lanza                                  o);
6758b73fa61SNathan Lanza 
6768b73fa61SNathan Lanza       // If there is a comment append it starting at column 60 or after one
6778b73fa61SNathan Lanza       // space past the last char
6788b73fa61SNathan Lanza       const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
6792f5cf851SGreg Clayton       if (c && c[0]) {
6802f5cf851SGreg Clayton         if (line.size() < comment_row)
6812f5cf851SGreg Clayton           line_strm.indent(comment_row - line_strm.str().size());
6822f5cf851SGreg Clayton         line_strm << " # " << c;
6832f5cf851SGreg Clayton       }
6842f5cf851SGreg Clayton       src_strm << line_strm.str() << "\n";
6852f5cf851SGreg Clayton       source.addr_to_line[inst_addr] = i + 1;
6862f5cf851SGreg Clayton     }
6872f5cf851SGreg Clayton     // Flush the source stream
6882f5cf851SGreg Clayton     src_strm.str();
6892f5cf851SGreg Clayton     auto sourceReference = VSCode::GetNextSourceReference();
6902f5cf851SGreg Clayton     g_vsc.source_map[sourceReference] = std::move(source);
6912f5cf851SGreg Clayton     g_vsc.addr_to_source_ref[low_pc] = sourceReference;
6922f5cf851SGreg Clayton     object.try_emplace("sourceReference", sourceReference);
6932f5cf851SGreg Clayton   }
6942f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
6952f5cf851SGreg Clayton }
6962f5cf851SGreg Clayton 
6972f5cf851SGreg Clayton // "StackFrame": {
6982f5cf851SGreg Clayton //   "type": "object",
6992f5cf851SGreg Clayton //   "description": "A Stackframe contains the source location.",
7002f5cf851SGreg Clayton //   "properties": {
7012f5cf851SGreg Clayton //     "id": {
7022f5cf851SGreg Clayton //       "type": "integer",
7032f5cf851SGreg Clayton //       "description": "An identifier for the stack frame. It must be unique
7042f5cf851SGreg Clayton //                       across all threads. This id can be used to retrieve
7052f5cf851SGreg Clayton //                       the scopes of the frame with the 'scopesRequest' or
7062f5cf851SGreg Clayton //                       to restart the execution of a stackframe."
7072f5cf851SGreg Clayton //     },
7082f5cf851SGreg Clayton //     "name": {
7092f5cf851SGreg Clayton //       "type": "string",
7102f5cf851SGreg Clayton //       "description": "The name of the stack frame, typically a method name."
7112f5cf851SGreg Clayton //     },
7122f5cf851SGreg Clayton //     "source": {
7132f5cf851SGreg Clayton //       "$ref": "#/definitions/Source",
7142f5cf851SGreg Clayton //       "description": "The optional source of the frame."
7152f5cf851SGreg Clayton //     },
7162f5cf851SGreg Clayton //     "line": {
7172f5cf851SGreg Clayton //       "type": "integer",
7182f5cf851SGreg Clayton //       "description": "The line within the file of the frame. If source is
7192f5cf851SGreg Clayton //                       null or doesn't exist, line is 0 and must be ignored."
7202f5cf851SGreg Clayton //     },
7212f5cf851SGreg Clayton //     "column": {
7222f5cf851SGreg Clayton //       "type": "integer",
7232f5cf851SGreg Clayton //       "description": "The column within the line. If source is null or
7242f5cf851SGreg Clayton //                       doesn't exist, column is 0 and must be ignored."
7252f5cf851SGreg Clayton //     },
7262f5cf851SGreg Clayton //     "endLine": {
7272f5cf851SGreg Clayton //       "type": "integer",
7282f5cf851SGreg Clayton //       "description": "An optional end line of the range covered by the
7292f5cf851SGreg Clayton //                       stack frame."
7302f5cf851SGreg Clayton //     },
7312f5cf851SGreg Clayton //     "endColumn": {
7322f5cf851SGreg Clayton //       "type": "integer",
7332f5cf851SGreg Clayton //       "description": "An optional end column of the range covered by the
7342f5cf851SGreg Clayton //                       stack frame."
7352f5cf851SGreg Clayton //     },
7362f5cf851SGreg Clayton //     "moduleId": {
7372f5cf851SGreg Clayton //       "type": ["integer", "string"],
7382f5cf851SGreg Clayton //       "description": "The module associated with this frame, if any."
7392f5cf851SGreg Clayton //     },
7402f5cf851SGreg Clayton //     "presentationHint": {
7412f5cf851SGreg Clayton //       "type": "string",
7422f5cf851SGreg Clayton //       "enum": [ "normal", "label", "subtle" ],
7432f5cf851SGreg Clayton //       "description": "An optional hint for how to present this frame in
7442f5cf851SGreg Clayton //                       the UI. A value of 'label' can be used to indicate
7452f5cf851SGreg Clayton //                       that the frame is an artificial frame that is used
7462f5cf851SGreg Clayton //                       as a visual label or separator. A value of 'subtle'
7472f5cf851SGreg Clayton //                       can be used to change the appearance of a frame in
7482f5cf851SGreg Clayton //                       a 'subtle' way."
7492f5cf851SGreg Clayton //     }
7502f5cf851SGreg Clayton //   },
7512f5cf851SGreg Clayton //   "required": [ "id", "name", "line", "column" ]
7522f5cf851SGreg Clayton // }
CreateStackFrame(lldb::SBFrame & frame)7532f5cf851SGreg Clayton llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
7542f5cf851SGreg Clayton   llvm::json::Object object;
7552f5cf851SGreg Clayton   int64_t frame_id = MakeVSCodeFrameID(frame);
7562f5cf851SGreg Clayton   object.try_emplace("id", frame_id);
75746c1f77eSJeffrey Tan 
75846c1f77eSJeffrey Tan   std::string frame_name;
75946c1f77eSJeffrey Tan   const char *func_name = frame.GetFunctionName();
76046c1f77eSJeffrey Tan   if (func_name)
76146c1f77eSJeffrey Tan     frame_name = func_name;
76246c1f77eSJeffrey Tan   else
76346c1f77eSJeffrey Tan     frame_name = "<unknown>";
76446c1f77eSJeffrey Tan   bool is_optimized = frame.GetFunction().GetIsOptimized();
76546c1f77eSJeffrey Tan   if (is_optimized)
76646c1f77eSJeffrey Tan     frame_name += " [opt]";
76746c1f77eSJeffrey Tan   EmplaceSafeString(object, "name", frame_name);
76846c1f77eSJeffrey Tan 
7692f5cf851SGreg Clayton   int64_t disasm_line = 0;
7702f5cf851SGreg Clayton   object.try_emplace("source", CreateSource(frame, disasm_line));
7712f5cf851SGreg Clayton 
7722f5cf851SGreg Clayton   auto line_entry = frame.GetLineEntry();
7732f5cf851SGreg Clayton   if (disasm_line > 0) {
7742f5cf851SGreg Clayton     object.try_emplace("line", disasm_line);
7752f5cf851SGreg Clayton   } else {
7762f5cf851SGreg Clayton     auto line = line_entry.GetLine();
7772f5cf851SGreg Clayton     if (line == UINT32_MAX)
7782f5cf851SGreg Clayton       line = 0;
7792f5cf851SGreg Clayton     object.try_emplace("line", line);
7802f5cf851SGreg Clayton   }
7812f5cf851SGreg Clayton   object.try_emplace("column", line_entry.GetColumn());
7822f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
7832f5cf851SGreg Clayton }
7842f5cf851SGreg Clayton 
7852f5cf851SGreg Clayton // "Thread": {
7862f5cf851SGreg Clayton //   "type": "object",
7872f5cf851SGreg Clayton //   "description": "A Thread",
7882f5cf851SGreg Clayton //   "properties": {
7892f5cf851SGreg Clayton //     "id": {
7902f5cf851SGreg Clayton //       "type": "integer",
7912f5cf851SGreg Clayton //       "description": "Unique identifier for the thread."
7922f5cf851SGreg Clayton //     },
7932f5cf851SGreg Clayton //     "name": {
7942f5cf851SGreg Clayton //       "type": "string",
7952f5cf851SGreg Clayton //       "description": "A name of the thread."
7962f5cf851SGreg Clayton //     }
7972f5cf851SGreg Clayton //   },
7982f5cf851SGreg Clayton //   "required": [ "id", "name" ]
7992f5cf851SGreg Clayton // }
CreateThread(lldb::SBThread & thread)8002f5cf851SGreg Clayton llvm::json::Value CreateThread(lldb::SBThread &thread) {
8012f5cf851SGreg Clayton   llvm::json::Object object;
8022f5cf851SGreg Clayton   object.try_emplace("id", (int64_t)thread.GetThreadID());
8032f5cf851SGreg Clayton   char thread_str[64];
8042f5cf851SGreg Clayton   snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
8052f5cf851SGreg Clayton   const char *name = thread.GetName();
8062f5cf851SGreg Clayton   if (name) {
8072f5cf851SGreg Clayton     std::string thread_with_name(thread_str);
8082f5cf851SGreg Clayton     thread_with_name += ' ';
8092f5cf851SGreg Clayton     thread_with_name += name;
810a0d52cbdSNathan Lanza     EmplaceSafeString(object, "name", thread_with_name);
8112f5cf851SGreg Clayton   } else {
812a0d52cbdSNathan Lanza     EmplaceSafeString(object, "name", std::string(thread_str));
8132f5cf851SGreg Clayton   }
8142f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
8152f5cf851SGreg Clayton }
8162f5cf851SGreg Clayton 
8172f5cf851SGreg Clayton // "StoppedEvent": {
8182f5cf851SGreg Clayton //   "allOf": [ { "$ref": "#/definitions/Event" }, {
8192f5cf851SGreg Clayton //     "type": "object",
8202f5cf851SGreg Clayton //     "description": "Event message for 'stopped' event type. The event
8212f5cf851SGreg Clayton //                     indicates that the execution of the debuggee has stopped
8222f5cf851SGreg Clayton //                     due to some condition. This can be caused by a break
8232f5cf851SGreg Clayton //                     point previously set, a stepping action has completed,
8242f5cf851SGreg Clayton //                     by executing a debugger statement etc.",
8252f5cf851SGreg Clayton //     "properties": {
8262f5cf851SGreg Clayton //       "event": {
8272f5cf851SGreg Clayton //         "type": "string",
8282f5cf851SGreg Clayton //         "enum": [ "stopped" ]
8292f5cf851SGreg Clayton //       },
8302f5cf851SGreg Clayton //       "body": {
8312f5cf851SGreg Clayton //         "type": "object",
8322f5cf851SGreg Clayton //         "properties": {
8332f5cf851SGreg Clayton //           "reason": {
8342f5cf851SGreg Clayton //             "type": "string",
8352f5cf851SGreg Clayton //             "description": "The reason for the event. For backward
8362f5cf851SGreg Clayton //                             compatibility this string is shown in the UI if
8372f5cf851SGreg Clayton //                             the 'description' attribute is missing (but it
8382f5cf851SGreg Clayton //                             must not be translated).",
8392f5cf851SGreg Clayton //             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
8402f5cf851SGreg Clayton //           },
8412f5cf851SGreg Clayton //           "description": {
8422f5cf851SGreg Clayton //             "type": "string",
8432f5cf851SGreg Clayton //             "description": "The full reason for the event, e.g. 'Paused
8442f5cf851SGreg Clayton //                             on exception'. This string is shown in the UI
8452f5cf851SGreg Clayton //                             as is."
8462f5cf851SGreg Clayton //           },
8472f5cf851SGreg Clayton //           "threadId": {
8482f5cf851SGreg Clayton //             "type": "integer",
8492f5cf851SGreg Clayton //             "description": "The thread which was stopped."
8502f5cf851SGreg Clayton //           },
8512f5cf851SGreg Clayton //           "text": {
8522f5cf851SGreg Clayton //             "type": "string",
8532f5cf851SGreg Clayton //             "description": "Additional information. E.g. if reason is
8542f5cf851SGreg Clayton //                             'exception', text contains the exception name.
8552f5cf851SGreg Clayton //                             This string is shown in the UI."
8562f5cf851SGreg Clayton //           },
8572f5cf851SGreg Clayton //           "allThreadsStopped": {
8582f5cf851SGreg Clayton //             "type": "boolean",
8592f5cf851SGreg Clayton //             "description": "If allThreadsStopped is true, a debug adapter
8602f5cf851SGreg Clayton //                             can announce that all threads have stopped.
8612f5cf851SGreg Clayton //                             The client should use this information to
8622f5cf851SGreg Clayton //                             enable that all threads can be expanded to
8632f5cf851SGreg Clayton //                             access their stacktraces. If the attribute
8642f5cf851SGreg Clayton //                             is missing or false, only the thread with the
8652f5cf851SGreg Clayton //                             given threadId can be expanded."
8662f5cf851SGreg Clayton //           }
8672f5cf851SGreg Clayton //         },
8682f5cf851SGreg Clayton //         "required": [ "reason" ]
8692f5cf851SGreg Clayton //       }
8702f5cf851SGreg Clayton //     },
8712f5cf851SGreg Clayton //     "required": [ "event", "body" ]
8722f5cf851SGreg Clayton //   }]
8732f5cf851SGreg Clayton // }
CreateThreadStopped(lldb::SBThread & thread,uint32_t stop_id)8742f5cf851SGreg Clayton llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
8752f5cf851SGreg Clayton                                       uint32_t stop_id) {
8768b5e6991SReid Kleckner   llvm::json::Object event(CreateEventObject("stopped"));
8772f5cf851SGreg Clayton   llvm::json::Object body;
8782f5cf851SGreg Clayton   switch (thread.GetStopReason()) {
8792f5cf851SGreg Clayton   case lldb::eStopReasonTrace:
8802f5cf851SGreg Clayton   case lldb::eStopReasonPlanComplete:
8812f5cf851SGreg Clayton     body.try_emplace("reason", "step");
8822f5cf851SGreg Clayton     break;
8832f5cf851SGreg Clayton   case lldb::eStopReasonBreakpoint: {
8842f5cf851SGreg Clayton     ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
8852f5cf851SGreg Clayton     if (exc_bp) {
8862f5cf851SGreg Clayton       body.try_emplace("reason", "exception");
887a0d52cbdSNathan Lanza       EmplaceSafeString(body, "description", exc_bp->label);
8882f5cf851SGreg Clayton     } else {
8892f5cf851SGreg Clayton       body.try_emplace("reason", "breakpoint");
8909cb227f5SGreg Clayton       char desc_str[64];
8919cb227f5SGreg Clayton       uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
8929cb227f5SGreg Clayton       uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
8939cb227f5SGreg Clayton       snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
8949cb227f5SGreg Clayton                bp_id, bp_loc_id);
8959cb227f5SGreg Clayton       EmplaceSafeString(body, "description", desc_str);
8962f5cf851SGreg Clayton     }
8972f5cf851SGreg Clayton   } break;
8982f5cf851SGreg Clayton   case lldb::eStopReasonWatchpoint:
8992f5cf851SGreg Clayton   case lldb::eStopReasonInstrumentation:
9002f5cf851SGreg Clayton     body.try_emplace("reason", "breakpoint");
9012f5cf851SGreg Clayton     break;
9020b697561SWalter Erquinigo   case lldb::eStopReasonProcessorTrace:
9030b697561SWalter Erquinigo     body.try_emplace("reason", "processor trace");
9042f5cf851SGreg Clayton     break;
9050b697561SWalter Erquinigo   case lldb::eStopReasonSignal:
9062f5cf851SGreg Clayton   case lldb::eStopReasonException:
9072f5cf851SGreg Clayton     body.try_emplace("reason", "exception");
9082f5cf851SGreg Clayton     break;
9092f5cf851SGreg Clayton   case lldb::eStopReasonExec:
9102f5cf851SGreg Clayton     body.try_emplace("reason", "entry");
9112f5cf851SGreg Clayton     break;
9126c37984eSMichał Górny   case lldb::eStopReasonFork:
9136c37984eSMichał Górny     body.try_emplace("reason", "fork");
9146c37984eSMichał Górny     break;
9156c37984eSMichał Górny   case lldb::eStopReasonVFork:
9166c37984eSMichał Górny     body.try_emplace("reason", "vfork");
9176c37984eSMichał Górny     break;
9186c37984eSMichał Górny   case lldb::eStopReasonVForkDone:
9196c37984eSMichał Górny     body.try_emplace("reason", "vforkdone");
9206c37984eSMichał Górny     break;
9212f5cf851SGreg Clayton   case lldb::eStopReasonThreadExiting:
9222f5cf851SGreg Clayton   case lldb::eStopReasonInvalid:
9232f5cf851SGreg Clayton   case lldb::eStopReasonNone:
9242f5cf851SGreg Clayton     break;
9252f5cf851SGreg Clayton   }
9262f5cf851SGreg Clayton   if (stop_id == 0)
9272f5cf851SGreg Clayton     body.try_emplace("reason", "entry");
9282f5cf851SGreg Clayton   const lldb::tid_t tid = thread.GetThreadID();
9292f5cf851SGreg Clayton   body.try_emplace("threadId", (int64_t)tid);
9302f5cf851SGreg Clayton   // If no description has been set, then set it to the default thread stopped
9312f5cf851SGreg Clayton   // description. If we have breakpoints that get hit and shouldn't be reported
9322f5cf851SGreg Clayton   // as breakpoints, then they will set the description above.
9332f5cf851SGreg Clayton   if (ObjectContainsKey(body, "description")) {
9342f5cf851SGreg Clayton     char description[1024];
9352f5cf851SGreg Clayton     if (thread.GetStopDescription(description, sizeof(description))) {
936a0d52cbdSNathan Lanza       EmplaceSafeString(body, "description", std::string(description));
9372f5cf851SGreg Clayton     }
9382f5cf851SGreg Clayton   }
9392f5cf851SGreg Clayton   if (tid == g_vsc.focus_tid) {
9402f5cf851SGreg Clayton     body.try_emplace("threadCausedFocus", true);
9412f5cf851SGreg Clayton   }
9422f5cf851SGreg Clayton   body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
9432f5cf851SGreg Clayton   body.try_emplace("allThreadsStopped", true);
9442f5cf851SGreg Clayton   event.try_emplace("body", std::move(body));
9452f5cf851SGreg Clayton   return llvm::json::Value(std::move(event));
9462f5cf851SGreg Clayton }
9472f5cf851SGreg Clayton 
GetNonNullVariableName(lldb::SBValue v)9481141ba67SWalter Erquinigo const char *GetNonNullVariableName(lldb::SBValue v) {
9491141ba67SWalter Erquinigo   const char *name = v.GetName();
9501141ba67SWalter Erquinigo   return name ? name : "<null>";
9511141ba67SWalter Erquinigo }
9521141ba67SWalter Erquinigo 
CreateUniqueVariableNameForDisplay(lldb::SBValue v,bool is_name_duplicated)953c9a0754bSWalter Erquinigo std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
954c9a0754bSWalter Erquinigo                                                bool is_name_duplicated) {
955c9a0754bSWalter Erquinigo   lldb::SBStream name_builder;
9561141ba67SWalter Erquinigo   name_builder.Print(GetNonNullVariableName(v));
957c9a0754bSWalter Erquinigo   if (is_name_duplicated) {
958c9a0754bSWalter Erquinigo     lldb::SBDeclaration declaration = v.GetDeclaration();
9591141ba67SWalter Erquinigo     const char *file_name = declaration.GetFileSpec().GetFilename();
960c9a0754bSWalter Erquinigo     const uint32_t line = declaration.GetLine();
961c9a0754bSWalter Erquinigo 
9621141ba67SWalter Erquinigo     if (file_name != nullptr && line > 0)
9631141ba67SWalter Erquinigo       name_builder.Printf(" @ %s:%u", file_name, line);
9641141ba67SWalter Erquinigo     else if (const char *location = v.GetLocation())
9651141ba67SWalter Erquinigo       name_builder.Printf(" @ %s", location);
966c9a0754bSWalter Erquinigo   }
967c9a0754bSWalter Erquinigo   return name_builder.GetData();
968c9a0754bSWalter Erquinigo }
969c9a0754bSWalter Erquinigo 
9702f5cf851SGreg Clayton // "Variable": {
9712f5cf851SGreg Clayton //   "type": "object",
9722f5cf851SGreg Clayton //   "description": "A Variable is a name/value pair. Optionally a variable
9732f5cf851SGreg Clayton //                   can have a 'type' that is shown if space permits or when
9742f5cf851SGreg Clayton //                   hovering over the variable's name. An optional 'kind' is
9752f5cf851SGreg Clayton //                   used to render additional properties of the variable,
9762f5cf851SGreg Clayton //                   e.g. different icons can be used to indicate that a
9772f5cf851SGreg Clayton //                   variable is public or private. If the value is
9782f5cf851SGreg Clayton //                   structured (has children), a handle is provided to
9792f5cf851SGreg Clayton //                   retrieve the children with the VariablesRequest. If
9802f5cf851SGreg Clayton //                   the number of named or indexed children is large, the
9812f5cf851SGreg Clayton //                   numbers should be returned via the optional
9822f5cf851SGreg Clayton //                   'namedVariables' and 'indexedVariables' attributes. The
9832f5cf851SGreg Clayton //                   client can use this optional information to present the
9842f5cf851SGreg Clayton //                   children in a paged UI and fetch them in chunks.",
9852f5cf851SGreg Clayton //   "properties": {
9862f5cf851SGreg Clayton //     "name": {
9872f5cf851SGreg Clayton //       "type": "string",
9882f5cf851SGreg Clayton //       "description": "The variable's name."
9892f5cf851SGreg Clayton //     },
9902f5cf851SGreg Clayton //     "value": {
9912f5cf851SGreg Clayton //       "type": "string",
9922f5cf851SGreg Clayton //       "description": "The variable's value. This can be a multi-line text,
9932f5cf851SGreg Clayton //                       e.g. for a function the body of a function."
9942f5cf851SGreg Clayton //     },
9952f5cf851SGreg Clayton //     "type": {
9962f5cf851SGreg Clayton //       "type": "string",
9972f5cf851SGreg Clayton //       "description": "The type of the variable's value. Typically shown in
9982f5cf851SGreg Clayton //                       the UI when hovering over the value."
9992f5cf851SGreg Clayton //     },
10002f5cf851SGreg Clayton //     "presentationHint": {
10012f5cf851SGreg Clayton //       "$ref": "#/definitions/VariablePresentationHint",
10022f5cf851SGreg Clayton //       "description": "Properties of a variable that can be used to determine
10032f5cf851SGreg Clayton //                       how to render the variable in the UI."
10042f5cf851SGreg Clayton //     },
10052f5cf851SGreg Clayton //     "evaluateName": {
10062f5cf851SGreg Clayton //       "type": "string",
10072f5cf851SGreg Clayton //       "description": "Optional evaluatable name of this variable which can
10082f5cf851SGreg Clayton //                       be passed to the 'EvaluateRequest' to fetch the
10092f5cf851SGreg Clayton //                       variable's value."
10102f5cf851SGreg Clayton //     },
10112f5cf851SGreg Clayton //     "variablesReference": {
10122f5cf851SGreg Clayton //       "type": "integer",
10132f5cf851SGreg Clayton //       "description": "If variablesReference is > 0, the variable is
10142f5cf851SGreg Clayton //                       structured and its children can be retrieved by
10152f5cf851SGreg Clayton //                       passing variablesReference to the VariablesRequest."
10162f5cf851SGreg Clayton //     },
10172f5cf851SGreg Clayton //     "namedVariables": {
10182f5cf851SGreg Clayton //       "type": "integer",
10192f5cf851SGreg Clayton //       "description": "The number of named child variables. The client can
10202f5cf851SGreg Clayton //                       use this optional information to present the children
10212f5cf851SGreg Clayton //                       in a paged UI and fetch them in chunks."
10222f5cf851SGreg Clayton //     },
10232f5cf851SGreg Clayton //     "indexedVariables": {
10242f5cf851SGreg Clayton //       "type": "integer",
10252f5cf851SGreg Clayton //       "description": "The number of indexed child variables. The client
10262f5cf851SGreg Clayton //                       can use this optional information to present the
10272f5cf851SGreg Clayton //                       children in a paged UI and fetch them in chunks."
10282f5cf851SGreg Clayton //     }
10292f5cf851SGreg Clayton //   },
10302f5cf851SGreg Clayton //   "required": [ "name", "value", "variablesReference" ]
10312f5cf851SGreg Clayton // }
CreateVariable(lldb::SBValue v,int64_t variablesReference,int64_t varID,bool format_hex,bool is_name_duplicated)10322f5cf851SGreg Clayton llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1033c9a0754bSWalter Erquinigo                                  int64_t varID, bool format_hex,
1034c9a0754bSWalter Erquinigo                                  bool is_name_duplicated) {
10352f5cf851SGreg Clayton   llvm::json::Object object;
1036c9a0754bSWalter Erquinigo   EmplaceSafeString(object, "name",
1037c9a0754bSWalter Erquinigo                     CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
1038c9a0754bSWalter Erquinigo 
10392f5cf851SGreg Clayton   if (format_hex)
10402f5cf851SGreg Clayton     v.SetFormat(lldb::eFormatHex);
10412f5cf851SGreg Clayton   SetValueForKey(v, object, "value");
104291d5bfdbSGreg Clayton   auto type_obj = v.GetType();
104391d5bfdbSGreg Clayton   auto type_cstr = type_obj.GetDisplayTypeName();
104491d5bfdbSGreg Clayton   // If we have a type with many many children, we would like to be able to
104591d5bfdbSGreg Clayton   // give a hint to the IDE that the type has indexed children so that the
104691d5bfdbSGreg Clayton   // request can be broken up in grabbing only a few children at a time. We want
104791d5bfdbSGreg Clayton   // to be careful and only call "v.GetNumChildren()" if we have an array type
104891d5bfdbSGreg Clayton   // or if we have a synthetic child provider. We don't want to call
104991d5bfdbSGreg Clayton   // "v.GetNumChildren()" on all objects as class, struct and union types don't
105091d5bfdbSGreg Clayton   // need to be completed if they are never expanded. So we want to avoid
105191d5bfdbSGreg Clayton   // calling this to only cases where we it makes sense to keep performance high
105291d5bfdbSGreg Clayton   // during normal debugging.
105391d5bfdbSGreg Clayton 
105491d5bfdbSGreg Clayton   // If we have an array type, say that it is indexed and provide the number of
105591d5bfdbSGreg Clayton   // children in case we have a huge array. If we don't do this, then we might
105691d5bfdbSGreg Clayton   // take a while to produce all children at onces which can delay your debug
105791d5bfdbSGreg Clayton   // session.
105891d5bfdbSGreg Clayton   const bool is_array = type_obj.IsArrayType();
105991d5bfdbSGreg Clayton   const bool is_synthetic = v.IsSynthetic();
106091d5bfdbSGreg Clayton   if (is_array || is_synthetic) {
106191d5bfdbSGreg Clayton     const auto num_children = v.GetNumChildren();
106291d5bfdbSGreg Clayton     if (is_array) {
106391d5bfdbSGreg Clayton       object.try_emplace("indexedVariables", num_children);
106491d5bfdbSGreg Clayton     } else {
106591d5bfdbSGreg Clayton       // If a type has a synthetic child provider, then the SBType of "v" won't
106691d5bfdbSGreg Clayton       // tell us anything about what might be displayed. So we can check if the
106791d5bfdbSGreg Clayton       // first child's name is "[0]" and then we can say it is indexed.
106891d5bfdbSGreg Clayton       const char *first_child_name = v.GetChildAtIndex(0).GetName();
106991d5bfdbSGreg Clayton       if (first_child_name && strcmp(first_child_name, "[0]") == 0)
107091d5bfdbSGreg Clayton         object.try_emplace("indexedVariables", num_children);
107191d5bfdbSGreg Clayton     }
107291d5bfdbSGreg Clayton   }
1073a0d52cbdSNathan Lanza   EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
10742f5cf851SGreg Clayton   if (varID != INT64_MAX)
10752f5cf851SGreg Clayton     object.try_emplace("id", varID);
10762f5cf851SGreg Clayton   if (v.MightHaveChildren())
10772f5cf851SGreg Clayton     object.try_emplace("variablesReference", variablesReference);
10782f5cf851SGreg Clayton   else
10792f5cf851SGreg Clayton     object.try_emplace("variablesReference", (int64_t)0);
10802f5cf851SGreg Clayton   lldb::SBStream evaluateStream;
10812f5cf851SGreg Clayton   v.GetExpressionPath(evaluateStream);
10822f5cf851SGreg Clayton   const char *evaluateName = evaluateStream.GetData();
10832f5cf851SGreg Clayton   if (evaluateName && evaluateName[0])
1084a0d52cbdSNathan Lanza     EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
10852f5cf851SGreg Clayton   return llvm::json::Value(std::move(object));
10862f5cf851SGreg Clayton }
10872f5cf851SGreg Clayton 
CreateCompileUnit(lldb::SBCompileUnit unit)108877c9aafcSWalter Erquinigo llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
108977c9aafcSWalter Erquinigo   llvm::json::Object object;
109077c9aafcSWalter Erquinigo   char unit_path_arr[PATH_MAX];
109177c9aafcSWalter Erquinigo   unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
109277c9aafcSWalter Erquinigo   std::string unit_path(unit_path_arr);
109377c9aafcSWalter Erquinigo   object.try_emplace("compileUnitPath", unit_path);
109477c9aafcSWalter Erquinigo   return llvm::json::Value(std::move(object));
109577c9aafcSWalter Erquinigo }
109677c9aafcSWalter Erquinigo 
1097132e57bcSWalter Erquinigo /// See
1098132e57bcSWalter Erquinigo /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1099132e57bcSWalter Erquinigo llvm::json::Object
CreateRunInTerminalReverseRequest(const llvm::json::Object & launch_request,llvm::StringRef debug_adaptor_path,llvm::StringRef comm_file)11000f0462caSWalter Erquinigo CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
11010f0462caSWalter Erquinigo                                   llvm::StringRef debug_adaptor_path,
11020f0462caSWalter Erquinigo                                   llvm::StringRef comm_file) {
1103132e57bcSWalter Erquinigo   llvm::json::Object reverse_request;
1104132e57bcSWalter Erquinigo   reverse_request.try_emplace("type", "request");
1105132e57bcSWalter Erquinigo   reverse_request.try_emplace("command", "runInTerminal");
1106132e57bcSWalter Erquinigo 
1107132e57bcSWalter Erquinigo   llvm::json::Object run_in_terminal_args;
1108132e57bcSWalter Erquinigo   // This indicates the IDE to open an embedded terminal, instead of opening the
1109132e57bcSWalter Erquinigo   // terminal in a new window.
1110132e57bcSWalter Erquinigo   run_in_terminal_args.try_emplace("kind", "integrated");
1111132e57bcSWalter Erquinigo 
1112132e57bcSWalter Erquinigo   auto launch_request_arguments = launch_request.getObject("arguments");
1113132e57bcSWalter Erquinigo   // The program path must be the first entry in the "args" field
11140f0462caSWalter Erquinigo   std::vector<std::string> args = {
11150f0462caSWalter Erquinigo       debug_adaptor_path.str(), "--comm-file", comm_file.str(),
11160f0462caSWalter Erquinigo       "--launch-target", GetString(launch_request_arguments, "program").str()};
11170f0462caSWalter Erquinigo   std::vector<std::string> target_args =
11180f0462caSWalter Erquinigo       GetStrings(launch_request_arguments, "args");
11190f0462caSWalter Erquinigo   args.insert(args.end(), target_args.begin(), target_args.end());
1120132e57bcSWalter Erquinigo   run_in_terminal_args.try_emplace("args", args);
1121132e57bcSWalter Erquinigo 
1122132e57bcSWalter Erquinigo   const auto cwd = GetString(launch_request_arguments, "cwd");
1123132e57bcSWalter Erquinigo   if (!cwd.empty())
1124132e57bcSWalter Erquinigo     run_in_terminal_args.try_emplace("cwd", cwd);
1125132e57bcSWalter Erquinigo 
1126132e57bcSWalter Erquinigo   // We need to convert the input list of environments variables into a
1127132e57bcSWalter Erquinigo   // dictionary
1128132e57bcSWalter Erquinigo   std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1129132e57bcSWalter Erquinigo   llvm::json::Object environment;
1130132e57bcSWalter Erquinigo   for (const std::string &env : envs) {
1131869f8363SJordan Rupprecht     size_t index = env.find('=');
1132132e57bcSWalter Erquinigo     environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1133132e57bcSWalter Erquinigo   }
1134132e57bcSWalter Erquinigo   run_in_terminal_args.try_emplace("env",
1135132e57bcSWalter Erquinigo                                    llvm::json::Value(std::move(environment)));
1136132e57bcSWalter Erquinigo 
1137132e57bcSWalter Erquinigo   reverse_request.try_emplace(
1138132e57bcSWalter Erquinigo       "arguments", llvm::json::Value(std::move(run_in_terminal_args)));
1139132e57bcSWalter Erquinigo   return reverse_request;
1140132e57bcSWalter Erquinigo }
1141132e57bcSWalter Erquinigo 
JSONToString(const llvm::json::Value & json)11420f0462caSWalter Erquinigo std::string JSONToString(const llvm::json::Value &json) {
11430f0462caSWalter Erquinigo   std::string data;
11440f0462caSWalter Erquinigo   llvm::raw_string_ostream os(data);
11450f0462caSWalter Erquinigo   os << json;
11460f0462caSWalter Erquinigo   os.flush();
11470f0462caSWalter Erquinigo   return data;
11480f0462caSWalter Erquinigo }
11490f0462caSWalter Erquinigo 
11502f5cf851SGreg Clayton } // namespace lldb_vscode
1151