Coverage Report

Created: 2023-09-30 09:22

/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 <optional>
12
#include <sstream>
13
#include <string.h>
14
15
#include "llvm/Support/FormatAdapters.h"
16
#include "llvm/Support/Path.h"
17
#include "llvm/Support/ScopedPrinter.h"
18
19
#include "lldb/API/SBBreakpoint.h"
20
#include "lldb/API/SBBreakpointLocation.h"
21
#include "lldb/API/SBDeclaration.h"
22
#include "lldb/API/SBStringList.h"
23
#include "lldb/API/SBStructuredData.h"
24
#include "lldb/API/SBValue.h"
25
#include "lldb/Host/PosixApi.h"
26
27
#include "ExceptionBreakpoint.h"
28
#include "JSONUtils.h"
29
#include "LLDBUtils.h"
30
#include "VSCode.h"
31
32
namespace lldb_vscode {
33
34
void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
35
84
                       llvm::StringRef str) {
36
84
  if (LLVM_LIKELY(llvm::json::isUTF8(str)))
37
84
    obj.try_emplace(key, str.str());
38
0
  else
39
0
    obj.try_emplace(key, llvm::json::fixUTF8(str));
40
84
}
41
42
0
llvm::StringRef GetAsString(const llvm::json::Value &value) {
43
0
  if (auto s = value.getAsString())
44
0
    return *s;
45
0
  return llvm::StringRef();
46
0
}
47
48
// Gets a string from a JSON object using the key, or returns an empty string.
49
69
llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
50
69
  if (std::optional<llvm::StringRef> value = obj.getString(key))
51
49
    return *value;
52
20
  return llvm::StringRef();
53
69
}
54
55
9
llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
56
9
  if (obj == nullptr)
57
0
    return llvm::StringRef();
58
9
  return GetString(*obj, key);
59
9
}
60
61
// Gets an unsigned integer from a JSON object using the key, or returns the
62
// specified fail value.
63
uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
64
9
                     uint64_t fail_value) {
65
9
  if (auto value = obj.getInteger(key))
66
5
    return (uint64_t)*value;
67
4
  return fail_value;
68
9
}
69
70
uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
71
6
                     uint64_t fail_value) {
72
6
  if (obj == nullptr)
73
0
    return fail_value;
74
6
  return GetUnsigned(*obj, key, fail_value);
75
6
}
76
77
bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
78
30
                bool fail_value) {
79
30
  if (auto value = obj.getBoolean(key))
80
13
    return *value;
81
17
  if (auto value = obj.getInteger(key))
82
0
    return *value != 0;
83
17
  return fail_value;
84
17
}
85
86
bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
87
30
                bool fail_value) {
88
30
  if (obj == nullptr)
89
0
    return fail_value;
90
30
  return GetBoolean(*obj, key, fail_value);
91
30
}
92
93
int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
94
16
                  int64_t fail_value) {
95
16
  if (auto value = obj.getInteger(key))
96
16
    return *value;
97
0
  return fail_value;
98
16
}
99
100
int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
101
0
                  int64_t fail_value) {
102
0
  if (obj == nullptr)
103
0
    return fail_value;
104
0
  return GetSigned(*obj, key, fail_value);
105
0
}
106
107
4
bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
108
4
  return obj.find(key) != obj.end();
109
4
}
110
111
std::vector<std::string> GetStrings(const llvm::json::Object *obj,
112
25
                                    llvm::StringRef key) {
113
25
  std::vector<std::string> strs;
114
25
  auto json_array = obj->getArray(key);
115
25
  if (!json_array)
116
21
    return strs;
117
25
  
for (const auto &value : *json_array)4
{
118
25
    switch (value.kind()) {
119
25
    case llvm::json::Value::String:
120
25
      strs.push_back(value.getAsString()->str());
121
25
      break;
122
0
    case llvm::json::Value::Number:
123
0
    case llvm::json::Value::Boolean:
124
0
      strs.push_back(llvm::to_string(value));
125
0
      break;
126
0
    case llvm::json::Value::Null:
127
0
    case llvm::json::Value::Object:
128
0
    case llvm::json::Value::Array:
129
0
      break;
130
25
    }
131
25
  }
132
4
  return strs;
133
4
}
134
135
/// Create a short summary for a container that contains the summary of its
136
/// first children, so that the user can get a glimpse of its contents at a
137
/// glance.
138
static std::optional<std::string>
139
0
TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
140
  // We gate this feature because it performs GetNumChildren(), which can
141
  // cause performance issues because LLDB needs to complete possibly huge
142
  // types.
143
0
  if (!g_vsc.enable_auto_variable_summaries)
144
0
    return std::nullopt;
145
146
0
  if (!v.MightHaveChildren())
147
0
    return std::nullopt;
148
  /// As this operation can be potentially slow, we limit the total time spent
149
  /// fetching children to a few ms.
150
0
  const auto max_evaluation_time = std::chrono::milliseconds(10);
151
  /// We don't want to generate a extremely long summary string, so we limit its
152
  /// length.
153
0
  const size_t max_length = 32;
154
155
0
  auto start = std::chrono::steady_clock::now();
156
0
  std::string summary;
157
0
  llvm::raw_string_ostream os(summary);
158
0
  os << "{";
159
160
0
  llvm::StringRef separator = "";
161
162
0
  for (size_t i = 0, e = v.GetNumChildren(); i < e; ++i) {
163
    // If we reached the time limit or exceeded the number of characters, we
164
    // dump `...` to signal that there are more elements in the collection.
165
0
    if (summary.size() > max_length ||
166
0
        (std::chrono::steady_clock::now() - start) > max_evaluation_time) {
167
0
      os << separator << "...";
168
0
      break;
169
0
    }
170
0
    lldb::SBValue child = v.GetChildAtIndex(i);
171
172
0
    if (llvm::StringRef name = child.GetName(); !name.empty()) {
173
0
      llvm::StringRef value;
174
0
      if (llvm::StringRef summary = child.GetSummary(); !summary.empty())
175
0
        value = summary;
176
0
      else
177
0
        value = child.GetValue();
178
179
0
      if (!value.empty()) {
180
        // If the child is an indexed entry, we don't show its index to save
181
        // characters.
182
0
        if (name.starts_with("["))
183
0
          os << separator << value;
184
0
        else
185
0
          os << separator << name << ":" << value;
186
0
        separator = ", ";
187
0
      }
188
0
    }
189
0
  }
