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