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 //===----------------------------------------------------------------------===//
13 #include "llvm/ADT/Optional.h"
14 #include "llvm/Support/FormatAdapters.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/ScopedPrinter.h"
18 #include "lldb/API/SBBreakpoint.h"
19 #include "lldb/API/SBBreakpointLocation.h"
20 #include "lldb/API/SBDeclaration.h"
21 #include "lldb/API/SBValue.h"
22 #include "lldb/Host/PosixApi.h"
24 #include "ExceptionBreakpoint.h"
25 #include "JSONUtils.h"
26 #include "LLDBUtils.h"
29 namespace lldb_vscode
{
31 void EmplaceSafeString(llvm::json::Object
&obj
, llvm::StringRef key
,
32 llvm::StringRef str
) {
33 if (LLVM_LIKELY(llvm::json::isUTF8(str
)))
34 obj
.try_emplace(key
, str
.str());
36 obj
.try_emplace(key
, llvm::json::fixUTF8(str
));
39 llvm::StringRef
GetAsString(const llvm::json::Value
&value
) {
40 if (auto s
= value
.getAsString())
42 return llvm::StringRef();
45 // Gets a string from a JSON object using the key, or returns an empty string.
46 llvm::StringRef
GetString(const llvm::json::Object
&obj
, llvm::StringRef key
) {
47 if (llvm::Optional
<llvm::StringRef
> value
= obj
.getString(key
))
49 return llvm::StringRef();
52 llvm::StringRef
GetString(const llvm::json::Object
*obj
, llvm::StringRef key
) {
54 return llvm::StringRef();
55 return GetString(*obj
, key
);
58 // Gets an unsigned integer from a JSON object using the key, or returns the
59 // specified fail value.
60 uint64_t GetUnsigned(const llvm::json::Object
&obj
, llvm::StringRef key
,
61 uint64_t fail_value
) {
62 if (auto value
= obj
.getInteger(key
))
63 return (uint64_t)*value
;
67 uint64_t GetUnsigned(const llvm::json::Object
*obj
, llvm::StringRef key
,
68 uint64_t fail_value
) {
71 return GetUnsigned(*obj
, key
, fail_value
);
74 bool GetBoolean(const llvm::json::Object
&obj
, llvm::StringRef key
,
76 if (auto value
= obj
.getBoolean(key
))
78 if (auto value
= obj
.getInteger(key
))
83 bool GetBoolean(const llvm::json::Object
*obj
, llvm::StringRef key
,
87 return GetBoolean(*obj
, key
, fail_value
);
90 int64_t GetSigned(const llvm::json::Object
&obj
, llvm::StringRef key
,
92 if (auto value
= obj
.getInteger(key
))
97 int64_t GetSigned(const llvm::json::Object
*obj
, llvm::StringRef key
,
101 return GetSigned(*obj
, key
, fail_value
);
104 bool ObjectContainsKey(const llvm::json::Object
&obj
, llvm::StringRef key
) {
105 return obj
.find(key
) != obj
.end();
108 std::vector
<std::string
> GetStrings(const llvm::json::Object
*obj
,
109 llvm::StringRef key
) {
110 std::vector
<std::string
> strs
;
111 auto json_array
= obj
->getArray(key
);
114 for (const auto &value
: *json_array
) {
115 switch (value
.kind()) {
116 case llvm::json::Value::String
:
117 strs
.push_back(value
.getAsString()->str());
119 case llvm::json::Value::Number
:
120 case llvm::json::Value::Boolean
:
121 strs
.push_back(llvm::to_string(value
));
123 case llvm::json::Value::Null
:
124 case llvm::json::Value::Object
:
125 case llvm::json::Value::Array
:
132 void SetValueForKey(lldb::SBValue
&v
, llvm::json::Object
&object
,
133 llvm::StringRef key
) {
135 llvm::StringRef value
= v
.GetValue();
136 llvm::StringRef summary
= v
.GetSummary();
137 llvm::StringRef type_name
= v
.GetType().GetDisplayTypeName();
140 llvm::raw_string_ostream
strm(result
);
141 if (!value
.empty()) {
143 if (!summary
.empty())
144 strm
<< ' ' << summary
;
145 } else if (!summary
.empty()) {
146 strm
<< ' ' << summary
;
147 } else if (!type_name
.empty()) {
149 lldb::addr_t address
= v
.GetLoadAddress();
150 if (address
!= LLDB_INVALID_ADDRESS
)
151 strm
<< " @ " << llvm::format_hex(address
, 0);
154 EmplaceSafeString(object
, key
, result
);
157 void FillResponse(const llvm::json::Object
&request
,
158 llvm::json::Object
&response
) {
159 // Fill in all of the needed response fields to a "request" and set "success"
160 // to true by default.
161 response
.try_emplace("type", "response");
162 response
.try_emplace("seq", (int64_t)0);
163 EmplaceSafeString(response
, "command", GetString(request
, "command"));
164 const int64_t seq
= GetSigned(request
, "seq", 0);
165 response
.try_emplace("request_seq", seq
);
166 response
.try_emplace("success", true);
171 // "description": "A Scope is a named container for variables. Optionally
172 // a scope can map to a source or a range within a source.",
176 // "description": "Name of the scope such as 'Arguments', 'Locals'."
178 // "presentationHint": {
180 // "description": "An optional hint for how to present this scope in the
181 // UI. If this attribute is missing, the scope is shown
182 // with a generic UI.",
183 // "_enum": [ "arguments", "locals", "registers" ],
185 // "variablesReference": {
186 // "type": "integer",
187 // "description": "The variables of this scope can be retrieved by
188 // passing the value of variablesReference to the
189 // VariablesRequest."
191 // "namedVariables": {
192 // "type": "integer",
193 // "description": "The number of named variables in this scope. The
194 // client can use this optional information to present
195 // the variables in a paged UI and fetch them in chunks."
197 // "indexedVariables": {
198 // "type": "integer",
199 // "description": "The number of indexed variables in this scope. The
200 // client can use this optional information to present
201 // the variables in a paged UI and fetch them in chunks."
204 // "type": "boolean",
205 // "description": "If true, the number of variables in this scope is
206 // large or expensive to retrieve."
209 // "$ref": "#/definitions/Source",
210 // "description": "Optional source for this scope."
213 // "type": "integer",
214 // "description": "Optional start line of the range covered by this
218 // "type": "integer",
219 // "description": "Optional start column of the range covered by this
223 // "type": "integer",
224 // "description": "Optional end line of the range covered by this scope."
227 // "type": "integer",
228 // "description": "Optional end column of the range covered by this
232 // "required": [ "name", "variablesReference", "expensive" ]
234 llvm::json::Value
CreateScope(const llvm::StringRef name
,
235 int64_t variablesReference
,
236 int64_t namedVariables
, bool expensive
) {
237 llvm::json::Object object
;
238 EmplaceSafeString(object
, "name", name
.str());
240 // TODO: Support "arguments" scope. At the moment lldb-vscode includes the
241 // arguments into the "locals" scope.
242 if (variablesReference
== VARREF_LOCALS
) {
243 object
.try_emplace("presentationHint", "locals");
244 } else if (variablesReference
== VARREF_REGS
) {
245 object
.try_emplace("presentationHint", "registers");
248 object
.try_emplace("variablesReference", variablesReference
);
249 object
.try_emplace("expensive", expensive
);
250 object
.try_emplace("namedVariables", namedVariables
);
251 return llvm::json::Value(std::move(object
));
256 // "description": "Information about a Breakpoint created in setBreakpoints
257 // or setFunctionBreakpoints.",
260 // "type": "integer",
261 // "description": "An optional unique identifier for the breakpoint."
264 // "type": "boolean",
265 // "description": "If true breakpoint could be set (but not necessarily
266 // at the desired location)."
270 // "description": "An optional message about the state of the breakpoint.
271 // This is shown to the user and can be used to explain
272 // why a breakpoint could not be verified."
275 // "$ref": "#/definitions/Source",
276 // "description": "The source where the breakpoint is located."
279 // "type": "integer",
280 // "description": "The start line of the actual range covered by the
284 // "type": "integer",
285 // "description": "An optional start column of the actual range covered
286 // by the breakpoint."
289 // "type": "integer",
290 // "description": "An optional end line of the actual range covered by
294 // "type": "integer",
295 // "description": "An optional end column of the actual range covered by
296 // the breakpoint. If no end line is given, then the end
297 // column is assumed to be in the start line."
300 // "required": [ "verified" ]
302 llvm::json::Value
CreateBreakpoint(lldb::SBBreakpoint
&bp
,
303 llvm::Optional
<llvm::StringRef
> request_path
,
304 llvm::Optional
<uint32_t> request_line
) {
305 // Each breakpoint location is treated as a separate breakpoint for VS code.
306 // They don't have the notion of a single breakpoint with multiple locations.
307 llvm::json::Object object
;
309 return llvm::json::Value(std::move(object
));
311 object
.try_emplace("verified", bp
.GetNumResolvedLocations() > 0);
312 object
.try_emplace("id", bp
.GetID());
313 // VS Code DAP doesn't currently allow one breakpoint to have multiple
314 // locations so we just report the first one. If we report all locations
315 // then the IDE starts showing the wrong line numbers and locations for
316 // other source file and line breakpoints in the same file.
318 // Below we search for the first resolved location in a breakpoint and report
319 // this as the breakpoint location since it will have a complete location
320 // that is at least loaded in the current process.
321 lldb::SBBreakpointLocation bp_loc
;
322 const auto num_locs
= bp
.GetNumLocations();
323 for (size_t i
= 0; i
< num_locs
; ++i
) {
324 bp_loc
= bp
.GetLocationAtIndex(i
);
325 if (bp_loc
.IsResolved())
328 // If not locations are resolved, use the first location.
329 if (!bp_loc
.IsResolved())
330 bp_loc
= bp
.GetLocationAtIndex(0);
331 auto bp_addr
= bp_loc
.GetAddress();
334 object
.try_emplace("source", CreateSource(*request_path
));
336 if (bp_addr
.IsValid()) {
337 auto line_entry
= bp_addr
.GetLineEntry();
338 const auto line
= line_entry
.GetLine();
339 if (line
!= UINT32_MAX
)
340 object
.try_emplace("line", line
);
341 object
.try_emplace("source", CreateSource(line_entry
));
343 // We try to add request_line as a fallback
345 object
.try_emplace("line", *request_line
);
346 return llvm::json::Value(std::move(object
));
349 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section
) {
350 uint64_t debug_info_size
= 0;
351 llvm::StringRef
section_name(section
.GetName());
352 if (section_name
.startswith(".debug") || section_name
.startswith("__debug") ||
353 section_name
.startswith(".apple") || section_name
.startswith("__apple"))
354 debug_info_size
+= section
.GetFileByteSize();
355 size_t num_sub_sections
= section
.GetNumSubSections();
356 for (size_t i
= 0; i
< num_sub_sections
; i
++) {
358 GetDebugInfoSizeInSection(section
.GetSubSectionAtIndex(i
));
360 return debug_info_size
;
363 static uint64_t GetDebugInfoSize(lldb::SBModule module
) {
364 uint64_t debug_info_size
= 0;
365 size_t num_sections
= module
.GetNumSections();
366 for (size_t i
= 0; i
< num_sections
; i
++) {
367 debug_info_size
+= GetDebugInfoSizeInSection(module
.GetSectionAtIndex(i
));
369 return debug_info_size
;
372 static std::string
ConvertDebugInfoSizeToString(uint64_t debug_info
) {
373 std::ostringstream oss
;
374 oss
<< std::fixed
<< std::setprecision(1);
375 if (debug_info
< 1024) {
376 oss
<< debug_info
<< "B";
377 } else if (debug_info
< 1024 * 1024) {
378 double kb
= double(debug_info
) / 1024.0;
380 } else if (debug_info
< 1024 * 1024 * 1024) {
381 double mb
= double(debug_info
) / (1024.0 * 1024.0);
384 double gb
= double(debug_info
) / (1024.0 * 1024.0 * 1024.0);
389 llvm::json::Value
CreateModule(lldb::SBModule
&module
) {
390 llvm::json::Object object
;
391 if (!module
.IsValid())
392 return llvm::json::Value(std::move(object
));
393 const char *uuid
= module
.GetUUIDString();
394 object
.try_emplace("id", uuid
? std::string(uuid
) : std::string(""));
395 object
.try_emplace("name", std::string(module
.GetFileSpec().GetFilename()));
396 char module_path_arr
[PATH_MAX
];
397 module
.GetFileSpec().GetPath(module_path_arr
, sizeof(module_path_arr
));
398 std::string
module_path(module_path_arr
);
399 object
.try_emplace("path", module_path
);
400 if (module
.GetNumCompileUnits() > 0) {
401 std::string symbol_str
= "Symbols loaded.";
402 std::string debug_info_size
;
403 uint64_t debug_info
= GetDebugInfoSize(module
);
404 if (debug_info
> 0) {
405 debug_info_size
= ConvertDebugInfoSizeToString(debug_info
);
407 object
.try_emplace("symbolStatus", symbol_str
);
408 object
.try_emplace("debugInfoSize", debug_info_size
);
409 char symbol_path_arr
[PATH_MAX
];
410 module
.GetSymbolFileSpec().GetPath(symbol_path_arr
,
411 sizeof(symbol_path_arr
));
412 std::string
symbol_path(symbol_path_arr
);
413 object
.try_emplace("symbolFilePath", symbol_path
);
415 object
.try_emplace("symbolStatus", "Symbols not found.");
417 std::string loaded_addr
= std::to_string(
418 module
.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc
.target
));
419 object
.try_emplace("addressRange", loaded_addr
);
420 std::string version_str
;
421 uint32_t version_nums
[3];
422 uint32_t num_versions
=
423 module
.GetVersion(version_nums
, sizeof(version_nums
) / sizeof(uint32_t));
424 for (uint32_t i
= 0; i
< num_versions
; ++i
) {
425 if (!version_str
.empty())
427 version_str
+= std::to_string(version_nums
[i
]);
429 if (!version_str
.empty())
430 object
.try_emplace("version", version_str
);
431 return llvm::json::Value(std::move(object
));
434 void AppendBreakpoint(lldb::SBBreakpoint
&bp
, llvm::json::Array
&breakpoints
,
435 llvm::Optional
<llvm::StringRef
> request_path
,
436 llvm::Optional
<uint32_t> request_line
) {
437 breakpoints
.emplace_back(CreateBreakpoint(bp
, request_path
, request_line
));
441 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
443 // "description": "Server-initiated event.",
447 // "enum": [ "event" ]
451 // "description": "Type of event."
454 // "type": [ "array", "boolean", "integer", "null", "number" ,
455 // "object", "string" ],
456 // "description": "Event-specific information."
459 // "required": [ "type", "event" ]
462 // "ProtocolMessage": {
464 // "description": "Base class of requests, responses, and events.",
467 // "type": "integer",
468 // "description": "Sequence number."
472 // "description": "Message type.",
473 // "_enum": [ "request", "response", "event" ]
476 // "required": [ "seq", "type" ]
478 llvm::json::Object
CreateEventObject(const llvm::StringRef event_name
) {
479 llvm::json::Object event
;
480 event
.try_emplace("seq", 0);
481 event
.try_emplace("type", "event");
482 EmplaceSafeString(event
, "event", event_name
);
486 // "ExceptionBreakpointsFilter": {
488 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an
489 // option for configuring how exceptions are dealt with.",
493 // "description": "The internal ID of the filter. This value is passed
494 // to the setExceptionBreakpoints request."
498 // "description": "The name of the filter. This will be shown in the UI."
501 // "type": "boolean",
502 // "description": "Initial value of the filter. If not specified a value
503 // 'false' is assumed."
506 // "required": [ "filter", "label" ]
509 CreateExceptionBreakpointFilter(const ExceptionBreakpoint
&bp
) {
510 llvm::json::Object object
;
511 EmplaceSafeString(object
, "filter", bp
.filter
);
512 EmplaceSafeString(object
, "label", bp
.label
);
513 object
.try_emplace("default", bp
.default_value
);
514 return llvm::json::Value(std::move(object
));
519 // "description": "A Source is a descriptor for source code. It is returned
520 // from the debug adapter as part of a StackFrame and it is
521 // used by clients when specifying breakpoints.",
525 // "description": "The short name of the source. Every source returned
526 // from the debug adapter has a name. When sending a
527 // source to the debug adapter this name is optional."
531 // "description": "The path of the source to be shown in the UI. It is
532 // only used to locate and load the content of the
533 // source if no sourceReference is specified (or its
536 // "sourceReference": {
538 // "description": "If sourceReference > 0 the contents of the source must
539 // be retrieved through the SourceRequest (even if a path
540 // is specified). A sourceReference is only valid for a
541 // session, so it must not be used to persist a source."
543 // "presentationHint": {
545 // "description": "An optional hint for how to present the source in the
546 // UI. A value of 'deemphasize' can be used to indicate
547 // that the source is not available or that it is
548 // skipped on stepping.",
549 // "enum": [ "normal", "emphasize", "deemphasize" ]
553 // "description": "The (optional) origin of this source: possible values
554 // 'internal module', 'inlined content from source map',
560 // "$ref": "#/definitions/Source"
562 // "description": "An optional list of sources that are related to this
563 // source. These may be the source that generated this
567 // "type":["array","boolean","integer","null","number","object","string"],
568 // "description": "Optional data that a debug adapter might want to loop
569 // through the client. The client should leave the data
570 // intact and persist it across sessions. The client
571 // should not interpret the data."
576 // "$ref": "#/definitions/Checksum"
578 // "description": "The checksums associated with this file."
582 llvm::json::Value
CreateSource(lldb::SBLineEntry
&line_entry
) {
583 llvm::json::Object object
;
584 lldb::SBFileSpec file
= line_entry
.GetFileSpec();
585 if (file
.IsValid()) {
586 const char *name
= file
.GetFilename();
588 EmplaceSafeString(object
, "name", name
);
589 char path
[PATH_MAX
] = "";
590 file
.GetPath(path
, sizeof(path
));
592 EmplaceSafeString(object
, "path", std::string(path
));
595 return llvm::json::Value(std::move(object
));
598 llvm::json::Value
CreateSource(llvm::StringRef source_path
) {
599 llvm::json::Object source
;
600 llvm::StringRef name
= llvm::sys::path::filename(source_path
);
601 EmplaceSafeString(source
, "name", name
);
602 EmplaceSafeString(source
, "path", source_path
);
603 return llvm::json::Value(std::move(source
));
606 llvm::json::Value
CreateSource(lldb::SBFrame
&frame
, int64_t &disasm_line
) {
608 auto line_entry
= frame
.GetLineEntry();
609 if (line_entry
.GetFileSpec().IsValid())
610 return CreateSource(line_entry
);
612 llvm::json::Object object
;
613 const auto pc
= frame
.GetPC();
615 lldb::SBInstructionList insts
;
616 lldb::SBFunction function
= frame
.GetFunction();
617 lldb::addr_t low_pc
= LLDB_INVALID_ADDRESS
;
618 if (function
.IsValid()) {
619 low_pc
= function
.GetStartAddress().GetLoadAddress(g_vsc
.target
);
620 auto addr_srcref
= g_vsc
.addr_to_source_ref
.find(low_pc
);
621 if (addr_srcref
!= g_vsc
.addr_to_source_ref
.end()) {
622 // We have this disassembly cached already, return the existing
624 object
.try_emplace("sourceReference", addr_srcref
->second
);
625 disasm_line
= g_vsc
.GetLineForPC(addr_srcref
->second
, pc
);
627 insts
= function
.GetInstructions(g_vsc
.target
);
630 lldb::SBSymbol symbol
= frame
.GetSymbol();
631 if (symbol
.IsValid()) {
632 low_pc
= symbol
.GetStartAddress().GetLoadAddress(g_vsc
.target
);
633 auto addr_srcref
= g_vsc
.addr_to_source_ref
.find(low_pc
);
634 if (addr_srcref
!= g_vsc
.addr_to_source_ref
.end()) {
635 // We have this disassembly cached already, return the existing
637 object
.try_emplace("sourceReference", addr_srcref
->second
);
638 disasm_line
= g_vsc
.GetLineForPC(addr_srcref
->second
, pc
);
640 insts
= symbol
.GetInstructions(g_vsc
.target
);
644 const auto num_insts
= insts
.GetSize();
645 if (low_pc
!= LLDB_INVALID_ADDRESS
&& num_insts
> 0) {
646 EmplaceSafeString(object
, "name", frame
.GetFunctionName());
647 SourceReference source
;
648 llvm::raw_string_ostream
src_strm(source
.content
);
650 for (size_t i
= 0; i
< num_insts
; ++i
) {
651 lldb::SBInstruction inst
= insts
.GetInstructionAtIndex(i
);
652 const auto inst_addr
= inst
.GetAddress().GetLoadAddress(g_vsc
.target
);
653 const char *m
= inst
.GetMnemonic(g_vsc
.target
);
654 const char *o
= inst
.GetOperands(g_vsc
.target
);
655 const char *c
= inst
.GetComment(g_vsc
.target
);
658 const auto inst_offset
= inst_addr
- low_pc
;
660 if (inst_offset
< 10)
662 else if (inst_offset
< 100)
664 else if (inst_offset
< 1000)
667 llvm::raw_string_ostream
line_strm(line
);
668 line_strm
<< llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr
,
669 inst_offset
, llvm::fmt_repeat(' ', spaces
), m
,
672 // If there is a comment append it starting at column 60 or after one
673 // space past the last char
674 const uint32_t comment_row
= std::max(line_strm
.str().size(), (size_t)60);
676 if (line
.size() < comment_row
)
677 line_strm
.indent(comment_row
- line_strm
.str().size());
678 line_strm
<< " # " << c
;
680 src_strm
<< line_strm
.str() << "\n";
681 source
.addr_to_line
[inst_addr
] = i
+ 1;
683 // Flush the source stream
685 auto sourceReference
= VSCode::GetNextSourceReference();
686 g_vsc
.source_map
[sourceReference
] = std::move(source
);
687 g_vsc
.addr_to_source_ref
[low_pc
] = sourceReference
;
688 object
.try_emplace("sourceReference", sourceReference
);
690 return llvm::json::Value(std::move(object
));
695 // "description": "A Stackframe contains the source location.",
698 // "type": "integer",
699 // "description": "An identifier for the stack frame. It must be unique
700 // across all threads. This id can be used to retrieve
701 // the scopes of the frame with the 'scopesRequest' or
702 // to restart the execution of a stackframe."
706 // "description": "The name of the stack frame, typically a method name."
709 // "$ref": "#/definitions/Source",
710 // "description": "The optional source of the frame."
713 // "type": "integer",
714 // "description": "The line within the file of the frame. If source is
715 // null or doesn't exist, line is 0 and must be ignored."
718 // "type": "integer",
719 // "description": "The column within the line. If source is null or
720 // doesn't exist, column is 0 and must be ignored."
723 // "type": "integer",
724 // "description": "An optional end line of the range covered by the
728 // "type": "integer",
729 // "description": "An optional end column of the range covered by the
733 // "type": ["integer", "string"],
734 // "description": "The module associated with this frame, if any."
736 // "presentationHint": {
738 // "enum": [ "normal", "label", "subtle" ],
739 // "description": "An optional hint for how to present this frame in
740 // the UI. A value of 'label' can be used to indicate
741 // that the frame is an artificial frame that is used
742 // as a visual label or separator. A value of 'subtle'
743 // can be used to change the appearance of a frame in
747 // "required": [ "id", "name", "line", "column" ]
749 llvm::json::Value
CreateStackFrame(lldb::SBFrame
&frame
) {
750 llvm::json::Object object
;
751 int64_t frame_id
= MakeVSCodeFrameID(frame
);
752 object
.try_emplace("id", frame_id
);
753 EmplaceSafeString(object
, "name", frame
.GetFunctionName());
754 int64_t disasm_line
= 0;
755 object
.try_emplace("source", CreateSource(frame
, disasm_line
));
757 auto line_entry
= frame
.GetLineEntry();
758 if (disasm_line
> 0) {
759 object
.try_emplace("line", disasm_line
);
761 auto line
= line_entry
.GetLine();
762 if (line
== UINT32_MAX
)
764 object
.try_emplace("line", line
);
766 object
.try_emplace("column", line_entry
.GetColumn());
767 return llvm::json::Value(std::move(object
));
772 // "description": "A Thread",
775 // "type": "integer",
776 // "description": "Unique identifier for the thread."
780 // "description": "A name of the thread."
783 // "required": [ "id", "name" ]
785 llvm::json::Value
CreateThread(lldb::SBThread
&thread
) {
786 llvm::json::Object object
;
787 object
.try_emplace("id", (int64_t)thread
.GetThreadID());
789 snprintf(thread_str
, sizeof(thread_str
), "Thread #%u", thread
.GetIndexID());
790 const char *name
= thread
.GetName();
792 std::string
thread_with_name(thread_str
);
793 thread_with_name
+= ' ';
794 thread_with_name
+= name
;
795 EmplaceSafeString(object
, "name", thread_with_name
);
797 EmplaceSafeString(object
, "name", std::string(thread_str
));
799 return llvm::json::Value(std::move(object
));
803 // "allOf": [ { "$ref": "#/definitions/Event" }, {
805 // "description": "Event message for 'stopped' event type. The event
806 // indicates that the execution of the debuggee has stopped
807 // due to some condition. This can be caused by a break
808 // point previously set, a stepping action has completed,
809 // by executing a debugger statement etc.",
813 // "enum": [ "stopped" ]
820 // "description": "The reason for the event. For backward
821 // compatibility this string is shown in the UI if
822 // the 'description' attribute is missing (but it
823 // must not be translated).",
824 // "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
828 // "description": "The full reason for the event, e.g. 'Paused
829 // on exception'. This string is shown in the UI
833 // "type": "integer",
834 // "description": "The thread which was stopped."
838 // "description": "Additional information. E.g. if reason is
839 // 'exception', text contains the exception name.
840 // This string is shown in the UI."
842 // "allThreadsStopped": {
843 // "type": "boolean",
844 // "description": "If allThreadsStopped is true, a debug adapter
845 // can announce that all threads have stopped.
846 // The client should use this information to
847 // enable that all threads can be expanded to
848 // access their stacktraces. If the attribute
849 // is missing or false, only the thread with the
850 // given threadId can be expanded."
853 // "required": [ "reason" ]
856 // "required": [ "event", "body" ]
859 llvm::json::Value
CreateThreadStopped(lldb::SBThread
&thread
,
861 llvm::json::Object
event(CreateEventObject("stopped"));
862 llvm::json::Object body
;
863 switch (thread
.GetStopReason()) {
864 case lldb::eStopReasonTrace
:
865 case lldb::eStopReasonPlanComplete
:
866 body
.try_emplace("reason", "step");
868 case lldb::eStopReasonBreakpoint
: {
869 ExceptionBreakpoint
*exc_bp
= g_vsc
.GetExceptionBPFromStopReason(thread
);
871 body
.try_emplace("reason", "exception");
872 EmplaceSafeString(body
, "description", exc_bp
->label
);
874 body
.try_emplace("reason", "breakpoint");
876 uint64_t bp_id
= thread
.GetStopReasonDataAtIndex(0);
877 uint64_t bp_loc_id
= thread
.GetStopReasonDataAtIndex(1);
878 snprintf(desc_str
, sizeof(desc_str
), "breakpoint %" PRIu64
".%" PRIu64
,
880 EmplaceSafeString(body
, "description", desc_str
);
883 case lldb::eStopReasonWatchpoint
:
884 case lldb::eStopReasonInstrumentation
:
885 body
.try_emplace("reason", "breakpoint");
887 case lldb::eStopReasonProcessorTrace
:
888 body
.try_emplace("reason", "processor trace");
890 case lldb::eStopReasonSignal
:
891 case lldb::eStopReasonException
:
892 body
.try_emplace("reason", "exception");
894 case lldb::eStopReasonExec
:
895 body
.try_emplace("reason", "entry");
897 case lldb::eStopReasonFork
:
898 body
.try_emplace("reason", "fork");
900 case lldb::eStopReasonVFork
:
901 body
.try_emplace("reason", "vfork");
903 case lldb::eStopReasonVForkDone
:
904 body
.try_emplace("reason", "vforkdone");
906 case lldb::eStopReasonThreadExiting
:
907 case lldb::eStopReasonInvalid
:
908 case lldb::eStopReasonNone
:
912 body
.try_emplace("reason", "entry");
913 const lldb::tid_t tid
= thread
.GetThreadID();
914 body
.try_emplace("threadId", (int64_t)tid
);
915 // If no description has been set, then set it to the default thread stopped
916 // description. If we have breakpoints that get hit and shouldn't be reported
917 // as breakpoints, then they will set the description above.
918 if (ObjectContainsKey(body
, "description")) {
919 char description
[1024];
920 if (thread
.GetStopDescription(description
, sizeof(description
))) {
921 EmplaceSafeString(body
, "description", std::string(description
));
924 if (tid
== g_vsc
.focus_tid
) {
925 body
.try_emplace("threadCausedFocus", true);
927 body
.try_emplace("preserveFocusHint", tid
!= g_vsc
.focus_tid
);
928 body
.try_emplace("allThreadsStopped", true);
929 event
.try_emplace("body", std::move(body
));
930 return llvm::json::Value(std::move(event
));
933 const char *GetNonNullVariableName(lldb::SBValue v
) {
934 const char *name
= v
.GetName();
935 return name
? name
: "<null>";
938 std::string
CreateUniqueVariableNameForDisplay(lldb::SBValue v
,
939 bool is_name_duplicated
) {
940 lldb::SBStream name_builder
;
941 name_builder
.Print(GetNonNullVariableName(v
));
942 if (is_name_duplicated
) {
943 lldb::SBDeclaration declaration
= v
.GetDeclaration();
944 const char *file_name
= declaration
.GetFileSpec().GetFilename();
945 const uint32_t line
= declaration
.GetLine();
947 if (file_name
!= nullptr && line
> 0)
948 name_builder
.Printf(" @ %s:%u", file_name
, line
);
949 else if (const char *location
= v
.GetLocation())
950 name_builder
.Printf(" @ %s", location
);
952 return name_builder
.GetData();
957 // "description": "A Variable is a name/value pair. Optionally a variable
958 // can have a 'type' that is shown if space permits or when
959 // hovering over the variable's name. An optional 'kind' is
960 // used to render additional properties of the variable,
961 // e.g. different icons can be used to indicate that a
962 // variable is public or private. If the value is
963 // structured (has children), a handle is provided to
964 // retrieve the children with the VariablesRequest. If
965 // the number of named or indexed children is large, the
966 // numbers should be returned via the optional
967 // 'namedVariables' and 'indexedVariables' attributes. The
968 // client can use this optional information to present the
969 // children in a paged UI and fetch them in chunks.",
973 // "description": "The variable's name."
977 // "description": "The variable's value. This can be a multi-line text,
978 // e.g. for a function the body of a function."
982 // "description": "The type of the variable's value. Typically shown in
983 // the UI when hovering over the value."
985 // "presentationHint": {
986 // "$ref": "#/definitions/VariablePresentationHint",
987 // "description": "Properties of a variable that can be used to determine
988 // how to render the variable in the UI."
992 // "description": "Optional evaluatable name of this variable which can
993 // be passed to the 'EvaluateRequest' to fetch the
994 // variable's value."
996 // "variablesReference": {
997 // "type": "integer",
998 // "description": "If variablesReference is > 0, the variable is
999 // structured and its children can be retrieved by
1000 // passing variablesReference to the VariablesRequest."
1002 // "namedVariables": {
1003 // "type": "integer",
1004 // "description": "The number of named child variables. The client can
1005 // use this optional information to present the children
1006 // in a paged UI and fetch them in chunks."
1008 // "indexedVariables": {
1009 // "type": "integer",
1010 // "description": "The number of indexed child variables. The client
1011 // can use this optional information to present the
1012 // children in a paged UI and fetch them in chunks."
1015 // "required": [ "name", "value", "variablesReference" ]
1017 llvm::json::Value
CreateVariable(lldb::SBValue v
, int64_t variablesReference
,
1018 int64_t varID
, bool format_hex
,
1019 bool is_name_duplicated
) {
1020 llvm::json::Object object
;
1021 EmplaceSafeString(object
, "name",
1022 CreateUniqueVariableNameForDisplay(v
, is_name_duplicated
));
1025 v
.SetFormat(lldb::eFormatHex
);
1026 SetValueForKey(v
, object
, "value");
1027 auto type_cstr
= v
.GetType().GetDisplayTypeName();
1028 EmplaceSafeString(object
, "type", type_cstr
? type_cstr
: NO_TYPENAME
);
1029 if (varID
!= INT64_MAX
)
1030 object
.try_emplace("id", varID
);
1031 if (v
.MightHaveChildren())
1032 object
.try_emplace("variablesReference", variablesReference
);
1034 object
.try_emplace("variablesReference", (int64_t)0);
1035 lldb::SBStream evaluateStream
;
1036 v
.GetExpressionPath(evaluateStream
);
1037 const char *evaluateName
= evaluateStream
.GetData();
1038 if (evaluateName
&& evaluateName
[0])
1039 EmplaceSafeString(object
, "evaluateName", std::string(evaluateName
));
1040 return llvm::json::Value(std::move(object
));
1043 llvm::json::Value
CreateCompileUnit(lldb::SBCompileUnit unit
) {
1044 llvm::json::Object object
;
1045 char unit_path_arr
[PATH_MAX
];
1046 unit
.GetFileSpec().GetPath(unit_path_arr
, sizeof(unit_path_arr
));
1047 std::string
unit_path(unit_path_arr
);
1048 object
.try_emplace("compileUnitPath", unit_path
);
1049 return llvm::json::Value(std::move(object
));
1053 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1055 CreateRunInTerminalReverseRequest(const llvm::json::Object
&launch_request
,
1056 llvm::StringRef debug_adaptor_path
,
1057 llvm::StringRef comm_file
) {
1058 llvm::json::Object reverse_request
;
1059 reverse_request
.try_emplace("type", "request");
1060 reverse_request
.try_emplace("command", "runInTerminal");
1062 llvm::json::Object run_in_terminal_args
;
1063 // This indicates the IDE to open an embedded terminal, instead of opening the
1064 // terminal in a new window.
1065 run_in_terminal_args
.try_emplace("kind", "integrated");
1067 auto launch_request_arguments
= launch_request
.getObject("arguments");
1068 // The program path must be the first entry in the "args" field
1069 std::vector
<std::string
> args
= {
1070 debug_adaptor_path
.str(), "--comm-file", comm_file
.str(),
1071 "--launch-target", GetString(launch_request_arguments
, "program").str()};
1072 std::vector
<std::string
> target_args
=
1073 GetStrings(launch_request_arguments
, "args");
1074 args
.insert(args
.end(), target_args
.begin(), target_args
.end());
1075 run_in_terminal_args
.try_emplace("args", args
);
1077 const auto cwd
= GetString(launch_request_arguments
, "cwd");
1079 run_in_terminal_args
.try_emplace("cwd", cwd
);
1081 // We need to convert the input list of environments variables into a
1083 std::vector
<std::string
> envs
= GetStrings(launch_request_arguments
, "env");
1084 llvm::json::Object environment
;
1085 for (const std::string
&env
: envs
) {
1086 size_t index
= env
.find('=');
1087 environment
.try_emplace(env
.substr(0, index
), env
.substr(index
+ 1));
1089 run_in_terminal_args
.try_emplace("env",
1090 llvm::json::Value(std::move(environment
)));
1092 reverse_request
.try_emplace(
1093 "arguments", llvm::json::Value(std::move(run_in_terminal_args
)));
1094 return reverse_request
;
1097 std::string
JSONToString(const llvm::json::Value
&json
) {
1099 llvm::raw_string_ostream
os(data
);
1105 } // namespace lldb_vscode