190
0
  os << "}";
191
192
0
  if (summary == "{...}" || summary == "{}")
193
0
    return std::nullopt;
194
0
  return summary;
195
0
}
196
197
/// Try to create a summary string for the given value that doesn't have a
198
/// summary of its own.
199
1
static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue value) {
200
1
  if (!g_vsc.enable_auto_variable_summaries)
201
1
    return std::nullopt;
202
203
  // We use the dereferenced value for generating the summary.
204
0
  if (value.GetType().IsPointerType() || value.GetType().IsReferenceType())
205
0
    value = value.Dereference();
206
207
  // We only support auto summaries for containers.
208
0
  return TryCreateAutoSummaryForContainer(value);
209
1
}
210
211
void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
212
1
                    llvm::StringRef key) {
213
1
  std::string result;
214
1
  llvm::raw_string_ostream strm(result);
215
216
1
  lldb::SBError error = v.GetError();
217
1
  if (!error.Success()) {
218
0
    strm << "<error: " << error.GetCString() << ">";
219
1
  } else {
220
1
    llvm::StringRef value = v.GetValue();
221
1
    llvm::StringRef nonAutoSummary = v.GetSummary();
222
1
    std::optional<std::string> summary = !nonAutoSummary.empty()
223
1
                                             ? 
nonAutoSummary.str()0
224
1
                                             : TryCreateAutoSummary(v);
225
1
    if (!value.empty()) {
226
1
      strm << value;
227
1
      if (summary)
228
0
        strm << ' ' << *summary;
229
1
    } else 
if (0
summary0
) {
230
0
      strm << *summary;
231
232
      // As last resort, we print its type and address if available.
233
0
    } else {
234
0
      if (llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
235
0
          !type_name.empty()) {
236
0
        strm << type_name;
237
0
        lldb::addr_t address = v.GetLoadAddress();
238
0
        if (address != LLDB_INVALID_ADDRESS)
239
0
          strm << " @ " << llvm::format_hex(address, 0);
240
0
      }
241
0
    }
242
1
  }
243
1
  EmplaceSafeString(object, key, result);
244
1
}
245
246
void FillResponse(const llvm::json::Object &request,
247
15
                  llvm::json::Object &response) {
248
  // Fill in all of the needed response fields to a "request" and set "success"
249
  // to true by default.
250
15
  response.try_emplace("type", "response");
251
15
  response.try_emplace("seq", (int64_t)0);
252
15
  EmplaceSafeString(response, "command", GetString(request, "command"));
253
15
  const int64_t seq = GetSigned(request, "seq", 0);
254
15
  response.try_emplace("request_seq", seq);
255
15
  response.try_emplace("success", true);
256
15
}
257
258
// "Scope": {
259
//   "type": "object",
260
//   "description": "A Scope is a named container for variables. Optionally
261
//                   a scope can map to a source or a range within a source.",
262
//   "properties": {
263
//     "name": {
264
//       "type": "string",
265
//       "description": "Name of the scope such as 'Arguments', 'Locals'."
266
//     },
267
//     "presentationHint": {
268
//       "type": "string",
269
//       "description": "An optional hint for how to present this scope in the
270
//                       UI. If this attribute is missing, the scope is shown
271
//                       with a generic UI.",
272
//       "_enum": [ "arguments", "locals", "registers" ],
273
//     },
274
//     "variablesReference": {
275
//       "type": "integer",
276
//       "description": "The variables of this scope can be retrieved by
277
//                       passing the value of variablesReference to the
278
//                       VariablesRequest."
279
//     },
280
//     "namedVariables": {
281
//       "type": "integer",
282
//       "description": "The number of named variables in this scope. The
283
//                       client can use this optional information to present
284
//                       the variables in a paged UI and fetch them in chunks."
285
//     },
286
//     "indexedVariables": {
287
//       "type": "integer",
288
//       "description": "The number of indexed variables in this scope. The
289
//                       client can use this optional information to present
290
//                       the variables in a paged UI and fetch them in chunks."
291
//     },
292
//     "expensive": {
293
//       "type": "boolean",
294
//       "description": "If true, the number of variables in this scope is
295
//                       large or expensive to retrieve."
296
//     },
297
//     "source": {
298
//       "$ref": "#/definitions/Source",
299
//       "description": "Optional source for this scope."
300
//     },
301
//     "line": {
302
//       "type": "integer",
303
//       "description": "Optional start line of the range covered by this
304
//                       scope."
305
//     },
306
//     "column": {
307
//       "type": "integer",
308
//       "description": "Optional start column of the range covered by this
309
//                       scope."
310
//     },
311
//     "endLine": {
312
//       "type": "integer",
313
//       "description": "Optional end line of the range covered by this scope."
314
//     },
315
//     "endColumn": {
316
//       "type": "integer",
317
//       "description": "Optional end column of the range covered by this
318
//                       scope."
319
//     }
320
//   },
321
//   "required": [ "name", "variablesReference", "expensive" ]
322
// }
323
llvm::json::Value CreateScope(const llvm::StringRef name,
324
                              int64_t variablesReference,
325
0
                              int64_t namedVariables, bool expensive) {
326
0
  llvm::json::Object object;
327
0
  EmplaceSafeString(object, "name", name.str());
328
329
  // TODO: Support "arguments" scope. At the moment lldb-vscode includes the
330
  // arguments into the "locals" scope.
331
0
  if (variablesReference == VARREF_LOCALS) {
332
0
    object.try_emplace("presentationHint", "locals");
333
0
  } else if (variablesReference == VARREF_REGS) {
334
0
    object.try_emplace("presentationHint", "registers");
335
0
  }
336
337
0
  object.try_emplace("variablesReference", variablesReference);
338
0
  object.try_emplace("expensive", expensive);
339
0
  object.try_emplace("namedVariables", namedVariables);
340
0
  return llvm::json::Value(std::move(object));
341
0
}
342
343
// "Breakpoint": {
344
//   "type": "object",
345
//   "description": "Information about a Breakpoint created in setBreakpoints
346
//                   or setFunctionBreakpoints.",
347
//   "properties": {
348
//     "id": {
349
//       "type": "integer",
350
//       "description": "An optional unique identifier for the breakpoint."
351
//     },
352
//     "verified": {
353
//       "type": "boolean",
354
//       "description": "If true breakpoint could be set (but not necessarily
355
//                       at the desired location)."
356
//     },
357
//     "message": {
358
//       "type": "string",
359
//       "description": "An optional message about the state of the breakpoint.
360
//                       This is shown to the user and can be used to explain
361
//                       why a breakpoint could not be verified."
362
//     },
363
//     "source": {
364
//       "$ref": "#/definitions/Source",
365
//       "description": "The source where the breakpoint is located."
366
//     },
367
//     "line": {
368
//       "type": "integer",
369
//       "description": "The start line of the actual range covered by the
370
//                       breakpoint."
371
//     },
372
//     "column": {
373
//       "type": "integer",
374
//       "description": "An optional start column of the actual range covered
375
//                       by the breakpoint."
376
//     },
377
//     "endLine": {
378
//       "type": "integer",
379
//       "description": "An optional end line of the actual range covered by
380
//                       the breakpoint."
381
//     },
382
//     "endColumn": {
383
//       "type": "integer",
384
//       "description": "An optional end column of the actual range covered by
385
//                       the breakpoint. If no end line is given, then the end
386
//                       column is assumed to be in the start line."
387
//     }
388
//   },
389
//   "required": [ "verified" ]
390
// }
391
llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
392
                                   std::optional<llvm::StringRef> request_path,
