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