1 //===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <algorithm>
10 #include <iomanip>
11 #include <sstream>
12 
13 #include "llvm/ADT/Optional.h"
14 #include "llvm/Support/FormatAdapters.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/ScopedPrinter.h"
17 
18 #include "lldb/API/SBBreakpoint.h"
19 #include "lldb/API/SBBreakpointLocation.h"
20 #include "lldb/API/SBDeclaration.h"
21 #include "lldb/API/SBValue.h"
22 #include "lldb/Host/PosixApi.h"
23 
24 #include "ExceptionBreakpoint.h"
25 #include "JSONUtils.h"
26 #include "LLDBUtils.h"
27 #include "VSCode.h"
28 
29 namespace lldb_vscode {
30 
31 void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
32                        llvm::StringRef str) {
33   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
34     obj.try_emplace(key, str.str());
35   else
36     obj.try_emplace(key, llvm::json::fixUTF8(str));
37 }
38 
39 llvm::StringRef GetAsString(const llvm::json::Value &value) {
40   if (auto s = value.getAsString())
41     return *s;
42   return llvm::StringRef();
43 }
44 
45 // Gets a string from a JSON object using the key, or returns an empty string.
46 llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
47   if (llvm::Optional<llvm::StringRef> value = obj.getString(key))
48     return *value;
49   return llvm::StringRef();
50 }
51 
52 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
53   if (obj == nullptr)
54     return llvm::StringRef();
55   return GetString(*obj, key);
56 }
57 
58 // Gets an unsigned integer from a JSON object using the key, or returns the
59 // specified fail value.
60 uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
61                      uint64_t fail_value) {
62   if (auto value = obj.getInteger(key))
63     return (uint64_t)*value;
64   return fail_value;
65 }
66 
67 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
68                      uint64_t fail_value) {
69   if (obj == nullptr)
70     return fail_value;
71   return GetUnsigned(*obj, key, fail_value);
72 }
73 
74 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
75                 bool fail_value) {
76   if (auto value = obj.getBoolean(key))
77     return *value;
78   if (auto value = obj.getInteger(key))
79     return *value != 0;
80   return fail_value;
81 }
82 
83 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
84                 bool fail_value) {
85   if (obj == nullptr)
86     return fail_value;
87   return GetBoolean(*obj, key, fail_value);
88 }
89 
90 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
91                   int64_t fail_value) {
92   if (auto value = obj.getInteger(key))
93     return *value;
94   return fail_value;
95 }
96 
97 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
98                   int64_t fail_value) {
99   if (obj == nullptr)
100     return fail_value;
101   return GetSigned(*obj, key, fail_value);
102 }
103 
104 bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
105   return obj.find(key) != obj.end();
106 }
107 
108 std::vector<std::string> GetStrings(const llvm::json::Object *obj,
109                                     llvm::StringRef key) {
110   std::vector<std::string> strs;
111   auto json_array = obj->getArray(key);
112   if (!json_array)
113     return strs;
114   for (const auto &value : *json_array) {
115     switch (value.kind()) {
116     case llvm::json::Value::String:
117       strs.push_back(value.getAsString()->str());
118       break;
119     case llvm::json::Value::Number:
120     case llvm::json::Value::Boolean:
121       strs.push_back(llvm::to_string(value));
122       break;
123     case llvm::json::Value::Null:
124     case llvm::json::Value::Object:
125     case llvm::json::Value::Array:
126       break;
127     }
128   }
129   return strs;
130 }
131 
132 void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
133                     llvm::StringRef key) {
134 
135   llvm::StringRef value = v.GetValue();
136   llvm::StringRef summary = v.GetSummary();
137   llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
138 
139   std::string result;
140   llvm::raw_string_ostream strm(result);
141   if (!value.empty()) {
142     strm << value;
143     if (!summary.empty())
144       strm << ' ' << summary;
145   } else if (!summary.empty()) {
146     strm << ' ' << summary;
147   } else if (!type_name.empty()) {
148     strm << type_name;
149     lldb::addr_t address = v.GetLoadAddress();
150     if (address != LLDB_INVALID_ADDRESS)
151       strm << " @ " << llvm::format_hex(address, 0);
152   }
153   strm.flush();
154   EmplaceSafeString(object, key, result);
155 }
156 
157 void FillResponse(const llvm::json::Object &request,
158                   llvm::json::Object &response) {
159   // Fill in all of the needed response fields to a "request" and set "success"
160   // to true by default.
161   response.try_emplace("type", "response");
162   response.try_emplace("seq", (int64_t)0);
163   EmplaceSafeString(response, "command", GetString(request, "command"));
164   const int64_t seq = GetSigned(request, "seq", 0);
165   response.try_emplace("request_seq", seq);
166   response.try_emplace("success", true);
167 }
168 
169 // "Scope": {
170 //   "type": "object",
171 //   "description": "A Scope is a named container for variables. Optionally
172 //                   a scope can map to a source or a range within a source.",
173 //   "properties": {
174 //     "name": {
175 //       "type": "string",
176 //       "description": "Name of the scope such as 'Arguments', 'Locals'."
177 //     },
178 //     "presentationHint": {
179 //       "type": "string",
180 //       "description": "An optional hint for how to present this scope in the
181 //                       UI. If this attribute is missing, the scope is shown
182 //                       with a generic UI.",
183 //       "_enum": [ "arguments", "locals", "registers" ],
184 //     },
185 //     "variablesReference": {
186 //       "type": "integer",
187 //       "description": "The variables of this scope can be retrieved by
188 //                       passing the value of variablesReference to the
189 //                       VariablesRequest."
190 //     },
191 //     "namedVariables": {
192 //       "type": "integer",
193 //       "description": "The number of named variables in this scope. The
194 //                       client can use this optional information to present
195 //                       the variables in a paged UI and fetch them in chunks."
196 //     },
197 //     "indexedVariables": {
198 //       "type": "integer",
199 //       "description": "The number of indexed variables in this scope. The
200 //                       client can use this optional information to present
201 //                       the variables in a paged UI and fetch them in chunks."
202 //     },
203 //     "expensive": {
204 //       "type": "boolean",
205 //       "description": "If true, the number of variables in this scope is
206 //                       large or expensive to retrieve."
207 //     },
208 //     "source": {
209 //       "$ref": "#/definitions/Source",
210 //       "description": "Optional source for this scope."
211 //     },
212 //     "line": {
213 //       "type": "integer",
214 //       "description": "Optional start line of the range covered by this
215 //                       scope."
216 //     },
217 //     "column": {
218 //       "type": "integer",
219 //       "description": "Optional start column of the range covered by this
220 //                       scope."
221 //     },
222 //     "endLine": {
223 //       "type": "integer",
224 //       "description": "Optional end line of the range covered by this scope."
225 //     },
226 //     "endColumn": {
227 //       "type": "integer",
228 //       "description": "Optional end column of the range covered by this
229 //                       scope."
230 //     }
231 //   },
232 //   "required": [ "name", "variablesReference", "expensive" ]
233 // }
234 llvm::json::Value CreateScope(const llvm::StringRef name,
235                               int64_t variablesReference,
236                               int64_t namedVariables, bool expensive) {
237   llvm::json::Object object;
238   EmplaceSafeString(object, "name", name.str());
239 
240   // TODO: Support "arguments" scope. At the moment lldb-vscode includes the
241   // arguments into the "locals" scope.
242   if (variablesReference == VARREF_LOCALS) {
243     object.try_emplace("presentationHint", "locals");
244   } else if (variablesReference == VARREF_REGS) {
245     object.try_emplace("presentationHint", "registers");
246   }
247 
248   object.try_emplace("variablesReference", variablesReference);
249   object.try_emplace("expensive", expensive);
250   object.try_emplace("namedVariables", namedVariables);
251   return llvm::json::Value(std::move(object));
252 }
253 
254 // "Breakpoint": {
255 //   "type": "object",
256 //   "description": "Information about a Breakpoint created in setBreakpoints
257 //                   or setFunctionBreakpoints.",
258 //   "properties": {
259 //     "id": {
260 //       "type": "integer",
261 //       "description": "An optional unique identifier for the breakpoint."
262 //     },
263 //     "verified": {
264 //       "type": "boolean",
265 //       "description": "If true breakpoint could be set (but not necessarily
266 //                       at the desired location)."
267 //     },
268 //     "message": {
269 //       "type": "string",
270 //       "description": "An optional message about the state of the breakpoint.
271 //                       This is shown to the user and can be used to explain
272 //                       why a breakpoint could not be verified."
273 //     },
274 //     "source": {
275 //       "$ref": "#/definitions/Source",
276 //       "description": "The source where the breakpoint is located."
277 //     },
278 //     "line": {
279 //       "type": "integer",
280 //       "description": "The start line of the actual range covered by the
281 //                       breakpoint."
282 //     },
283 //     "column": {
284 //       "type": "integer",
285 //       "description": "An optional start column of the actual range covered
286 //                       by the breakpoint."
287 //     },
288 //     "endLine": {
289 //       "type": "integer",
290 //       "description": "An optional end line of the actual range covered by
291 //                       the breakpoint."
292 //     },
293 //     "endColumn": {
294 //       "type": "integer",
295 //       "description": "An optional end column of the actual range covered by
296 //                       the breakpoint. If no end line is given, then the end
297 //                       column is assumed to be in the start line."
298 //     }
299 //   },
300 //   "required": [ "verified" ]
301 // }
302 llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
303                                    llvm::Optional<llvm::StringRef> request_path,
304                                    llvm::Optional<uint32_t> request_line) {
305   // Each breakpoint location is treated as a separate breakpoint for VS code.
306   // They don't have the notion of a single breakpoint with multiple locations.
307   llvm::json::Object object;
308   if (!bp.IsValid())
309     return llvm::json::Value(std::move(object));
310 
311   object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
312   object.try_emplace("id", bp.GetID());
313   // VS Code DAP doesn't currently allow one breakpoint to have multiple
314   // locations so we just report the first one. If we report all locations
315   // then the IDE starts showing the wrong line numbers and locations for
316   // other source file and line breakpoints in the same file.
317 
318   // Below we search for the first resolved location in a breakpoint and report
319   // this as the breakpoint location since it will have a complete location
320   // that is at least loaded in the current process.
321   lldb::SBBreakpointLocation bp_loc;
322   const auto num_locs = bp.GetNumLocations();
323   for (size_t i = 0; i < num_locs; ++i) {
324     bp_loc = bp.GetLocationAtIndex(i);
325     if (bp_loc.IsResolved())
326       break;
327   }
328   // If not locations are resolved, use the first location.
329   if (!bp_loc.IsResolved())
330     bp_loc = bp.GetLocationAtIndex(0);
331   auto bp_addr = bp_loc.GetAddress();
332 
333   if (request_path)
334     object.try_emplace("source", CreateSource(*request_path));
335 
336   if (bp_addr.IsValid()) {
337     auto line_entry = bp_addr.GetLineEntry();
338     const auto line = line_entry.GetLine();
339     if (line != UINT32_MAX)
340       object.try_emplace("line", line);
341     object.try_emplace("source", CreateSource(line_entry));
342   }
343   // We try to add request_line as a fallback
344   if (request_line)
345     object.try_emplace("line", *request_line);
346   return llvm::json::Value(std::move(object));
347 }
348 
349 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
350   uint64_t debug_info_size = 0;
351   llvm::StringRef section_name(section.GetName());
352   if (section_name.startswith(".debug") || section_name.startswith("__debug") ||
353       section_name.startswith(".apple") || section_name.startswith("__apple"))
354     debug_info_size += section.GetFileByteSize();
355   size_t num_sub_sections = section.GetNumSubSections();
356   for (size_t i = 0; i < num_sub_sections; i++) {
357     debug_info_size +=
358         GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
359   }
360   return debug_info_size;
361 }
362 
363 static uint64_t GetDebugInfoSize(lldb::SBModule module) {
364   uint64_t debug_info_size = 0;
365   size_t num_sections = module.GetNumSections();
366   for (size_t i = 0; i < num_sections; i++) {
367     debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
368   }
369   return debug_info_size;
370 }
371 
372 static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
373   std::ostringstream oss;
374   oss << std::fixed << std::setprecision(1);
375   if (debug_info < 1024) {
376     oss << debug_info << "B";
377   } else if (debug_info < 1024 * 1024) {
378     double kb = double(debug_info) / 1024.0;
379     oss << kb << "KB";
380   } else if (debug_info < 1024 * 1024 * 1024) {
381     double mb = double(debug_info) / (1024.0 * 1024.0);
382     oss << mb << "MB";
383   } else {
384     double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
385     oss << gb << "GB";
386   }
387   return oss.str();
388 }
389 llvm::json::Value CreateModule(lldb::SBModule &module) {
390   llvm::json::Object object;
391   if (!module.IsValid())
392     return llvm::json::Value(std::move(object));
393   const char *uuid = module.GetUUIDString();
394   object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
395   object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
396   char module_path_arr[PATH_MAX];
397   module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
398   std::string module_path(module_path_arr);
399   object.try_emplace("path", module_path);
400   if (module.GetNumCompileUnits() > 0) {
401     std::string symbol_str = "Symbols loaded.";
402     std::string debug_info_size;
403     uint64_t debug_info = GetDebugInfoSize(module);
404     if (debug_info > 0) {
405       debug_info_size = ConvertDebugInfoSizeToString(debug_info);
406     }
407     object.try_emplace("symbolStatus", symbol_str);
408     object.try_emplace("debugInfoSize", debug_info_size);
409     char symbol_path_arr[PATH_MAX];
410     module.GetSymbolFileSpec().GetPath(symbol_path_arr,
411                                        sizeof(symbol_path_arr));
412     std::string symbol_path(symbol_path_arr);
413     object.try_emplace("symbolFilePath", symbol_path);
414   } else {
415     object.try_emplace("symbolStatus", "Symbols not found.");
416   }
417   std::string loaded_addr = std::to_string(
418       module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target));
419   object.try_emplace("addressRange", loaded_addr);
420   std::string version_str;
421   uint32_t version_nums[3];
422   uint32_t num_versions =
423       module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
424   for (uint32_t i = 0; i < num_versions; ++i) {
425     if (!version_str.empty())
426       version_str += ".";
427     version_str += std::to_string(version_nums[i]);
428   }
429   if (!version_str.empty())
430     object.try_emplace("version", version_str);
431   return llvm::json::Value(std::move(object));
432 }
433 
434 void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
435                       llvm::Optional<llvm::StringRef> request_path,
436                       llvm::Optional<uint32_t> request_line) {
437   breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
438 }
439 
440 // "Event": {
441 //   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
442 //     "type": "object",
443 //     "description": "Server-initiated event.",
444 //     "properties": {
445 //       "type": {
446 //         "type": "string",
447 //         "enum": [ "event" ]
448 //       },
449 //       "event": {
450 //         "type": "string",
451 //         "description": "Type of event."
452 //       },
453 //       "body": {
454 //         "type": [ "array", "boolean", "integer", "null", "number" ,
455 //                   "object", "string" ],
456 //         "description": "Event-specific information."
457 //       }
458 //     },
459 //     "required": [ "type", "event" ]
460 //   }]
461 // },
462 // "ProtocolMessage": {
463 //   "type": "object",
464 //   "description": "Base class of requests, responses, and events.",
465 //   "properties": {
466 //         "seq": {
467 //           "type": "integer",
468 //           "description": "Sequence number."
469 //         },
470 //         "type": {
471 //           "type": "string",
472 //           "description": "Message type.",
473 //           "_enum": [ "request", "response", "event" ]
474 //         }
475 //   },
476 //   "required": [ "seq", "type" ]
477 // }
478 llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
479   llvm::json::Object event;
480   event.try_emplace("seq", 0);
481   event.try_emplace("type", "event");
482   EmplaceSafeString(event, "event", event_name);
483   return event;
484 }
485 
486 // "ExceptionBreakpointsFilter": {
487 //   "type": "object",
488 //   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
489 //                   option for configuring how exceptions are dealt with.",
490 //   "properties": {
491 //     "filter": {
492 //       "type": "string",
493 //       "description": "The internal ID of the filter. This value is passed
494 //                       to the setExceptionBreakpoints request."
495 //     },
496 //     "label": {
497 //       "type": "string",
498 //       "description": "The name of the filter. This will be shown in the UI."
499 //     },
500 //     "default": {
501 //       "type": "boolean",
502 //       "description": "Initial value of the filter. If not specified a value
503 //                       'false' is assumed."
504 //     }
505 //   },
506 //   "required": [ "filter", "label" ]
507 // }
508 llvm::json::Value
509 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
510   llvm::json::Object object;
511   EmplaceSafeString(object, "filter", bp.filter);
512   EmplaceSafeString(object, "label", bp.label);
513   object.try_emplace("default", bp.default_value);
514   return llvm::json::Value(std::move(object));
515 }
516 
517 // "Source": {
518 //   "type": "object",
519 //   "description": "A Source is a descriptor for source code. It is returned
520 //                   from the debug adapter as part of a StackFrame and it is
521 //                   used by clients when specifying breakpoints.",
522 //   "properties": {
523 //     "name": {
524 //       "type": "string",
525 //       "description": "The short name of the source. Every source returned
526 //                       from the debug adapter has a name. When sending a
527 //                       source to the debug adapter this name is optional."
528 //     },
529 //     "path": {
530 //       "type": "string",
531 //       "description": "The path of the source to be shown in the UI. It is
532 //                       only used to locate and load the content of the
533 //                       source if no sourceReference is specified (or its
534 //                       value is 0)."
535 //     },
536 //     "sourceReference": {
537 //       "type": "number",
538 //       "description": "If sourceReference > 0 the contents of the source must
539 //                       be retrieved through the SourceRequest (even if a path
540 //                       is specified). A sourceReference is only valid for a
541 //                       session, so it must not be used to persist a source."
542 //     },
543 //     "presentationHint": {
544 //       "type": "string",
545 //       "description": "An optional hint for how to present the source in the
546 //                       UI. A value of 'deemphasize' can be used to indicate
547 //                       that the source is not available or that it is
548 //                       skipped on stepping.",
549 //       "enum": [ "normal", "emphasize", "deemphasize" ]
550 //     },
551 //     "origin": {
552 //       "type": "string",
553 //       "description": "The (optional) origin of this source: possible values
554 //                       'internal module', 'inlined content from source map',
555 //                       etc."
556 //     },
557 //     "sources": {
558 //       "type": "array",
559 //       "items": {
560 //         "$ref": "#/definitions/Source"
561 //       },
562 //       "description": "An optional list of sources that are related to this
563 //                       source. These may be the source that generated this
564 //                       source."
565 //     },
566 //     "adapterData": {
567 //       "type":["array","boolean","integer","null","number","object","string"],
568 //       "description": "Optional data that a debug adapter might want to loop
569 //                       through the client. The client should leave the data
570 //                       intact and persist it across sessions. The client
571 //                       should not interpret the data."
572 //     },
573 //     "checksums": {
574 //       "type": "array",
575 //       "items": {
576 //         "$ref": "#/definitions/Checksum"
577 //       },
578 //       "description": "The checksums associated with this file."
579 //     }
580 //   }
581 // }
582 llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
583   llvm::json::Object object;
584   lldb::SBFileSpec file = line_entry.GetFileSpec();
585   if (file.IsValid()) {
586     const char *name = file.GetFilename();
587     if (name)
588       EmplaceSafeString(object, "name", name);
589     char path[PATH_MAX] = "";
590     file.GetPath(path, sizeof(path));
591     if (path[0]) {
592       EmplaceSafeString(object, "path", std::string(path));
593     }
594   }
595   return llvm::json::Value(std::move(object));
596 }
597 
598 llvm::json::Value CreateSource(llvm::StringRef source_path) {
599   llvm::json::Object source;
600   llvm::StringRef name = llvm::sys::path::filename(source_path);
601   EmplaceSafeString(source, "name", name);
602   EmplaceSafeString(source, "path", source_path);
603   return llvm::json::Value(std::move(source));
604 }
605 
606 llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
607   disasm_line = 0;
608   auto line_entry = frame.GetLineEntry();
609   if (line_entry.GetFileSpec().IsValid())
610     return CreateSource(line_entry);
611 
612   llvm::json::Object object;
613   const auto pc = frame.GetPC();
614 
615   lldb::SBInstructionList insts;
616   lldb::SBFunction function = frame.GetFunction();
617   lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
618   if (function.IsValid()) {
619     low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
620     auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
621     if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
622       // We have this disassembly cached already, return the existing
623       // sourceReference
624       object.try_emplace("sourceReference", addr_srcref->second);
625       disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
626     } else {
627       insts = function.GetInstructions(g_vsc.target);
628     }
629   } else {
630     lldb::SBSymbol symbol = frame.GetSymbol();
631     if (symbol.IsValid()) {
632       low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
633       auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
634       if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
635         // We have this disassembly cached already, return the existing
636         // sourceReference
637         object.try_emplace("sourceReference", addr_srcref->second);
638         disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
639       } else {
640         insts = symbol.GetInstructions(g_vsc.target);
641       }
642     }
643   }
644   const auto num_insts = insts.GetSize();
645   if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
646     EmplaceSafeString(object, "name", frame.GetFunctionName());
647     SourceReference source;
648     llvm::raw_string_ostream src_strm(source.content);
649     std::string line;
650     for (size_t i = 0; i < num_insts; ++i) {
651       lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
652       const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
653       const char *m = inst.GetMnemonic(g_vsc.target);
654       const char *o = inst.GetOperands(g_vsc.target);
655       const char *c = inst.GetComment(g_vsc.target);
656       if (pc == inst_addr)
657         disasm_line = i + 1;
658       const auto inst_offset = inst_addr - low_pc;
659       int spaces = 0;
660       if (inst_offset < 10)
661         spaces = 3;
662       else if (inst_offset < 100)
663         spaces = 2;
664       else if (inst_offset < 1000)
665         spaces = 1;
666       line.clear();
667       llvm::raw_string_ostream line_strm(line);
668       line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
669                                  inst_offset, llvm::fmt_repeat(' ', spaces), m,
670                                  o);
671 
672       // If there is a comment append it starting at column 60 or after one
673       // space past the last char
674       const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
675       if (c && c[0]) {
676         if (line.size() < comment_row)
677           line_strm.indent(comment_row - line_strm.str().size());
678         line_strm << " # " << c;
679       }
680       src_strm << line_strm.str() << "\n";
681       source.addr_to_line[inst_addr] = i + 1;
682     }
683     // Flush the source stream
684     src_strm.str();
685     auto sourceReference = VSCode::GetNextSourceReference();
686     g_vsc.source_map[sourceReference] = std::move(source);
687     g_vsc.addr_to_source_ref[low_pc] = sourceReference;
688     object.try_emplace("sourceReference", sourceReference);
689   }
690   return llvm::json::Value(std::move(object));
691 }
692 
693 // "StackFrame": {
694 //   "type": "object",
695 //   "description": "A Stackframe contains the source location.",
696 //   "properties": {
697 //     "id": {
698 //       "type": "integer",
699 //       "description": "An identifier for the stack frame. It must be unique
700 //                       across all threads. This id can be used to retrieve
701 //                       the scopes of the frame with the 'scopesRequest' or
702 //                       to restart the execution of a stackframe."
703 //     },
704 //     "name": {
705 //       "type": "string",
706 //       "description": "The name of the stack frame, typically a method name."
707 //     },
708 //     "source": {
709 //       "$ref": "#/definitions/Source",
710 //       "description": "The optional source of the frame."
711 //     },
712 //     "line": {
713 //       "type": "integer",
714 //       "description": "The line within the file of the frame. If source is
715 //                       null or doesn't exist, line is 0 and must be ignored."
716 //     },
717 //     "column": {
718 //       "type": "integer",
719 //       "description": "The column within the line. If source is null or
720 //                       doesn't exist, column is 0 and must be ignored."
721 //     },
722 //     "endLine": {
723 //       "type": "integer",
724 //       "description": "An optional end line of the range covered by the
725 //                       stack frame."
726 //     },
727 //     "endColumn": {
728 //       "type": "integer",
729 //       "description": "An optional end column of the range covered by the
730 //                       stack frame."
731 //     },
732 //     "moduleId": {
733 //       "type": ["integer", "string"],
734 //       "description": "The module associated with this frame, if any."
735 //     },
736 //     "presentationHint": {
737 //       "type": "string",
738 //       "enum": [ "normal", "label", "subtle" ],
739 //       "description": "An optional hint for how to present this frame in
740 //                       the UI. A value of 'label' can be used to indicate
741 //                       that the frame is an artificial frame that is used
742 //                       as a visual label or separator. A value of 'subtle'
743 //                       can be used to change the appearance of a frame in
744 //                       a 'subtle' way."
745 //     }
746 //   },
747 //   "required": [ "id", "name", "line", "column" ]
748 // }
749 llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
750   llvm::json::Object object;
751   int64_t frame_id = MakeVSCodeFrameID(frame);
752   object.try_emplace("id", frame_id);
753   EmplaceSafeString(object, "name", frame.GetFunctionName());
754   int64_t disasm_line = 0;
755   object.try_emplace("source", CreateSource(frame, disasm_line));
756 
757   auto line_entry = frame.GetLineEntry();
758   if (disasm_line > 0) {
759     object.try_emplace("line", disasm_line);
760   } else {
761     auto line = line_entry.GetLine();
762     if (line == UINT32_MAX)
763       line = 0;
764     object.try_emplace("line", line);
765   }
766   object.try_emplace("column", line_entry.GetColumn());
767   return llvm::json::Value(std::move(object));
768 }
769 
770 // "Thread": {
771 //   "type": "object",
772 //   "description": "A Thread",
773 //   "properties": {
774 //     "id": {
775 //       "type": "integer",
776 //       "description": "Unique identifier for the thread."
777 //     },
778 //     "name": {
779 //       "type": "string",
780 //       "description": "A name of the thread."
781 //     }
782 //   },
783 //   "required": [ "id", "name" ]
784 // }
785 llvm::json::Value CreateThread(lldb::SBThread &thread) {
786   llvm::json::Object object;
787   object.try_emplace("id", (int64_t)thread.GetThreadID());
788   char thread_str[64];
789   snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
790   const char *name = thread.GetName();
791   if (name) {
792     std::string thread_with_name(thread_str);
793     thread_with_name += ' ';
794     thread_with_name += name;
795     EmplaceSafeString(object, "name", thread_with_name);
796   } else {
797     EmplaceSafeString(object, "name", std::string(thread_str));
798   }
799   return llvm::json::Value(std::move(object));
800 }
801 
802 // "StoppedEvent": {
803 //   "allOf": [ { "$ref": "#/definitions/Event" }, {
804 //     "type": "object",
805 //     "description": "Event message for 'stopped' event type. The event
806 //                     indicates that the execution of the debuggee has stopped
807 //                     due to some condition. This can be caused by a break
808 //                     point previously set, a stepping action has completed,
809 //                     by executing a debugger statement etc.",
810 //     "properties": {
811 //       "event": {
812 //         "type": "string",
813 //         "enum": [ "stopped" ]
814 //       },
815 //       "body": {
816 //         "type": "object",
817 //         "properties": {
818 //           "reason": {
819 //             "type": "string",
820 //             "description": "The reason for the event. For backward
821 //                             compatibility this string is shown in the UI if
822 //                             the 'description' attribute is missing (but it
823 //                             must not be translated).",
824 //             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
825 //           },
826 //           "description": {
827 //             "type": "string",
828 //             "description": "The full reason for the event, e.g. 'Paused
829 //                             on exception'. This string is shown in the UI
830 //                             as is."
831 //           },
832 //           "threadId": {
833 //             "type": "integer",
834 //             "description": "The thread which was stopped."
835 //           },
836 //           "text": {
837 //             "type": "string",
838 //             "description": "Additional information. E.g. if reason is
839 //                             'exception', text contains the exception name.
840 //                             This string is shown in the UI."
841 //           },
842 //           "allThreadsStopped": {
843 //             "type": "boolean",
844 //             "description": "If allThreadsStopped is true, a debug adapter
845 //                             can announce that all threads have stopped.
846 //                             The client should use this information to
847 //                             enable that all threads can be expanded to
848 //                             access their stacktraces. If the attribute
849 //                             is missing or false, only the thread with the
850 //                             given threadId can be expanded."
851 //           }
852 //         },
853 //         "required": [ "reason" ]
854 //       }
855 //     },
856 //     "required": [ "event", "body" ]
857 //   }]
858 // }
859 llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
860                                       uint32_t stop_id) {
861   llvm::json::Object event(CreateEventObject("stopped"));
862   llvm::json::Object body;
863   switch (thread.GetStopReason()) {
864   case lldb::eStopReasonTrace:
865   case lldb::eStopReasonPlanComplete:
866     body.try_emplace("reason", "step");
867     break;
868   case lldb::eStopReasonBreakpoint: {
869     ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
870     if (exc_bp) {
871       body.try_emplace("reason", "exception");
872       EmplaceSafeString(body, "description", exc_bp->label);
873     } else {
874       body.try_emplace("reason", "breakpoint");
875       char desc_str[64];
876       uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
877       uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
878       snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
879                bp_id, bp_loc_id);
880       EmplaceSafeString(body, "description", desc_str);
881     }
882   } break;
883   case lldb::eStopReasonWatchpoint:
884   case lldb::eStopReasonInstrumentation:
885     body.try_emplace("reason", "breakpoint");
886     break;
887   case lldb::eStopReasonProcessorTrace:
888     body.try_emplace("reason", "processor trace");
889     break;
890   case lldb::eStopReasonSignal:
891   case lldb::eStopReasonException:
892     body.try_emplace("reason", "exception");
893     break;
894   case lldb::eStopReasonExec:
895     body.try_emplace("reason", "entry");
896     break;
897   case lldb::eStopReasonFork:
898     body.try_emplace("reason", "fork");
899     break;
900   case lldb::eStopReasonVFork:
901     body.try_emplace("reason", "vfork");
902     break;
903   case lldb::eStopReasonVForkDone:
904     body.try_emplace("reason", "vforkdone");
905     break;
906   case lldb::eStopReasonThreadExiting:
907   case lldb::eStopReasonInvalid:
908   case lldb::eStopReasonNone:
909     break;
910   }
911   if (stop_id == 0)
912     body.try_emplace("reason", "entry");
913   const lldb::tid_t tid = thread.GetThreadID();
914   body.try_emplace("threadId", (int64_t)tid);
915   // If no description has been set, then set it to the default thread stopped
916   // description. If we have breakpoints that get hit and shouldn't be reported
917   // as breakpoints, then they will set the description above.
918   if (ObjectContainsKey(body, "description")) {
919     char description[1024];
920     if (thread.GetStopDescription(description, sizeof(description))) {
921       EmplaceSafeString(body, "description", std::string(description));
922     }
923   }
924   if (tid == g_vsc.focus_tid) {
925     body.try_emplace("threadCausedFocus", true);
926   }
927   body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
928   body.try_emplace("allThreadsStopped", true);
929   event.try_emplace("body", std::move(body));
930   return llvm::json::Value(std::move(event));
931 }
932 
933 const char *GetNonNullVariableName(lldb::SBValue v) {
934   const char *name = v.GetName();
935   return name ? name : "<null>";
936 }
937 
938 std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
939                                                bool is_name_duplicated) {
940   lldb::SBStream name_builder;
941   name_builder.Print(GetNonNullVariableName(v));
942   if (is_name_duplicated) {
943     lldb::SBDeclaration declaration = v.GetDeclaration();
944     const char *file_name = declaration.GetFileSpec().GetFilename();
945     const uint32_t line = declaration.GetLine();
946 
947     if (file_name != nullptr && line > 0)
948       name_builder.Printf(" @ %s:%u", file_name, line);
949     else if (const char *location = v.GetLocation())
950       name_builder.Printf(" @ %s", location);
951   }
952   return name_builder.GetData();
953 }
954 
955 // "Variable": {
956 //   "type": "object",
957 //   "description": "A Variable is a name/value pair. Optionally a variable
958 //                   can have a 'type' that is shown if space permits or when
959 //                   hovering over the variable's name. An optional 'kind' is
960 //                   used to render additional properties of the variable,
961 //                   e.g. different icons can be used to indicate that a
962 //                   variable is public or private. If the value is
963 //                   structured (has children), a handle is provided to
964 //                   retrieve the children with the VariablesRequest. If
965 //                   the number of named or indexed children is large, the
966 //                   numbers should be returned via the optional
967 //                   'namedVariables' and 'indexedVariables' attributes. The
968 //                   client can use this optional information to present the
969 //                   children in a paged UI and fetch them in chunks.",
970 //   "properties": {
971 //     "name": {
972 //       "type": "string",
973 //       "description": "The variable's name."
974 //     },
975 //     "value": {
976 //       "type": "string",
977 //       "description": "The variable's value. This can be a multi-line text,
978 //                       e.g. for a function the body of a function."
979 //     },
980 //     "type": {
981 //       "type": "string",
982 //       "description": "The type of the variable's value. Typically shown in
983 //                       the UI when hovering over the value."
984 //     },
985 //     "presentationHint": {
986 //       "$ref": "#/definitions/VariablePresentationHint",
987 //       "description": "Properties of a variable that can be used to determine
988 //                       how to render the variable in the UI."
989 //     },
990 //     "evaluateName": {
991 //       "type": "string",
992 //       "description": "Optional evaluatable name of this variable which can
993 //                       be passed to the 'EvaluateRequest' to fetch the
994 //                       variable's value."
995 //     },
996 //     "variablesReference": {
997 //       "type": "integer",
998 //       "description": "If variablesReference is > 0, the variable is
999 //                       structured and its children can be retrieved by
1000 //                       passing variablesReference to the VariablesRequest."
1001 //     },
1002 //     "namedVariables": {
1003 //       "type": "integer",
1004 //       "description": "The number of named child variables. The client can
1005 //                       use this optional information to present the children
1006 //                       in a paged UI and fetch them in chunks."
1007 //     },
1008 //     "indexedVariables": {
1009 //       "type": "integer",
1010 //       "description": "The number of indexed child variables. The client
1011 //                       can use this optional information to present the
1012 //                       children in a paged UI and fetch them in chunks."
1013 //     }
1014 //   },
1015 //   "required": [ "name", "value", "variablesReference" ]
1016 // }
1017 llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1018                                  int64_t varID, bool format_hex,
1019                                  bool is_name_duplicated) {
1020   llvm::json::Object object;
1021   EmplaceSafeString(object, "name",
1022                     CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
1023 
1024   if (format_hex)
1025     v.SetFormat(lldb::eFormatHex);
1026   SetValueForKey(v, object, "value");
1027   auto type_cstr = v.GetType().GetDisplayTypeName();
1028   EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
1029   if (varID != INT64_MAX)
1030     object.try_emplace("id", varID);
1031   if (v.MightHaveChildren())
1032     object.try_emplace("variablesReference", variablesReference);
1033   else
1034     object.try_emplace("variablesReference", (int64_t)0);
1035   lldb::SBStream evaluateStream;
1036   v.GetExpressionPath(evaluateStream);
1037   const char *evaluateName = evaluateStream.GetData();
1038   if (evaluateName && evaluateName[0])
1039     EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
1040   return llvm::json::Value(std::move(object));
1041 }
1042 
1043 llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
1044   llvm::json::Object object;
1045   char unit_path_arr[PATH_MAX];
1046   unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
1047   std::string unit_path(unit_path_arr);
1048   object.try_emplace("compileUnitPath", unit_path);
1049   return llvm::json::Value(std::move(object));
1050 }
1051 
1052 /// See
1053 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1054 llvm::json::Object
1055 CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
1056                                   llvm::StringRef debug_adaptor_path,
1057                                   llvm::StringRef comm_file) {
1058   llvm::json::Object reverse_request;
1059   reverse_request.try_emplace("type", "request");
1060   reverse_request.try_emplace("command", "runInTerminal");
1061 
1062   llvm::json::Object run_in_terminal_args;
1063   // This indicates the IDE to open an embedded terminal, instead of opening the
1064   // terminal in a new window.
1065   run_in_terminal_args.try_emplace("kind", "integrated");
1066 
1067   auto launch_request_arguments = launch_request.getObject("arguments");
1068   // The program path must be the first entry in the "args" field
1069   std::vector<std::string> args = {
1070       debug_adaptor_path.str(), "--comm-file", comm_file.str(),
1071       "--launch-target", GetString(launch_request_arguments, "program").str()};
1072   std::vector<std::string> target_args =
1073       GetStrings(launch_request_arguments, "args");
1074   args.insert(args.end(), target_args.begin(), target_args.end());
1075   run_in_terminal_args.try_emplace("args", args);
1076 
1077   const auto cwd = GetString(launch_request_arguments, "cwd");
1078   if (!cwd.empty())
1079     run_in_terminal_args.try_emplace("cwd", cwd);
1080 
1081   // We need to convert the input list of environments variables into a
1082   // dictionary
1083   std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1084   llvm::json::Object environment;
1085   for (const std::string &env : envs) {
1086     size_t index = env.find('=');
1087     environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1088   }
1089   run_in_terminal_args.try_emplace("env",
1090                                    llvm::json::Value(std::move(environment)));
1091 
1092   reverse_request.try_emplace(
1093       "arguments", llvm::json::Value(std::move(run_in_terminal_args)));
1094   return reverse_request;
1095 }
1096 
1097 std::string JSONToString(const llvm::json::Value &json) {
1098   std::string data;
1099   llvm::raw_string_ostream os(data);
1100   os << json;
1101   os.flush();
1102   return data;
1103 }
1104 
1105 } // namespace lldb_vscode
1106