393
                                   std::optional<uint32_t> request_line,
394
1
                                   std::optional<uint32_t> request_column) {
395
  // Each breakpoint location is treated as a separate breakpoint for VS code.
396
  // They don't have the notion of a single breakpoint with multiple locations.
397
1
  llvm::json::Object object;
398
1
  if (!bp.IsValid())
399
0
    return llvm::json::Value(std::move(object));
400
401
1
  object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
402
1
  object.try_emplace("id", bp.GetID());
403
  // VS Code DAP doesn't currently allow one breakpoint to have multiple
404
  // locations so we just report the first one. If we report all locations
405
  // then the IDE starts showing the wrong line numbers and locations for
406
  // other source file and line breakpoints in the same file.
407
408
  // Below we search for the first resolved location in a breakpoint and report
409
  // this as the breakpoint location since it will have a complete location
410
  // that is at least loaded in the current process.
411
1
  lldb::SBBreakpointLocation bp_loc;
412
1
  const auto num_locs = bp.GetNumLocations();
413
1
  for (size_t i = 0; i < num_locs; 
++i0
) {
414
1
    bp_loc = bp.GetLocationAtIndex(i);
415
1
    if (bp_loc.IsResolved())
416
1
      break;
417
1
  }
418
  // If not locations are resolved, use the first location.
419
1
  if (!bp_loc.IsResolved())
420
0
    bp_loc = bp.GetLocationAtIndex(0);
421
1
  auto bp_addr = bp_loc.GetAddress();
422
423
1
  if (request_path)
424
1
    object.try_emplace("source", CreateSource(*request_path));
425
426
1
  if (bp_addr.IsValid()) {
427
1
    std::string formatted_addr =
428
1
        "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(g_vsc.target));
429
1
    object.try_emplace("instructionReference", formatted_addr);
430
1
    auto line_entry = bp_addr.GetLineEntry();
431
1
    const auto line = line_entry.GetLine();
432
1
    if (line != UINT32_MAX)
433
1
      object.try_emplace("line", line);
434
1
    const auto column = line_entry.GetColumn();
435
1
    if (column != 0)
436
1
      object.try_emplace("column", column);
437
1
    object.try_emplace("source", CreateSource(line_entry));
438
1
  }
439
  // We try to add request_line as a fallback
440
1
  if (request_line)
441
1
    object.try_emplace("line", *request_line);
442
1
  if (request_column)
443
0
    object.try_emplace("column", *request_column);
444
1
  return llvm::json::Value(std::move(object));
445
1
}
446
447
0
static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
448
0
  uint64_t debug_info_size = 0;
449
0
  llvm::StringRef section_name(section.GetName());
450
0
  if (section_name.startswith(".debug") || section_name.startswith("__debug") ||
451
0
      section_name.startswith(".apple") || section_name.startswith("__apple"))
452
0
    debug_info_size += section.GetFileByteSize();
453
0
  size_t num_sub_sections = section.GetNumSubSections();
454
0
  for (size_t i = 0; i < num_sub_sections; i++) {
455
0
    debug_info_size +=
456
0
        GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
457
0
  }
458
0
  return debug_info_size;
459
0
}
460
461
0
static uint64_t GetDebugInfoSize(lldb::SBModule module) {
462
0
  uint64_t debug_info_size = 0;
463
0
  size_t num_sections = module.GetNumSections();
464
0
  for (size_t i = 0; i < num_sections; i++) {
465
0
    debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
466
0
  }
467
0
  return debug_info_size;
468
0
}
469
470
0
static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
471
0
  std::ostringstream oss;
472
0
  oss << std::fixed << std::setprecision(1);
473
0
  if (debug_info < 1024) {
474
0
    oss << debug_info << "B";
475
0
  } else if (debug_info < 1024 * 1024) {
476
0
    double kb = double(debug_info) / 1024.0;
477
0
    oss << kb << "KB";
478
0
  } else if (debug_info < 1024 * 1024 * 1024) {
479
0
    double mb = double(debug_info) / (1024.0 * 1024.0);
480
0
    oss << mb << "MB";
481
0
  } else {
482
0
    double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
483
0
    oss << gb << "GB";
484
0
  }
485
0
  return oss.str();
486
0
}
487
0
llvm::json::Value CreateModule(lldb::SBModule &module) {
488
0
  llvm::json::Object object;
489
0
  if (!module.IsValid())
490
0
    return llvm::json::Value(std::move(object));
491
0
  const char *uuid = module.GetUUIDString();
492
0
  object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
493
0
  object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
494
0
  char module_path_arr[PATH_MAX];
495
0
  module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
496
0
  std::string module_path(module_path_arr);
497
0
  object.try_emplace("path", module_path);
498
0
  if (module.GetNumCompileUnits() > 0) {
499
0
    std::string symbol_str = "Symbols loaded.";
500
0
    std::string debug_info_size;
501
0
    uint64_t debug_info = GetDebugInfoSize(module);
502
0
    if (debug_info > 0) {
503
0
      debug_info_size = ConvertDebugInfoSizeToString(debug_info);
504
0
    }
505
0
    object.try_emplace("symbolStatus", symbol_str);
506
0
    object.try_emplace("debugInfoSize", debug_info_size);
507
0
    char symbol_path_arr[PATH_MAX];
508
0
    module.GetSymbolFileSpec().GetPath(symbol_path_arr,
509
0
                                       sizeof(symbol_path_arr));
510
0
    std::string symbol_path(symbol_path_arr);
511
0
    object.try_emplace("symbolFilePath", symbol_path);
512
0
  } else {
513
0
    object.try_emplace("symbolStatus", "Symbols not found.");
514
0
  }
515
0
  std::string loaded_addr = std::to_string(
516
0
      module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target));
