Coverage Report

Created: 2022-01-25 06:29

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/tools/lldb-vscode/JSONUtils.cpp
Line
Count
Source (jump to first uncovered line)
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 <algorithm>
10
#include <iomanip>
11
#include <sstream>
12
13
#include "llvm/ADT/Optional.h"
14
#include "llvm/Support/FormatAdapters.h"
15
#include "llvm/Support/Path.h"
16
#include "llvm/Support/ScopedPrinter.h"
17
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"
23
24
#include "ExceptionBreakpoint.h"
25
#include "JSONUtils.h"
26
#include "LLDBUtils.h"
27
#include "VSCode.h"
28
29
namespace lldb_vscode {
30
31
void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
32
2
                       llvm::StringRef str) {
33
2
  if (LLVM_LIKELY(llvm::json::isUTF8(str)))
34
2
    obj.try_emplace(key, str.str());
35
0
  else
36
0
    obj.try_emplace(key, llvm::json::fixUTF8(str));
37
2
}
38
39
0
llvm::StringRef GetAsString(const llvm::json::Value &value) {
40
0
  if (auto s = value.getAsString())
41
0
    return *s;
42
0
  return llvm::StringRef();
43
0
}
44
45
// Gets a string from a JSON object using the key, or returns an empty string.
46
3
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
47
3
  if (llvm::Optional<llvm::StringRef> value = obj.getString(key))
48
3
    return *value;
49
0
  return llvm::StringRef();
50
3
}
51
52
0
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
53
0
  if (obj == nullptr)
54
0
    return llvm::StringRef();
55
0
  return GetString(*obj, key);
56
0
}
57
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
0
                     uint64_t fail_value) {
62
0
  if (auto value = obj.getInteger(key))
63
0
    return (uint64_t)*value;
64
0
  return fail_value;
65
0
}
66
67
uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
68
0
                     uint64_t fail_value) {
69
0
  if (obj == nullptr)
70
0
    return fail_value;
71
0
  return GetUnsigned(*obj, key, fail_value);
72
0
}
73
74
bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
75
1
                bool fail_value) {
76
1
  if (auto value = obj.getBoolean(key))
77
0
    return *value;
78
1
  if (auto value = obj.getInteger(key))
79
0
    return *value != 0;
80
1
  return fail_value;
81
1
}
82
83
bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
84
1
                bool fail_value) {
85
1
  if (obj == nullptr)
86
0
    return fail_value;
87
1
  return GetBoolean(*obj, key, fail_value);
88
1
}
89
90
int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
91
1
                  int64_t fail_value) {
92
1
  if (auto value = obj.getInteger(key))
93
1
    return *value;
94
0
  return fail_value;
95
1
}
96
97
int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
98
0
                  int64_t fail_value) {
99
0
  if (obj == nullptr)
100
0
    return fail_value;
101
0
  return GetSigned(*obj, key, fail_value);
102
0
}
103
104
0
bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
105
0
  return obj.find(key) != obj.end();
