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