517
0
  object.try_emplace("addressRange", loaded_addr);
518
0
  std::string version_str;
519
0
  uint32_t version_nums[3];
520
0
  uint32_t num_versions =
521
0
      module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
522
0
  for (uint32_t i = 0; i < num_versions; ++i) {
523
0
    if (!version_str.empty())
524
0
      version_str += ".";
525
0
    version_str += std::to_string(version_nums[i]);
526
0
  }
527
0
  if (!version_str.empty())
528
0
    object.try_emplace("version", version_str);
529
0
  return llvm::json::Value(std::move(object));
530
0
}
531
532
void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
533
                      std::optional<llvm::StringRef> request_path,
534
1
                      std::optional<uint32_t> request_line) {
535
1
  breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
536
1
}
537
538
// "Event": {
539
//   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
540
//     "type": "object",
541
//     "description": "Server-initiated event.",
542
//     "properties": {
543
//       "type": {
544
//         "type": "string",
545
//         "enum": [ "event" ]
546
//       },
547
//       "event": {
548
//         "type": "string",
549
//         "description": "Type of event."
550
//       },
551
//       "body": {
552
//         "type": [ "array", "boolean", "integer", "null", "number" ,
553
//                   "object", "string" ],
554
//         "description": "Event-specific information."
555
//       }
556
//     },
557
//     "required": [ "type", "event" ]
558
//   }]
559
// },
560
// "ProtocolMessage": {
561
//   "type": "object",
562
//   "description": "Base class of requests, responses, and events.",
563
//   "properties": {
564
//         "seq": {
565
//           "type": "integer",
566
//           "description": "Sequence number."
567
//         },
568
//         "type": {
569
//           "type": "string",
570
//           "description": "Message type.",
571
//           "_enum": [ "request", "response", "event" ]
572
//         }
573
//   },
574
//   "required": [ "seq", "type" ]
575
// }
576
17
llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
577
17
  llvm::json::Object event;
578
17
  event.try_emplace("seq", 0);
579
17
  event.try_emplace("type", "event");
580
17
  EmplaceSafeString(event, "event", event_name);
581
17
  return event;
582
17
}
583
584
// "ExceptionBreakpointsFilter": {
585
//   "type": "object",
586
//   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
587
//                   option for configuring how exceptions are dealt with.",
588
//   "properties": {
589
//     "filter": {
590
//       "type": "string",
591
//       "description": "The internal ID of the filter. This value is passed
592
//                       to the setExceptionBreakpoints request."
593
//     },
594
//     "label": {
595
//       "type": "string",
596
//       "description": "The name of the filter. This will be shown in the UI."
597
//     },
598
//     "default": {
599
//       "type": "boolean",
600
//       "description": "Initial value of the filter. If not specified a value
601
//                       'false' is assumed."
602
//     }
603
//   },
604
//   "required": [ "filter", "label" ]
605
// }
606
llvm::json::Value
607
18
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
608
18
  llvm::json::Object object;
609
18
  EmplaceSafeString(object, "filter", bp.filter);
610
18
  EmplaceSafeString(object, "label", bp.label);
611
18
  object.try_emplace("default", bp.default_value);
612
18
  return llvm::json::Value(std::move(object));
613
18
}
614
615
// "Source": {
616
//   "type": "object",
617
//   "description": "A Source is a descriptor for source code. It is returned
618
//                   from the debug adapter as part of a StackFrame and it is
619
//                   used by clients when specifying breakpoints.",
620
//   "properties": {
621
//     "name": {
622
//       "type": "string",
623
//       "description": "The short name of the source. Every source returned
624
//                       from the debug adapter has a name. When sending a
625
//                       source to the debug adapter this name is optional."
626
//     },
627
//     "path": {
628
//       "type": "string",
629
//       "description": "The path of the source to be shown in the UI. It is
630
//                       only used to locate and load the content of the
631
//                       source if no sourceReference is specified (or its
632
//                       value is 0)."
633
//     },
634
//     "sourceReference": {
635
//       "type": "number",
636
//       "description": "If sourceReference > 0 the contents of the source must
637
//                       be retrieved through the SourceRequest (even if a path
638
//                       is specified). A sourceReference is only valid for a
639
//                       session, so it must not be used to persist a source."
640
//     },
641
//     "presentationHint": {
642
//       "type": "string",
643
//       "description": "An optional hint for how to present the source in the
644
//                       UI. A value of 'deemphasize' can be used to indicate
645
//                       that the source is not available or that it is
646
//                       skipped on stepping.",
647
//       "enum": [ "normal", "emphasize", "deemphasize" ]
648
//     },
649
//     "origin": {
650
//       "type": "string",
651
//       "description": "The (optional) origin of this source: possible values
652
//                       'internal module', 'inlined content from source map',
653
//                       etc."
654
//     },
655
//     "sources": {
656
//       "type": "array",
657
//       "items": {
658
//         "$ref": "#/definitions/Source"
659
//       },
660
//       "description": "An optional list of sources that are related to this
661
//                       source. These may be the source that generated this
662
//                       source."
663
//     },
664
//     "adapterData": {
665
//       "type":["array","boolean","integer","null","number","object","string"],
666
//       "description": "Optional data that a debug adapter might want to loop
667
//                       through the client. The client should leave the data
668
//                       intact and persist it across sessions. The client
669
//                       should not interpret the data."
670
//     },
671
//     "checksums": {
672
//       "type": "array",
673
//       "items": {
674
//         "$ref": "#/definitions/Checksum"
675
//       },
676
//       "description": "The checksums associated with this file."
677
//     }
678
//   }
679
// }
680
1
llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
681
1
  llvm::json::Object object;
682
1
  lldb::SBFileSpec file = line_entry.GetFileSpec();