106
0
}
107
108
std::vector<std::string> GetStrings(const llvm::json::Object *obj,
109
0
                                    llvm::StringRef key) {
110
0
  std::vector<std::string> strs;
111
0
  auto json_array = obj->getArray(key);
112
0
  if (!json_array)
113
0
    return strs;
114
0
  for (const auto &value : *json_array) {
115
0
    switch (value.kind()) {
116
0
    case llvm::json::Value::String:
117
0
      strs.push_back(value.getAsString()->str());
118
0
      break;
119
0
    case llvm::json::Value::Number:
120
0
    case llvm::json::Value::Boolean:
121
0
      strs.push_back(llvm::to_string(value));
122
0
      break;
123
0
    case llvm::json::Value::Null:
124
0
    case llvm::json::Value::Object:
125
0
    case llvm::json::Value::Array:
126
0
      break;
127
0
    }
128
0
  }
129
0
  return strs;
130
0
}
131
132
void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
133
0
                    llvm::StringRef key) {
134
135
0
  llvm::StringRef value = v.GetValue();
136
0
  llvm::StringRef summary = v.GetSummary();
137
0
  llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
138
139
0
  std::string result;
140
0
  llvm::raw_string_ostream strm(result);
141
0
  if (!value.empty()) {
142
0
    strm << value;
143
0
    if (!summary.empty())
144
0
      strm << ' ' << summary;
145
0
  } else if (!summary.empty()) {
146
0
    strm << ' ' << summary;
147
0
  } else if (!type_name.empty()) {
148
0
    strm << type_name;
149
0
    lldb::addr_t address = v.GetLoadAddress();
150
0
    if (address != LLDB_INVALID_ADDRESS)
151
0
      strm << " @ " << llvm::format_hex(address, 0);
152
0
  }
153
0
  strm.flush();
154
0
  EmplaceSafeString(object, key, result);
155
0
}
156
157
void FillResponse(const llvm::json::Object &request,
158
1
                  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
1
  response.try_emplace("type", "response");
162
1
  response.try_emplace("seq", (int64_t)0);
163
1
  EmplaceSafeString(response, "command", GetString(request, "command"));
164
1
  const int64_t seq = GetSigned(request, "seq", 0);
165
1
  response.try_emplace("request_seq", seq);
166
1
  response.try_emplace("success", true);
167
1
}
168
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
0
                              int64_t namedVariables, bool expensive) {
237
0
  llvm::json::Object object;
238
0
  EmplaceSafeString(object, "name", name.str());
239
240
  // TODO: Support "arguments" scope. At the moment lldb-vscode includes the
241
  // arguments into the "locals" scope.
242
0
  if (variablesReference == VARREF_LOCALS) {
243
0
    object.try_emplace("presentationHint", "locals");
244
0
  } else if (variablesReference == VARREF_REGS) {
245
0
    object.try_emplace("presentationHint", "registers");
246
0
  }
247
248
0
  object.try_emplace("variablesReference", variablesReference);
249
0
  object.try_emplace("expensive", expensive);
250
0
  object.try_emplace("namedVariables", namedVariables);
251
0
  return llvm::json::Value(std::move(object));
252
0
}
253
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
0
                                   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
0
  llvm::json::Object object;
308
0
  if (!bp.IsValid())
309
0
    return llvm::json::Value(std::move(object));
310
311
0
  object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
312
0
  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.
317
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
0
  lldb::SBBreakpointLocation bp_loc;
322
0
  const auto num_locs = bp.GetNumLocations();
323
0
  for (size_t i = 0; i < num_locs; ++i) {
324
0
    bp_loc = bp.GetLocationAtIndex(i);
325
0
    if (bp_loc.IsResolved())
326
0
      break;
327
0
  }
328
  // If not locations are resolved, use the first location.
329
0
  if (!bp_loc.IsResolved())
330
0
    bp_loc = bp.GetLocationAtIndex(0);
331
0
  auto bp_addr = bp_loc.GetAddress();
332
333
0
  if (request_path)
334
0
    object.try_emplace("source", CreateSource(*request_path));
335
336
0
  if (bp_addr.IsValid()) {
337
0
    auto line_entry = bp_addr.GetLineEntry();
338
0
    const auto line = line_entry.GetLine();
339
0
    if (line != UINT32_MAX)
340
0
      object.try_emplace("line", line);
341
0
    object.try_emplace("source", CreateSource(line_entry));
342
0
  }
343
  // We try to add request_line as a fallback
344
0
  if (request_line)
345
0
    object.try_emplace("line", *request_line);
346
0
  return llvm::json::Value(std::move(object));
347
0
}
348
349
0
static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
350
0
  uint64_t debug_info_size = 0;
351
0
  llvm::StringRef section_name(section.GetName());
352
0
  if (section_name.startswith(".debug") || section_name.startswith("__debug") ||
353
0
      section_name.startswith(".apple") || section_name.startswith("__apple"))
354
0
    debug_info_size += section.GetFileByteSize();
355
0
  size_t num_sub_sections = section.GetNumSubSections();
356
0
  for (size_t i = 0; i < num_sub_sections; i++) {
357
0
    debug_info_size +=
358
0
        GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
359
0
  }
360
0
  return debug_info_size;
361
0
}
362
363
0
static uint64_t GetDebugInfoSize(lldb::SBModule module) {
364
0
  uint64_t debug_info_size = 0;
365
0
  size_t num_sections = module.GetNumSections();
366
0
  for (size_t i = 0; i < num_sections; i++) {
367
0
    debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
368
0
  }
369
0
  return debug_info_size;
370
0
}
371
372
0
static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
373
0
  std::ostringstream oss;
