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