Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / tools / lldb-dap / JSONUtils.cpp
blob46528a2a28d4d6ff7098522118f63133d41cd278
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/Support/FormatAdapters.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/ScopedPrinter.h"
19 #include "lldb/API/SBBreakpoint.h"
20 #include "lldb/API/SBBreakpointLocation.h"
21 #include "lldb/API/SBDeclaration.h"
22 #include "lldb/API/SBStringList.h"
23 #include "lldb/API/SBStructuredData.h"
24 #include "lldb/API/SBValue.h"
25 #include "lldb/Host/PosixApi.h"
27 #include "DAP.h"
28 #include "ExceptionBreakpoint.h"
29 #include "JSONUtils.h"
30 #include "LLDBUtils.h"
32 namespace lldb_dap {
34 void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
35 llvm::StringRef str) {
36 if (LLVM_LIKELY(llvm::json::isUTF8(str)))
37 obj.try_emplace(key, str.str());
38 else
39 obj.try_emplace(key, llvm::json::fixUTF8(str));
42 llvm::StringRef GetAsString(const llvm::json::Value &value) {
43 if (auto s = value.getAsString())
44 return *s;
45 return llvm::StringRef();
48 // Gets a string from a JSON object using the key, or returns an empty string.
49 llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
50 llvm::StringRef defaultValue) {
51 if (std::optional<llvm::StringRef> value = obj.getString(key))
52 return *value;
53 return defaultValue;
56 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
57 llvm::StringRef defaultValue) {
58 if (obj == nullptr)
59 return defaultValue;
60 return GetString(*obj, key);
63 // Gets an unsigned integer from a JSON object using the key, or returns the
64 // specified fail value.
65 uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
66 uint64_t fail_value) {
67 if (auto value = obj.getInteger(key))
68 return (uint64_t)*value;
69 return fail_value;
72 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
73 uint64_t fail_value) {
74 if (obj == nullptr)
75 return fail_value;
76 return GetUnsigned(*obj, key, fail_value);
79 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
80 bool fail_value) {
81 if (auto value = obj.getBoolean(key))
82 return *value;
83 if (auto value = obj.getInteger(key))
84 return *value != 0;
85 return fail_value;
88 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
89 bool fail_value) {
90 if (obj == nullptr)
91 return fail_value;
92 return GetBoolean(*obj, key, fail_value);
95 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
96 int64_t fail_value) {
97 if (auto value = obj.getInteger(key))
98 return *value;
99 return fail_value;
102 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
103 int64_t fail_value) {
104 if (obj == nullptr)
105 return fail_value;
106 return GetSigned(*obj, key, fail_value);
109 bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
110 return obj.find(key) != obj.end();
113 std::vector<std::string> GetStrings(const llvm::json::Object *obj,
114 llvm::StringRef key) {
115 std::vector<std::string> strs;
116 auto json_array = obj->getArray(key);
117 if (!json_array)
118 return strs;
119 for (const auto &value : *json_array) {
120 switch (value.kind()) {
121 case llvm::json::Value::String:
122 strs.push_back(value.getAsString()->str());
123 break;
124 case llvm::json::Value::Number:
125 case llvm::json::Value::Boolean:
126 strs.push_back(llvm::to_string(value));
127 break;
128 case llvm::json::Value::Null:
129 case llvm::json::Value::Object:
130 case llvm::json::Value::Array:
131 break;
134 return strs;
137 /// Create a short summary for a container that contains the summary of its
138 /// first children, so that the user can get a glimpse of its contents at a
139 /// glance.
140 static std::optional<std::string>
141 TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
142 // We gate this feature because it performs GetNumChildren(), which can
143 // cause performance issues because LLDB needs to complete possibly huge
144 // types.
145 if (!g_dap.enable_auto_variable_summaries)
146 return std::nullopt;
148 if (!v.MightHaveChildren())
149 return std::nullopt;
150 /// As this operation can be potentially slow, we limit the total time spent
151 /// fetching children to a few ms.
152 const auto max_evaluation_time = std::chrono::milliseconds(10);
153 /// We don't want to generate a extremely long summary string, so we limit its
154 /// length.
155 const size_t max_length = 32;
157 auto start = std::chrono::steady_clock::now();
158 std::string summary;
159 llvm::raw_string_ostream os(summary);
160 os << "{";
162 llvm::StringRef separator = "";
164 for (size_t i = 0, e = v.GetNumChildren(); i < e; ++i) {
165 // If we reached the time limit or exceeded the number of characters, we
166 // dump `...` to signal that there are more elements in the collection.
167 if (summary.size() > max_length ||
168 (std::chrono::steady_clock::now() - start) > max_evaluation_time) {
169 os << separator << "...";
170 break;
172 lldb::SBValue child = v.GetChildAtIndex(i);
174 if (llvm::StringRef name = child.GetName(); !name.empty()) {
175 llvm::StringRef value;
176 if (llvm::StringRef summary = child.GetSummary(); !summary.empty())
177 value = summary;
178 else
179 value = child.GetValue();
181 if (!value.empty()) {
182 // If the child is an indexed entry, we don't show its index to save
183 // characters.
184 if (name.starts_with("["))
185 os << separator << value;
186 else
187 os << separator << name << ":" << value;
188 separator = ", ";
192 os << "}";
194 if (summary == "{...}" || summary == "{}")
195 return std::nullopt;
196 return summary;
199 /// Try to create a summary string for the given value that doesn't have a
200 /// summary of its own.
201 static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue value) {
202 if (!g_dap.enable_auto_variable_summaries)
203 return std::nullopt;
205 // We use the dereferenced value for generating the summary.
206 if (value.GetType().IsPointerType() || value.GetType().IsReferenceType())
207 value = value.Dereference();
209 // We only support auto summaries for containers.
210 return TryCreateAutoSummaryForContainer(value);
213 void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
214 llvm::StringRef key) {
215 std::string result;
216 llvm::raw_string_ostream strm(result);
218 lldb::SBError error = v.GetError();
219 if (!error.Success()) {
220 strm << "<error: " << error.GetCString() << ">";
221 } else {
222 llvm::StringRef value = v.GetValue();
223 llvm::StringRef nonAutoSummary = v.GetSummary();
224 std::optional<std::string> summary = !nonAutoSummary.empty()
225 ? nonAutoSummary.str()
226 : TryCreateAutoSummary(v);
227 if (!value.empty()) {
228 strm << value;
229 if (summary)
230 strm << ' ' << *summary;
231 } else if (summary) {
232 strm << *summary;
234 // As last resort, we print its type and address if available.
235 } else {
236 if (llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
237 !type_name.empty()) {
238 strm << type_name;
239 lldb::addr_t address = v.GetLoadAddress();
240 if (address != LLDB_INVALID_ADDRESS)
241 strm << " @ " << llvm::format_hex(address, 0);
245 EmplaceSafeString(object, key, result);
248 void FillResponse(const llvm::json::Object &request,
249 llvm::json::Object &response) {
250 // Fill in all of the needed response fields to a "request" and set "success"
251 // to true by default.
252 response.try_emplace("type", "response");
253 response.try_emplace("seq", (int64_t)0);
254 EmplaceSafeString(response, "command", GetString(request, "command"));
255 const int64_t seq = GetSigned(request, "seq", 0);
256 response.try_emplace("request_seq", seq);
257 response.try_emplace("success", true);
260 // "Scope": {
261 // "type": "object",
262 // "description": "A Scope is a named container for variables. Optionally
263 // a scope can map to a source or a range within a source.",
264 // "properties": {
265 // "name": {
266 // "type": "string",
267 // "description": "Name of the scope such as 'Arguments', 'Locals'."
268 // },
269 // "presentationHint": {
270 // "type": "string",
271 // "description": "An optional hint for how to present this scope in the
272 // UI. If this attribute is missing, the scope is shown
273 // with a generic UI.",
274 // "_enum": [ "arguments", "locals", "registers" ],
275 // },
276 // "variablesReference": {
277 // "type": "integer",
278 // "description": "The variables of this scope can be retrieved by
279 // passing the value of variablesReference to the
280 // VariablesRequest."
281 // },
282 // "namedVariables": {
283 // "type": "integer",
284 // "description": "The number of named variables in this scope. The
285 // client can use this optional information to present
286 // the variables in a paged UI and fetch them in chunks."
287 // },
288 // "indexedVariables": {
289 // "type": "integer",
290 // "description": "The number of indexed variables in this scope. The
291 // client can use this optional information to present
292 // the variables in a paged UI and fetch them in chunks."
293 // },
294 // "expensive": {
295 // "type": "boolean",
296 // "description": "If true, the number of variables in this scope is
297 // large or expensive to retrieve."
298 // },
299 // "source": {
300 // "$ref": "#/definitions/Source",
301 // "description": "Optional source for this scope."
302 // },
303 // "line": {
304 // "type": "integer",
305 // "description": "Optional start line of the range covered by this
306 // scope."
307 // },
308 // "column": {
309 // "type": "integer",
310 // "description": "Optional start column of the range covered by this
311 // scope."
312 // },
313 // "endLine": {
314 // "type": "integer",
315 // "description": "Optional end line of the range covered by this scope."
316 // },
317 // "endColumn": {
318 // "type": "integer",
319 // "description": "Optional end column of the range covered by this
320 // scope."
321 // }
322 // },
323 // "required": [ "name", "variablesReference", "expensive" ]
324 // }
325 llvm::json::Value CreateScope(const llvm::StringRef name,
326 int64_t variablesReference,
327 int64_t namedVariables, bool expensive) {
328 llvm::json::Object object;
329 EmplaceSafeString(object, "name", name.str());
331 // TODO: Support "arguments" scope. At the moment lldb-dap includes the
332 // arguments into the "locals" scope.
333 if (variablesReference == VARREF_LOCALS) {
334 object.try_emplace("presentationHint", "locals");
335 } else if (variablesReference == VARREF_REGS) {
336 object.try_emplace("presentationHint", "registers");
339 object.try_emplace("variablesReference", variablesReference);
340 object.try_emplace("expensive", expensive);
341 object.try_emplace("namedVariables", namedVariables);
342 return llvm::json::Value(std::move(object));
345 // "Breakpoint": {
346 // "type": "object",
347 // "description": "Information about a Breakpoint created in setBreakpoints
348 // or setFunctionBreakpoints.",
349 // "properties": {
350 // "id": {
351 // "type": "integer",
352 // "description": "An optional unique identifier for the breakpoint."
353 // },
354 // "verified": {
355 // "type": "boolean",
356 // "description": "If true breakpoint could be set (but not necessarily
357 // at the desired location)."
358 // },
359 // "message": {
360 // "type": "string",
361 // "description": "An optional message about the state of the breakpoint.
362 // This is shown to the user and can be used to explain
363 // why a breakpoint could not be verified."
364 // },
365 // "source": {
366 // "$ref": "#/definitions/Source",
367 // "description": "The source where the breakpoint is located."
368 // },
369 // "line": {
370 // "type": "integer",
371 // "description": "The start line of the actual range covered by the
372 // breakpoint."
373 // },
374 // "column": {
375 // "type": "integer",
376 // "description": "An optional start column of the actual range covered
377 // by the breakpoint."
378 // },
379 // "endLine": {
380 // "type": "integer",
381 // "description": "An optional end line of the actual range covered by
382 // the breakpoint."
383 // },
384 // "endColumn": {
385 // "type": "integer",
386 // "description": "An optional end column of the actual range covered by
387 // the breakpoint. If no end line is given, then the end
388 // column is assumed to be in the start line."
389 // }
390 // },
391 // "required": [ "verified" ]
392 // }
393 llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
394 std::optional<llvm::StringRef> request_path,
395 std::optional<uint32_t> request_line,
396 std::optional<uint32_t> request_column) {
397 // Each breakpoint location is treated as a separate breakpoint for VS code.
398 // They don't have the notion of a single breakpoint with multiple locations.
399 llvm::json::Object object;
400 if (!bp.IsValid())
401 return llvm::json::Value(std::move(object));
403 object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
404 object.try_emplace("id", bp.GetID());
405 // VS Code DAP doesn't currently allow one breakpoint to have multiple
406 // locations so we just report the first one. If we report all locations
407 // then the IDE starts showing the wrong line numbers and locations for
408 // other source file and line breakpoints in the same file.
410 // Below we search for the first resolved location in a breakpoint and report
411 // this as the breakpoint location since it will have a complete location
412 // that is at least loaded in the current process.
413 lldb::SBBreakpointLocation bp_loc;
414 const auto num_locs = bp.GetNumLocations();
415 for (size_t i = 0; i < num_locs; ++i) {
416 bp_loc = bp.GetLocationAtIndex(i);
417 if (bp_loc.IsResolved())
418 break;
420 // If not locations are resolved, use the first location.
421 if (!bp_loc.IsResolved())
422 bp_loc = bp.GetLocationAtIndex(0);
423 auto bp_addr = bp_loc.GetAddress();
425 if (request_path)
426 object.try_emplace("source", CreateSource(*request_path));
428 if (bp_addr.IsValid()) {
429 std::string formatted_addr =
430 "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(g_dap.target));
431 object.try_emplace("instructionReference", formatted_addr);
432 auto line_entry = bp_addr.GetLineEntry();
433 const auto line = line_entry.GetLine();
434 if (line != UINT32_MAX)
435 object.try_emplace("line", line);
436 const auto column = line_entry.GetColumn();
437 if (column != 0)
438 object.try_emplace("column", column);
439 object.try_emplace("source", CreateSource(line_entry));
441 // We try to add request_line as a fallback
442 if (request_line)
443 object.try_emplace("line", *request_line);
444 if (request_column)
445 object.try_emplace("column", *request_column);
446 return llvm::json::Value(std::move(object));
449 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
450 uint64_t debug_info_size = 0;
451 llvm::StringRef section_name(section.GetName());
452 if (section_name.startswith(".debug") || section_name.startswith("__debug") ||
453 section_name.startswith(".apple") || section_name.startswith("__apple"))
454 debug_info_size += section.GetFileByteSize();
455 size_t num_sub_sections = section.GetNumSubSections();
456 for (size_t i = 0; i < num_sub_sections; i++) {
457 debug_info_size +=
458 GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
460 return debug_info_size;
463 static uint64_t GetDebugInfoSize(lldb::SBModule module) {
464 uint64_t debug_info_size = 0;
465 size_t num_sections = module.GetNumSections();
466 for (size_t i = 0; i < num_sections; i++) {
467 debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
469 return debug_info_size;
472 static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
473 std::ostringstream oss;
474 oss << std::fixed << std::setprecision(1);
475 if (debug_info < 1024) {
476 oss << debug_info << "B";
477 } else if (debug_info < 1024 * 1024) {
478 double kb = double(debug_info) / 1024.0;
479 oss << kb << "KB";
480 } else if (debug_info < 1024 * 1024 * 1024) {
481 double mb = double(debug_info) / (1024.0 * 1024.0);
482 oss << mb << "MB";
483 } else {
484 double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
485 oss << gb << "GB";
487 return oss.str();
489 llvm::json::Value CreateModule(lldb::SBModule &module) {
490 llvm::json::Object object;
491 if (!module.IsValid())
492 return llvm::json::Value(std::move(object));
493 const char *uuid = module.GetUUIDString();
494 object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
495 object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
496 char module_path_arr[PATH_MAX];
497 module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
498 std::string module_path(module_path_arr);
499 object.try_emplace("path", module_path);
500 if (module.GetNumCompileUnits() > 0) {
501 std::string symbol_str = "Symbols loaded.";
502 std::string debug_info_size;
503 uint64_t debug_info = GetDebugInfoSize(module);
504 if (debug_info > 0) {
505 debug_info_size = ConvertDebugInfoSizeToString(debug_info);
507 object.try_emplace("symbolStatus", symbol_str);
508 object.try_emplace("debugInfoSize", debug_info_size);
509 char symbol_path_arr[PATH_MAX];
510 module.GetSymbolFileSpec().GetPath(symbol_path_arr,
511 sizeof(symbol_path_arr));
512 std::string symbol_path(symbol_path_arr);
513 object.try_emplace("symbolFilePath", symbol_path);
514 } else {
515 object.try_emplace("symbolStatus", "Symbols not found.");
517 std::string loaded_addr = std::to_string(
518 module.GetObjectFileHeaderAddress().GetLoadAddress(g_dap.target));
519 object.try_emplace("addressRange", loaded_addr);
520 std::string version_str;
521 uint32_t version_nums[3];
522 uint32_t num_versions =
523 module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
524 for (uint32_t i = 0; i < num_versions; ++i) {
525 if (!version_str.empty())
526 version_str += ".";
527 version_str += std::to_string(version_nums[i]);
529 if (!version_str.empty())
530 object.try_emplace("version", version_str);
531 return llvm::json::Value(std::move(object));
534 void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
535 std::optional<llvm::StringRef> request_path,
536 std::optional<uint32_t> request_line) {
537 breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
540 // "Event": {
541 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
542 // "type": "object",
543 // "description": "Server-initiated event.",
544 // "properties": {
545 // "type": {
546 // "type": "string",
547 // "enum": [ "event" ]
548 // },
549 // "event": {
550 // "type": "string",
551 // "description": "Type of event."
552 // },
553 // "body": {
554 // "type": [ "array", "boolean", "integer", "null", "number" ,
555 // "object", "string" ],
556 // "description": "Event-specific information."
557 // }
558 // },
559 // "required": [ "type", "event" ]
560 // }]
561 // },
562 // "ProtocolMessage": {
563 // "type": "object",
564 // "description": "Base class of requests, responses, and events.",
565 // "properties": {
566 // "seq": {
567 // "type": "integer",
568 // "description": "Sequence number."
569 // },
570 // "type": {
571 // "type": "string",
572 // "description": "Message type.",
573 // "_enum": [ "request", "response", "event" ]
574 // }
575 // },
576 // "required": [ "seq", "type" ]
577 // }
578 llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
579 llvm::json::Object event;
580 event.try_emplace("seq", 0);
581 event.try_emplace("type", "event");
582 EmplaceSafeString(event, "event", event_name);
583 return event;
586 // "ExceptionBreakpointsFilter": {
587 // "type": "object",
588 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an
589 // option for configuring how exceptions are dealt with.",
590 // "properties": {
591 // "filter": {
592 // "type": "string",
593 // "description": "The internal ID of the filter. This value is passed
594 // to the setExceptionBreakpoints request."
595 // },
596 // "label": {
597 // "type": "string",
598 // "description": "The name of the filter. This will be shown in the UI."
599 // },
600 // "default": {
601 // "type": "boolean",
602 // "description": "Initial value of the filter. If not specified a value
603 // 'false' is assumed."
604 // }
605 // },
606 // "required": [ "filter", "label" ]
607 // }
608 llvm::json::Value
609 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
610 llvm::json::Object object;
611 EmplaceSafeString(object, "filter", bp.filter);
612 EmplaceSafeString(object, "label", bp.label);
613 object.try_emplace("default", bp.default_value);
614 return llvm::json::Value(std::move(object));
617 // "Source": {
618 // "type": "object",
619 // "description": "A Source is a descriptor for source code. It is returned
620 // from the debug adapter as part of a StackFrame and it is
621 // used by clients when specifying breakpoints.",
622 // "properties": {
623 // "name": {
624 // "type": "string",
625 // "description": "The short name of the source. Every source returned
626 // from the debug adapter has a name. When sending a
627 // source to the debug adapter this name is optional."
628 // },
629 // "path": {
630 // "type": "string",
631 // "description": "The path of the source to be shown in the UI. It is
632 // only used to locate and load the content of the
633 // source if no sourceReference is specified (or its
634 // value is 0)."
635 // },
636 // "sourceReference": {
637 // "type": "number",
638 // "description": "If sourceReference > 0 the contents of the source must
639 // be retrieved through the SourceRequest (even if a path
640 // is specified). A sourceReference is only valid for a
641 // session, so it must not be used to persist a source."
642 // },
643 // "presentationHint": {
644 // "type": "string",
645 // "description": "An optional hint for how to present the source in the
646 // UI. A value of 'deemphasize' can be used to indicate
647 // that the source is not available or that it is
648 // skipped on stepping.",
649 // "enum": [ "normal", "emphasize", "deemphasize" ]
650 // },
651 // "origin": {
652 // "type": "string",
653 // "description": "The (optional) origin of this source: possible values
654 // 'internal module', 'inlined content from source map',
655 // etc."
656 // },
657 // "sources": {
658 // "type": "array",
659 // "items": {
660 // "$ref": "#/definitions/Source"
661 // },
662 // "description": "An optional list of sources that are related to this
663 // source. These may be the source that generated this
664 // source."
665 // },
666 // "adapterData": {
667 // "type":["array","boolean","integer","null","number","object","string"],
668 // "description": "Optional data that a debug adapter might want to loop
669 // through the client. The client should leave the data
670 // intact and persist it across sessions. The client
671 // should not interpret the data."
672 // },
673 // "checksums": {
674 // "type": "array",
675 // "items": {
676 // "$ref": "#/definitions/Checksum"
677 // },
678 // "description": "The checksums associated with this file."
679 // }
680 // }
681 // }
682 llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
683 llvm::json::Object object;
684 lldb::SBFileSpec file = line_entry.GetFileSpec();
685 if (file.IsValid()) {
686 const char *name = file.GetFilename();
687 if (name)
688 EmplaceSafeString(object, "name", name);
689 char path[PATH_MAX] = "";
690 if (file.GetPath(path, sizeof(path)) &&
691 lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
692 EmplaceSafeString(object, "path", std::string(path));
695 return llvm::json::Value(std::move(object));
698 llvm::json::Value CreateSource(llvm::StringRef source_path) {
699 llvm::json::Object source;
700 llvm::StringRef name = llvm::sys::path::filename(source_path);
701 EmplaceSafeString(source, "name", name);
702 EmplaceSafeString(source, "path", source_path);
703 return llvm::json::Value(std::move(source));
706 std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
707 auto line_entry = frame.GetLineEntry();
708 // A line entry of 0 indicates the line is compiler generated i.e. no source
709 // file is associated with the frame.
710 if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0)
711 return CreateSource(line_entry);
713 return {};
716 // "StackFrame": {
717 // "type": "object",
718 // "description": "A Stackframe contains the source location.",
719 // "properties": {
720 // "id": {
721 // "type": "integer",
722 // "description": "An identifier for the stack frame. It must be unique
723 // across all threads. This id can be used to retrieve
724 // the scopes of the frame with the 'scopesRequest' or
725 // to restart the execution of a stackframe."
726 // },
727 // "name": {
728 // "type": "string",
729 // "description": "The name of the stack frame, typically a method name."
730 // },
731 // "source": {
732 // "$ref": "#/definitions/Source",
733 // "description": "The optional source of the frame."
734 // },
735 // "line": {
736 // "type": "integer",
737 // "description": "The line within the file of the frame. If source is
738 // null or doesn't exist, line is 0 and must be ignored."
739 // },
740 // "column": {
741 // "type": "integer",
742 // "description": "The column within the line. If source is null or
743 // doesn't exist, column is 0 and must be ignored."
744 // },
745 // "endLine": {
746 // "type": "integer",
747 // "description": "An optional end line of the range covered by the
748 // stack frame."
749 // },
750 // "endColumn": {
751 // "type": "integer",
752 // "description": "An optional end column of the range covered by the
753 // stack frame."
754 // },
755 // "instructionPointerReference": {
756 // "type": "string",
757 // "description": "A memory reference for the current instruction
758 // pointer
759 // in this frame."
760 // },
761 // "moduleId": {
762 // "type": ["integer", "string"],
763 // "description": "The module associated with this frame, if any."
764 // },
765 // "presentationHint": {
766 // "type": "string",
767 // "enum": [ "normal", "label", "subtle" ],
768 // "description": "An optional hint for how to present this frame in
769 // the UI. A value of 'label' can be used to indicate
770 // that the frame is an artificial frame that is used
771 // as a visual label or separator. A value of 'subtle'
772 // can be used to change the appearance of a frame in
773 // a 'subtle' way."
774 // }
775 // },
776 // "required": [ "id", "name", "line", "column" ]
777 // }
778 llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
779 llvm::json::Object object;
780 int64_t frame_id = MakeDAPFrameID(frame);
781 object.try_emplace("id", frame_id);
783 // `function_name` can be a nullptr, which throws an error when assigned to an
784 // `std::string`.
785 const char *function_name = frame.GetDisplayFunctionName();
786 std::string frame_name =
787 function_name == nullptr ? std::string() : function_name;
788 if (frame_name.empty()) {
789 // If the function name is unavailable, display the pc address as a 16-digit
790 // hex string, e.g. "0x0000000000012345"
791 llvm::raw_string_ostream os(frame_name);
792 os << llvm::format_hex(frame.GetPC(), 18);
794 bool is_optimized = frame.GetFunction().GetIsOptimized();
795 if (is_optimized)
796 frame_name += " [opt]";
797 EmplaceSafeString(object, "name", frame_name);
799 auto source = CreateSource(frame);
801 if (source) {
802 object.try_emplace("source", *source);
803 auto line_entry = frame.GetLineEntry();
804 auto line = line_entry.GetLine();
805 if (line && line != LLDB_INVALID_LINE_NUMBER)
806 object.try_emplace("line", line);
807 auto column = line_entry.GetColumn();
808 if (column && column != LLDB_INVALID_COLUMN_NUMBER)
809 object.try_emplace("column", column);
810 } else {
811 object.try_emplace("line", 0);
812 object.try_emplace("column", 0);
813 object.try_emplace("presentationHint", "subtle");
816 const auto pc = frame.GetPC();
817 if (pc != LLDB_INVALID_ADDRESS) {
818 std::string formatted_addr = "0x" + llvm::utohexstr(pc);
819 object.try_emplace("instructionPointerReference", formatted_addr);
822 return llvm::json::Value(std::move(object));
825 // "Thread": {
826 // "type": "object",
827 // "description": "A Thread",
828 // "properties": {
829 // "id": {
830 // "type": "integer",
831 // "description": "Unique identifier for the thread."
832 // },
833 // "name": {
834 // "type": "string",
835 // "description": "A name of the thread."
836 // }
837 // },
838 // "required": [ "id", "name" ]
839 // }
840 llvm::json::Value CreateThread(lldb::SBThread &thread) {
841 llvm::json::Object object;
842 object.try_emplace("id", (int64_t)thread.GetThreadID());
843 const char *thread_name = thread.GetName();
844 const char *queue_name = thread.GetQueueName();
846 std::string thread_str;
847 if (thread_name) {
848 thread_str = std::string(thread_name);
849 } else if (queue_name) {
850 auto kind = thread.GetQueue().GetKind();
851 std::string queue_kind_label = "";
852 if (kind == lldb::eQueueKindSerial) {
853 queue_kind_label = " (serial)";
854 } else if (kind == lldb::eQueueKindConcurrent) {
855 queue_kind_label = " (concurrent)";
858 thread_str = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
859 queue_name, queue_kind_label)
860 .str();
861 } else {
862 thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
865 EmplaceSafeString(object, "name", thread_str);
867 return llvm::json::Value(std::move(object));
870 // "StoppedEvent": {
871 // "allOf": [ { "$ref": "#/definitions/Event" }, {
872 // "type": "object",
873 // "description": "Event message for 'stopped' event type. The event
874 // indicates that the execution of the debuggee has stopped
875 // due to some condition. This can be caused by a break
876 // point previously set, a stepping action has completed,
877 // by executing a debugger statement etc.",
878 // "properties": {
879 // "event": {
880 // "type": "string",
881 // "enum": [ "stopped" ]
882 // },
883 // "body": {
884 // "type": "object",
885 // "properties": {
886 // "reason": {
887 // "type": "string",
888 // "description": "The reason for the event. For backward
889 // compatibility this string is shown in the UI if
890 // the 'description' attribute is missing (but it
891 // must not be translated).",
892 // "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
893 // },
894 // "description": {
895 // "type": "string",
896 // "description": "The full reason for the event, e.g. 'Paused
897 // on exception'. This string is shown in the UI
898 // as is."
899 // },
900 // "threadId": {
901 // "type": "integer",
902 // "description": "The thread which was stopped."
903 // },
904 // "text": {
905 // "type": "string",
906 // "description": "Additional information. E.g. if reason is
907 // 'exception', text contains the exception name.
908 // This string is shown in the UI."
909 // },
910 // "allThreadsStopped": {
911 // "type": "boolean",
912 // "description": "If allThreadsStopped is true, a debug adapter
913 // can announce that all threads have stopped.
914 // The client should use this information to
915 // enable that all threads can be expanded to
916 // access their stacktraces. If the attribute
917 // is missing or false, only the thread with the
918 // given threadId can be expanded."
919 // }
920 // },
921 // "required": [ "reason" ]
922 // }
923 // },
924 // "required": [ "event", "body" ]
925 // }]
926 // }
927 llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
928 uint32_t stop_id) {
929 llvm::json::Object event(CreateEventObject("stopped"));
930 llvm::json::Object body;
931 switch (thread.GetStopReason()) {
932 case lldb::eStopReasonTrace:
933 case lldb::eStopReasonPlanComplete:
934 body.try_emplace("reason", "step");
935 break;
936 case lldb::eStopReasonBreakpoint: {
937 ExceptionBreakpoint *exc_bp = g_dap.GetExceptionBPFromStopReason(thread);
938 if (exc_bp) {
939 body.try_emplace("reason", "exception");
940 EmplaceSafeString(body, "description", exc_bp->label);
941 } else {
942 body.try_emplace("reason", "breakpoint");
943 char desc_str[64];
944 uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
945 uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
946 snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
947 bp_id, bp_loc_id);
948 body.try_emplace("hitBreakpointIds",
949 llvm::json::Array{llvm::json::Value(bp_id)});
950 EmplaceSafeString(body, "description", desc_str);
952 } break;
953 case lldb::eStopReasonWatchpoint:
954 case lldb::eStopReasonInstrumentation:
955 body.try_emplace("reason", "breakpoint");
956 break;
957 case lldb::eStopReasonProcessorTrace:
958 body.try_emplace("reason", "processor trace");
959 break;
960 case lldb::eStopReasonSignal:
961 case lldb::eStopReasonException:
962 body.try_emplace("reason", "exception");
963 break;
964 case lldb::eStopReasonExec:
965 body.try_emplace("reason", "entry");
966 break;
967 case lldb::eStopReasonFork:
968 body.try_emplace("reason", "fork");
969 break;
970 case lldb::eStopReasonVFork:
971 body.try_emplace("reason", "vfork");
972 break;
973 case lldb::eStopReasonVForkDone:
974 body.try_emplace("reason", "vforkdone");
975 break;
976 case lldb::eStopReasonThreadExiting:
977 case lldb::eStopReasonInvalid:
978 case lldb::eStopReasonNone:
979 break;
981 if (stop_id == 0)
982 body.try_emplace("reason", "entry");
983 const lldb::tid_t tid = thread.GetThreadID();
984 body.try_emplace("threadId", (int64_t)tid);
985 // If no description has been set, then set it to the default thread stopped
986 // description. If we have breakpoints that get hit and shouldn't be reported
987 // as breakpoints, then they will set the description above.
988 if (!ObjectContainsKey(body, "description")) {
989 char description[1024];
990 if (thread.GetStopDescription(description, sizeof(description))) {
991 EmplaceSafeString(body, "description", std::string(description));
994 // "threadCausedFocus" is used in tests to validate breaking behavior.
995 if (tid == g_dap.focus_tid) {
996 body.try_emplace("threadCausedFocus", true);
998 body.try_emplace("preserveFocusHint", tid != g_dap.focus_tid);
999 body.try_emplace("allThreadsStopped", true);
1000 event.try_emplace("body", std::move(body));
1001 return llvm::json::Value(std::move(event));
1004 const char *GetNonNullVariableName(lldb::SBValue v) {
1005 const char *name = v.GetName();
1006 return name ? name : "<null>";
1009 std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
1010 bool is_name_duplicated) {
1011 lldb::SBStream name_builder;
1012 name_builder.Print(GetNonNullVariableName(v));
1013 if (is_name_duplicated) {
1014 lldb::SBDeclaration declaration = v.GetDeclaration();
1015 const char *file_name = declaration.GetFileSpec().GetFilename();
1016 const uint32_t line = declaration.GetLine();
1018 if (file_name != nullptr && line > 0)
1019 name_builder.Printf(" @ %s:%u", file_name, line);
1020 else if (const char *location = v.GetLocation())
1021 name_builder.Printf(" @ %s", location);
1023 return name_builder.GetData();
1026 // "Variable": {
1027 // "type": "object",
1028 // "description": "A Variable is a name/value pair. Optionally a variable
1029 // can have a 'type' that is shown if space permits or when
1030 // hovering over the variable's name. An optional 'kind' is
1031 // used to render additional properties of the variable,
1032 // e.g. different icons can be used to indicate that a
1033 // variable is public or private. If the value is
1034 // structured (has children), a handle is provided to
1035 // retrieve the children with the VariablesRequest. If
1036 // the number of named or indexed children is large, the
1037 // numbers should be returned via the optional
1038 // 'namedVariables' and 'indexedVariables' attributes. The
1039 // client can use this optional information to present the
1040 // children in a paged UI and fetch them in chunks.",
1041 // "properties": {
1042 // "name": {
1043 // "type": "string",
1044 // "description": "The variable's name."
1045 // },
1046 // "value": {
1047 // "type": "string",
1048 // "description": "The variable's value. This can be a multi-line text,
1049 // e.g. for a function the body of a function."
1050 // },
1051 // "type": {
1052 // "type": "string",
1053 // "description": "The type of the variable's value. Typically shown in
1054 // the UI when hovering over the value."
1055 // },
1056 // "presentationHint": {
1057 // "$ref": "#/definitions/VariablePresentationHint",
1058 // "description": "Properties of a variable that can be used to determine
1059 // how to render the variable in the UI."
1060 // },
1061 // "evaluateName": {
1062 // "type": "string",
1063 // "description": "Optional evaluatable name of this variable which can
1064 // be passed to the 'EvaluateRequest' to fetch the
1065 // variable's value."
1066 // },
1067 // "variablesReference": {
1068 // "type": "integer",
1069 // "description": "If variablesReference is > 0, the variable is
1070 // structured and its children can be retrieved by
1071 // passing variablesReference to the VariablesRequest."
1072 // },
1073 // "namedVariables": {
1074 // "type": "integer",
1075 // "description": "The number of named child variables. The client can
1076 // use this optional information to present the children
1077 // in a paged UI and fetch them in chunks."
1078 // },
1079 // "indexedVariables": {
1080 // "type": "integer",
1081 // "description": "The number of indexed child variables. The client
1082 // can use this optional information to present the
1083 // children in a paged UI and fetch them in chunks."
1084 // }
1085 // },
1086 // "required": [ "name", "value", "variablesReference" ]
1087 // }
1088 llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1089 int64_t varID, bool format_hex,
1090 bool is_name_duplicated,
1091 std::optional<std::string> custom_name) {
1092 llvm::json::Object object;
1093 EmplaceSafeString(
1094 object, "name",
1095 custom_name ? *custom_name
1096 : CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
1098 if (format_hex)
1099 v.SetFormat(lldb::eFormatHex);
1100 SetValueForKey(v, object, "value");
1101 auto type_obj = v.GetType();
1102 auto type_cstr = type_obj.GetDisplayTypeName();
1103 // If we have a type with many children, we would like to be able to
1104 // give a hint to the IDE that the type has indexed children so that the
1105 // request can be broken up in grabbing only a few children at a time. We want
1106 // to be careful and only call "v.GetNumChildren()" if we have an array type
1107 // or if we have a synthetic child provider. We don't want to call
1108 // "v.GetNumChildren()" on all objects as class, struct and union types don't
1109 // need to be completed if they are never expanded. So we want to avoid
1110 // calling this to only cases where we it makes sense to keep performance high
1111 // during normal debugging.
1113 // If we have an array type, say that it is indexed and provide the number of
1114 // children in case we have a huge array. If we don't do this, then we might
1115 // take a while to produce all children at onces which can delay your debug
1116 // session.
1117 const bool is_array = type_obj.IsArrayType();
1118 const bool is_synthetic = v.IsSynthetic();
1119 if (is_array || is_synthetic) {
1120 const auto num_children = v.GetNumChildren();
1121 // We create a "[raw]" fake child for each synthetic type, so we have to
1122 // account for it when returning indexed variables. We don't need to do this
1123 // for non-indexed ones.
1124 bool has_raw_child = is_synthetic && g_dap.enable_synthetic_child_debugging;
1125 int actual_num_children = num_children + (has_raw_child ? 1 : 0);
1126 if (is_array) {
1127 object.try_emplace("indexedVariables", actual_num_children);
1128 } else if (num_children > 0) {
1129 // If a type has a synthetic child provider, then the SBType of "v" won't
1130 // tell us anything about what might be displayed. So we can check if the
1131 // first child's name is "[0]" and then we can say it is indexed.
1132 const char *first_child_name = v.GetChildAtIndex(0).GetName();
1133 if (first_child_name && strcmp(first_child_name, "[0]") == 0)
1134 object.try_emplace("indexedVariables", actual_num_children);
1137 EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
1138 if (varID != INT64_MAX)
1139 object.try_emplace("id", varID);
1140 if (v.MightHaveChildren())
1141 object.try_emplace("variablesReference", variablesReference);
1142 else
1143 object.try_emplace("variablesReference", (int64_t)0);
1144 lldb::SBStream evaluateStream;
1145 v.GetExpressionPath(evaluateStream);
1146 const char *evaluateName = evaluateStream.GetData();
1147 if (evaluateName && evaluateName[0])
1148 EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
1149 return llvm::json::Value(std::move(object));
1152 llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
1153 llvm::json::Object object;
1154 char unit_path_arr[PATH_MAX];
1155 unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
1156 std::string unit_path(unit_path_arr);
1157 object.try_emplace("compileUnitPath", unit_path);
1158 return llvm::json::Value(std::move(object));
1161 /// See
1162 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1163 llvm::json::Object
1164 CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
1165 llvm::StringRef debug_adaptor_path,
1166 llvm::StringRef comm_file,
1167 lldb::pid_t debugger_pid) {
1168 llvm::json::Object run_in_terminal_args;
1169 // This indicates the IDE to open an embedded terminal, instead of opening the
1170 // terminal in a new window.
1171 run_in_terminal_args.try_emplace("kind", "integrated");
1173 auto launch_request_arguments = launch_request.getObject("arguments");
1174 // The program path must be the first entry in the "args" field
1175 std::vector<std::string> args = {debug_adaptor_path.str(), "--comm-file",
1176 comm_file.str()};
1177 if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
1178 args.push_back("--debugger-pid");
1179 args.push_back(std::to_string(debugger_pid));
1181 args.push_back("--launch-target");
1182 args.push_back(GetString(launch_request_arguments, "program").str());
1183 std::vector<std::string> target_args =
1184 GetStrings(launch_request_arguments, "args");
1185 args.insert(args.end(), target_args.begin(), target_args.end());
1186 run_in_terminal_args.try_emplace("args", args);
1188 const auto cwd = GetString(launch_request_arguments, "cwd");
1189 if (!cwd.empty())
1190 run_in_terminal_args.try_emplace("cwd", cwd);
1192 // We need to convert the input list of environments variables into a
1193 // dictionary
1194 std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1195 llvm::json::Object environment;
1196 for (const std::string &env : envs) {
1197 size_t index = env.find('=');
1198 environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1200 run_in_terminal_args.try_emplace("env",
1201 llvm::json::Value(std::move(environment)));
1203 return run_in_terminal_args;
1206 // Keep all the top level items from the statistics dump, except for the
1207 // "modules" array. It can be huge and cause delay
1208 // Array and dictionary value will return as <key, JSON string> pairs
1209 void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key,
1210 llvm::json::Object &out) {
1211 lldb::SBStructuredData value = data.GetValueForKey(key);
1212 std::string key_utf8 = llvm::json::fixUTF8(key);
1213 if (strcmp(key, "modules") == 0)
1214 return;
1215 switch (value.GetType()) {
1216 case lldb::eStructuredDataTypeFloat:
1217 out.try_emplace(key_utf8, value.GetFloatValue());
1218 break;
1219 case lldb::eStructuredDataTypeUnsignedInteger:
1220 out.try_emplace(key_utf8, value.GetIntegerValue((uint64_t)0));
1221 break;
1222 case lldb::eStructuredDataTypeSignedInteger:
1223 out.try_emplace(key_utf8, value.GetIntegerValue((int64_t)0));
1224 break;
1225 case lldb::eStructuredDataTypeArray: {
1226 lldb::SBStream contents;
1227 value.GetAsJSON(contents);
1228 out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1229 } break;
1230 case lldb::eStructuredDataTypeBoolean:
1231 out.try_emplace(key_utf8, value.GetBooleanValue());
1232 break;
1233 case lldb::eStructuredDataTypeString: {
1234 // Get the string size before reading
1235 const size_t str_length = value.GetStringValue(nullptr, 0);
1236 std::string str(str_length + 1, 0);
1237 value.GetStringValue(&str[0], str_length);
1238 out.try_emplace(key_utf8, llvm::json::fixUTF8(str));
1239 } break;
1240 case lldb::eStructuredDataTypeDictionary: {
1241 lldb::SBStream contents;
1242 value.GetAsJSON(contents);
1243 out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1244 } break;
1245 case lldb::eStructuredDataTypeNull:
1246 case lldb::eStructuredDataTypeGeneric:
1247 case lldb::eStructuredDataTypeInvalid:
1248 break;
1252 void addStatistic(llvm::json::Object &event) {
1253 lldb::SBStructuredData statistics = g_dap.target.GetStatistics();
1254 bool is_dictionary =
1255 statistics.GetType() == lldb::eStructuredDataTypeDictionary;
1256 if (!is_dictionary)
1257 return;
1258 llvm::json::Object stats_body;
1260 lldb::SBStringList keys;
1261 if (!statistics.GetKeys(keys))
1262 return;
1263 for (size_t i = 0; i < keys.GetSize(); i++) {
1264 const char *key = keys.GetStringAtIndex(i);
1265 FilterAndGetValueForKey(statistics, key, stats_body);
1267 event.try_emplace("statistics", std::move(stats_body));
1270 llvm::json::Object CreateTerminatedEventObject() {
1271 llvm::json::Object event(CreateEventObject("terminated"));
1272 addStatistic(event);
1273 return event;
1276 std::string JSONToString(const llvm::json::Value &json) {
1277 std::string data;
1278 llvm::raw_string_ostream os(data);
1279 os << json;
1280 os.flush();
1281 return data;
1284 } // namespace lldb_dap