374
0
  oss << std::fixed << std::setprecision(1);
375
0
  if (debug_info < 1024) {
376
0
    oss << debug_info << "B";
377
0
  } else if (debug_info < 1024 * 1024) {
378
0
    double kb = double(debug_info) / 1024.0;
379
0
    oss << kb << "KB";
380
0
  } else if (debug_info < 1024 * 1024 * 1024) {
381
0
    double mb = double(debug_info) / (1024.0 * 1024.0);
382
0
    oss << mb << "MB";
383
0
  } else {
384
0
    double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
385
0
    oss << gb << "GB";
386
0
  }
387
0
  return oss.str();
388
0
}
389
0
llvm::json::Value CreateModule(lldb::SBModule &module) {
390
0
  llvm::json::Object object;
391
0
  if (!module.IsValid())
392
0
    return llvm::json::Value(std::move(object));
393
0
  const char *uuid = module.GetUUIDString();
394
0
  object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
395
0
  object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
396
0
  char module_path_arr[PATH_MAX];
397
0
  module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
398
0
  std::string module_path(module_path_arr);
399
0
  object.try_emplace("path", module_path);
400
0
  if (module.GetNumCompileUnits() > 0) {
401
0
    std::string symbol_str = "Symbols loaded.";
402
0
    std::string debug_info_size;
403
0
    uint64_t debug_info = GetDebugInfoSize(module);
404
0
    if (debug_info > 0) {
405
0
      debug_info_size = ConvertDebugInfoSizeToString(debug_info);
406
0
    }
407
0
    object.try_emplace("symbolStatus", symbol_str);
408
0
    object.try_emplace("debugInfoSize", debug_info_size);
409
0
    char symbol_path_arr[PATH_MAX];
410
0
    module.GetSymbolFileSpec().GetPath(symbol_path_arr,
411
0
                                       sizeof(symbol_path_arr));
412
0
    std::string symbol_path(symbol_path_arr);
413
0
    object.try_emplace("symbolFilePath", symbol_path);
414
0
  } else {
415
0
    object.try_emplace("symbolStatus", "Symbols not found.");
416
0
  }
417
0
  std::string loaded_addr = std::to_string(
418
0
      module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target));
419
0
  object.try_emplace("addressRange", loaded_addr);
420
0
  std::string version_str;
421
0
  uint32_t version_nums[3];
422
0
  uint32_t num_versions =
423
0
      module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
424
0
  for (uint32_t i = 0; i < num_versions; ++i) {
425
0
    if (!version_str.empty())
426
0
      version_str += ".";
427
0
    version_str += std::to_string(version_nums[i]);
428
0
  }
429
0
  if (!version_str.empty())
430
0
    object.try_emplace("version", version_str);
431
0
  return llvm::json::Value(std::move(object));
432
0
}
433
434
void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
435
                      llvm::Optional<llvm::StringRef> request_path,
436
0
                      llvm::Optional<uint32_t> request_line) {
437
0
  breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
438
0
}
439
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
1
llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
479
1
  llvm::json::Object event;
480
1
  event.try_emplace("seq", 0);
481
1
  event.try_emplace("type", "event");
482
1
  EmplaceSafeString(event, "event", event_name);
483
1
  return event;
484
1
}
485
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
0
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
510
0
  llvm::json::Object object;
511
0
  EmplaceSafeString(object, "filter", bp.filter);
512
0
  EmplaceSafeString(object, "label", bp.label);
513
0
  object.try_emplace("default", bp.default_value);
514
0
  return llvm::json::Value(std::move(object));
515
0
}
516
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
0
llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
583
0
  llvm::json::Object object;
584
0
  lldb::SBFileSpec file = line_entry.GetFileSpec();
585
0
  if (file.IsValid()) {
586
0
    const char *name = file.GetFilename();
587
0
    if (name)
588
0
      EmplaceSafeString(object, "name", name);
589
0
    char path[PATH_MAX] = "";
590
0
    file.GetPath(path, sizeof(path));
591
0
    if (path[0]) {
592
0
      EmplaceSafeString(object, "path", std::string(path));
593
0
    }
594
0
  }
595
0
  return llvm::json::Value(std::move(object));
