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