683
1
  if (file.IsValid()) {
684
1
    const char *name = file.GetFilename();
685
1
    if (name)
686
1
      EmplaceSafeString(object, "name", name);
687
1
    char path[PATH_MAX] = "";
688
1
    if (file.GetPath(path, sizeof(path)) &&
689
1
        lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
690
1
      EmplaceSafeString(object, "path", std::string(path));
691
1
    }
692
1
  }
693
1
  return llvm::json::Value(std::move(object));
694
1
}
695
696
1
llvm::json::Value CreateSource(llvm::StringRef source_path) {
697
1
  llvm::json::Object source;
698
1
  llvm::StringRef name = llvm::sys::path::filename(source_path);
699
1
  EmplaceSafeString(source, "name", name);
700
1
  EmplaceSafeString(source, "path", source_path);
701
1
  return llvm::json::Value(std::move(source));
702
1
}
703
704
1
std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
705
1
  auto line_entry = frame.GetLineEntry();
706
  // A line entry of 0 indicates the line is compiler generated i.e. no source
707
  // file is associated with the frame.
708
1
  if (line_entry.GetFileSpec().IsValid() && 
line_entry.GetLine() != 00
)
709
0
    return CreateSource(line_entry);
710
711
1
  return {};
712
1
}
713
714
// "StackFrame": {
715
//   "type": "object",
716
//   "description": "A Stackframe contains the source location.",
717
//   "properties": {
718
//     "id": {
719
//       "type": "integer",
720
//       "description": "An identifier for the stack frame. It must be unique
721
//                       across all threads. This id can be used to retrieve
722
//                       the scopes of the frame with the 'scopesRequest' or
723
//                       to restart the execution of a stackframe."
724
//     },
725
//     "name": {
726
//       "type": "string",
727
//       "description": "The name of the stack frame, typically a method name."
728
//     },
729
//     "source": {
730
//       "$ref": "#/definitions/Source",
731
//       "description": "The optional source of the frame."
732
//     },
733
//     "line": {
734
//       "type": "integer",
735
//       "description": "The line within the file of the frame. If source is
736
//                       null or doesn't exist, line is 0 and must be ignored."
737
//     },
738
//     "column": {
739
//       "type": "integer",
740
//       "description": "The column within the line. If source is null or
741
//                       doesn't exist, column is 0 and must be ignored."
742
//     },
743
//     "endLine": {
744
//       "type": "integer",
745
//       "description": "An optional end line of the range covered by the
746
//                       stack frame."
747
//     },
748
//     "endColumn": {
749
//       "type": "integer",
750
//       "description": "An optional end column of the range covered by the
751
//                       stack frame."
752
//     },
753
//     "instructionPointerReference": {
754
//       "type": "string",
755
//       "description": "A memory reference for the current instruction
756
// pointer
757
//                       in this frame."
758
//     },
759
//     "moduleId": {
760
//       "type": ["integer", "string"],
761
//       "description": "The module associated with this frame, if any."
762
//     },
763
//     "presentationHint": {
764
//       "type": "string",
765
//       "enum": [ "normal", "label", "subtle" ],
766
//       "description": "An optional hint for how to present this frame in
767
//                       the UI. A value of 'label' can be used to indicate
768
//                       that the frame is an artificial frame that is used
769
//                       as a visual label or separator. A value of 'subtle'
770
//                       can be used to change the appearance of a frame in
771
//                       a 'subtle' way."
772
//     }
773
//   },
774
//   "required": [ "id", "name", "line", "column" ]
775
// }
776
1
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
777
1
  llvm::json::Object object;
778
1
  int64_t frame_id = MakeVSCodeFrameID(frame);
779
1
  object.try_emplace("id", frame_id);
780
781
  // `function_name` can be a nullptr, which throws an error when assigned to an
782
  // `std::string`.
783
1
  const char *function_name = frame.GetDisplayFunctionName();
784
1
  std::string frame_name =
785
1
      function_name == nullptr ? 
std::string()0
: function_name;
786
1
  if (frame_name.empty()) {
787
    // If the function name is unavailable, display the pc address as a 16-digit
788
    // hex string, e.g. "0x0000000000012345"
789
0
    llvm::raw_string_ostream os(frame_name);
790
0
    os << llvm::format_hex(frame.GetPC(), 18);
791
0
  }
792
1
  bool is_optimized = frame.GetFunction().GetIsOptimized();
793
1
  if (is_optimized)
794
0
    frame_name += " [opt]";
795
1
  EmplaceSafeString(object, "name", frame_name);
796
797
1
  auto source = CreateSource(frame);
798
799
1
  if (source) {
800
0
    object.try_emplace("source", *source);
801
0
    auto line_entry = frame.GetLineEntry();
802
0
    auto line = line_entry.GetLine();
803
0
    if (line && line != LLDB_INVALID_LINE_NUMBER)
804
0
      object.try_emplace("line", line);
805
0
    auto column = line_entry.GetColumn();
806
0
    if (column && column != LLDB_INVALID_COLUMN_NUMBER)
807
0
      object.try_emplace("column", column);
808
1
  } else {
809
1
    object.try_emplace("line", 0);
810
1
    object.try_emplace("column", 0);
811
1
    object.try_emplace("presentationHint", "subtle");
812
1
  }
813
814
1
  const auto pc = frame.GetPC();
815
1
  if (pc != LLDB_INVALID_ADDRESS) {
816
1
    std::string formatted_addr = "0x" + llvm::utohexstr(pc);
817
1
    object.try_emplace("instructionPointerReference", formatted_addr);
818
1
  }
819
820
1
  return llvm::json::Value(std::move(object));
821
1
}
822
823
// "Thread": {
824
//   "type": "object",
825
//   "description": "A Thread",
826
//   "properties": {
827
//     "id": {
828
//       "type": "integer",
829
//       "description": "Unique identifier for the thread."
830
//     },
831
//     "name": {
832
//       "type": "string",
833
//       "description": "A name of the thread."
834
//     }
835
//   },
836
//   "required": [ "id", "name" ]
837
// }
838
1
llvm::json::Value CreateThread(lldb::SBThread &thread) {
839
1
  llvm::json::Object object;
840
1
  object.try_emplace("id", (int64_t)thread.GetThreadID());
841
1
  const char *thread_name = thread.GetName();
842
1
  const char *queue_name = thread.GetQueueName();
843
844
1
  std::string thread_str;
845
1
  if (thread_name) {
846
0
    thread_str = std::string(thread_name);
847
1
  } else if (queue_name) {
848
1
    auto kind = thread.GetQueue().GetKind();
849
1
    std::string queue_kind_label = "";
850
1
    if (kind == lldb::eQueueKindSerial) {
851
1
      queue_kind_label = " (serial)";
852
1
    } else 
if (0
kind == lldb::eQueueKindConcurrent0
) {
853
0
      queue_kind_label = " (concurrent)";
854
0
    }
855
856
1
    thread_str = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
857
1
                               queue_name, queue_kind_label)
858
1
                     .str();
859
1
  } else {
860
0
    thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
861
0
  }