596
0
}
597
598
0
llvm::json::Value CreateSource(llvm::StringRef source_path) {
599
0
  llvm::json::Object source;
600
0
  llvm::StringRef name = llvm::sys::path::filename(source_path);
601
0
  EmplaceSafeString(source, "name", name);
602
0
  EmplaceSafeString(source, "path", source_path);
603
0
  return llvm::json::Value(std::move(source));
604
0
}
605
606
0
llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
607
0
  disasm_line = 0;
608
0
  auto line_entry = frame.GetLineEntry();
609
0
  if (line_entry.GetFileSpec().IsValid())
610
0
    return CreateSource(line_entry);
611
612
0
  llvm::json::Object object;
613
0
  const auto pc = frame.GetPC();
614
615
0
  lldb::SBInstructionList insts;
616
0
  lldb::SBFunction function = frame.GetFunction();
617
0
  lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
618
0
  if (function.IsValid()) {
619
0
    low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
620
0
    auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
621
0
    if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
622
      // We have this disassembly cached already, return the existing
623
      // sourceReference
624
0
      object.try_emplace("sourceReference", addr_srcref->second);
625
0
      disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
626
0
    } else {
627
0
      insts = function.GetInstructions(g_vsc.target);
628
0
    }
629
0
  } else {
630
0
    lldb::SBSymbol symbol = frame.GetSymbol();
631
0
    if (symbol.IsValid()) {
632
0
      low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
633
0
      auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
634
0
      if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
635
        // We have this disassembly cached already, return the existing
636
        // sourceReference
637
0
        object.try_emplace("sourceReference", addr_srcref->second);
638
0
        disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
639
0
      } else {
640
0
        insts = symbol.GetInstructions(g_vsc.target);
641
0
      }
642
0
    }
643
0
  }
644
0
  const auto num_insts = insts.GetSize();
645
0
  if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
646
0
    EmplaceSafeString(object, "name", frame.GetFunctionName());
647
0
    SourceReference source;
648
0
    llvm::raw_string_ostream src_strm(source.content);
649
0
    std::string line;
650
0
    for (size_t i = 0; i < num_insts; ++i) {
651
0
      lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
652
0
      const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
653
0
      const char *m = inst.GetMnemonic(g_vsc.target);
654
0
      const char *o = inst.GetOperands(g_vsc.target);
655
0
      const char *c = inst.GetComment(g_vsc.target);
656
0
      if (pc == inst_addr)
657
0
        disasm_line = i + 1;
658
0
      const auto inst_offset = inst_addr - low_pc;
659
0
      int spaces = 0;
660
0
      if (inst_offset < 10)
661
0
        spaces = 3;
662
0
      else if (inst_offset < 100)
663
0
        spaces = 2;
664
0
      else if (inst_offset < 1000)
665
0
        spaces = 1;
666
0
      line.clear();
667
0
      llvm::raw_string_ostream line_strm(line);
668
0
      line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
669
0
                                 inst_offset, llvm::fmt_repeat(' ', spaces), m,
670
0
                                 o);
671
672
      // If there is a comment append it starting at column 60 or after one
673
      // space past the last char
674
0
      const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
675
0
      if (c && c[0]) {
676
0
        if (line.size() < comment_row)
677
0
          line_strm.indent(comment_row - line_strm.str().size());
678
0
        line_strm << " # " << c;
679
0
      }
680
0
      src_strm << line_strm.str() << "\n";
681
0
      source.addr_to_line[inst_addr] = i + 1;
682
0
    }
683
    // Flush the source stream
684
0
    src_strm.str();
685
0
    auto sourceReference = VSCode::GetNextSourceReference();
686
0
    g_vsc.source_map[sourceReference] = std::move(source);
687
0
    g_vsc.addr_to_source_ref[low_pc] = sourceReference;
688
0
    object.try_emplace("sourceReference", sourceReference);
689
0
  }
690
0
  return llvm::json::Value(std::move(object));
691
0
}
692
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
0
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
750
0
  llvm::json::Object object;
751
0
  int64_t frame_id = MakeVSCodeFrameID(frame);
752
0
  object.try_emplace("id", frame_id);
753
0
  EmplaceSafeString(object, "name", frame.GetFunctionName());
754
0
  int64_t disasm_line = 0;
755
0
  object.try_emplace("source", CreateSource(frame, disasm_line));
756
757
0
  auto line_entry = frame.GetLineEntry();
