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/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"
30 #include "ExceptionBreakpoint.h"
31 #include "JSONUtils.h"
32 #include "LLDBUtils.h"
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());
41 obj
.try_emplace(key
, llvm::json::fixUTF8(str
));
44 llvm::StringRef
GetAsString(const llvm::json::Value
&value
) {
45 if (auto s
= value
.getAsString())
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
))
58 llvm::StringRef
GetString(const llvm::json::Object
*obj
, llvm::StringRef key
,
59 llvm::StringRef 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
;
74 uint64_t GetUnsigned(const llvm::json::Object
*obj
, llvm::StringRef key
,
75 uint64_t fail_value
) {
78 return GetUnsigned(*obj
, key
, fail_value
);
81 bool GetBoolean(const llvm::json::Object
&obj
, llvm::StringRef key
,
83 if (auto value
= obj
.getBoolean(key
))
85 if (auto value
= obj
.getInteger(key
))
90 bool GetBoolean(const llvm::json::Object
*obj
, llvm::StringRef key
,
94 return GetBoolean(*obj
, key
, fail_value
);
97 int64_t GetSigned(const llvm::json::Object
&obj
, llvm::StringRef key
,
99 if (auto value
= obj
.getInteger(key
))
104 int64_t GetSigned(const llvm::json::Object
*obj
, llvm::StringRef key
,
105 int64_t 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
);
121 for (const auto &value
: *json_array
) {
122 switch (value
.kind()) {
123 case llvm::json::Value::String
:
124 strs
.push_back(value
.getAsString()->str());
126 case llvm::json::Value::Number
:
127 case llvm::json::Value::Boolean
:
128 strs
.push_back(llvm::to_string(value
));
130 case llvm::json::Value::Null
:
131 case llvm::json::Value::Object
:
132 case llvm::json::Value::Array
:
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
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
152 if (!g_dap
.enable_auto_variable_summaries
)
155 if (!v
.MightHaveChildren())
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
162 const size_t max_length
= 32;
164 auto start
= std::chrono::steady_clock::now();
166 llvm::raw_string_ostream
os(summary
);
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
<< "...";
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())
185 else if (llvm::StringRef value
= child
.GetValue(); !value
.empty())
187 else if (IsClassStructOrUnionType(child
.GetType()))
192 // If the child is an indexed entry, we don't show its index to save
194 if (name
.starts_with("["))
195 os
<< separator
<< desc
;
197 os
<< separator
<< name
<< ":" << desc
;
203 if (summary
== "{...}" || 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
)
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);
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.",
241 // "description": "Name of the scope such as 'Arguments', 'Locals'."
243 // "presentationHint": {
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" ],
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."
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."
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."
269 // "type": "boolean",
270 // "description": "If true, the number of variables in this scope is
271 // large or expensive to retrieve."
274 // "$ref": "#/definitions/Source",
275 // "description": "Optional source for this scope."
278 // "type": "integer",
279 // "description": "Optional start line of the range covered by this
283 // "type": "integer",
284 // "description": "Optional start column of the range covered by this
288 // "type": "integer",
289 // "description": "Optional end line of the range covered by this scope."
292 // "type": "integer",
293 // "description": "Optional end column of the range covered by this
297 // "required": [ "name", "variablesReference", "expensive" ]
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
));
321 // "description": "Information about a Breakpoint created in setBreakpoints
322 // or setFunctionBreakpoints.",
325 // "type": "integer",
326 // "description": "An optional unique identifier for the breakpoint."
329 // "type": "boolean",
330 // "description": "If true breakpoint could be set (but not necessarily
331 // at the desired location)."
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."
340 // "$ref": "#/definitions/Source",
341 // "description": "The source where the breakpoint is located."
344 // "type": "integer",
345 // "description": "The start line of the actual range covered by the
349 // "type": "integer",
350 // "description": "An optional start column of the actual range covered
351 // by the breakpoint."
354 // "type": "integer",
355 // "description": "An optional end line of the actual range covered by
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."
365 // "required": [ "verified" ]
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
;
373 object
.try_emplace("source", CreateSource(*request_path
));
374 bp
->CreateJsonObject(object
);
375 // We try to add request_line as a fallback
377 object
.try_emplace("line", *request_line
);
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
++) {
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;
415 } else if (debug_info
< 1024 * 1024 * 1024) {
416 double mb
= double(debug_info
) / (1024.0 * 1024.0);
419 double gb
= double(debug_info
) / (1024.0 * 1024.0 * 1024.0);
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
);
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())
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
));
476 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
478 // "description": "Server-initiated event.",
482 // "enum": [ "event" ]
486 // "description": "Type of event."
489 // "type": [ "array", "boolean", "integer", "null", "number" ,
490 // "object", "string" ],
491 // "description": "Event-specific information."
494 // "required": [ "type", "event" ]
497 // "ProtocolMessage": {
499 // "description": "Base class of requests, responses, and events.",
502 // "type": "integer",
503 // "description": "Sequence number."
507 // "description": "Message type.",
508 // "_enum": [ "request", "response", "event" ]
511 // "required": [ "seq", "type" ]
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
);
521 // "ExceptionBreakpointsFilter": {
523 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an
524 // option for configuring how exceptions are dealt with.",
528 // "description": "The internal ID of the filter. This value is passed
529 // to the setExceptionBreakpoints request."
533 // "description": "The name of the filter. This will be shown in the UI."
536 // "type": "boolean",
537 // "description": "Initial value of the filter. If not specified a value
538 // 'false' is assumed."
541 // "required": [ "filter", "label" ]
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
));
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.",
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."
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
571 // "sourceReference": {
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."
578 // "presentationHint": {
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" ]
588 // "description": "The (optional) origin of this source: possible values
589 // 'internal module', 'inlined content from source map',
595 // "$ref": "#/definitions/Source"
597 // "description": "An optional list of sources that are related to this
598 // source. These may be the source that generated this
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."
611 // "$ref": "#/definitions/Checksum"
613 // "description": "The checksums associated with this file."
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();
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
);
653 // "description": "A Stackframe contains the source location.",
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."
664 // "description": "The name of the stack frame, typically a method name."
667 // "$ref": "#/definitions/Source",
668 // "description": "The optional source of the frame."
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."
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."
681 // "type": "integer",
682 // "description": "An optional end line of the range covered by the
686 // "type": "integer",
687 // "description": "An optional end column of the range covered by the
690 // "instructionPointerReference": {
692 // "description": "A memory reference for the current instruction
697 // "type": ["integer", "string"],
698 // "description": "The module associated with this frame, if any."
700 // "presentationHint": {
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
711 // "required": [ "id", "name", "line", "column" ]
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
726 } else if (const char *name
= frame
.GetDisplayFunctionName()) {
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
);
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
);
752 object
.try_emplace("line", 0);
753 auto column
= line_entry
.GetColumn();
754 object
.try_emplace("column", column
);
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
));
771 // "description": "A Thread",
774 // "type": "integer",
775 // "description": "Unique identifier for the thread."
779 // "description": "A name of the thread."
782 // "required": [ "id", "name" ]
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();
793 const char *thread_name
= thread
.GetName();
794 const char *queue_name
= thread
.GetQueueName();
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)";
808 llvm::formatv("Thread {0} Queue: {1}{2}", thread
.GetIndexID(),
809 queue_name
, queue_kind_label
)
812 thread_str
= llvm::formatv("Thread {0}", thread
.GetIndexID()).str();
816 EmplaceSafeString(object
, "name", thread_str
);
818 return llvm::json::Value(std::move(object
));
822 // "allOf": [ { "$ref": "#/definitions/Event" }, {
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.",
832 // "enum": [ "stopped" ]
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" ]
847 // "description": "The full reason for the event, e.g. 'Paused
848 // on exception'. This string is shown in the UI
852 // "type": "integer",
853 // "description": "The thread which was stopped."
857 // "description": "Additional information. E.g. if reason is
858 // 'exception', text contains the exception name.
859 // This string is shown in the UI."
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."
872 // "required": [ "reason" ]
875 // "required": [ "event", "body" ]
878 llvm::json::Value
CreateThreadStopped(lldb::SBThread
&thread
,
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");
887 case lldb::eStopReasonBreakpoint
: {
888 ExceptionBreakpoint
*exc_bp
= g_dap
.GetExceptionBPFromStopReason(thread
);
890 body
.try_emplace("reason", "exception");
891 EmplaceSafeString(body
, "description", exc_bp
->label
);
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
);
903 case lldb::eStopReasonWatchpoint
:
904 case lldb::eStopReasonInstrumentation
:
905 body
.try_emplace("reason", "breakpoint");
907 case lldb::eStopReasonProcessorTrace
:
908 body
.try_emplace("reason", "processor trace");
910 case lldb::eStopReasonSignal
:
911 case lldb::eStopReasonException
:
912 body
.try_emplace("reason", "exception");
914 case lldb::eStopReasonExec
:
915 body
.try_emplace("reason", "entry");
917 case lldb::eStopReasonFork
:
918 body
.try_emplace("reason", "fork");
920 case lldb::eStopReasonVFork
:
921 body
.try_emplace("reason", "vfork");
923 case lldb::eStopReasonVForkDone
:
924 body
.try_emplace("reason", "vforkdone");
926 case lldb::eStopReasonThreadExiting
:
927 case lldb::eStopReasonInvalid
:
928 case lldb::eStopReasonNone
:
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
)
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();
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
) {
994 v
.SetFormat(lldb::eFormatHex
);
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
<< ">";
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.
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
;
1039 EmplaceSafeString(extensions
, "error", *error
);
1041 EmplaceSafeString(extensions
, "value", value
);
1042 if (!summary
.empty())
1043 EmplaceSafeString(extensions
, "summary", 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
));
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
;
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();
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.",
1102 // "type": "string",
1103 // "description": "The variable's name."
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."
1111 // "type": "string",
1112 // "description": "The type of the variable's value. Typically shown in
1113 // the UI when hovering over the value."
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."
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."
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."
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."
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."
1146 // "$__lldb_extensions": {
1147 // "description": "Unofficial extensions to the protocol",
1150 // "type": "object",
1151 // "description": "The source location where the variable was declared.
1152 // This value won't be present if no declaration is
1156 // "type": "string",
1157 // "description": "The source file path where the variable was
1161 // "type": "number",
1162 // "description": "The 1-indexed source line where the variable was
1166 // "type": "number",
1167 // "description": "The 1-indexed source column where the variable
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
1181 // "type": "string",
1182 // "description": "The summary string of the variable. This is
1183 // effectively SBValue.GetSummary()."
1186 // "type": "string",
1187 // "description": "The auto generated summary if using
1188 // `enableAutoVariableSummaries`."
1191 // "type": "string",
1192 // "description": "An error message generated if LLDB couldn't inspect
1197 // "required": [ "name", "value", "variablesReference" ]
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
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
)
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
);
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
));
1267 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
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",
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");
1295 run_in_terminal_args
.try_emplace("cwd", cwd
);
1297 // We need to convert the input list of environments variables into a
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)
1320 switch (value
.GetType()) {
1321 case lldb::eStructuredDataTypeFloat
:
1322 out
.try_emplace(key_utf8
, value
.GetFloatValue());
1324 case lldb::eStructuredDataTypeUnsignedInteger
:
1325 out
.try_emplace(key_utf8
, value
.GetIntegerValue((uint64_t)0));
1327 case lldb::eStructuredDataTypeSignedInteger
:
1328 out
.try_emplace(key_utf8
, value
.GetIntegerValue((int64_t)0));
1330 case lldb::eStructuredDataTypeArray
: {
1331 lldb::SBStream contents
;
1332 value
.GetAsJSON(contents
);
1333 out
.try_emplace(key_utf8
, llvm::json::fixUTF8(contents
.GetData()));
1335 case lldb::eStructuredDataTypeBoolean
:
1336 out
.try_emplace(key_utf8
, value
.GetBooleanValue());
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
));
1345 case lldb::eStructuredDataTypeDictionary
: {
1346 lldb::SBStream contents
;
1347 value
.GetAsJSON(contents
);
1348 out
.try_emplace(key_utf8
, llvm::json::fixUTF8(contents
.GetData()));
1350 case lldb::eStructuredDataTypeNull
:
1351 case lldb::eStructuredDataTypeGeneric
:
1352 case lldb::eStructuredDataTypeInvalid
:
1357 void addStatistic(llvm::json::Object
&event
) {
1358 lldb::SBStructuredData statistics
= g_dap
.target
.GetStatistics();
1359 bool is_dictionary
=
1360 statistics
.GetType() == lldb::eStructuredDataTypeDictionary
;
1363 llvm::json::Object stats_body
;
1365 lldb::SBStringList keys
;
1366 if (!statistics
.GetKeys(keys
))
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
);
1381 std::string
JSONToString(const llvm::json::Value
&json
) {
1383 llvm::raw_string_ostream
os(data
);
1389 } // namespace lldb_dap