862
863
1
  EmplaceSafeString(object, "name", thread_str);
864
865
1
  return llvm::json::Value(std::move(object));
866
1
}
867
868
// "StoppedEvent": {
869
//   "allOf": [ { "$ref": "#/definitions/Event" }, {
870
//     "type": "object",
871
//     "description": "Event message for 'stopped' event type. The event
872
//                     indicates that the execution of the debuggee has stopped
873
//                     due to some condition. This can be caused by a break
874
//                     point previously set, a stepping action has completed,
875
//                     by executing a debugger statement etc.",
876
//     "properties": {
877
//       "event": {
878
//         "type": "string",
879
//         "enum": [ "stopped" ]
880
//       },
881
//       "body": {
882
//         "type": "object",
883
//         "properties": {
884
//           "reason": {
885
//             "type": "string",
886
//             "description": "The reason for the event. For backward
887
//                             compatibility this string is shown in the UI if
888
//                             the 'description' attribute is missing (but it
889
//                             must not be translated).",
890
//             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
891
//           },
892
//           "description": {
893
//             "type": "string",
894
//             "description": "The full reason for the event, e.g. 'Paused
895
//                             on exception'. This string is shown in the UI
896
//                             as is."
897
//           },
898
//           "threadId": {
899
//             "type": "integer",
900
//             "description": "The thread which was stopped."
901
//           },
902
//           "text": {
903
//             "type": "string",
904
//             "description": "Additional information. E.g. if reason is
905
//                             'exception', text contains the exception name.
906
//                             This string is shown in the UI."
907
//           },
908
//           "allThreadsStopped": {
909
//             "type": "boolean",
910
//             "description": "If allThreadsStopped is true, a debug adapter
911
//                             can announce that all threads have stopped.
912
//                             The client should use this information to
913
//                             enable that all threads can be expanded to
914
//                             access their stacktraces. If the attribute
915
//                             is missing or false, only the thread with the
916
//                             given threadId can be expanded."
917
//           }
918
//         },
919
//         "required": [ "reason" ]
920
//       }
921
//     },
922
//     "required": [ "event", "body" ]
923
//   }]
924
// }
925
llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
926
1
                                      uint32_t stop_id) {
927
1
  llvm::json::Object event(CreateEventObject("stopped"));
928
1
  llvm::json::Object body;
929
1
  switch (thread.GetStopReason()) {
930
0
  case lldb::eStopReasonTrace:
931
0
  case lldb::eStopReasonPlanComplete:
932
0
    body.try_emplace("reason", "step");
933
0
    break;
934
1
  case lldb::eStopReasonBreakpoint: {
935
1
    ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
936
1
    if (exc_bp) {
937
0
      body.try_emplace("reason", "exception");
938
0
      EmplaceSafeString(body, "description", exc_bp->label);
939
1
    } else {
940
1
      body.try_emplace("reason", "breakpoint");
941
1
      char desc_str[64];
942
1
      uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
943
1
      uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
944
1
      snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
945
1
               bp_id, bp_loc_id);
946
1
      body.try_emplace("hitBreakpointIds",
947
1
                       llvm::json::Array{llvm::json::Value(bp_id)});
948
1
      EmplaceSafeString(body, "description", desc_str);
949
1
    }
950
1
  } break;
951
0
  case lldb::eStopReasonWatchpoint:
952
0
  case lldb::eStopReasonInstrumentation:
953
0
    body.try_emplace("reason", "breakpoint");
954
0
    break;
955
0
  case lldb::eStopReasonProcessorTrace:
956
0
    body.try_emplace("reason", "processor trace");
957
0
    break;
958
0
  case lldb::eStopReasonSignal:
959
0
  case lldb::eStopReasonException:
960
0
    body.try_emplace("reason", "exception");
961
0
    break;
962
0
  case lldb::eStopReasonExec:
963
0
    body.try_emplace("reason", "entry");
964
0
    break;
965
0
  case lldb::eStopReasonFork:
966
0
    body.try_emplace("reason", "fork");
967
0
    break;
968
0
  case lldb::eStopReasonVFork:
969
0
    body.try_emplace("reason", "vfork");
970
0
    break;
971
0
  case lldb::eStopReasonVForkDone:
972
0
    body.try_emplace("reason", "vforkdone");
973
0
    break;
974
0
  case lldb::eStopReasonThreadExiting:
975
0
  case lldb::eStopReasonInvalid:
976
0
  case lldb::eStopReasonNone:
977
0
    break;
978
1
  }
979
1
  if (stop_id == 0)
980
0
    body.try_emplace("reason", "entry");
981
1
  const lldb::tid_t tid = thread.GetThreadID();
982
1
  body.try_emplace("threadId", (int64_t)tid);
983
  // If no description has been set, then set it to the default thread stopped
984
  // description. If we have breakpoints that get hit and shouldn't be reported
985
  // as breakpoints, then they will set the description above.
986
1
  if (!ObjectContainsKey(body, "description")) {
987
0
    char description[1024];
988
0
    if (thread.GetStopDescription(description, sizeof(description))) {
989
0
      EmplaceSafeString(body, "description", std::string(description));
990
0
    }
991
0
  }
992
  // "threadCausedFocus" is used in tests to validate breaking behavior.
993
1
  if (tid == g_vsc.focus_tid) {
994
1
    body.try_emplace("threadCausedFocus", true);
995
1
  }
996
1
  body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
997
1
  body.try_emplace("allThreadsStopped", true);
998
1
  event.try_emplace("body", std::move(body));
999
1
  return llvm::json::Value(std::move(event));
