1 //===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
28 #include "ExceptionBreakpoint.h"
29 #include "JSONUtils.h"
30 #include "LLDBUtils.h"
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());
39 obj
.try_emplace(key
, llvm::json::fixUTF8(str
));
42 llvm::StringRef
GetAsString(const llvm::json::Value
&value
) {
43 if (auto s
= value
.getAsString())
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
))
56 llvm::StringRef
GetString(const llvm::json::Object
*obj
, llvm::StringRef key
,
57 llvm::StringRef 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
;
72 uint64_t GetUnsigned(const llvm::json::Object
*obj
, llvm::StringRef key
,
73 uint64_t fail_value
) {
76 return GetUnsigned(*obj
, key
, fail_value
);
79 bool GetBoolean(const llvm::json::Object
&obj
, llvm::StringRef key
,
81 if (auto value
= obj
.getBoolean(key
))
83 if (auto value
= obj
.getInteger(key
))
88 bool GetBoolean(const llvm::json::Object
*obj
, llvm::StringRef key
,
92 return GetBoolean(*obj
, key
, fail_value
);
95 int64_t GetSigned(const llvm::json::Object
&obj
, llvm::StringRef key
,
97 if (auto value
= obj
.getInteger(key
))
102 int64_t GetSigned(const llvm::json::Object
*obj
, llvm::StringRef key
,
103 int64_t 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
);
119 for (const auto &value
: *json_array
) {
120 switch (value
.kind()) {
121 case llvm::json::Value::String
:
122 strs
.push_back(value
.getAsString()->str());
124 case llvm::json::Value::Number
:
125 case llvm::json::Value::Boolean
:
126 strs
.push_back(llvm::to_string(value
));
128 case llvm::json::Value::Null
:
129 case llvm::json::Value::Object
:
130 case llvm::json::Value::Array
:
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
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
145 if (!g_dap
.enable_auto_variable_summaries
)
148 if (!v
.MightHaveChildren())
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
155 const size_t max_length
= 32;
157 auto start
= std::chrono::steady_clock::now();
159 llvm::raw_string_ostream
os(summary
);
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
<< "...";
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())
179 value
= child
.GetValue();
181 if (!value
.empty()) {
182 // If the child is an indexed entry, we don't show its index to save
184 if (name
.starts_with("["))
185 os
<< separator
<< value
;
187 os
<< separator
<< name
<< ":" << value
;
194 if (summary
== "{...}" || 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
)
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
) {
216 llvm::raw_string_ostream
strm(result
);
218 lldb::SBError error
= v
.GetError();
219 if (!error
.Success()) {
220 strm
<< "<error: " << error
.GetCString() << ">";
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()) {
230 strm
<< ' ' << *summary
;
231 } else if (summary
) {
234 // As last resort, we print its type and address if available.
236 if (llvm::StringRef type_name
= v
.GetType().GetDisplayTypeName();
237 !type_name
.empty()) {
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);
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.",
267 // "description": "Name of the scope such as 'Arguments', 'Locals'."
269 // "presentationHint": {
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" ],
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."
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."
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."
295 // "type": "boolean",
296 // "description": "If true, the number of variables in this scope is
297 // large or expensive to retrieve."
300 // "$ref": "#/definitions/Source",
301 // "description": "Optional source for this scope."
304 // "type": "integer",
305 // "description": "Optional start line of the range covered by this
309 // "type": "integer",
310 // "description": "Optional start column of the range covered by this
314 // "type": "integer",
315 // "description": "Optional end line of the range covered by this scope."
318 // "type": "integer",
319 // "description": "Optional end column of the range covered by this
323 // "required": [ "name", "variablesReference", "expensive" ]
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
));
347 // "description": "Information about a Breakpoint created in setBreakpoints
348 // or setFunctionBreakpoints.",
351 // "type": "integer",
352 // "description": "An optional unique identifier for the breakpoint."
355 // "type": "boolean",
356 // "description": "If true breakpoint could be set (but not necessarily
357 // at the desired location)."
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."
366 // "$ref": "#/definitions/Source",
367 // "description": "The source where the breakpoint is located."
370 // "type": "integer",
371 // "description": "The start line of the actual range covered by the
375 // "type": "integer",
376 // "description": "An optional start column of the actual range covered
377 // by the breakpoint."
380 // "type": "integer",
381 // "description": "An optional end line of the actual range covered by
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."
391 // "required": [ "verified" ]
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
;
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())
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();
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();
438 object
.try_emplace("column", column
);
439 object
.try_emplace("source", CreateSource(line_entry
));
441 // We try to add request_line as a fallback
443 object
.try_emplace("line", *request_line
);
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
++) {
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;
480 } else if (debug_info
< 1024 * 1024 * 1024) {
481 double mb
= double(debug_info
) / (1024.0 * 1024.0);
484 double gb
= double(debug_info
) / (1024.0 * 1024.0 * 1024.0);
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
);
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())
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
));
541 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
543 // "description": "Server-initiated event.",
547 // "enum": [ "event" ]
551 // "description": "Type of event."
554 // "type": [ "array", "boolean", "integer", "null", "number" ,
555 // "object", "string" ],
556 // "description": "Event-specific information."
559 // "required": [ "type", "event" ]
562 // "ProtocolMessage": {
564 // "description": "Base class of requests, responses, and events.",
567 // "type": "integer",
568 // "description": "Sequence number."
572 // "description": "Message type.",
573 // "_enum": [ "request", "response", "event" ]
576 // "required": [ "seq", "type" ]
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
);
586 // "ExceptionBreakpointsFilter": {
588 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an
589 // option for configuring how exceptions are dealt with.",
593 // "description": "The internal ID of the filter. This value is passed
594 // to the setExceptionBreakpoints request."
598 // "description": "The name of the filter. This will be shown in the UI."
601 // "type": "boolean",
602 // "description": "Initial value of the filter. If not specified a value
603 // 'false' is assumed."
606 // "required": [ "filter", "label" ]
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
));
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.",
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."
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
636 // "sourceReference": {
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."
643 // "presentationHint": {
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" ]
653 // "description": "The (optional) origin of this source: possible values
654 // 'internal module', 'inlined content from source map',
660 // "$ref": "#/definitions/Source"
662 // "description": "An optional list of sources that are related to this
663 // source. These may be the source that generated this
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."
676 // "$ref": "#/definitions/Checksum"
678 // "description": "The checksums associated with this file."
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();
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
);
718 // "description": "A Stackframe contains the source location.",
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."
729 // "description": "The name of the stack frame, typically a method name."
732 // "$ref": "#/definitions/Source",
733 // "description": "The optional source of the frame."
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."
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."
746 // "type": "integer",
747 // "description": "An optional end line of the range covered by the
751 // "type": "integer",
752 // "description": "An optional end column of the range covered by the
755 // "instructionPointerReference": {
757 // "description": "A memory reference for the current instruction
762 // "type": ["integer", "string"],
763 // "description": "The module associated with this frame, if any."
765 // "presentationHint": {
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
776 // "required": [ "id", "name", "line", "column" ]
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
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();
796 frame_name
+= " [opt]";
797 EmplaceSafeString(object
, "name", frame_name
);
799 auto source
= CreateSource(frame
);
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
);
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
));
827 // "description": "A Thread",
830 // "type": "integer",
831 // "description": "Unique identifier for the thread."
835 // "description": "A name of the thread."
838 // "required": [ "id", "name" ]
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
;
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
)
862 thread_str
= llvm::formatv("Thread {0}", thread
.GetIndexID()).str();
865 EmplaceSafeString(object
, "name", thread_str
);
867 return llvm::json::Value(std::move(object
));
871 // "allOf": [ { "$ref": "#/definitions/Event" }, {
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.",
881 // "enum": [ "stopped" ]
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" ]
896 // "description": "The full reason for the event, e.g. 'Paused
897 // on exception'. This string is shown in the UI
901 // "type": "integer",
902 // "description": "The thread which was stopped."
906 // "description": "Additional information. E.g. if reason is
907 // 'exception', text contains the exception name.
908 // This string is shown in the UI."
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."
921 // "required": [ "reason" ]
924 // "required": [ "event", "body" ]
927 llvm::json::Value
CreateThreadStopped(lldb::SBThread
&thread
,
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");
936 case lldb::eStopReasonBreakpoint
: {
937 ExceptionBreakpoint
*exc_bp
= g_dap
.GetExceptionBPFromStopReason(thread
);
939 body
.try_emplace("reason", "exception");
940 EmplaceSafeString(body
, "description", exc_bp
->label
);
942 body
.try_emplace("reason", "breakpoint");
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
,
948 body
.try_emplace("hitBreakpointIds",
949 llvm::json::Array
{llvm::json::Value(bp_id
)});
950 EmplaceSafeString(body
, "description", desc_str
);
953 case lldb::eStopReasonWatchpoint
:
954 case lldb::eStopReasonInstrumentation
:
955 body
.try_emplace("reason", "breakpoint");
957 case lldb::eStopReasonProcessorTrace
:
958 body
.try_emplace("reason", "processor trace");
960 case lldb::eStopReasonSignal
:
961 case lldb::eStopReasonException
:
962 body
.try_emplace("reason", "exception");
964 case lldb::eStopReasonExec
:
965 body
.try_emplace("reason", "entry");
967 case lldb::eStopReasonFork
:
968 body
.try_emplace("reason", "fork");
970 case lldb::eStopReasonVFork
:
971 body
.try_emplace("reason", "vfork");
973 case lldb::eStopReasonVForkDone
:
974 body
.try_emplace("reason", "vforkdone");
976 case lldb::eStopReasonThreadExiting
:
977 case lldb::eStopReasonInvalid
:
978 case lldb::eStopReasonNone
:
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();
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.",
1043 // "type": "string",
1044 // "description": "The variable's name."
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."
1052 // "type": "string",
1053 // "description": "The type of the variable's value. Typically shown in
1054 // the UI when hovering over the value."
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."
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."
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."
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."
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."
1086 // "required": [ "name", "value", "variablesReference" ]
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
;
1095 custom_name
? *custom_name
1096 : CreateUniqueVariableNameForDisplay(v
, is_name_duplicated
));
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
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);
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
);
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
));
1162 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
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",
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");
1190 run_in_terminal_args
.try_emplace("cwd", cwd
);
1192 // We need to convert the input list of environments variables into a
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)
1215 switch (value
.GetType()) {
1216 case lldb::eStructuredDataTypeFloat
:
1217 out
.try_emplace(key_utf8
, value
.GetFloatValue());
1219 case lldb::eStructuredDataTypeUnsignedInteger
:
1220 out
.try_emplace(key_utf8
, value
.GetIntegerValue((uint64_t)0));
1222 case lldb::eStructuredDataTypeSignedInteger
:
1223 out
.try_emplace(key_utf8
, value
.GetIntegerValue((int64_t)0));
1225 case lldb::eStructuredDataTypeArray
: {
1226 lldb::SBStream contents
;
1227 value
.GetAsJSON(contents
);
1228 out
.try_emplace(key_utf8
, llvm::json::fixUTF8(contents
.GetData()));
1230 case lldb::eStructuredDataTypeBoolean
:
1231 out
.try_emplace(key_utf8
, value
.GetBooleanValue());
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
));
1240 case lldb::eStructuredDataTypeDictionary
: {
1241 lldb::SBStream contents
;
1242 value
.GetAsJSON(contents
);
1243 out
.try_emplace(key_utf8
, llvm::json::fixUTF8(contents
.GetData()));
1245 case lldb::eStructuredDataTypeNull
:
1246 case lldb::eStructuredDataTypeGeneric
:
1247 case lldb::eStructuredDataTypeInvalid
:
1252 void addStatistic(llvm::json::Object
&event
) {
1253 lldb::SBStructuredData statistics
= g_dap
.target
.GetStatistics();
1254 bool is_dictionary
=
1255 statistics
.GetType() == lldb::eStructuredDataTypeDictionary
;
1258 llvm::json::Object stats_body
;
1260 lldb::SBStringList keys
;
1261 if (!statistics
.GetKeys(keys
))
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
);
1276 std::string
JSONToString(const llvm::json::Value
&json
) {
1278 llvm::raw_string_ostream
os(data
);
1284 } // namespace lldb_dap