758
0
  if (disasm_line > 0) {
759
0
    object.try_emplace("line", disasm_line);
760
0
  } else {
761
0
    auto line = line_entry.GetLine();
762
0
    if (line == UINT32_MAX)
763
0
      line = 0;
764
0
    object.try_emplace("line", line);
765
0
  }
766
0
  object.try_emplace("column", line_entry.GetColumn());
767
0
  return llvm::json::Value(std::move(object));
768
0
}
769
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
0
llvm::json::Value CreateThread(lldb::SBThread &thread) {
786
0
  llvm::json::Object object;
787
0
  object.try_emplace("id", (int64_t)thread.GetThreadID());
788
0
  char thread_str[64];
789
0
  snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
790
0
  const char *name = thread.GetName();
791
0
  if (name) {
792
0
    std::string thread_with_name(thread_str);
793
0
    thread_with_name += ' ';
794
0
    thread_with_name += name;
795
0
    EmplaceSafeString(object, "name", thread_with_name);
796
0
  } else {
797
0
    EmplaceSafeString(object, "name", std::string(thread_str));
798
0
  }
799
0
  return llvm::json::Value(std::move(object));
800
0
}
801
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
0
                                      uint32_t stop_id) {
861
0
  llvm::json::Object event(CreateEventObject("stopped"));
862
0
  llvm::json::Object body;
863
0
  switch (thread.GetStopReason()) {
864
0
  case lldb::eStopReasonTrace:
865
0
  case lldb::eStopReasonPlanComplete:
866
0
    body.try_emplace("reason", "step");
867
0
    break;
868
0
  case lldb::eStopReasonBreakpoint: {
869
0
    ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
870
0
    if (exc_bp) {
871
0
      body.try_emplace("reason", "exception");
872
0
      EmplaceSafeString(body, "description", exc_bp->label);
873
0
    } else {
874
0
      body.try_emplace("reason", "breakpoint");
875
0
      char desc_str[64];
876
0
      uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
877
0
      uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
878
0
      snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
879
0
               bp_id, bp_loc_id);
880
0
      EmplaceSafeString(body, "description", desc_str);
881
0
    }
882
0
  } break;
883
0
  case lldb::eStopReasonWatchpoint:
884
0
  case lldb::eStopReasonInstrumentation:
885
0
    body.try_emplace("reason", "breakpoint");
886
0
    break;
887
0
  case lldb::eStopReasonProcessorTrace:
888
0
    body.try_emplace("reason", "processor trace");
889
0
    break;
890
0
  case lldb::eStopReasonSignal:
891
0
  case lldb::eStopReasonException:
892
0
    body.try_emplace("reason", "exception");
893
0
    break;
894
0
  case lldb::eStopReasonExec:
895
0
    body.try_emplace("reason", "entry");
896
0
    break;
897
0
  case lldb::eStopReasonFork:
898
0
    body.try_emplace("reason", "fork");
899
0
    break;
900
0
  case lldb::eStopReasonVFork:
901
0
    body.try_emplace("reason", "vfork");
902
0
    break;
903
0
  case lldb::eStopReasonVForkDone:
904
0
    body.try_emplace("reason", "vforkdone");
905
0
    break;
906
0
  case lldb::eStopReasonThreadExiting:
907
0
  case lldb::eStopReasonInvalid:
908
0
  case lldb::eStopReasonNone:
909
0
    break;
910
0
  }
911
0
  if (stop_id == 0)
912
0
    body.try_emplace("reason", "entry");
913
0
  const lldb::tid_t tid = thread.GetThreadID();
914
0
  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
0
  if (ObjectContainsKey(body, "description")) {
919
0
    char description[1024];
920
0
    if (thread.GetStopDescription(description, sizeof(description))) {
921
0
      EmplaceSafeString(body, "description", std::string(description));
922
0
    }
923
0
  }
924
0
  if (tid == g_vsc.focus_tid) {
925
0
    body.try_emplace("threadCausedFocus", true);
926
0
  }
927
0
  body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
928
0
  body.try_emplace("allThreadsStopped", true);
929
0
  event.try_emplace("body", std::move(body));
930
0
  return llvm::json::Value(std::move(event));