1000
1
}
1001
1002
0
const char *GetNonNullVariableName(lldb::SBValue v) {
1003
0
  const char *name = v.GetName();
1004
0
  return name ? name : "<null>";
1005
0
}
1006
1007
std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
1008
0
                                               bool is_name_duplicated) {
1009
0
  lldb::SBStream name_builder;
1010
0
  name_builder.Print(GetNonNullVariableName(v));
1011
0
  if (is_name_duplicated) {
1012
0
    lldb::SBDeclaration declaration = v.GetDeclaration();
1013
0
    const char *file_name = declaration.GetFileSpec().GetFilename();
1014
0
    const uint32_t line = declaration.GetLine();
1015
1016
0
    if (file_name != nullptr && line > 0)
1017
0
      name_builder.Printf(" @ %s:%u", file_name, line);
1018
0
    else if (const char *location = v.GetLocation())
1019
0
      name_builder.Printf(" @ %s", location);
1020
0
  }
1021
0
  return name_builder.GetData();
1022
0
}
1023
1024
// "Variable": {
1025
//   "type": "object",
1026
//   "description": "A Variable is a name/value pair. Optionally a variable
1027
//                   can have a 'type' that is shown if space permits or when
1028
//                   hovering over the variable's name. An optional 'kind' is
1029
//                   used to render additional properties of the variable,
1030
//                   e.g. different icons can be used to indicate that a
1031
//                   variable is public or private. If the value is
1032
//                   structured (has children), a handle is provided to
1033
//                   retrieve the children with the VariablesRequest. If
1034
//                   the number of named or indexed children is large, the
1035
//                   numbers should be returned via the optional
1036
//                   'namedVariables' and 'indexedVariables' attributes. The
1037
//                   client can use this optional information to present the
1038
//                   children in a paged UI and fetch them in chunks.",
1039
//   "properties": {
1040
//     "name": {
1041
//       "type": "string",
1042
//       "description": "The variable's name."
1043
//     },
1044
//     "value": {
1045
//       "type": "string",
1046
//       "description": "The variable's value. This can be a multi-line text,
1047
//                       e.g. for a function the body of a function."
1048
//     },
1049
//     "type": {
1050
//       "type": "string",
1051
//       "description": "The type of the variable's value. Typically shown in
1052
//                       the UI when hovering over the value."
1053
//     },
1054
//     "presentationHint": {
1055
//       "$ref": "#/definitions/VariablePresentationHint",
1056
//       "description": "Properties of a variable that can be used to determine
1057
//                       how to render the variable in the UI."
1058
//     },
1059
//     "evaluateName": {
1060
//       "type": "string",
1061
//       "description": "Optional evaluatable name of this variable which can
1062
//                       be passed to the 'EvaluateRequest' to fetch the
1063
//                       variable's value."
1064
//     },
1065
//     "variablesReference": {
1066
//       "type": "integer",
1067
//       "description": "If variablesReference is > 0, the variable is
1068
//                       structured and its children can be retrieved by
1069
//                       passing variablesReference to the VariablesRequest."
1070
//     },
1071
//     "namedVariables": {
1072
//       "type": "integer",
1073
//       "description": "The number of named child variables. The client can
1074
//                       use this optional information to present the children
1075
//                       in a paged UI and fetch them in chunks."
1076
//     },
1077
//     "indexedVariables": {
1078
//       "type": "integer",
1079
//       "description": "The number of indexed child variables. The client
1080
//                       can use this optional information to present the
1081
//                       children in a paged UI and fetch them in chunks."
1082
//     }
1083
//   },
1084
//   "required": [ "name", "value", "variablesReference" ]
1085
// }
1086
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1087
                                 int64_t varID, bool format_hex,
1088
                                 bool is_name_duplicated,
1089
0
                                 std::optional<std::string> custom_name) {
1090
0
  llvm::json::Object object;
1091
0
  EmplaceSafeString(
1092
0
      object, "name",
1093
0
      custom_name ? *custom_name
1094
0
                  : CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
1095
1096
0
  if (format_hex)
1097
0
    v.SetFormat(lldb::eFormatHex);
1098
0
  SetValueForKey(v, object, "value");
1099
0
  auto type_obj = v.GetType();
1100
0
  auto type_cstr = type_obj.GetDisplayTypeName();
1101
  // If we have a type with many children, we would like to be able to
1102
  // give a hint to the IDE that the type has indexed children so that the
1103
  // request can be broken up in grabbing only a few children at a time. We want
1104
  // to be careful and only call "v.GetNumChildren()" if we have an array type
1105
  // or if we have a synthetic child provider. We don't want to call
1106
  // "v.GetNumChildren()" on all objects as class, struct and union types don't
1107
  // need to be completed if they are never expanded. So we want to avoid
1108
  // calling this to only cases where we it makes sense to keep performance high
1109
  // during normal debugging.
1110
1111
  // If we have an array type, say that it is indexed and provide the number of
1112
  // children in case we have a huge array. If we don't do this, then we might
1113
  // take a while to produce all children at onces which can delay your debug
1114
  // session.
1115
0
  const bool is_array = type_obj.IsArrayType();
1116
0
  const bool is_synthetic = v.IsSynthetic();
1117
0
  if (is_array || is_synthetic) {
1118
0
    const auto num_children = v.GetNumChildren();
1119
    // We create a "[raw]" fake child for each synthetic type, so we have to
1120
    // account for it when returning indexed variables. We don't need to do this
1121
    // for non-indexed ones.
1122
0
    bool has_raw_child = is_synthetic && g_vsc.enable_synthetic_child_debugging;
1123
0
    int actual_num_children = num_children + (has_raw_child ? 1 : 0);
1124
0
    if (is_array) {
1125
0
      object.try_emplace("indexedVariables", actual_num_children);
1126
0
    } else if (num_children > 0) {
1127
      // If a type has a synthetic child provider, then the SBType of "v" won't
1128
      // tell us anything about what might be displayed. So we can check if the
1129
      // first child's name is "[0]" and then we can say it is indexed.
1130
0
      const char *first_child_name = v.GetChildAtIndex(0).GetName();
1131
0
      if (first_child_name && strcmp(first_child_name, "[0]") == 0)
1132
0
        object.try_emplace("indexedVariables", actual_num_children);
1133
0
    }
1134
0
  }
1135
0
  EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
1136
0
  if (varID != INT64_MAX)
1137
0
    object.try_emplace("id", varID);
1138
0
  if (v.MightHaveChildren())
1139
0
    object.try_emplace("variablesReference", variablesReference);
1140
0
  else
1141
0
    object.try_emplace("variablesReference", (int64_t)0);
1142
0
  lldb::SBStream evaluateStream;
1143
0
  v.GetExpressionPath(evaluateStream);
1144
0
  const char *evaluateName = evaluateStream.GetData();
1145
0
  if (evaluateName && evaluateName[0])
1146
0
    EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
1147
0
  return llvm::json::Value(std::move(object));
1148
0
}
1149
1150
0
llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
1151
0
  llvm::json::Object object;
1152
0
  char unit_path_arr[PATH_MAX];
1153
0
  unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
1154
0
  std::string unit_path(unit_path_arr);
1155
0
  object.try_emplace("compileUnitPath", unit_path);
1156
0
  return llvm::json::Value(std::move(object));
1157
0
}
1158
1159
/// See
1160
/// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1161
llvm::json::Object
1162
CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
1163
                                  llvm::StringRef debug_adaptor_path,
