1//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "JSONUtils.h"
10#include "DAP.h"
11#include "ExceptionBreakpoint.h"
12#include "LLDBUtils.h"
13#include "ProtocolUtils.h"
14#include "lldb/API/SBAddress.h"
15#include "lldb/API/SBCompileUnit.h"
16#include "lldb/API/SBDeclaration.h"
17#include "lldb/API/SBEnvironment.h"
18#include "lldb/API/SBError.h"
19#include "lldb/API/SBFileSpec.h"
20#include "lldb/API/SBFrame.h"
21#include "lldb/API/SBFunction.h"
22#include "lldb/API/SBInstructionList.h"
23#include "lldb/API/SBLineEntry.h"
24#include "lldb/API/SBModule.h"
25#include "lldb/API/SBQueue.h"
26#include "lldb/API/SBSection.h"
27#include "lldb/API/SBStream.h"
28#include "lldb/API/SBStringList.h"
29#include "lldb/API/SBStructuredData.h"
30#include "lldb/API/SBTarget.h"
31#include "lldb/API/SBThread.h"
32#include "lldb/API/SBType.h"
33#include "lldb/API/SBValue.h"
34#include "lldb/Host/PosixApi.h" // IWYU pragma: keep
35#include "lldb/lldb-defines.h"
36#include "lldb/lldb-enumerations.h"
37#include "lldb/lldb-types.h"
38#include "llvm/ADT/DenseMap.h"
39#include "llvm/ADT/StringExtras.h"
40#include "llvm/ADT/StringRef.h"
41#include "llvm/Support/Compiler.h"
42#include "llvm/Support/Format.h"
43#include "llvm/Support/FormatVariadic.h"
44#include "llvm/Support/JSON.h"
45#include "llvm/Support/Path.h"
46#include "llvm/Support/ScopedPrinter.h"
47#include "llvm/Support/raw_ostream.h"
48#include <chrono>
49#include <cstddef>
50#include <iomanip>
51#include <optional>
52#include <sstream>
53#include <string>
54#include <utility>
55#include <vector>
56
57namespace lldb_dap {
58
59void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
60 llvm::StringRef str) {
61 if (LLVM_LIKELY(llvm::json::isUTF8(str)))
62 obj.try_emplace(K: key, Args: str.str());
63 else
64 obj.try_emplace(K: key, Args: llvm::json::fixUTF8(S: str));
65}
66
67llvm::StringRef GetAsString(const llvm::json::Value &value) {
68 if (auto s = value.getAsString())
69 return *s;
70 return llvm::StringRef();
71}
72
73// Gets a string from a JSON object using the key, or returns an empty string.
74std::optional<llvm::StringRef> GetString(const llvm::json::Object &obj,
75 llvm::StringRef key) {
76 return obj.getString(K: key);
77}
78
79std::optional<llvm::StringRef> GetString(const llvm::json::Object *obj,
80 llvm::StringRef key) {
81 if (obj == nullptr)
82 return std::nullopt;
83
84 return GetString(obj: *obj, key);
85}
86
87std::optional<bool> GetBoolean(const llvm::json::Object &obj,
88 llvm::StringRef key) {
89 if (auto value = obj.getBoolean(K: key))
90 return *value;
91 if (auto value = obj.getInteger(K: key))
92 return *value != 0;
93 return std::nullopt;
94}
95
96std::optional<bool> GetBoolean(const llvm::json::Object *obj,
97 llvm::StringRef key) {
98 if (obj != nullptr)
99 return GetBoolean(obj: *obj, key);
100 return std::nullopt;
101}
102
103bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
104 return obj.find(K: key) != obj.end();
105}
106
107std::string EncodeMemoryReference(lldb::addr_t addr) {
108 return "0x" + llvm::utohexstr(X: addr);
109}
110
111std::optional<lldb::addr_t>
112DecodeMemoryReference(llvm::StringRef memoryReference) {
113 if (!memoryReference.starts_with(Prefix: "0x"))
114 return std::nullopt;
115
116 lldb::addr_t addr;
117 if (memoryReference.consumeInteger(Radix: 0, Result&: addr))
118 return std::nullopt;
119
120 return addr;
121}
122
123bool DecodeMemoryReference(const llvm::json::Value &v, llvm::StringLiteral key,
124 lldb::addr_t &out, llvm::json::Path path,
125 bool required) {
126 const llvm::json::Object *v_obj = v.getAsObject();
127 if (!v_obj) {
128 path.report(Message: "expected object");
129 return false;
130 }
131
132 const llvm::json::Value *mem_ref_value = v_obj->get(K: key);
133 if (!mem_ref_value) {
134 if (!required)
135 return true;
136
137 path.field(Field: key).report(Message: "missing value");
138 return false;
139 }
140
141 const std::optional<llvm::StringRef> mem_ref_str =
142 mem_ref_value->getAsString();
143 if (!mem_ref_str) {
144 path.field(Field: key).report(Message: "expected string");
145 return false;
146 }
147
148 const std::optional<lldb::addr_t> addr_opt =
149 DecodeMemoryReference(memoryReference: *mem_ref_str);
150 if (!addr_opt) {
151 path.field(Field: key).report(Message: "malformed memory reference");
152 return false;
153 }
154
155 out = *addr_opt;
156 return true;
157}
158
159std::vector<std::string> GetStrings(const llvm::json::Object *obj,
160 llvm::StringRef key) {
161 std::vector<std::string> strs;
162 const auto *json_array = obj->getArray(K: key);
163 if (!json_array)
164 return strs;
165 for (const auto &value : *json_array) {
166 switch (value.kind()) {
167 case llvm::json::Value::String:
168 strs.push_back(x: value.getAsString()->str());
169 break;
170 case llvm::json::Value::Number:
171 case llvm::json::Value::Boolean:
172 strs.push_back(x: llvm::to_string(Value: value));
173 break;
174 case llvm::json::Value::Null:
175 case llvm::json::Value::Object:
176 case llvm::json::Value::Array:
177 break;
178 }
179 }
180 return strs;
181}
182
183std::unordered_map<std::string, std::string>
184GetStringMap(const llvm::json::Object &obj, llvm::StringRef key) {
185 std::unordered_map<std::string, std::string> strs;
186 const auto *const json_object = obj.getObject(K: key);
187 if (!json_object)
188 return strs;
189
190 for (const auto &[key, value] : *json_object) {
191 switch (value.kind()) {
192 case llvm::json::Value::String:
193 strs.emplace(args: key.str(), args: value.getAsString()->str());
194 break;
195 case llvm::json::Value::Number:
196 case llvm::json::Value::Boolean:
197 strs.emplace(args: key.str(), args: llvm::to_string(Value: value));
198 break;
199 case llvm::json::Value::Null:
200 case llvm::json::Value::Object:
201 case llvm::json::Value::Array:
202 break;
203 }
204 }
205 return strs;
206}
207
208static bool IsClassStructOrUnionType(lldb::SBType t) {
209 return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct |
210 lldb::eTypeClassArray)) != 0;
211}
212
213/// Create a short summary for a container that contains the summary of its
214/// first children, so that the user can get a glimpse of its contents at a
215/// glance.
216static std::optional<std::string>
217TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
218 if (!v.MightHaveChildren())
219 return std::nullopt;
220 /// As this operation can be potentially slow, we limit the total time spent
221 /// fetching children to a few ms.
222 const auto max_evaluation_time = std::chrono::milliseconds(10);
223 /// We don't want to generate a extremely long summary string, so we limit its
224 /// length.
225 const size_t max_length = 32;
226
227 auto start = std::chrono::steady_clock::now();
228 std::string summary;
229 llvm::raw_string_ostream os(summary);
230 os << "{";
231
232 llvm::StringRef separator = "";
233
234 for (size_t i = 0, e = v.GetNumChildren(); i < e; ++i) {
235 // If we reached the time limit or exceeded the number of characters, we
236 // dump `...` to signal that there are more elements in the collection.
237 if (summary.size() > max_length ||
238 (std::chrono::steady_clock::now() - start) > max_evaluation_time) {
239 os << separator << "...";
240 break;
241 }
242 lldb::SBValue child = v.GetChildAtIndex(idx: i);
243
244 if (llvm::StringRef name = child.GetName(); !name.empty()) {
245 llvm::StringRef desc;
246 if (llvm::StringRef summary = child.GetSummary(); !summary.empty())
247 desc = summary;
248 else if (llvm::StringRef value = child.GetValue(); !value.empty())
249 desc = value;
250 else if (IsClassStructOrUnionType(t: child.GetType()))
251 desc = "{...}";
252 else
253 continue;
254
255 // If the child is an indexed entry, we don't show its index to save
256 // characters.
257 if (name.starts_with(Prefix: "["))
258 os << separator << desc;
259 else
260 os << separator << name << ":" << desc;
261 separator = ", ";
262 }
263 }
264 os << "}";
265
266 if (summary == "{...}" || summary == "{}")
267 return std::nullopt;
268 return summary;
269}
270
271/// Try to create a summary string for the given value that doesn't have a
272/// summary of its own.
273static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue &value) {
274 // We use the dereferenced value for generating the summary.
275 if (value.GetType().IsPointerType() || value.GetType().IsReferenceType())
276 value = value.Dereference();
277
278 // We only support auto summaries for containers.
279 return TryCreateAutoSummaryForContainer(v&: value);
280}
281
282void FillResponse(const llvm::json::Object &request,
283 llvm::json::Object &response) {
284 // Fill in all of the needed response fields to a "request" and set "success"
285 // to true by default.
286 response.try_emplace(K: "type", Args: "response");
287 response.try_emplace(K: "seq", Args: (int64_t)0);
288 EmplaceSafeString(obj&: response, key: "command",
289 str: GetString(obj: request, key: "command").value_or(u: ""));
290 const uint64_t seq = GetInteger<uint64_t>(obj: request, key: "seq").value_or(u: 0);
291 response.try_emplace(K: "request_seq", Args: seq);
292 response.try_emplace(K: "success", Args: true);
293}
294
295// "Scope": {
296// "type": "object",
297// "description": "A Scope is a named container for variables. Optionally
298// a scope can map to a source or a range within a source.",
299// "properties": {
300// "name": {
301// "type": "string",
302// "description": "Name of the scope such as 'Arguments', 'Locals'."
303// },
304// "presentationHint": {
305// "type": "string",
306// "description": "An optional hint for how to present this scope in the
307// UI. If this attribute is missing, the scope is shown
308// with a generic UI.",
309// "_enum": [ "arguments", "locals", "registers" ],
310// },
311// "variablesReference": {
312// "type": "integer",
313// "description": "The variables of this scope can be retrieved by
314// passing the value of variablesReference to the
315// VariablesRequest."
316// },
317// "namedVariables": {
318// "type": "integer",
319// "description": "The number of named variables in this scope. The
320// client can use this optional information to present
321// the variables in a paged UI and fetch them in chunks."
322// },
323// "indexedVariables": {
324// "type": "integer",
325// "description": "The number of indexed variables in this scope. The
326// client can use this optional information to present
327// the variables in a paged UI and fetch them in chunks."
328// },
329// "expensive": {
330// "type": "boolean",
331// "description": "If true, the number of variables in this scope is
332// large or expensive to retrieve."
333// },
334// "source": {
335// "$ref": "#/definitions/Source",
336// "description": "Optional source for this scope."
337// },
338// "line": {
339// "type": "integer",
340// "description": "Optional start line of the range covered by this
341// scope."
342// },
343// "column": {
344// "type": "integer",
345// "description": "Optional start column of the range covered by this
346// scope."
347// },
348// "endLine": {
349// "type": "integer",
350// "description": "Optional end line of the range covered by this scope."
351// },
352// "endColumn": {
353// "type": "integer",
354// "description": "Optional end column of the range covered by this
355// scope."
356// }
357// },
358// "required": [ "name", "variablesReference", "expensive" ]
359// }
360llvm::json::Value CreateScope(const llvm::StringRef name,
361 int64_t variablesReference,
362 int64_t namedVariables, bool expensive) {
363 llvm::json::Object object;
364 EmplaceSafeString(obj&: object, key: "name", str: name.str());
365
366 // TODO: Support "arguments" scope. At the moment lldb-dap includes the
367 // arguments into the "locals" scope.
368 if (variablesReference == VARREF_LOCALS) {
369 object.try_emplace(K: "presentationHint", Args: "locals");
370 } else if (variablesReference == VARREF_REGS) {
371 object.try_emplace(K: "presentationHint", Args: "registers");
372 }
373
374 object.try_emplace(K: "variablesReference", Args&: variablesReference);
375 object.try_emplace(K: "expensive", Args&: expensive);
376 object.try_emplace(K: "namedVariables", Args&: namedVariables);
377 return llvm::json::Value(std::move(object));
378}
379
380// "Event": {
381// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
382// "type": "object",
383// "description": "Server-initiated event.",
384// "properties": {
385// "type": {
386// "type": "string",
387// "enum": [ "event" ]
388// },
389// "event": {
390// "type": "string",
391// "description": "Type of event."
392// },
393// "body": {
394// "type": [ "array", "boolean", "integer", "null", "number" ,
395// "object", "string" ],
396// "description": "Event-specific information."
397// }
398// },
399// "required": [ "type", "event" ]
400// }]
401// },
402// "ProtocolMessage": {
403// "type": "object",
404// "description": "Base class of requests, responses, and events.",
405// "properties": {
406// "seq": {
407// "type": "integer",
408// "description": "Sequence number."
409// },
410// "type": {
411// "type": "string",
412// "description": "Message type.",
413// "_enum": [ "request", "response", "event" ]
414// }
415// },
416// "required": [ "seq", "type" ]
417// }
418llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
419 llvm::json::Object event;
420 event.try_emplace(K: "seq", Args: 0);
421 event.try_emplace(K: "type", Args: "event");
422 EmplaceSafeString(obj&: event, key: "event", str: event_name);
423 return event;
424}
425
426// "StackFrame": {
427// "type": "object",
428// "description": "A Stackframe contains the source location.",
429// "properties": {
430// "id": {
431// "type": "integer",
432// "description": "An identifier for the stack frame. It must be unique
433// across all threads. This id can be used to retrieve
434// the scopes of the frame with the 'scopesRequest' or
435// to restart the execution of a stackframe."
436// },
437// "name": {
438// "type": "string",
439// "description": "The name of the stack frame, typically a method name."
440// },
441// "source": {
442// "$ref": "#/definitions/Source",
443// "description": "The optional source of the frame."
444// },
445// "line": {
446// "type": "integer",
447// "description": "The line within the file of the frame. If source is
448// null or doesn't exist, line is 0 and must be ignored."
449// },
450// "column": {
451// "type": "integer",
452// "description": "The column within the line. If source is null or
453// doesn't exist, column is 0 and must be ignored."
454// },
455// "endLine": {
456// "type": "integer",
457// "description": "An optional end line of the range covered by the
458// stack frame."
459// },
460// "endColumn": {
461// "type": "integer",
462// "description": "An optional end column of the range covered by the
463// stack frame."
464// },
465// "instructionPointerReference": {
466// "type": "string",
467// "description": "A memory reference for the current instruction
468// pointer in this frame."
469// },
470// "moduleId": {
471// "type": ["integer", "string"],
472// "description": "The module associated with this frame, if any."
473// },
474// "presentationHint": {
475// "type": "string",
476// "enum": [ "normal", "label", "subtle" ],
477// "description": "An optional hint for how to present this frame in
478// the UI. A value of 'label' can be used to indicate
479// that the frame is an artificial frame that is used
480// as a visual label or separator. A value of 'subtle'
481// can be used to change the appearance of a frame in
482// a 'subtle' way."
483// }
484// },
485// "required": [ "id", "name", "line", "column" ]
486// }
487llvm::json::Value CreateStackFrame(DAP &dap, lldb::SBFrame &frame,
488 lldb::SBFormat &format) {
489 llvm::json::Object object;
490 int64_t frame_id = MakeDAPFrameID(frame);
491 object.try_emplace(K: "id", Args&: frame_id);
492
493 std::string frame_name;
494 lldb::SBStream stream;
495 if (format && frame.GetDescriptionWithFormat(format, output&: stream).Success()) {
496 frame_name = stream.GetData();
497
498 // `function_name` can be a nullptr, which throws an error when assigned to
499 // an `std::string`.
500 } else if (const char *name = frame.GetDisplayFunctionName()) {
501 frame_name = name;
502 }
503
504 if (frame_name.empty()) {
505 // If the function name is unavailable, display the pc address as a 16-digit
506 // hex string, e.g. "0x0000000000012345"
507 frame_name = GetLoadAddressString(addr: frame.GetPC());
508 }
509
510 // We only include `[opt]` if a custom frame format is not specified.
511 if (!format && frame.GetFunction().GetIsOptimized())
512 frame_name += " [opt]";
513
514 EmplaceSafeString(obj&: object, key: "name", str: frame_name);
515
516 std::optional<protocol::Source> source = dap.ResolveSource(frame);
517
518 if (source && !IsAssemblySource(source: *source)) {
519 // This is a normal source with a valid line entry.
520 auto line_entry = frame.GetLineEntry();
521 object.try_emplace(K: "line", Args: line_entry.GetLine());
522 auto column = line_entry.GetColumn();
523 object.try_emplace(K: "column", Args&: column);
524 } else if (frame.GetSymbol().IsValid()) {
525 // This is a source where the disassembly is used, but there is a valid
526 // symbol. Calculate the line of the current PC from the start of the
527 // current symbol.
528 lldb::SBInstructionList inst_list = dap.target.ReadInstructions(
529 start_addr: frame.GetSymbol().GetStartAddress(), end_addr: frame.GetPCAddress(), flavor_string: nullptr);
530 size_t inst_line = inst_list.GetSize();
531
532 // Line numbers are 1-based.
533 object.try_emplace(K: "line", Args: inst_line + 1);
534 object.try_emplace(K: "column", Args: 1);
535 } else {
536 // No valid line entry or symbol.
537 object.try_emplace(K: "line", Args: 1);
538 object.try_emplace(K: "column", Args: 1);
539 }
540
541 if (source)
542 object.try_emplace(K: "source", Args: std::move(source).value());
543
544 const auto pc = frame.GetPC();
545 if (pc != LLDB_INVALID_ADDRESS) {
546 std::string formatted_addr = "0x" + llvm::utohexstr(X: pc);
547 object.try_emplace(K: "instructionPointerReference", Args&: formatted_addr);
548 }
549
550 if (frame.IsArtificial() || frame.IsHidden())
551 object.try_emplace(K: "presentationHint", Args: "subtle");
552
553 return llvm::json::Value(std::move(object));
554}
555
556llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread,
557 lldb::SBFormat &format) {
558 std::string name;
559 lldb::SBStream stream;
560 if (format && thread.GetDescriptionWithFormat(format, output&: stream).Success()) {
561 name = stream.GetData();
562 } else {
563 const uint32_t thread_idx = thread.GetExtendedBacktraceOriginatingIndexID();
564 const char *queue_name = thread.GetQueueName();
565 if (queue_name != nullptr) {
566 name = llvm::formatv(Fmt: "Enqueued from {0} (Thread {1})", Vals&: queue_name,
567 Vals: thread_idx);
568 } else {
569 name = llvm::formatv(Fmt: "Thread {0}", Vals: thread_idx);
570 }
571 }
572
573 return llvm::json::Value(llvm::json::Object{{.K: "id", .V: thread.GetThreadID() + 1},
574 {.K: "name", .V: name},
575 {.K: "presentationHint", .V: "label"}});
576}
577
578// "StoppedEvent": {
579// "allOf": [ { "$ref": "#/definitions/Event" }, {
580// "type": "object",
581// "description": "Event message for 'stopped' event type. The event
582// indicates that the execution of the debuggee has stopped
583// due to some condition. This can be caused by a break
584// point previously set, a stepping action has completed,
585// by executing a debugger statement etc.",
586// "properties": {
587// "event": {
588// "type": "string",
589// "enum": [ "stopped" ]
590// },
591// "body": {
592// "type": "object",
593// "properties": {
594// "reason": {
595// "type": "string",
596// "description": "The reason for the event. For backward
597// compatibility this string is shown in the UI if
598// the 'description' attribute is missing (but it
599// must not be translated).",
600// "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
601// },
602// "description": {
603// "type": "string",
604// "description": "The full reason for the event, e.g. 'Paused
605// on exception'. This string is shown in the UI
606// as is."
607// },
608// "threadId": {
609// "type": "integer",
610// "description": "The thread which was stopped."
611// },
612// "text": {
613// "type": "string",
614// "description": "Additional information. E.g. if reason is
615// 'exception', text contains the exception name.
616// This string is shown in the UI."
617// },
618// "allThreadsStopped": {
619// "type": "boolean",
620// "description": "If allThreadsStopped is true, a debug adapter
621// can announce that all threads have stopped.
622// The client should use this information to
623// enable that all threads can be expanded to
624// access their stacktraces. If the attribute
625// is missing or false, only the thread with the
626// given threadId can be expanded."
627// }
628// },
629// "required": [ "reason" ]
630// }
631// },
632// "required": [ "event", "body" ]
633// }]
634// }
635llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread,
636 uint32_t stop_id) {
637 llvm::json::Object event(CreateEventObject(event_name: "stopped"));
638 llvm::json::Object body;
639 switch (thread.GetStopReason()) {
640 case lldb::eStopReasonTrace:
641 case lldb::eStopReasonPlanComplete:
642 body.try_emplace(K: "reason", Args: "step");
643 break;
644 case lldb::eStopReasonBreakpoint: {
645 ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
646 if (exc_bp) {
647 body.try_emplace(K: "reason", Args: "exception");
648 EmplaceSafeString(obj&: body, key: "description", str: exc_bp->GetLabel());
649 } else {
650 InstructionBreakpoint *inst_bp =
651 dap.GetInstructionBPFromStopReason(thread);
652 if (inst_bp) {
653 body.try_emplace(K: "reason", Args: "instruction breakpoint");
654 } else {
655 body.try_emplace(K: "reason", Args: "breakpoint");
656 }
657 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx: 0);
658 lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(idx: 1);
659 std::string desc_str =
660 llvm::formatv(Fmt: "breakpoint {0}.{1}", Vals&: bp_id, Vals&: bp_loc_id);
661 body.try_emplace(K: "hitBreakpointIds",
662 Args: llvm::json::Array{llvm::json::Value(bp_id)});
663 EmplaceSafeString(obj&: body, key: "description", str: desc_str);
664 }
665 } break;
666 case lldb::eStopReasonWatchpoint:
667 case lldb::eStopReasonInstrumentation:
668 body.try_emplace(K: "reason", Args: "breakpoint");
669 break;
670 case lldb::eStopReasonProcessorTrace:
671 body.try_emplace(K: "reason", Args: "processor trace");
672 break;
673 case lldb::eStopReasonHistoryBoundary:
674 body.try_emplace(K: "reason", Args: "history boundary");
675 break;
676 case lldb::eStopReasonSignal:
677 case lldb::eStopReasonException:
678 body.try_emplace(K: "reason", Args: "exception");
679 break;
680 case lldb::eStopReasonExec:
681 body.try_emplace(K: "reason", Args: "entry");
682 break;
683 case lldb::eStopReasonFork:
684 body.try_emplace(K: "reason", Args: "fork");
685 break;
686 case lldb::eStopReasonVFork:
687 body.try_emplace(K: "reason", Args: "vfork");
688 break;
689 case lldb::eStopReasonVForkDone:
690 body.try_emplace(K: "reason", Args: "vforkdone");
691 break;
692 case lldb::eStopReasonInterrupt:
693 body.try_emplace(K: "reason", Args: "async interrupt");
694 break;
695 case lldb::eStopReasonThreadExiting:
696 case lldb::eStopReasonInvalid:
697 case lldb::eStopReasonNone:
698 break;
699 }
700 if (stop_id == 0)
701 body.try_emplace(K: "reason", Args: "entry");
702 const lldb::tid_t tid = thread.GetThreadID();
703 body.try_emplace(K: "threadId", Args: (int64_t)tid);
704 // If no description has been set, then set it to the default thread stopped
705 // description. If we have breakpoints that get hit and shouldn't be reported
706 // as breakpoints, then they will set the description above.
707 if (!ObjectContainsKey(obj: body, key: "description")) {
708 char description[1024];
709 if (thread.GetStopDescription(dst_or_null: description, dst_len: sizeof(description))) {
710 EmplaceSafeString(obj&: body, key: "description", str: description);
711 }
712 }
713 // "threadCausedFocus" is used in tests to validate breaking behavior.
714 if (tid == dap.focus_tid) {
715 body.try_emplace(K: "threadCausedFocus", Args: true);
716 }
717 body.try_emplace(K: "preserveFocusHint", Args: tid != dap.focus_tid);
718 body.try_emplace(K: "allThreadsStopped", Args: true);
719 event.try_emplace(K: "body", Args: std::move(body));
720 return llvm::json::Value(std::move(event));
721}
722
723const char *GetNonNullVariableName(lldb::SBValue &v) {
724 const char *name = v.GetName();
725 return name ? name : "<null>";
726}
727
728std::string CreateUniqueVariableNameForDisplay(lldb::SBValue &v,
729 bool is_name_duplicated) {
730 lldb::SBStream name_builder;
731 name_builder.Print(str: GetNonNullVariableName(v));
732 if (is_name_duplicated) {
733 lldb::SBDeclaration declaration = v.GetDeclaration();
734 const char *file_name = declaration.GetFileSpec().GetFilename();
735 const uint32_t line = declaration.GetLine();
736
737 if (file_name != nullptr && line > 0)
738 name_builder.Printf(format: " @ %s:%u", file_name, line);
739 else if (const char *location = v.GetLocation())
740 name_builder.Printf(format: " @ %s", location);
741 }
742 return name_builder.GetData();
743}
744
745VariableDescription::VariableDescription(lldb::SBValue v,
746 bool auto_variable_summaries,
747 bool format_hex,
748 bool is_name_duplicated,
749 std::optional<std::string> custom_name)
750 : v(v) {
751 name = custom_name
752 ? *custom_name
753 : CreateUniqueVariableNameForDisplay(v, is_name_duplicated);
754
755 type_obj = v.GetType();
756 std::string raw_display_type_name =
757 llvm::StringRef(type_obj.GetDisplayTypeName()).str();
758 display_type_name =
759 !raw_display_type_name.empty() ? raw_display_type_name : NO_TYPENAME;
760
761 // Only format hex/default if there is no existing special format.
762 if (v.GetFormat() == lldb::eFormatDefault ||
763 v.GetFormat() == lldb::eFormatHex) {
764 if (format_hex)
765 v.SetFormat(lldb::eFormatHex);
766 else
767 v.SetFormat(lldb::eFormatDefault);
768 }
769
770 llvm::raw_string_ostream os_display_value(display_value);
771
772 if (lldb::SBError sb_error = v.GetError(); sb_error.Fail()) {
773 error = sb_error.GetCString();
774 os_display_value << "<error: " << error << ">";
775 } else {
776 value = llvm::StringRef(v.GetValue()).str();
777 summary = llvm::StringRef(v.GetSummary()).str();
778 if (summary.empty() && auto_variable_summaries)
779 auto_summary = TryCreateAutoSummary(value&: v);
780
781 std::optional<std::string> effective_summary =
782 !summary.empty() ? summary : auto_summary;
783
784 if (!value.empty()) {
785 os_display_value << value;
786 if (effective_summary)
787 os_display_value << " " << *effective_summary;
788 } else if (effective_summary) {
789 os_display_value << *effective_summary;
790
791 // As last resort, we print its type and address if available.
792 } else {
793 if (!raw_display_type_name.empty()) {
794 os_display_value << raw_display_type_name;
795 lldb::addr_t address = v.GetLoadAddress();
796 if (address != LLDB_INVALID_ADDRESS)
797 os_display_value << " @ " << llvm::format_hex(N: address, Width: 0);
798 }
799 }
800 }
801
802 lldb::SBStream evaluateStream;
803 v.GetExpressionPath(description&: evaluateStream);
804 evaluate_name = llvm::StringRef(evaluateStream.GetData()).str();
805}
806
807std::string VariableDescription::GetResult(llvm::StringRef context) {
808 // In repl context, the results can be displayed as multiple lines so more
809 // detailed descriptions can be returned.
810 if (context != "repl")
811 return display_value;
812
813 if (!v.IsValid())
814 return display_value;
815
816 // Try the SBValue::GetDescription(), which may call into language runtime
817 // specific formatters (see ValueObjectPrinter).
818 lldb::SBStream stream;
819 v.GetDescription(description&: stream);
820 llvm::StringRef description = stream.GetData();
821 return description.trim().str();
822}
823
824bool ValuePointsToCode(lldb::SBValue v) {
825 if (!v.GetType().GetPointeeType().IsFunctionType())
826 return false;
827
828 lldb::addr_t addr = v.GetValueAsAddress();
829 lldb::SBLineEntry line_entry =
830 v.GetTarget().ResolveLoadAddress(vm_addr: addr).GetLineEntry();
831
832 return line_entry.IsValid();
833}
834
835int64_t PackLocation(int64_t var_ref, bool is_value_location) {
836 return var_ref << 1 | is_value_location;
837}
838
839std::pair<int64_t, bool> UnpackLocation(int64_t location_id) {
840 return std::pair{location_id >> 1, location_id & 1};
841}
842
843llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) {
844 llvm::json::Object object;
845 char unit_path_arr[PATH_MAX];
846 unit.GetFileSpec().GetPath(dst_path: unit_path_arr, dst_len: sizeof(unit_path_arr));
847 std::string unit_path(unit_path_arr);
848 object.try_emplace(K: "compileUnitPath", Args&: unit_path);
849 return llvm::json::Value(std::move(object));
850}
851
852/// See
853/// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
854llvm::json::Object CreateRunInTerminalReverseRequest(
855 llvm::StringRef program, const std::vector<std::string> &args,
856 const llvm::StringMap<std::string> &env, llvm::StringRef cwd,
857 llvm::StringRef comm_file, lldb::pid_t debugger_pid, bool external) {
858 llvm::json::Object run_in_terminal_args;
859 if (external) {
860 // This indicates the IDE to open an external terminal window.
861 run_in_terminal_args.try_emplace(K: "kind", Args: "external");
862 } else {
863 // This indicates the IDE to open an embedded terminal, instead of opening
864 // the terminal in a new window.
865 run_in_terminal_args.try_emplace(K: "kind", Args: "integrated");
866 }
867 // The program path must be the first entry in the "args" field
868 std::vector<std::string> req_args = {DAP::debug_adapter_path.str(),
869 "--comm-file", comm_file.str()};
870 if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
871 req_args.push_back(x: "--debugger-pid");
872 req_args.push_back(x: std::to_string(val: debugger_pid));
873 }
874 req_args.push_back(x: "--launch-target");
875 req_args.push_back(x: program.str());
876 req_args.insert(position: req_args.end(), first: args.begin(), last: args.end());
877 run_in_terminal_args.try_emplace(K: "args", Args&: req_args);
878
879 if (!cwd.empty())
880 run_in_terminal_args.try_emplace(K: "cwd", Args&: cwd);
881
882 if (!env.empty()) {
883 llvm::json::Object env_json;
884 for (const auto &kv : env) {
885 if (!kv.first().empty())
886 env_json.try_emplace(K: kv.first(), Args: kv.second);
887 }
888 run_in_terminal_args.try_emplace(K: "env",
889 Args: llvm::json::Value(std::move(env_json)));
890 }
891
892 return run_in_terminal_args;
893}
894
895// Keep all the top level items from the statistics dump, except for the
896// "modules" array. It can be huge and cause delay
897// Array and dictionary value will return as <key, JSON string> pairs
898static void FilterAndGetValueForKey(const lldb::SBStructuredData data,
899 const char *key, llvm::json::Object &out) {
900 lldb::SBStructuredData value = data.GetValueForKey(key);
901 std::string key_utf8 = llvm::json::fixUTF8(S: key);
902 if (llvm::StringRef(key) == "modules")
903 return;
904 switch (value.GetType()) {
905 case lldb::eStructuredDataTypeFloat:
906 out.try_emplace(K: key_utf8, Args: value.GetFloatValue());
907 break;
908 case lldb::eStructuredDataTypeUnsignedInteger:
909 out.try_emplace(K: key_utf8, Args: value.GetIntegerValue(fail_value: (uint64_t)0));
910 break;
911 case lldb::eStructuredDataTypeSignedInteger:
912 out.try_emplace(K: key_utf8, Args: value.GetIntegerValue(fail_value: (int64_t)0));
913 break;
914 case lldb::eStructuredDataTypeArray: {
915 lldb::SBStream contents;
916 value.GetAsJSON(stream&: contents);
917 out.try_emplace(K: key_utf8, Args: llvm::json::fixUTF8(S: contents.GetData()));
918 } break;
919 case lldb::eStructuredDataTypeBoolean:
920 out.try_emplace(K: key_utf8, Args: value.GetBooleanValue());
921 break;
922 case lldb::eStructuredDataTypeString: {
923 // Get the string size before reading
924 const size_t str_length = value.GetStringValue(dst: nullptr, dst_len: 0);
925 std::string str(str_length + 1, 0);
926 value.GetStringValue(dst: &str[0], dst_len: str_length);
927 out.try_emplace(K: key_utf8, Args: llvm::json::fixUTF8(S: str));
928 } break;
929 case lldb::eStructuredDataTypeDictionary: {
930 lldb::SBStream contents;
931 value.GetAsJSON(stream&: contents);
932 out.try_emplace(K: key_utf8, Args: llvm::json::fixUTF8(S: contents.GetData()));
933 } break;
934 case lldb::eStructuredDataTypeNull:
935 case lldb::eStructuredDataTypeGeneric:
936 case lldb::eStructuredDataTypeInvalid:
937 break;
938 }
939}
940
941static void addStatistic(lldb::SBTarget &target, llvm::json::Object &event) {
942 lldb::SBStructuredData statistics = target.GetStatistics();
943 bool is_dictionary =
944 statistics.GetType() == lldb::eStructuredDataTypeDictionary;
945 if (!is_dictionary)
946 return;
947 llvm::json::Object stats_body;
948
949 lldb::SBStringList keys;
950 if (!statistics.GetKeys(keys))
951 return;
952 for (size_t i = 0; i < keys.GetSize(); i++) {
953 const char *key = keys.GetStringAtIndex(idx: i);
954 FilterAndGetValueForKey(data: statistics, key, out&: stats_body);
955 }
956 llvm::json::Object body{{.K: "$__lldb_statistics", .V: std::move(stats_body)}};
957 event.try_emplace(K: "body", Args: std::move(body));
958}
959
960llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target) {
961 llvm::json::Object event(CreateEventObject(event_name: "terminated"));
962 addStatistic(target, event);
963 return event;
964}
965
966std::string JSONToString(const llvm::json::Value &json) {
967 std::string data;
968 llvm::raw_string_ostream os(data);
969 os << json;
970 return data;
971}
972
973} // namespace lldb_dap
974

source code of lldb/tools/lldb-dap/JSONUtils.cpp