Bump version to 19.1.0 (final)
[llvm-project.git] / lldb / tools / lldb-dap / JSONUtils.cpp
blob544e9ffb172bfec01d5f0ef812f19f64bd2ecb34
1 //===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include <algorithm>
10 #include <iomanip>
11 #include <optional>
12 #include <sstream>
13 #include <string.h>
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/FormatAdapters.h"
17 #include "llvm/Support/FormatVariadic.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/ScopedPrinter.h"
21 #include "lldb/API/SBBreakpoint.h"
22 #include "lldb/API/SBBreakpointLocation.h"
23 #include "lldb/API/SBDeclaration.h"
24 #include "lldb/API/SBStringList.h"
25 #include "lldb/API/SBStructuredData.h"
26 #include "lldb/API/SBValue.h"
27 #include "lldb/Host/PosixApi.h"
29 #include "DAP.h"
30 #include "ExceptionBreakpoint.h"
31 #include "JSONUtils.h"
32 #include "LLDBUtils.h"
34 namespace lldb_dap {
36 void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
37 llvm::StringRef str) {
38 if (LLVM_LIKELY(llvm::json::isUTF8(str)))
39 obj.try_emplace(key, str.str());
40 else
41 obj.try_emplace(key, llvm::json::fixUTF8(str));
44 llvm::StringRef GetAsString(const llvm::json::Value &value) {
45 if (auto s = value.getAsString())
46 return *s;
47 return llvm::StringRef();
50 // Gets a string from a JSON object using the key, or returns an empty string.
51 llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
52 llvm::StringRef defaultValue) {
53 if (std::optional<llvm::StringRef> value = obj.getString(key))
54 return *value;
55 return defaultValue;
58 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
59 llvm::StringRef defaultValue) {
60 if (obj == nullptr)
61 return defaultValue;
62 return GetString(*obj, key, defaultValue);
65 // Gets an unsigned integer from a JSON object using the key, or returns the
66 // specified fail value.
67 uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
68 uint64_t fail_value) {
69 if (auto value = obj.getInteger(key))
70 return (uint64_t)*value;
71 return fail_value;
74 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
75 uint64_t fail_value) {
76 if (obj == nullptr)
77 return fail_value;
78 return GetUnsigned(*obj, key, fail_value);
81 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
82 bool fail_value) {
83 if (auto value = obj.getBoolean(key))
84 return *value;
85 if (auto value = obj.getInteger(key))
86 return *value != 0;
87 return fail_value;
90 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
91 bool fail_value) {
92 if (obj == nullptr)
93 return fail_value;
94 return GetBoolean(*obj, key, fail_value);
97 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
98 int64_t fail_value) {
99 if (auto value = obj.getInteger(key))
100 return *value;
101 return fail_value;
104 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
105 int64_t fail_value) {
106 if (obj == nullptr)
107 return fail_value;
108 return GetSigned(*obj, key, fail_value);
111 bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
112 return obj.find(key) != obj.end();
115 std::vector<std::string> GetStrings(const llvm::json::Object *obj,
116 llvm::StringRef key) {
117 std::vector<std::string> strs;
118 auto json_array = obj->getArray(key);
119 if (!json_array)
120 return strs;
121 for (const auto &value : *json_array) {
122 switch (value.kind()) {
123 case llvm::json::Value::String:
124 strs.push_back(value.getAsString()->str());
125 break;
126 case llvm::json::Value::Number:
127 case llvm::json::Value::Boolean:
128 strs.push_back(llvm::to_string(value));
129 break;
130 case llvm::json::Value::Null:
131 case llvm::json::Value::Object:
132 case llvm::json::Value::Array:
133 break;
136 return strs;
139 static bool IsClassStructOrUnionType(lldb::SBType t) {
140 return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct |
141 lldb::eTypeClassArray)) != 0;
144 /// Create a short summary for a container that contains the summary of its
145 /// first children, so that the user can get a glimpse of its contents at a
146 /// glance.
147 static std::optional<std::string>
148 TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
149 // We gate this feature because it performs GetNumChildren(), which can
150 // cause performance issues because LLDB needs to complete possibly huge
151 // types.
152 if (!g_dap.enable_auto_variable_summaries)
153 return std::nullopt;
155 if (!v.MightHaveChildren())
156 return std::nullopt;
157 /// As this operation can be potentially slow, we limit the total time spent
158 /// fetching children to a few ms.
159 const auto max_evaluation_time = std::chrono::milliseconds(10);
160 /// We don't want to generate a extremely long summary string, so we limit its
161 /// length.
162 const size_t max_length = 32;
164 auto start = std::chrono::steady_clock::now();
165 std::string summary;
166 llvm::raw_string_ostream os(summary);
167 os << "{";
169 llvm::StringRef separator = "";
171 for (size_t i = 0, e = v.GetNumChildren(); i < e; ++i) {
172 // If we reached the time limit or exceeded the number of characters, we
173 // dump `...` to signal that there are more elements in the collection.
174 if (summary.size() > max_length ||
175 (std::chrono::steady_clock::now() - start) > max_evaluation_time) {
176 os << separator << "...";
177 break;
179 lldb::SBValue child = v.GetChildAtIndex(i);
181 if (llvm::StringRef name = child.GetName(); !name.empty()) {
182 llvm::StringRef desc;
183 if (llvm::StringRef summary = child.GetSummary(); !summary.empty())
184 desc = summary;
185 else if (llvm::StringRef value = child.GetValue(); !value.empty())
186 desc = value;
187 else if (IsClassStructOrUnionType(child.GetType()))
188 desc = "{...}";
189 else
190 continue;
192 // If the child is an indexed entry, we don't show its index to save
193 // characters.
194 if (name.starts_with("["))
195 os << separator << desc;
196 else
197 os << separator << name << ":" << desc;
198 separator = ", ";
201 os << "}";
203 if (summary == "{...}" || summary == "{}")
204 return std::nullopt;
205 return summary;
208 /// Try to create a summary string for the given value that doesn't have a
209 /// summary of its own.
210 static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue value) {
211 if (!g_dap.enable_auto_variable_summaries)
212 return std::nullopt;
214 // We use the dereferenced value for generating the summary.
215 if (value.GetType().IsPointerType() || value.GetType().IsReferenceType())
216 value = value.Dereference();
218 // We only support auto summaries for containers.
219 return TryCreateAutoSummaryForContainer(value);
222 void FillResponse(const llvm::json::Object &request,
223 llvm::json::Object &response) {
224 // Fill in all of the needed response fields to a "request" and set "success"
225 // to true by default.
226 response.try_emplace("type", "response");
227 response.try_emplace("seq", (int64_t)0);
228 EmplaceSafeString(response, "command", GetString(request, "command"));
229 const int64_t seq = GetSigned(request, "seq", 0);
230 response.try_emplace("request_seq", seq);
231 response.try_emplace("success", true);
234 // "Scope": {
235 // "type": "object",
236 // "description": "A Scope is a named container for variables. Optionally
237 // a scope can map to a source or a range within a source.",
238 // "properties": {
239 // "name": {
240 // "type": "string",
241 // "description": "Name of the scope such as 'Arguments', 'Locals'."
242 // },
243 // "presentationHint": {
244 // "type": "string",
245 // "description": "An optional hint for how to present this scope in the
246 // UI. If this attribute is missing, the scope is shown
247 // with a generic UI.",
248 // "_enum": [ "arguments", "locals", "registers" ],
249 // },
250 // "variablesReference": {
251 // "type": "integer",
252 // "description": "The variables of this scope can be retrieved by
253 // passing the value of variablesReference to the
254 // VariablesRequest."
255 // },
256 // "namedVariables": {
257 // "type": "integer",
258 // "description": "The number of named variables in this scope. The
259 // client can use this optional information to present
260 // the variables in a paged UI and fetch them in chunks."
261 // },
262 // "indexedVariables": {
263 // "type": "integer",
264 // "description": "The number of indexed variables in this scope. The
265 // client can use this optional information to present
266 // the variables in a paged UI and fetch them in chunks."
267 // },
268 // "expensive": {
269 // "type": "boolean",
270 // "description": "If true, the number of variables in this scope is
271 // large or expensive to retrieve."
272 // },
273 // "source": {
274 // "$ref": "#/definitions/Source",
275 // "description": "Optional source for this scope."
276 // },
277 // "line": {
278 // "type": "integer",
279 // "description": "Optional start line of the range covered by this
280 // scope."
281 // },
282 // "column": {
283 // "type": "integer",
284 // "description": "Optional start column of the range covered by this
285 // scope."
286 // },
287 // "endLine": {
288 // "type": "integer",
289 // "description": "Optional end line of the range covered by this scope."
290 // },
291 // "endColumn": {
292 // "type": "integer",
293 // "description": "Optional end column of the range covered by this
294 // scope."
295 // }
296 // },
297 // "required": [ "name", "variablesReference", "expensive" ]
298 // }
299 llvm::json::Value CreateScope(const llvm::StringRef name,
300 int64_t variablesReference,
301 int64_t namedVariables, bool expensive) {
302 llvm::json::Object object;
303 EmplaceSafeString(object, "name", name.str());
305 // TODO: Support "arguments" scope. At the moment lldb-dap includes the
306 // arguments into the "locals" scope.
307 if (variablesReference == VARREF_LOCALS) {
308 object.try_emplace("presentationHint", "locals");
309 } else if (variablesReference == VARREF_REGS) {
310 object.try_emplace("presentationHint", "registers");
313 object.try_emplace("variablesReference", variablesReference);
314 object.try_emplace("expensive", expensive);
315 object.try_emplace("namedVariables", namedVariables);
316 return llvm::json::Value(std::move(object));
319 // "Breakpoint": {
320 // "type": "object",
321 // "description": "Information about a Breakpoint created in setBreakpoints
322 // or setFunctionBreakpoints.",
323 // "properties": {
324 // "id": {
325 // "type": "integer",
326 // "description": "An optional unique identifier for the breakpoint."
327 // },
328 // "verified": {
329 // "type": "boolean",
330 // "description": "If true breakpoint could be set (but not necessarily
331 // at the desired location)."
332 // },
333 // "message": {
334 // "type": "string",
335 // "description": "An optional message about the state of the breakpoint.
336 // This is shown to the user and can be used to explain
337 // why a breakpoint could not be verified."
338 // },
339 // "source": {
340 // "$ref": "#/definitions/Source",
341 // "description": "The source where the breakpoint is located."
342 // },
343 // "line": {
344 // "type": "integer",
345 // "description": "The start line of the actual range covered by the
346 // breakpoint."
347 // },
348 // "column": {
349 // "type": "integer",
350 // "description": "An optional start column of the actual range covered
351 // by the breakpoint."
352 // },
353 // "endLine": {
354 // "type": "integer",
355 // "description": "An optional end line of the actual range covered by
356 // the breakpoint."
357 // },
358 // "endColumn": {
359 // "type": "integer",
360 // "description": "An optional end column of the actual range covered by
361 // the breakpoint. If no end line is given, then the end
362 // column is assumed to be in the start line."
363 // }
364 // },
365 // "required": [ "verified" ]
366 // }
367 llvm::json::Value CreateBreakpoint(BreakpointBase *bp,
368 std::optional<llvm::StringRef> request_path,
369 std::optional<uint32_t> request_line,
370 std::optional<uint32_t> request_column) {
371 llvm::json::Object object;
372 if (request_path)
373 object.try_emplace("source", CreateSource(*request_path));
374 bp->CreateJsonObject(object);
375 // We try to add request_line as a fallback
376 if (request_line)
377 object.try_emplace("line", *request_line);
378 if (request_column)
379 object.try_emplace("column", *request_column);
380 return llvm::json::Value(std::move(object));
383 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
384 uint64_t debug_info_size = 0;
385 llvm::StringRef section_name(section.GetName());
386 if (section_name.starts_with(".debug") ||
387 section_name.starts_with("__debug") ||
388 section_name.starts_with(".apple") || section_name.starts_with("__apple"))
389 debug_info_size += section.GetFileByteSize();
390 size_t num_sub_sections = section.GetNumSubSections();
391 for (size_t i = 0; i < num_sub_sections; i++) {
392 debug_info_size +=
393 GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
395 return debug_info_size;
398 static uint64_t GetDebugInfoSize(lldb::SBModule module) {
399 uint64_t debug_info_size = 0;
400 size_t num_sections = module.GetNumSections();
401 for (size_t i = 0; i < num_sections; i++) {
402 debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
404 return debug_info_size;
407 static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
408 std::ostringstream oss;
409 oss << std::fixed << std::setprecision(1);
410 if (debug_info < 1024) {
411 oss << debug_info << "B";
412 } else if (debug_info < 1024 * 1024) {
413 double kb = double(debug_info) / 1024.0;
414 oss << kb << "KB";
415 } else if (debug_info < 1024 * 1024 * 1024) {
416 double mb = double(debug_info) / (1024.0 * 1024.0);
417 oss << mb << "MB";
418 } else {
419 double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
420 oss << gb << "GB";
422 return oss.str();
424 llvm::json::Value CreateModule(lldb::SBModule &module) {
425 llvm::json::Object object;
426 if (!module.IsValid())
427 return llvm::json::Value(std::move(object));
428 const char *uuid = module.GetUUIDString();
429 object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
430 object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
431 char module_path_arr[PATH_MAX];
432 module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
433 std::string module_path(module_path_arr);
434 object.try_emplace("path", module_path);
435 if (module.GetNumCompileUnits() > 0) {
436 std::string symbol_str = "Symbols loaded.";
437 std::string debug_info_size;
438 uint64_t debug_info = GetDebugInfoSize(module);
439 if (debug_info > 0) {
440 debug_info_size = ConvertDebugInfoSizeToString(debug_info);
442 object.try_emplace("symbolStatus", symbol_str);
443 object.try_emplace("debugInfoSize", debug_info_size);
444 char symbol_path_arr[PATH_MAX];
445 module.GetSymbolFileSpec().GetPath(symbol_path_arr,
446 sizeof(symbol_path_arr));
447 std::string symbol_path(symbol_path_arr);
448 object.try_emplace("symbolFilePath", symbol_path);
449 } else {
450 object.try_emplace("symbolStatus", "Symbols not found.");
452 std::string loaded_addr = std::to_string(
453 module.GetObjectFileHeaderAddress().GetLoadAddress(g_dap.target));
454 object.try_emplace("addressRange", loaded_addr);
455 std::string version_str;
456 uint32_t version_nums[3];
457 uint32_t num_versions =
458 module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
459 for (uint32_t i = 0; i < num_versions; ++i) {
460 if (!version_str.empty())
461 version_str += ".";
462 version_str += std::to_string(version_nums[i]);
464 if (!version_str.empty())
465 object.try_emplace("version", version_str);
466 return llvm::json::Value(std::move(object));
469 void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints,
470 std::optional<llvm::StringRef> request_path,
471 std::optional<uint32_t> request_line) {
472 breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
475 // "Event": {
476 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
477 // "type": "object",
478 // "description": "Server-initiated event.",
479 // "properties": {
480 // "type": {
481 // "type": "string",
482 // "enum": [ "event" ]
483 // },
484 // "event": {
485 // "type": "string",
486 // "description": "Type of event."
487 // },
488 // "body": {
489 // "type": [ "array", "boolean", "integer", "null", "number" ,
490 // "object", "string" ],
491 // "description": "Event-specific information."
492 // }
493 // },
494 // "required": [ "type", "event" ]
495 // }]
496 // },
497 // "ProtocolMessage": {
498 // "type": "object",
499 // "description": "Base class of requests, responses, and events.",
500 // "properties": {
501 // "seq": {
502 // "type": "integer",
503 // "description": "Sequence number."
504 // },
505 // "type": {
506 // "type": "string",
507 // "description": "Message type.",
508 // "_enum": [ "request", "response", "event" ]
509 // }
510 // },
511 // "required": [ "seq", "type" ]
512 // }
513 llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
514 llvm::json::Object event;
515 event.try_emplace("seq", 0);
516 event.try_emplace("type", "event");
517 EmplaceSafeString(event, "event", event_name);
518 return event;
521 // "ExceptionBreakpointsFilter": {
522 // "type": "object",
523 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an
524 // option for configuring how exceptions are dealt with.",
525 // "properties": {
526 // "filter": {
527 // "type": "string",
528 // "description": "The internal ID of the filter. This value is passed
529 // to the setExceptionBreakpoints request."
530 // },
531 // "label": {
532 // "type": "string",
533 // "description": "The name of the filter. This will be shown in the UI."
534 // },
535 // "default": {
536 // "type": "boolean",
537 // "description": "Initial value of the filter. If not specified a value
538 // 'false' is assumed."
539 // }
540 // },
541 // "required": [ "filter", "label" ]
542 // }
543 llvm::json::Value
544 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
545 llvm::json::Object object;
546 EmplaceSafeString(object, "filter", bp.filter);
547 EmplaceSafeString(object, "label", bp.label);
548 object.try_emplace("default", bp.default_value);
549 return llvm::json::Value(std::move(object));
552 // "Source": {
553 // "type": "object",
554 // "description": "A Source is a descriptor for source code. It is returned
555 // from the debug adapter as part of a StackFrame and it is
556 // used by clients when specifying breakpoints.",
557 // "properties": {
558 // "name": {
559 // "type": "string",
560 // "description": "The short name of the source. Every source returned
561 // from the debug adapter has a name. When sending a
562 // source to the debug adapter this name is optional."
563 // },
564 // "path": {
565 // "type": "string",
566 // "description": "The path of the source to be shown in the UI. It is
567 // only used to locate and load the content of the
568 // source if no sourceReference is specified (or its
569 // value is 0)."
570 // },
571 // "sourceReference": {
572 // "type": "number",
573 // "description": "If sourceReference > 0 the contents of the source must
574 // be retrieved through the SourceRequest (even if a path
575 // is specified). A sourceReference is only valid for a
576 // session, so it must not be used to persist a source."
577 // },
578 // "presentationHint": {
579 // "type": "string",
580 // "description": "An optional hint for how to present the source in the
581 // UI. A value of 'deemphasize' can be used to indicate
582 // that the source is not available or that it is
583 // skipped on stepping.",
584 // "enum": [ "normal", "emphasize", "deemphasize" ]
585 // },
586 // "origin": {
587 // "type": "string",
588 // "description": "The (optional) origin of this source: possible values
589 // 'internal module', 'inlined content from source map',
590 // etc."
591 // },
592 // "sources": {
593 // "type": "array",
594 // "items": {
595 // "$ref": "#/definitions/Source"
596 // },
597 // "description": "An optional list of sources that are related to this
598 // source. These may be the source that generated this
599 // source."
600 // },
601 // "adapterData": {
602 // "type":["array","boolean","integer","null","number","object","string"],
603 // "description": "Optional data that a debug adapter might want to loop
604 // through the client. The client should leave the data
605 // intact and persist it across sessions. The client
606 // should not interpret the data."
607 // },
608 // "checksums": {
609 // "type": "array",
610 // "items": {
611 // "$ref": "#/definitions/Checksum"
612 // },
613 // "description": "The checksums associated with this file."
614 // }
615 // }
616 // }
617 llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
618 llvm::json::Object object;
619 lldb::SBFileSpec file = line_entry.GetFileSpec();
620 if (file.IsValid()) {
621 const char *name = file.GetFilename();
622 if (name)
623 EmplaceSafeString(object, "name", name);
624 char path[PATH_MAX] = "";
625 if (file.GetPath(path, sizeof(path)) &&
626 lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
627 EmplaceSafeString(object, "path", std::string(path));
630 return llvm::json::Value(std::move(object));
633 llvm::json::Value CreateSource(llvm::StringRef source_path) {
634 llvm::json::Object source;
635 llvm::StringRef name = llvm::sys::path::filename(source_path);
636 EmplaceSafeString(source, "name", name);
637 EmplaceSafeString(source, "path", source_path);
638 return llvm::json::Value(std::move(source));
641 std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
642 auto line_entry = frame.GetLineEntry();
643 // A line entry of 0 indicates the line is compiler generated i.e. no source
644 // file is associated with the frame.
645 if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0)
646 return CreateSource(line_entry);
648 return {};
651 // "StackFrame": {
652 // "type": "object",
653 // "description": "A Stackframe contains the source location.",
654 // "properties": {
655 // "id": {
656 // "type": "integer",
657 // "description": "An identifier for the stack frame. It must be unique
658 // across all threads. This id can be used to retrieve
659 // the scopes of the frame with the 'scopesRequest' or
660 // to restart the execution of a stackframe."
661 // },
662 // "name": {
663 // "type": "string",
664 // "description": "The name of the stack frame, typically a method name."
665 // },
666 // "source": {
667 // "$ref": "#/definitions/Source",
668 // "description": "The optional source of the frame."
669 // },
670 // "line": {
671 // "type": "integer",
672 // "description": "The line within the file of the frame. If source is
673 // null or doesn't exist, line is 0 and must be ignored."
674 // },
675 // "column": {
676 // "type": "integer",
677 // "description": "The column within the line. If source is null or
678 // doesn't exist, column is 0 and must be ignored."
679 // },
680 // "endLine": {
681 // "type": "integer",
682 // "description": "An optional end line of the range covered by the
683 // stack frame."
684 // },
685 // "endColumn": {
686 // "type": "integer",
687 // "description": "An optional end column of the range covered by the
688 // stack frame."
689 // },
690 // "instructionPointerReference": {
691 // "type": "string",
692 // "description": "A memory reference for the current instruction
693 // pointer
694 // in this frame."
695 // },
696 // "moduleId": {
697 // "type": ["integer", "string"],
698 // "description": "The module associated with this frame, if any."
699 // },
700 // "presentationHint": {
701 // "type": "string",
702 // "enum": [ "normal", "label", "subtle" ],
703 // "description": "An optional hint for how to present this frame in
704 // the UI. A value of 'label' can be used to indicate
705 // that the frame is an artificial frame that is used
706 // as a visual label or separator. A value of 'subtle'
707 // can be used to change the appearance of a frame in
708 // a 'subtle' way."
709 // }
710 // },
711 // "required": [ "id", "name", "line", "column" ]
712 // }
713 llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
714 llvm::json::Object object;
715 int64_t frame_id = MakeDAPFrameID(frame);
716 object.try_emplace("id", frame_id);
718 std::string frame_name;
719 lldb::SBStream stream;
720 if (g_dap.frame_format &&
721 frame.GetDescriptionWithFormat(g_dap.frame_format, stream).Success()) {
722 frame_name = stream.GetData();
724 // `function_name` can be a nullptr, which throws an error when assigned to
725 // an `std::string`.
726 } else if (const char *name = frame.GetDisplayFunctionName()) {
727 frame_name = name;
730 if (frame_name.empty()) {
731 // If the function name is unavailable, display the pc address as a 16-digit
732 // hex string, e.g. "0x0000000000012345"
733 llvm::raw_string_ostream os(frame_name);
734 os << llvm::format_hex(frame.GetPC(), 18);
737 // We only include `[opt]` if a custom frame format is not specified.
738 if (!g_dap.frame_format && frame.GetFunction().GetIsOptimized())
739 frame_name += " [opt]";
741 EmplaceSafeString(object, "name", frame_name);
743 auto source = CreateSource(frame);
745 if (source) {
746 object.try_emplace("source", *source);
747 auto line_entry = frame.GetLineEntry();
748 auto line = line_entry.GetLine();
749 if (line && line != LLDB_INVALID_LINE_NUMBER)
750 object.try_emplace("line", line);
751 else
752 object.try_emplace("line", 0);
753 auto column = line_entry.GetColumn();
754 object.try_emplace("column", column);
755 } else {
756 object.try_emplace("line", 0);
757 object.try_emplace("column", 0);
760 const auto pc = frame.GetPC();
761 if (pc != LLDB_INVALID_ADDRESS) {
762 std::string formatted_addr = "0x" + llvm::utohexstr(pc);
763 object.try_emplace("instructionPointerReference", formatted_addr);
766 return llvm::json::Value(std::move(object));
769 // "Thread": {
770 // "type": "object",
771 // "description": "A Thread",
772 // "properties": {
773 // "id": {
774 // "type": "integer",
775 // "description": "Unique identifier for the thread."
776 // },
777 // "name": {
778 // "type": "string",
779 // "description": "A name of the thread."
780 // }
781 // },
782 // "required": [ "id", "name" ]
783 // }
784 llvm::json::Value CreateThread(lldb::SBThread &thread) {
785 llvm::json::Object object;
786 object.try_emplace("id", (int64_t)thread.GetThreadID());
787 std::string thread_str;
788 lldb::SBStream stream;
789 if (g_dap.thread_format &&
790 thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
791 thread_str = stream.GetData();
792 } else {
793 const char *thread_name = thread.GetName();
794 const char *queue_name = thread.GetQueueName();
796 if (thread_name) {
797 thread_str = std::string(thread_name);
798 } else if (queue_name) {
799 auto kind = thread.GetQueue().GetKind();
800 std::string queue_kind_label = "";
801 if (kind == lldb::eQueueKindSerial) {
802 queue_kind_label = " (serial)";
803 } else if (kind == lldb::eQueueKindConcurrent) {
804 queue_kind_label = " (concurrent)";
807 thread_str =
808 llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
809 queue_name, queue_kind_label)
810 .str();
811 } else {
812 thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
816 EmplaceSafeString(object, "name", thread_str);
818 return llvm::json::Value(std::move(object));
821 // "StoppedEvent": {
822 // "allOf": [ { "$ref": "#/definitions/Event" }, {
823 // "type": "object",
824 // "description": "Event message for 'stopped' event type. The event
825 // indicates that the execution of the debuggee has stopped
826 // due to some condition. This can be caused by a break
827 // point previously set, a stepping action has completed,
828 // by executing a debugger statement etc.",
829 // "properties": {
830 // "event": {
831 // "type": "string",
832 // "enum": [ "stopped" ]
833 // },
834 // "body": {
835 // "type": "object",
836 // "properties": {
837 // "reason": {
838 // "type": "string",
839 // "description": "The reason for the event. For backward
840 // compatibility this string is shown in the UI if
841 // the 'description' attribute is missing (but it
842 // must not be translated).",
843 // "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
844 // },
845 // "description": {
846 // "type": "string",
847 // "description": "The full reason for the event, e.g. 'Paused
848 // on exception'. This string is shown in the UI
849 // as is."
850 // },
851 // "threadId": {
852 // "type": "integer",
853 // "description": "The thread which was stopped."
854 // },
855 // "text": {
856 // "type": "string",
857 // "description": "Additional information. E.g. if reason is
858 // 'exception', text contains the exception name.
859 // This string is shown in the UI."
860 // },
861 // "allThreadsStopped": {
862 // "type": "boolean",
863 // "description": "If allThreadsStopped is true, a debug adapter
864 // can announce that all threads have stopped.
865 // The client should use this information to
866 // enable that all threads can be expanded to
867 // access their stacktraces. If the attribute
868 // is missing or false, only the thread with the
869 // given threadId can be expanded."
870 // }
871 // },
872 // "required": [ "reason" ]
873 // }
874 // },
875 // "required": [ "event", "body" ]
876 // }]
877 // }
878 llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
879 uint32_t stop_id) {
880 llvm::json::Object event(CreateEventObject("stopped"));
881 llvm::json::Object body;
882 switch (thread.GetStopReason()) {
883 case lldb::eStopReasonTrace:
884 case lldb::eStopReasonPlanComplete:
885 body.try_emplace("reason", "step");
886 break;
887 case lldb::eStopReasonBreakpoint: {
888 ExceptionBreakpoint *exc_bp = g_dap.GetExceptionBPFromStopReason(thread);
889 if (exc_bp) {
890 body.try_emplace("reason", "exception");
891 EmplaceSafeString(body, "description", exc_bp->label);
892 } else {
893 body.try_emplace("reason", "breakpoint");
894 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
895 lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
896 std::string desc_str =
897 llvm::formatv("breakpoint {0}.{1}", bp_id, bp_loc_id);
898 body.try_emplace("hitBreakpointIds",
899 llvm::json::Array{llvm::json::Value(bp_id)});
900 EmplaceSafeString(body, "description", desc_str);
902 } break;
903 case lldb::eStopReasonWatchpoint:
904 case lldb::eStopReasonInstrumentation:
905 body.try_emplace("reason", "breakpoint");
906 break;
907 case lldb::eStopReasonProcessorTrace:
908 body.try_emplace("reason", "processor trace");
909 break;
910 case lldb::eStopReasonSignal:
911 case lldb::eStopReasonException:
912 body.try_emplace("reason", "exception");
913 break;
914 case lldb::eStopReasonExec:
915 body.try_emplace("reason", "entry");
916 break;
917 case lldb::eStopReasonFork:
918 body.try_emplace("reason", "fork");
919 break;
920 case lldb::eStopReasonVFork:
921 body.try_emplace("reason", "vfork");
922 break;
923 case lldb::eStopReasonVForkDone:
924 body.try_emplace("reason", "vforkdone");
925 break;
926 case lldb::eStopReasonThreadExiting:
927 case lldb::eStopReasonInvalid:
928 case lldb::eStopReasonNone:
929 break;
931 if (stop_id == 0)
932 body.try_emplace("reason", "entry");
933 const lldb::tid_t tid = thread.GetThreadID();
934 body.try_emplace("threadId", (int64_t)tid);
935 // If no description has been set, then set it to the default thread stopped
936 // description. If we have breakpoints that get hit and shouldn't be reported
937 // as breakpoints, then they will set the description above.
938 if (!ObjectContainsKey(body, "description")) {
939 char description[1024];
940 if (thread.GetStopDescription(description, sizeof(description))) {
941 EmplaceSafeString(body, "description", std::string(description));
944 // "threadCausedFocus" is used in tests to validate breaking behavior.
945 if (tid == g_dap.focus_tid) {
946 body.try_emplace("threadCausedFocus", true);
948 body.try_emplace("preserveFocusHint", tid != g_dap.focus_tid);
949 body.try_emplace("allThreadsStopped", true);
950 event.try_emplace("body", std::move(body));
951 return llvm::json::Value(std::move(event));
954 const char *GetNonNullVariableName(lldb::SBValue v) {
955 const char *name = v.GetName();
956 return name ? name : "<null>";
959 std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
960 bool is_name_duplicated) {
961 lldb::SBStream name_builder;
962 name_builder.Print(GetNonNullVariableName(v));
963 if (is_name_duplicated) {
964 lldb::SBDeclaration declaration = v.GetDeclaration();
965 const char *file_name = declaration.GetFileSpec().GetFilename();
966 const uint32_t line = declaration.GetLine();
968 if (file_name != nullptr && line > 0)
969 name_builder.Printf(" @ %s:%u", file_name, line);
970 else if (const char *location = v.GetLocation())
971 name_builder.Printf(" @ %s", location);
973 return name_builder.GetData();
976 VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
977 bool is_name_duplicated,
978 std::optional<std::string> custom_name)
979 : v(v) {
980 name = custom_name
981 ? *custom_name
982 : CreateUniqueVariableNameForDisplay(v, is_name_duplicated);
984 type_obj = v.GetType();
985 std::string raw_display_type_name =
986 llvm::StringRef(type_obj.GetDisplayTypeName()).str();
987 display_type_name =
988 !raw_display_type_name.empty() ? raw_display_type_name : NO_TYPENAME;
990 // Only format hex/default if there is no existing special format.
991 if (v.GetFormat() == lldb::eFormatDefault ||
992 v.GetFormat() == lldb::eFormatHex) {
993 if (format_hex)
994 v.SetFormat(lldb::eFormatHex);
995 else
996 v.SetFormat(lldb::eFormatDefault);
999 llvm::raw_string_ostream os_display_value(display_value);
1001 if (lldb::SBError sb_error = v.GetError(); sb_error.Fail()) {
1002 error = sb_error.GetCString();
1003 os_display_value << "<error: " << error << ">";
1004 } else {
1005 value = llvm::StringRef(v.GetValue()).str();
1006 summary = llvm::StringRef(v.GetSummary()).str();
1007 if (summary.empty())
1008 auto_summary = TryCreateAutoSummary(v);
1010 std::optional<std::string> effective_summary =
1011 !summary.empty() ? summary : auto_summary;
1013 if (!value.empty()) {
1014 os_display_value << value;
1015 if (effective_summary)
1016 os_display_value << " " << *effective_summary;
1017 } else if (effective_summary) {
1018 os_display_value << *effective_summary;
1020 // As last resort, we print its type and address if available.
1021 } else {
1022 if (!raw_display_type_name.empty()) {
1023 os_display_value << raw_display_type_name;
1024 lldb::addr_t address = v.GetLoadAddress();
1025 if (address != LLDB_INVALID_ADDRESS)
1026 os_display_value << " @ " << llvm::format_hex(address, 0);
1031 lldb::SBStream evaluateStream;
1032 v.GetExpressionPath(evaluateStream);
1033 evaluate_name = llvm::StringRef(evaluateStream.GetData()).str();
1036 llvm::json::Object VariableDescription::GetVariableExtensionsJSON() {
1037 llvm::json::Object extensions;
1038 if (error)
1039 EmplaceSafeString(extensions, "error", *error);
1040 if (!value.empty())
1041 EmplaceSafeString(extensions, "value", value);
1042 if (!summary.empty())
1043 EmplaceSafeString(extensions, "summary", summary);
1044 if (auto_summary)
1045 EmplaceSafeString(extensions, "autoSummary", *auto_summary);
1047 if (lldb::SBDeclaration decl = v.GetDeclaration(); decl.IsValid()) {
1048 llvm::json::Object decl_obj;
1049 if (lldb::SBFileSpec file = decl.GetFileSpec(); file.IsValid()) {
1050 char path[PATH_MAX] = "";
1051 if (file.GetPath(path, sizeof(path)) &&
1052 lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
1053 decl_obj.try_emplace("path", std::string(path));
1057 if (int line = decl.GetLine())
1058 decl_obj.try_emplace("line", line);
1059 if (int column = decl.GetColumn())
1060 decl_obj.try_emplace("column", column);
1062 if (!decl_obj.empty())
1063 extensions.try_emplace("declaration", std::move(decl_obj));
1065 return extensions;
1068 std::string VariableDescription::GetResult(llvm::StringRef context) {
1069 // In repl context, the results can be displayed as multiple lines so more
1070 // detailed descriptions can be returned.
1071 if (context != "repl")
1072 return display_value;
1074 if (!v.IsValid())
1075 return display_value;
1077 // Try the SBValue::GetDescription(), which may call into language runtime
1078 // specific formatters (see ValueObjectPrinter).
1079 lldb::SBStream stream;
1080 v.GetDescription(stream);
1081 llvm::StringRef description = stream.GetData();
1082 return description.trim().str();
1085 // "Variable": {
1086 // "type": "object",
1087 // "description": "A Variable is a name/value pair. Optionally a variable
1088 // can have a 'type' that is shown if space permits or when
1089 // hovering over the variable's name. An optional 'kind' is
1090 // used to render additional properties of the variable,
1091 // e.g. different icons can be used to indicate that a
1092 // variable is public or private. If the value is
1093 // structured (has children), a handle is provided to
1094 // retrieve the children with the VariablesRequest. If
1095 // the number of named or indexed children is large, the
1096 // numbers should be returned via the optional
1097 // 'namedVariables' and 'indexedVariables' attributes. The
1098 // client can use this optional information to present the
1099 // children in a paged UI and fetch them in chunks.",
1100 // "properties": {
1101 // "name": {
1102 // "type": "string",
1103 // "description": "The variable's name."
1104 // },
1105 // "value": {
1106 // "type": "string",
1107 // "description": "The variable's value. This can be a multi-line text,
1108 // e.g. for a function the body of a function."
1109 // },
1110 // "type": {
1111 // "type": "string",
1112 // "description": "The type of the variable's value. Typically shown in
1113 // the UI when hovering over the value."
1114 // },
1115 // "presentationHint": {
1116 // "$ref": "#/definitions/VariablePresentationHint",
1117 // "description": "Properties of a variable that can be used to determine
1118 // how to render the variable in the UI."
1119 // },
1120 // "evaluateName": {
1121 // "type": "string",
1122 // "description": "Optional evaluatable name of this variable which can
1123 // be passed to the 'EvaluateRequest' to fetch the
1124 // variable's value."
1125 // },
1126 // "variablesReference": {
1127 // "type": "integer",
1128 // "description": "If variablesReference is > 0, the variable is
1129 // structured and its children can be retrieved by
1130 // passing variablesReference to the VariablesRequest."
1131 // },
1132 // "namedVariables": {
1133 // "type": "integer",
1134 // "description": "The number of named child variables. The client can
1135 // use this optional information to present the children
1136 // in a paged UI and fetch them in chunks."
1137 // },
1138 // "indexedVariables": {
1139 // "type": "integer",
1140 // "description": "The number of indexed child variables. The client
1141 // can use this optional information to present the
1142 // children in a paged UI and fetch them in chunks."
1143 // }
1146 // "$__lldb_extensions": {
1147 // "description": "Unofficial extensions to the protocol",
1148 // "properties": {
1149 // "declaration": {
1150 // "type": "object",
1151 // "description": "The source location where the variable was declared.
1152 // This value won't be present if no declaration is
1153 // available.",
1154 // "properties": {
1155 // "path": {
1156 // "type": "string",
1157 // "description": "The source file path where the variable was
1158 // declared."
1159 // },
1160 // "line": {
1161 // "type": "number",
1162 // "description": "The 1-indexed source line where the variable was
1163 // declared."
1164 // },
1165 // "column": {
1166 // "type": "number",
1167 // "description": "The 1-indexed source column where the variable
1168 // was declared."
1169 // }
1170 // }
1171 // },
1172 // "value":
1173 // "type": "string",
1174 // "description": "The internal value of the variable as returned by
1175 // This is effectively SBValue.GetValue(). The other
1176 // `value` entry in the top-level variable response is,
1177 // on the other hand, just a display string for the
1178 // variable."
1179 // },
1180 // "summary":
1181 // "type": "string",
1182 // "description": "The summary string of the variable. This is
1183 // effectively SBValue.GetSummary()."
1184 // },
1185 // "autoSummary":
1186 // "type": "string",
1187 // "description": "The auto generated summary if using
1188 // `enableAutoVariableSummaries`."
1189 // },
1190 // "error":
1191 // "type": "string",
1192 // "description": "An error message generated if LLDB couldn't inspect
1193 // the variable."
1194 // }
1195 // }
1196 // },
1197 // "required": [ "name", "value", "variablesReference" ]
1198 // }
1199 llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1200 int64_t varID, bool format_hex,
1201 bool is_name_duplicated,
1202 std::optional<std::string> custom_name) {
1203 VariableDescription desc(v, format_hex, is_name_duplicated, custom_name);
1204 llvm::json::Object object;
1205 EmplaceSafeString(object, "name", desc.name);
1206 EmplaceSafeString(object, "value", desc.display_value);
1208 if (!desc.evaluate_name.empty())
1209 EmplaceSafeString(object, "evaluateName", desc.evaluate_name);
1211 // If we have a type with many children, we would like to be able to
1212 // give a hint to the IDE that the type has indexed children so that the
1213 // request can be broken up in grabbing only a few children at a time. We
1214 // want to be careful and only call "v.GetNumChildren()" if we have an array
1215 // type or if we have a synthetic child provider producing indexed children.
1216 // We don't want to call "v.GetNumChildren()" on all objects as class, struct
1217 // and union types don't need to be completed if they are never expanded. So
1218 // we want to avoid calling this to only cases where we it makes sense to keep
1219 // performance high during normal debugging.
1221 // If we have an array type, say that it is indexed and provide the number
1222 // of children in case we have a huge array. If we don't do this, then we
1223 // might take a while to produce all children at onces which can delay your
1224 // debug session.
1225 if (desc.type_obj.IsArrayType()) {
1226 object.try_emplace("indexedVariables", v.GetNumChildren());
1227 } else if (v.IsSynthetic()) {
1228 // For a type with a synthetic child provider, the SBType of "v" won't tell
1229 // us anything about what might be displayed. Instead, we check if the first
1230 // child's name is "[0]" and then say it is indexed. We call
1231 // GetNumChildren() only if the child name matches to avoid a potentially
1232 // expensive operation.
1233 if (lldb::SBValue first_child = v.GetChildAtIndex(0)) {
1234 llvm::StringRef first_child_name = first_child.GetName();
1235 if (first_child_name == "[0]") {
1236 size_t num_children = v.GetNumChildren();
1237 // If we are creating a "[raw]" fake child for each synthetic type, we
1238 // have to account for it when returning indexed variables.
1239 if (g_dap.enable_synthetic_child_debugging)
1240 ++num_children;
1241 object.try_emplace("indexedVariables", num_children);
1245 EmplaceSafeString(object, "type", desc.display_type_name);
1246 if (varID != INT64_MAX)
1247 object.try_emplace("id", varID);
1248 if (v.MightHaveChildren())
1249 object.try_emplace("variablesReference", variablesReference);
1250 else
1251 object.try_emplace("variablesReference", (int64_t)0);
1253 object.try_emplace("$__lldb_extensions", desc.GetVariableExtensionsJSON());
1254 return llvm::json::Value(std::move(object));
1257 llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
1258 llvm::json::Object object;
1259 char unit_path_arr[PATH_MAX];
1260 unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
1261 std::string unit_path(unit_path_arr);
1262 object.try_emplace("compileUnitPath", unit_path);
1263 return llvm::json::Value(std::move(object));
1266 /// See
1267 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1268 llvm::json::Object
1269 CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
1270 llvm::StringRef debug_adaptor_path,
1271 llvm::StringRef comm_file,
1272 lldb::pid_t debugger_pid) {
1273 llvm::json::Object run_in_terminal_args;
1274 // This indicates the IDE to open an embedded terminal, instead of opening the
1275 // terminal in a new window.
1276 run_in_terminal_args.try_emplace("kind", "integrated");
1278 auto launch_request_arguments = launch_request.getObject("arguments");
1279 // The program path must be the first entry in the "args" field
1280 std::vector<std::string> args = {debug_adaptor_path.str(), "--comm-file",
1281 comm_file.str()};
1282 if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
1283 args.push_back("--debugger-pid");
1284 args.push_back(std::to_string(debugger_pid));
1286 args.push_back("--launch-target");
1287 args.push_back(GetString(launch_request_arguments, "program").str());
1288 std::vector<std::string> target_args =
1289 GetStrings(launch_request_arguments, "args");
1290 args.insert(args.end(), target_args.begin(), target_args.end());
1291 run_in_terminal_args.try_emplace("args", args);
1293 const auto cwd = GetString(launch_request_arguments, "cwd");
1294 if (!cwd.empty())
1295 run_in_terminal_args.try_emplace("cwd", cwd);
1297 // We need to convert the input list of environments variables into a
1298 // dictionary
1299 std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1300 llvm::json::Object environment;
1301 for (const std::string &env : envs) {
1302 size_t index = env.find('=');
1303 environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1305 run_in_terminal_args.try_emplace("env",
1306 llvm::json::Value(std::move(environment)));
1308 return run_in_terminal_args;
1311 // Keep all the top level items from the statistics dump, except for the
1312 // "modules" array. It can be huge and cause delay
1313 // Array and dictionary value will return as <key, JSON string> pairs
1314 void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key,
1315 llvm::json::Object &out) {
1316 lldb::SBStructuredData value = data.GetValueForKey(key);
1317 std::string key_utf8 = llvm::json::fixUTF8(key);
1318 if (strcmp(key, "modules") == 0)
1319 return;
1320 switch (value.GetType()) {
1321 case lldb::eStructuredDataTypeFloat:
1322 out.try_emplace(key_utf8, value.GetFloatValue());
1323 break;
1324 case lldb::eStructuredDataTypeUnsignedInteger:
1325 out.try_emplace(key_utf8, value.GetIntegerValue((uint64_t)0));
1326 break;
1327 case lldb::eStructuredDataTypeSignedInteger:
1328 out.try_emplace(key_utf8, value.GetIntegerValue((int64_t)0));
1329 break;
1330 case lldb::eStructuredDataTypeArray: {
1331 lldb::SBStream contents;
1332 value.GetAsJSON(contents);
1333 out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1334 } break;
1335 case lldb::eStructuredDataTypeBoolean:
1336 out.try_emplace(key_utf8, value.GetBooleanValue());
1337 break;
1338 case lldb::eStructuredDataTypeString: {
1339 // Get the string size before reading
1340 const size_t str_length = value.GetStringValue(nullptr, 0);
1341 std::string str(str_length + 1, 0);
1342 value.GetStringValue(&str[0], str_length);
1343 out.try_emplace(key_utf8, llvm::json::fixUTF8(str));
1344 } break;
1345 case lldb::eStructuredDataTypeDictionary: {
1346 lldb::SBStream contents;
1347 value.GetAsJSON(contents);
1348 out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1349 } break;
1350 case lldb::eStructuredDataTypeNull:
1351 case lldb::eStructuredDataTypeGeneric:
1352 case lldb::eStructuredDataTypeInvalid:
1353 break;
1357 void addStatistic(llvm::json::Object &event) {
1358 lldb::SBStructuredData statistics = g_dap.target.GetStatistics();
1359 bool is_dictionary =
1360 statistics.GetType() == lldb::eStructuredDataTypeDictionary;
1361 if (!is_dictionary)
1362 return;
1363 llvm::json::Object stats_body;
1365 lldb::SBStringList keys;
1366 if (!statistics.GetKeys(keys))
1367 return;
1368 for (size_t i = 0; i < keys.GetSize(); i++) {
1369 const char *key = keys.GetStringAtIndex(i);
1370 FilterAndGetValueForKey(statistics, key, stats_body);
1372 event.try_emplace("statistics", std::move(stats_body));
1375 llvm::json::Object CreateTerminatedEventObject() {
1376 llvm::json::Object event(CreateEventObject("terminated"));
1377 addStatistic(event);
1378 return event;
1381 std::string JSONToString(const llvm::json::Value &json) {
1382 std::string data;
1383 llvm::raw_string_ostream os(data);
1384 os << json;
1385 os.flush();
1386 return data;
1389 } // namespace lldb_dap