1164
                                  llvm::StringRef comm_file,
1165
0
                                  lldb::pid_t debugger_pid) {
1166
0
  llvm::json::Object run_in_terminal_args;
1167
  // This indicates the IDE to open an embedded terminal, instead of opening the
1168
  // terminal in a new window.
1169
0
  run_in_terminal_args.try_emplace("kind", "integrated");
1170
1171
0
  auto launch_request_arguments = launch_request.getObject("arguments");
1172
  // The program path must be the first entry in the "args" field
1173
0
  std::vector<std::string> args = {
1174
0
      debug_adaptor_path.str(), "--comm-file", comm_file.str()};
1175
0
  if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
1176
0
    args.push_back("--debugger-pid");
1177
0
    args.push_back(std::to_string(debugger_pid));
1178
0
  }
1179
0
  args.push_back("--launch-target");
1180
0
  args.push_back(GetString(launch_request_arguments, "program").str());
1181
0
  std::vector<std::string> target_args =
1182
0
      GetStrings(launch_request_arguments, "args");
1183
0
  args.insert(args.end(), target_args.begin(), target_args.end());
1184
0
  run_in_terminal_args.try_emplace("args", args);
1185
1186
0
  const auto cwd = GetString(launch_request_arguments, "cwd");
1187
0
  if (!cwd.empty())
1188
0
    run_in_terminal_args.try_emplace("cwd", cwd);
1189
1190
  // We need to convert the input list of environments variables into a
1191
  // dictionary
1192
0
  std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1193
0
  llvm::json::Object environment;
1194
0
  for (const std::string &env : envs) {
1195
0
    size_t index = env.find('=');
1196
0
    environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1197
0
  }
1198
0
  run_in_terminal_args.try_emplace("env",
1199
0
                                   llvm::json::Value(std::move(environment)));
1200
1201
0
  return run_in_terminal_args;
1202
0
}
1203
1204
// Keep all the top level items from the statistics dump, except for the
1205
// "modules" array. It can be huge and cause delay
1206
// Array and dictionary value will return as <key, JSON string> pairs
1207
void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key,
1208
54
                             llvm::json::Object &out) {
1209
54
  lldb::SBStructuredData value = data.GetValueForKey(key);
1210
54
  std::string key_utf8 = llvm::json::fixUTF8(key);
1211
54
  if (strcmp(key, "modules") == 0)
1212
3
    return;
1213
51
  switch (value.GetType()) {
1214
9
  case lldb::eStructuredDataTypeFloat:
1215
9
    out.try_emplace(key_utf8, value.GetFloatValue());
1216
9
    break;
1217
36
  case lldb::eStructuredDataTypeUnsignedInteger:
1218
36
    out.try_emplace(key_utf8, value.GetIntegerValue((uint64_t)0));
1219
36
    break;
1220
0
  case lldb::eStructuredDataTypeSignedInteger:
1221
0
    out.try_emplace(key_utf8, value.GetIntegerValue((int64_t)0));
1222
0
    break;
1223
3
  case lldb::eStructuredDataTypeArray: {
1224
3
    lldb::SBStream contents;
1225
3
    value.GetAsJSON(contents);
1226
3
    out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1227
3
  } break;
1228
0
  case lldb::eStructuredDataTypeBoolean:
1229
0
    out.try_emplace(key_utf8, value.GetBooleanValue());
1230
0
    break;
1231
0
  case lldb::eStructuredDataTypeString: {
1232
    // Get the string size before reading
1233
0
    const size_t str_length = value.GetStringValue(nullptr, 0);
1234
0
    std::string str(str_length + 1, 0);
1235
0
    value.GetStringValue(&str[0], str_length);
1236
0
    out.try_emplace(key_utf8, llvm::json::fixUTF8(str));
1237
0
  } break;
1238
3
  case lldb::eStructuredDataTypeDictionary: {
1239
3
    lldb::SBStream contents;
1240
3
    value.GetAsJSON(contents);
1241
3
    out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1242
3
  } break;
1243
0
  case lldb::eStructuredDataTypeNull:
1244
0
  case lldb::eStructuredDataTypeGeneric:
1245
0
  case lldb::eStructuredDataTypeInvalid:
1246
0
    break;
1247
51
  }
1248
51
}
1249
1250
4
void addStatistic(llvm::json::Object &event) {
1251
4
  lldb::SBStructuredData statistics = g_vsc.target.GetStatistics();
1252
4
  bool is_dictionary =
1253
4
      statistics.GetType() == lldb::eStructuredDataTypeDictionary;
1254
4
  if (!is_dictionary)
1255
1
    return;
1256
3
  llvm::json::Object stats_body;
1257
1258
3
  lldb::SBStringList keys;
1259
3
  if (!statistics.GetKeys(keys))
1260
0
    return;
1261
57
  
for (size_t i = 0; 3
i < keys.GetSize();
i++54
) {
1262
54
    const char *key = keys.GetStringAtIndex(i);
1263
54
    FilterAndGetValueForKey(statistics, key, stats_body);
1264
54
  }
1265
3
  event.try_emplace("statistics", std::move(stats_body));
1266
3
}
1267
1268
4
llvm::json::Object CreateTerminatedEventObject() {
1269
4
  llvm::json::Object event(CreateEventObject("terminated"));
1270
4
  addStatistic(event);
1271
4
  return event;
1272
4
}
1273
1274
0
std::string JSONToString(const llvm::json::Value &json) {
1275
0
  std::string data;
1276
0
  llvm::raw_string_ostream os(data);
1277
0
  os << json;
1278
0
  os.flush();
1279
0
  return data;
1280
0
}
1281
1282
} // namespace lldb_vscode