931
0
}
932
933
0
const char *GetNonNullVariableName(lldb::SBValue v) {
934
0
  const char *name = v.GetName();
935
0
  return name ? name : "<null>";
936
0
}
937
938
std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
939
0
                                               bool is_name_duplicated) {
940
0
  lldb::SBStream name_builder;
941
0
  name_builder.Print(GetNonNullVariableName(v));
942
0
  if (is_name_duplicated) {
943
0
    lldb::SBDeclaration declaration = v.GetDeclaration();
944
0
    const char *file_name = declaration.GetFileSpec().GetFilename();
945
0
    const uint32_t line = declaration.GetLine();
946
947
0
    if (file_name != nullptr && line > 0)
948
0
      name_builder.Printf(" @ %s:%u", file_name, line);
949
0
    else if (const char *location = v.GetLocation())
950
0
      name_builder.Printf(" @ %s", location);
951
0
  }
952
0
  return name_builder.GetData();
953
0
}
954
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
0
                                 bool is_name_duplicated) {
1020
0
  llvm::json::Object object;
1021
0
  EmplaceSafeString(object, "name",
1022
0
                    CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
1023
1024
0
  if (format_hex)
1025
0
    v.SetFormat(lldb::eFormatHex);
1026
0
  SetValueForKey(v, object, "value");
1027
0
  auto type_cstr = v.GetType().GetDisplayTypeName();
1028
0
  EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
1029
0
  if (varID != INT64_MAX)
1030
0
    object.try_emplace("id", varID);
1031
0
  if (v.MightHaveChildren())
1032
0
    object.try_emplace("variablesReference", variablesReference);
1033
0
  else
1034
0
    object.try_emplace("variablesReference", (int64_t)0);
1035
0
  lldb::SBStream evaluateStream;
1036
0
  v.GetExpressionPath(evaluateStream);
1037
0
  const char *evaluateName = evaluateStream.GetData();
1038
0
  if (evaluateName && evaluateName[0])
1039
0
    EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
1040
0
  return llvm::json::Value(std::move(object));
1041
0
}
1042
1043
0
llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
1044
0
  llvm::json::Object object;
1045
0
  char unit_path_arr[PATH_MAX];
1046
0
  unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
1047
0
  std::string unit_path(unit_path_arr);
1048
0
  object.try_emplace("compileUnitPath", unit_path);
1049
0
  return llvm::json::Value(std::move(object));
1050
0
}
1051
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
0
                                  llvm::StringRef comm_file) {
1058
0
  llvm::json::Object reverse_request;
1059
0
  reverse_request.try_emplace("type", "request");
1060
0
  reverse_request.try_emplace("command", "runInTerminal");
1061
1062
0
  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
0
  run_in_terminal_args.try_emplace("kind", "integrated");
1066
1067
0
  auto launch_request_arguments = launch_request.getObject("arguments");
1068
  // The program path must be the first entry in the "args" field
1069
0
  std::vector<std::string> args = {
1070
0
      debug_adaptor_path.str(), "--comm-file", comm_file.str(),
1071
0
      "--launch-target", GetString(launch_request_arguments, "program").str()};
1072
0
  std::vector<std::string> target_args =
1073
0
      GetStrings(launch_request_arguments, "args");
1074
0
  args.insert(args.end(), target_args.begin(), target_args.end());
1075
0
  run_in_terminal_args.try_emplace("args", args);
1076
1077
0
  const auto cwd = GetString(launch_request_arguments, "cwd");
1078
0
  if (!cwd.empty())
1079
0
    run_in_terminal_args.try_emplace("cwd", cwd);
1080
1081
  // We need to convert the input list of environments variables into a
1082
  // dictionary
1083
0
  std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1084
0
  llvm::json::Object environment;
1085
0
  for (const std::string &env : envs) {
1086
0
    size_t index = env.find('=');
1087
0
    environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1088
0
  }
1089
0
  run_in_terminal_args.try_emplace("env",
1090
0
                                   llvm::json::Value(std::move(environment)));
1091
1092
0
  reverse_request.try_emplace(
1093
0
      "arguments", llvm::json::Value(std::move(run_in_terminal_args)));
1094
0
  return reverse_request;
1095
0
}
1096
1097
0
std::string JSONToString(const llvm::json::Value &json) {
1098
0
  std::string data;
1099
0
  llvm::raw_string_ostream os(data);
1100
0
  os << json;
1101
0
  os.flush();
1102
0
  return data;
1103
0
}
1104
1105
} // namespace lldb_vscode