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