[sanitizer] Improve FreeBSD ASLR detection
[llvm-project.git] / lldb / tools / lldb-vscode / JSONUtils.cpp
blob58a08796e738d67efd8897b98e4b2230a855ad09
1 //===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include <algorithm>
10 #include <iomanip>
11 #include <sstream>
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"
27 #include "VSCode.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());
35 else
36 obj.try_emplace(key, llvm::json::fixUTF8(str));
39 llvm::StringRef GetAsString(const llvm::json::Value &value) {
40 if (auto s = value.getAsString())
41 return *s;
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))
48 return *value;
49 return llvm::StringRef();
52 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
53 if (obj == nullptr)
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;
64 return fail_value;
67 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
68 uint64_t fail_value) {
69 if (obj == nullptr)
70 return fail_value;
71 return GetUnsigned(*obj, key, fail_value);
74 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
75 bool fail_value) {
76 if (auto value = obj.getBoolean(key))
77 return *value;
78 if (auto value = obj.getInteger(key))
79 return *value != 0;
80 return fail_value;
83 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
84 bool fail_value) {
85 if (obj == nullptr)
86 return fail_value;
87 return GetBoolean(*obj, key, fail_value);
90 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
91 int64_t fail_value) {
92 if (auto value = obj.getInteger(key))
93 return *value;
94 return fail_value;
97 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
98 int64_t fail_value) {
99 if (obj == nullptr)
100 return fail_value;
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);
112 if (!json_array)
113 return strs;
114 for (const auto &value : *json_array) {
115 switch (value.kind()) {
116 case llvm::json::Value::String:
117 strs.push_back(value.getAsString()->str());
118 break;
119 case llvm::json::Value::Number:
120 case llvm::json::Value::Boolean:
121 strs.push_back(llvm::to_string(value));
122 break;
123 case llvm::json::Value::Null:
124 case llvm::json::Value::Object:
125 case llvm::json::Value::Array:
126 break;
129 return strs;
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();
139 std::string result;
140 llvm::raw_string_ostream strm(result);
141 if (!value.empty()) {
142 strm << value;
143 if (!summary.empty())
144 strm << ' ' << summary;
145 } else if (!summary.empty()) {
146 strm << ' ' << summary;
147 } else if (!type_name.empty()) {
148 strm << type_name;
149 lldb::addr_t address = v.GetLoadAddress();
150 if (address != LLDB_INVALID_ADDRESS)
151 strm << " @ " << llvm::format_hex(address, 0);
153 strm.flush();
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);
169 // "Scope": {
170 // "type": "object",
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.",
173 // "properties": {
174 // "name": {
175 // "type": "string",
176 // "description": "Name of the scope such as 'Arguments', 'Locals'."
177 // },
178 // "presentationHint": {
179 // "type": "string",
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" ],
184 // },
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."
190 // },
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."
196 // },
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."
202 // },
203 // "expensive": {
204 // "type": "boolean",
205 // "description": "If true, the number of variables in this scope is
206 // large or expensive to retrieve."
207 // },
208 // "source": {
209 // "$ref": "#/definitions/Source",
210 // "description": "Optional source for this scope."
211 // },
212 // "line": {
213 // "type": "integer",
214 // "description": "Optional start line of the range covered by this
215 // scope."
216 // },
217 // "column": {
218 // "type": "integer",
219 // "description": "Optional start column of the range covered by this
220 // scope."
221 // },
222 // "endLine": {
223 // "type": "integer",
224 // "description": "Optional end line of the range covered by this scope."
225 // },
226 // "endColumn": {
227 // "type": "integer",
228 // "description": "Optional end column of the range covered by this
229 // scope."
230 // }
231 // },
232 // "required": [ "name", "variablesReference", "expensive" ]
233 // }
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));
254 // "Breakpoint": {
255 // "type": "object",
256 // "description": "Information about a Breakpoint created in setBreakpoints
257 // or setFunctionBreakpoints.",
258 // "properties": {
259 // "id": {
260 // "type": "integer",
261 // "description": "An optional unique identifier for the breakpoint."
262 // },
263 // "verified": {
264 // "type": "boolean",
265 // "description": "If true breakpoint could be set (but not necessarily
266 // at the desired location)."
267 // },
268 // "message": {
269 // "type": "string",
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."
273 // },
274 // "source": {
275 // "$ref": "#/definitions/Source",
276 // "description": "The source where the breakpoint is located."
277 // },
278 // "line": {
279 // "type": "integer",
280 // "description": "The start line of the actual range covered by the
281 // breakpoint."
282 // },
283 // "column": {
284 // "type": "integer",
285 // "description": "An optional start column of the actual range covered
286 // by the breakpoint."
287 // },
288 // "endLine": {
289 // "type": "integer",
290 // "description": "An optional end line of the actual range covered by
291 // the breakpoint."
292 // },
293 // "endColumn": {
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."
298 // }
299 // },
300 // "required": [ "verified" ]
301 // }
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;
308 if (!bp.IsValid())
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())
326 break;
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();
333 if (request_path)
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
344 if (request_line)
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++) {
357 debug_info_size +=
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;
379 oss << kb << "KB";
380 } else if (debug_info < 1024 * 1024 * 1024) {
381 double mb = double(debug_info) / (1024.0 * 1024.0);
382 oss << mb << "MB";
383 } else {
384 double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
385 oss << gb << "GB";
387 return oss.str();
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);
414 } else {
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())
426 version_str += ".";
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));
440 // "Event": {
441 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
442 // "type": "object",
443 // "description": "Server-initiated event.",
444 // "properties": {
445 // "type": {
446 // "type": "string",
447 // "enum": [ "event" ]
448 // },
449 // "event": {
450 // "type": "string",
451 // "description": "Type of event."
452 // },
453 // "body": {
454 // "type": [ "array", "boolean", "integer", "null", "number" ,
455 // "object", "string" ],
456 // "description": "Event-specific information."
457 // }
458 // },
459 // "required": [ "type", "event" ]
460 // }]
461 // },
462 // "ProtocolMessage": {
463 // "type": "object",
464 // "description": "Base class of requests, responses, and events.",
465 // "properties": {
466 // "seq": {
467 // "type": "integer",
468 // "description": "Sequence number."
469 // },
470 // "type": {
471 // "type": "string",
472 // "description": "Message type.",
473 // "_enum": [ "request", "response", "event" ]
474 // }
475 // },
476 // "required": [ "seq", "type" ]
477 // }
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);
483 return event;
486 // "ExceptionBreakpointsFilter": {
487 // "type": "object",
488 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an
489 // option for configuring how exceptions are dealt with.",
490 // "properties": {
491 // "filter": {
492 // "type": "string",
493 // "description": "The internal ID of the filter. This value is passed
494 // to the setExceptionBreakpoints request."
495 // },
496 // "label": {
497 // "type": "string",
498 // "description": "The name of the filter. This will be shown in the UI."
499 // },
500 // "default": {
501 // "type": "boolean",
502 // "description": "Initial value of the filter. If not specified a value
503 // 'false' is assumed."
504 // }
505 // },
506 // "required": [ "filter", "label" ]
507 // }
508 llvm::json::Value
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));
517 // "Source": {
518 // "type": "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.",
522 // "properties": {
523 // "name": {
524 // "type": "string",
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."
528 // },
529 // "path": {
530 // "type": "string",
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
534 // value is 0)."
535 // },
536 // "sourceReference": {
537 // "type": "number",
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."
542 // },
543 // "presentationHint": {
544 // "type": "string",
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" ]
550 // },
551 // "origin": {
552 // "type": "string",
553 // "description": "The (optional) origin of this source: possible values
554 // 'internal module', 'inlined content from source map',
555 // etc."
556 // },
557 // "sources": {
558 // "type": "array",
559 // "items": {
560 // "$ref": "#/definitions/Source"
561 // },
562 // "description": "An optional list of sources that are related to this
563 // source. These may be the source that generated this
564 // source."
565 // },
566 // "adapterData": {
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."
572 // },
573 // "checksums": {
574 // "type": "array",
575 // "items": {
576 // "$ref": "#/definitions/Checksum"
577 // },
578 // "description": "The checksums associated with this file."
579 // }
580 // }
581 // }
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();
587 if (name)
588 EmplaceSafeString(object, "name", name);
589 char path[PATH_MAX] = "";
590 file.GetPath(path, sizeof(path));
591 if (path[0]) {
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) {
607 disasm_line = 0;
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
623 // sourceReference
624 object.try_emplace("sourceReference", addr_srcref->second);
625 disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
626 } else {
627 insts = function.GetInstructions(g_vsc.target);
629 } else {
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
636 // sourceReference
637 object.try_emplace("sourceReference", addr_srcref->second);
638 disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
639 } else {
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);
649 std::string line;
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);
656 if (pc == inst_addr)
657 disasm_line = i + 1;
658 const auto inst_offset = inst_addr - low_pc;
659 int spaces = 0;
660 if (inst_offset < 10)
661 spaces = 3;
662 else if (inst_offset < 100)
663 spaces = 2;
664 else if (inst_offset < 1000)
665 spaces = 1;
666 line.clear();
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);
675 if (c && c[0]) {
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
684 src_strm.str();
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));
693 // "StackFrame": {
694 // "type": "object",
695 // "description": "A Stackframe contains the source location.",
696 // "properties": {
697 // "id": {
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."
703 // },
704 // "name": {
705 // "type": "string",
706 // "description": "The name of the stack frame, typically a method name."
707 // },
708 // "source": {
709 // "$ref": "#/definitions/Source",
710 // "description": "The optional source of the frame."
711 // },
712 // "line": {
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."
716 // },
717 // "column": {
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."
721 // },
722 // "endLine": {
723 // "type": "integer",
724 // "description": "An optional end line of the range covered by the
725 // stack frame."
726 // },
727 // "endColumn": {
728 // "type": "integer",
729 // "description": "An optional end column of the range covered by the
730 // stack frame."
731 // },
732 // "moduleId": {
733 // "type": ["integer", "string"],
734 // "description": "The module associated with this frame, if any."
735 // },
736 // "presentationHint": {
737 // "type": "string",
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
744 // a 'subtle' way."
745 // }
746 // },
747 // "required": [ "id", "name", "line", "column" ]
748 // }
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);
760 } else {
761 auto line = line_entry.GetLine();
762 if (line == UINT32_MAX)
763 line = 0;
764 object.try_emplace("line", line);
766 object.try_emplace("column", line_entry.GetColumn());
767 return llvm::json::Value(std::move(object));
770 // "Thread": {
771 // "type": "object",
772 // "description": "A Thread",
773 // "properties": {
774 // "id": {
775 // "type": "integer",
776 // "description": "Unique identifier for the thread."
777 // },
778 // "name": {
779 // "type": "string",
780 // "description": "A name of the thread."
781 // }
782 // },
783 // "required": [ "id", "name" ]
784 // }
785 llvm::json::Value CreateThread(lldb::SBThread &thread) {
786 llvm::json::Object object;
787 object.try_emplace("id", (int64_t)thread.GetThreadID());
788 char thread_str[64];
789 snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
790 const char *name = thread.GetName();
791 if (name) {
792 std::string thread_with_name(thread_str);
793 thread_with_name += ' ';
794 thread_with_name += name;
795 EmplaceSafeString(object, "name", thread_with_name);
796 } else {
797 EmplaceSafeString(object, "name", std::string(thread_str));
799 return llvm::json::Value(std::move(object));
802 // "StoppedEvent": {
803 // "allOf": [ { "$ref": "#/definitions/Event" }, {
804 // "type": "object",
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.",
810 // "properties": {
811 // "event": {
812 // "type": "string",
813 // "enum": [ "stopped" ]
814 // },
815 // "body": {
816 // "type": "object",
817 // "properties": {
818 // "reason": {
819 // "type": "string",
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" ]
825 // },
826 // "description": {
827 // "type": "string",
828 // "description": "The full reason for the event, e.g. 'Paused
829 // on exception'. This string is shown in the UI
830 // as is."
831 // },
832 // "threadId": {
833 // "type": "integer",
834 // "description": "The thread which was stopped."
835 // },
836 // "text": {
837 // "type": "string",
838 // "description": "Additional information. E.g. if reason is
839 // 'exception', text contains the exception name.
840 // This string is shown in the UI."
841 // },
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."
851 // }
852 // },
853 // "required": [ "reason" ]
854 // }
855 // },
856 // "required": [ "event", "body" ]
857 // }]
858 // }
859 llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
860 uint32_t stop_id) {
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");
867 break;
868 case lldb::eStopReasonBreakpoint: {
869 ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
870 if (exc_bp) {
871 body.try_emplace("reason", "exception");
872 EmplaceSafeString(body, "description", exc_bp->label);
873 } else {
874 body.try_emplace("reason", "breakpoint");
875 char desc_str[64];
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,
879 bp_id, bp_loc_id);
880 EmplaceSafeString(body, "description", desc_str);
882 } break;
883 case lldb::eStopReasonWatchpoint:
884 case lldb::eStopReasonInstrumentation:
885 body.try_emplace("reason", "breakpoint");
886 break;
887 case lldb::eStopReasonProcessorTrace:
888 body.try_emplace("reason", "processor trace");
889 break;
890 case lldb::eStopReasonSignal:
891 case lldb::eStopReasonException:
892 body.try_emplace("reason", "exception");
893 break;
894 case lldb::eStopReasonExec:
895 body.try_emplace("reason", "entry");
896 break;
897 case lldb::eStopReasonFork:
898 body.try_emplace("reason", "fork");
899 break;
900 case lldb::eStopReasonVFork:
901 body.try_emplace("reason", "vfork");
902 break;
903 case lldb::eStopReasonVForkDone:
904 body.try_emplace("reason", "vforkdone");
905 break;
906 case lldb::eStopReasonThreadExiting:
907 case lldb::eStopReasonInvalid:
908 case lldb::eStopReasonNone:
909 break;
911 if (stop_id == 0)
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();
955 // "Variable": {
956 // "type": "object",
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.",
970 // "properties": {
971 // "name": {
972 // "type": "string",
973 // "description": "The variable's name."
974 // },
975 // "value": {
976 // "type": "string",
977 // "description": "The variable's value. This can be a multi-line text,
978 // e.g. for a function the body of a function."
979 // },
980 // "type": {
981 // "type": "string",
982 // "description": "The type of the variable's value. Typically shown in
983 // the UI when hovering over the value."
984 // },
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."
989 // },
990 // "evaluateName": {
991 // "type": "string",
992 // "description": "Optional evaluatable name of this variable which can
993 // be passed to the 'EvaluateRequest' to fetch the
994 // variable's value."
995 // },
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."
1001 // },
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."
1007 // },
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."
1013 // }
1014 // },
1015 // "required": [ "name", "value", "variablesReference" ]
1016 // }
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));
1024 if (format_hex)
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);
1033 else
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));
1052 /// See
1053 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1054 llvm::json::Object
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");
1078 if (!cwd.empty())
1079 run_in_terminal_args.try_emplace("cwd", cwd);
1081 // We need to convert the input list of environments variables into a
1082 // dictionary
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) {
1098 std::string data;
1099 llvm::raw_string_ostream os(data);
1100 os << json;
1101 os.flush();
1102 return data;
1105 } // namespace lldb_vscode