1061da546Spatrick //===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include <algorithm>
10be691f3bSpatrick #include <iomanip>
11*f6aab3d8Srobert #include <optional>
12be691f3bSpatrick #include <sstream>
13*f6aab3d8Srobert #include <string.h>
14061da546Spatrick 
15061da546Spatrick #include "llvm/Support/FormatAdapters.h"
16dda28197Spatrick #include "llvm/Support/Path.h"
17dda28197Spatrick #include "llvm/Support/ScopedPrinter.h"
18061da546Spatrick 
19061da546Spatrick #include "lldb/API/SBBreakpoint.h"
20061da546Spatrick #include "lldb/API/SBBreakpointLocation.h"
21be691f3bSpatrick #include "lldb/API/SBDeclaration.h"
22*f6aab3d8Srobert #include "lldb/API/SBStringList.h"
23*f6aab3d8Srobert #include "lldb/API/SBStructuredData.h"
24061da546Spatrick #include "lldb/API/SBValue.h"
25061da546Spatrick #include "lldb/Host/PosixApi.h"
26061da546Spatrick 
27061da546Spatrick #include "ExceptionBreakpoint.h"
28061da546Spatrick #include "JSONUtils.h"
29061da546Spatrick #include "LLDBUtils.h"
30061da546Spatrick #include "VSCode.h"
31061da546Spatrick 
32061da546Spatrick namespace lldb_vscode {
33061da546Spatrick 
EmplaceSafeString(llvm::json::Object & obj,llvm::StringRef key,llvm::StringRef str)34061da546Spatrick void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
35061da546Spatrick                        llvm::StringRef str) {
36061da546Spatrick   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
37061da546Spatrick     obj.try_emplace(key, str.str());
38061da546Spatrick   else
39061da546Spatrick     obj.try_emplace(key, llvm::json::fixUTF8(str));
40061da546Spatrick }
41061da546Spatrick 
GetAsString(const llvm::json::Value & value)42061da546Spatrick llvm::StringRef GetAsString(const llvm::json::Value &value) {
43061da546Spatrick   if (auto s = value.getAsString())
44061da546Spatrick     return *s;
45061da546Spatrick   return llvm::StringRef();
46061da546Spatrick }
47061da546Spatrick 
48061da546Spatrick // Gets a string from a JSON object using the key, or returns an empty string.
GetString(const llvm::json::Object & obj,llvm::StringRef key)49061da546Spatrick llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
50*f6aab3d8Srobert   if (std::optional<llvm::StringRef> value = obj.getString(key))
51dda28197Spatrick     return *value;
52061da546Spatrick   return llvm::StringRef();
53061da546Spatrick }
54061da546Spatrick 
GetString(const llvm::json::Object * obj,llvm::StringRef key)55061da546Spatrick llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
56061da546Spatrick   if (obj == nullptr)
57061da546Spatrick     return llvm::StringRef();
58061da546Spatrick   return GetString(*obj, key);
59061da546Spatrick }
60061da546Spatrick 
61061da546Spatrick // Gets an unsigned integer from a JSON object using the key, or returns the
62061da546Spatrick // specified fail value.
GetUnsigned(const llvm::json::Object & obj,llvm::StringRef key,uint64_t fail_value)63061da546Spatrick uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
64061da546Spatrick                      uint64_t fail_value) {
65061da546Spatrick   if (auto value = obj.getInteger(key))
66061da546Spatrick     return (uint64_t)*value;
67061da546Spatrick   return fail_value;
68061da546Spatrick }
69061da546Spatrick 
GetUnsigned(const llvm::json::Object * obj,llvm::StringRef key,uint64_t fail_value)70061da546Spatrick uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
71061da546Spatrick                      uint64_t fail_value) {
72061da546Spatrick   if (obj == nullptr)
73061da546Spatrick     return fail_value;
74061da546Spatrick   return GetUnsigned(*obj, key, fail_value);
75061da546Spatrick }
76061da546Spatrick 
GetBoolean(const llvm::json::Object & obj,llvm::StringRef key,bool fail_value)77061da546Spatrick bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
78061da546Spatrick                 bool fail_value) {
79061da546Spatrick   if (auto value = obj.getBoolean(key))
80061da546Spatrick     return *value;
81061da546Spatrick   if (auto value = obj.getInteger(key))
82061da546Spatrick     return *value != 0;
83061da546Spatrick   return fail_value;
84061da546Spatrick }
85061da546Spatrick 
GetBoolean(const llvm::json::Object * obj,llvm::StringRef key,bool fail_value)86061da546Spatrick bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
87061da546Spatrick                 bool fail_value) {
88061da546Spatrick   if (obj == nullptr)
89061da546Spatrick     return fail_value;
90061da546Spatrick   return GetBoolean(*obj, key, fail_value);
91061da546Spatrick }
92061da546Spatrick 
GetSigned(const llvm::json::Object & obj,llvm::StringRef key,int64_t fail_value)93061da546Spatrick int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
94061da546Spatrick                   int64_t fail_value) {
95061da546Spatrick   if (auto value = obj.getInteger(key))
96061da546Spatrick     return *value;
97061da546Spatrick   return fail_value;
98061da546Spatrick }
99061da546Spatrick 
GetSigned(const llvm::json::Object * obj,llvm::StringRef key,int64_t fail_value)100061da546Spatrick int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
101061da546Spatrick                   int64_t fail_value) {
102061da546Spatrick   if (obj == nullptr)
103061da546Spatrick     return fail_value;
104061da546Spatrick   return GetSigned(*obj, key, fail_value);
105061da546Spatrick }
106061da546Spatrick 
ObjectContainsKey(const llvm::json::Object & obj,llvm::StringRef key)107061da546Spatrick bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
108061da546Spatrick   return obj.find(key) != obj.end();
109061da546Spatrick }
110061da546Spatrick 
GetStrings(const llvm::json::Object * obj,llvm::StringRef key)111061da546Spatrick std::vector<std::string> GetStrings(const llvm::json::Object *obj,
112061da546Spatrick                                     llvm::StringRef key) {
113061da546Spatrick   std::vector<std::string> strs;
114061da546Spatrick   auto json_array = obj->getArray(key);
115061da546Spatrick   if (!json_array)
116061da546Spatrick     return strs;
117061da546Spatrick   for (const auto &value : *json_array) {
118061da546Spatrick     switch (value.kind()) {
119061da546Spatrick     case llvm::json::Value::String:
120061da546Spatrick       strs.push_back(value.getAsString()->str());
121061da546Spatrick       break;
122061da546Spatrick     case llvm::json::Value::Number:
123dda28197Spatrick     case llvm::json::Value::Boolean:
124dda28197Spatrick       strs.push_back(llvm::to_string(value));
125061da546Spatrick       break;
126061da546Spatrick     case llvm::json::Value::Null:
127061da546Spatrick     case llvm::json::Value::Object:
128061da546Spatrick     case llvm::json::Value::Array:
129061da546Spatrick       break;
130061da546Spatrick     }
131061da546Spatrick   }
132061da546Spatrick   return strs;
133061da546Spatrick }
134061da546Spatrick 
SetValueForKey(lldb::SBValue & v,llvm::json::Object & object,llvm::StringRef key)135061da546Spatrick void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
136061da546Spatrick                     llvm::StringRef key) {
137*f6aab3d8Srobert   std::string result;
138*f6aab3d8Srobert   llvm::raw_string_ostream strm(result);
139061da546Spatrick 
140*f6aab3d8Srobert   lldb::SBError error = v.GetError();
141*f6aab3d8Srobert   if (!error.Success()) {
142*f6aab3d8Srobert     strm << "<error: " << error.GetCString() << ">";
143*f6aab3d8Srobert   } else {
144061da546Spatrick     llvm::StringRef value = v.GetValue();
145061da546Spatrick     llvm::StringRef summary = v.GetSummary();
146061da546Spatrick     llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
147061da546Spatrick     if (!value.empty()) {
148061da546Spatrick       strm << value;
149061da546Spatrick       if (!summary.empty())
150061da546Spatrick         strm << ' ' << summary;
151061da546Spatrick     } else if (!summary.empty()) {
152061da546Spatrick       strm << ' ' << summary;
153061da546Spatrick     } else if (!type_name.empty()) {
154061da546Spatrick       strm << type_name;
155061da546Spatrick       lldb::addr_t address = v.GetLoadAddress();
156061da546Spatrick       if (address != LLDB_INVALID_ADDRESS)
157061da546Spatrick         strm << " @ " << llvm::format_hex(address, 0);
158061da546Spatrick     }
159*f6aab3d8Srobert   }
160061da546Spatrick   strm.flush();
161061da546Spatrick   EmplaceSafeString(object, key, result);
162061da546Spatrick }
163061da546Spatrick 
FillResponse(const llvm::json::Object & request,llvm::json::Object & response)164061da546Spatrick void FillResponse(const llvm::json::Object &request,
165061da546Spatrick                   llvm::json::Object &response) {
166061da546Spatrick   // Fill in all of the needed response fields to a "request" and set "success"
167061da546Spatrick   // to true by default.
168061da546Spatrick   response.try_emplace("type", "response");
169061da546Spatrick   response.try_emplace("seq", (int64_t)0);
170061da546Spatrick   EmplaceSafeString(response, "command", GetString(request, "command"));
171061da546Spatrick   const int64_t seq = GetSigned(request, "seq", 0);
172061da546Spatrick   response.try_emplace("request_seq", seq);
173061da546Spatrick   response.try_emplace("success", true);
174061da546Spatrick }
175061da546Spatrick 
176061da546Spatrick // "Scope": {
177061da546Spatrick //   "type": "object",
178061da546Spatrick //   "description": "A Scope is a named container for variables. Optionally
179061da546Spatrick //                   a scope can map to a source or a range within a source.",
180061da546Spatrick //   "properties": {
181061da546Spatrick //     "name": {
182061da546Spatrick //       "type": "string",
183061da546Spatrick //       "description": "Name of the scope such as 'Arguments', 'Locals'."
184061da546Spatrick //     },
185*f6aab3d8Srobert //     "presentationHint": {
186*f6aab3d8Srobert //       "type": "string",
187*f6aab3d8Srobert //       "description": "An optional hint for how to present this scope in the
188*f6aab3d8Srobert //                       UI. If this attribute is missing, the scope is shown
189*f6aab3d8Srobert //                       with a generic UI.",
190*f6aab3d8Srobert //       "_enum": [ "arguments", "locals", "registers" ],
191*f6aab3d8Srobert //     },
192061da546Spatrick //     "variablesReference": {
193061da546Spatrick //       "type": "integer",
194061da546Spatrick //       "description": "The variables of this scope can be retrieved by
195061da546Spatrick //                       passing the value of variablesReference to the
196061da546Spatrick //                       VariablesRequest."
197061da546Spatrick //     },
198061da546Spatrick //     "namedVariables": {
199061da546Spatrick //       "type": "integer",
200061da546Spatrick //       "description": "The number of named variables in this scope. The
201061da546Spatrick //                       client can use this optional information to present
202061da546Spatrick //                       the variables in a paged UI and fetch them in chunks."
203061da546Spatrick //     },
204061da546Spatrick //     "indexedVariables": {
205061da546Spatrick //       "type": "integer",
206061da546Spatrick //       "description": "The number of indexed variables in this scope. The
207061da546Spatrick //                       client can use this optional information to present
208061da546Spatrick //                       the variables in a paged UI and fetch them in chunks."
209061da546Spatrick //     },
210061da546Spatrick //     "expensive": {
211061da546Spatrick //       "type": "boolean",
212061da546Spatrick //       "description": "If true, the number of variables in this scope is
213061da546Spatrick //                       large or expensive to retrieve."
214061da546Spatrick //     },
215061da546Spatrick //     "source": {
216061da546Spatrick //       "$ref": "#/definitions/Source",
217061da546Spatrick //       "description": "Optional source for this scope."
218061da546Spatrick //     },
219061da546Spatrick //     "line": {
220061da546Spatrick //       "type": "integer",
221061da546Spatrick //       "description": "Optional start line of the range covered by this
222061da546Spatrick //                       scope."
223061da546Spatrick //     },
224061da546Spatrick //     "column": {
225061da546Spatrick //       "type": "integer",
226061da546Spatrick //       "description": "Optional start column of the range covered by this
227061da546Spatrick //                       scope."
228061da546Spatrick //     },
229061da546Spatrick //     "endLine": {
230061da546Spatrick //       "type": "integer",
231061da546Spatrick //       "description": "Optional end line of the range covered by this scope."
232061da546Spatrick //     },
233061da546Spatrick //     "endColumn": {
234061da546Spatrick //       "type": "integer",
235061da546Spatrick //       "description": "Optional end column of the range covered by this
236061da546Spatrick //                       scope."
237061da546Spatrick //     }
238061da546Spatrick //   },
239061da546Spatrick //   "required": [ "name", "variablesReference", "expensive" ]
240061da546Spatrick // }
CreateScope(const llvm::StringRef name,int64_t variablesReference,int64_t namedVariables,bool expensive)241061da546Spatrick llvm::json::Value CreateScope(const llvm::StringRef name,
242061da546Spatrick                               int64_t variablesReference,
243061da546Spatrick                               int64_t namedVariables, bool expensive) {
244061da546Spatrick   llvm::json::Object object;
245061da546Spatrick   EmplaceSafeString(object, "name", name.str());
246*f6aab3d8Srobert 
247*f6aab3d8Srobert   // TODO: Support "arguments" scope. At the moment lldb-vscode includes the
248*f6aab3d8Srobert   // arguments into the "locals" scope.
249*f6aab3d8Srobert   if (variablesReference == VARREF_LOCALS) {
250*f6aab3d8Srobert     object.try_emplace("presentationHint", "locals");
251*f6aab3d8Srobert   } else if (variablesReference == VARREF_REGS) {
252*f6aab3d8Srobert     object.try_emplace("presentationHint", "registers");
253*f6aab3d8Srobert   }
254*f6aab3d8Srobert 
255061da546Spatrick   object.try_emplace("variablesReference", variablesReference);
256061da546Spatrick   object.try_emplace("expensive", expensive);
257061da546Spatrick   object.try_emplace("namedVariables", namedVariables);
258061da546Spatrick   return llvm::json::Value(std::move(object));
259061da546Spatrick }
260061da546Spatrick 
261061da546Spatrick // "Breakpoint": {
262061da546Spatrick //   "type": "object",
263061da546Spatrick //   "description": "Information about a Breakpoint created in setBreakpoints
264061da546Spatrick //                   or setFunctionBreakpoints.",
265061da546Spatrick //   "properties": {
266061da546Spatrick //     "id": {
267061da546Spatrick //       "type": "integer",
268061da546Spatrick //       "description": "An optional unique identifier for the breakpoint."
269061da546Spatrick //     },
270061da546Spatrick //     "verified": {
271061da546Spatrick //       "type": "boolean",
272061da546Spatrick //       "description": "If true breakpoint could be set (but not necessarily
273061da546Spatrick //                       at the desired location)."
274061da546Spatrick //     },
275061da546Spatrick //     "message": {
276061da546Spatrick //       "type": "string",
277061da546Spatrick //       "description": "An optional message about the state of the breakpoint.
278061da546Spatrick //                       This is shown to the user and can be used to explain
279061da546Spatrick //                       why a breakpoint could not be verified."
280061da546Spatrick //     },
281061da546Spatrick //     "source": {
282061da546Spatrick //       "$ref": "#/definitions/Source",
283061da546Spatrick //       "description": "The source where the breakpoint is located."
284061da546Spatrick //     },
285061da546Spatrick //     "line": {
286061da546Spatrick //       "type": "integer",
287061da546Spatrick //       "description": "The start line of the actual range covered by the
288061da546Spatrick //                       breakpoint."
289061da546Spatrick //     },
290061da546Spatrick //     "column": {
291061da546Spatrick //       "type": "integer",
292061da546Spatrick //       "description": "An optional start column of the actual range covered
293061da546Spatrick //                       by the breakpoint."
294061da546Spatrick //     },
295061da546Spatrick //     "endLine": {
296061da546Spatrick //       "type": "integer",
297061da546Spatrick //       "description": "An optional end line of the actual range covered by
298061da546Spatrick //                       the breakpoint."
299061da546Spatrick //     },
300061da546Spatrick //     "endColumn": {
301061da546Spatrick //       "type": "integer",
302061da546Spatrick //       "description": "An optional end column of the actual range covered by
303061da546Spatrick //                       the breakpoint. If no end line is given, then the end
304061da546Spatrick //                       column is assumed to be in the start line."
305061da546Spatrick //     }
306061da546Spatrick //   },
307061da546Spatrick //   "required": [ "verified" ]
308061da546Spatrick // }
CreateBreakpoint(lldb::SBBreakpoint & bp,std::optional<llvm::StringRef> request_path,std::optional<uint32_t> request_line)309dda28197Spatrick llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
310*f6aab3d8Srobert                                    std::optional<llvm::StringRef> request_path,
311*f6aab3d8Srobert                                    std::optional<uint32_t> request_line) {
312061da546Spatrick   // Each breakpoint location is treated as a separate breakpoint for VS code.
313061da546Spatrick   // They don't have the notion of a single breakpoint with multiple locations.
314061da546Spatrick   llvm::json::Object object;
315dda28197Spatrick   if (!bp.IsValid())
316061da546Spatrick     return llvm::json::Value(std::move(object));
317061da546Spatrick 
318dda28197Spatrick   object.try_emplace("verified", bp.GetNumResolvedLocations() > 0);
319dda28197Spatrick   object.try_emplace("id", bp.GetID());
320dda28197Spatrick   // VS Code DAP doesn't currently allow one breakpoint to have multiple
321dda28197Spatrick   // locations so we just report the first one. If we report all locations
322dda28197Spatrick   // then the IDE starts showing the wrong line numbers and locations for
323dda28197Spatrick   // other source file and line breakpoints in the same file.
324dda28197Spatrick 
325dda28197Spatrick   // Below we search for the first resolved location in a breakpoint and report
326dda28197Spatrick   // this as the breakpoint location since it will have a complete location
327dda28197Spatrick   // that is at least loaded in the current process.
328dda28197Spatrick   lldb::SBBreakpointLocation bp_loc;
329dda28197Spatrick   const auto num_locs = bp.GetNumLocations();
330dda28197Spatrick   for (size_t i = 0; i < num_locs; ++i) {
331dda28197Spatrick     bp_loc = bp.GetLocationAtIndex(i);
332dda28197Spatrick     if (bp_loc.IsResolved())
333dda28197Spatrick       break;
334dda28197Spatrick   }
335dda28197Spatrick   // If not locations are resolved, use the first location.
336dda28197Spatrick   if (!bp_loc.IsResolved())
337dda28197Spatrick     bp_loc = bp.GetLocationAtIndex(0);
338061da546Spatrick   auto bp_addr = bp_loc.GetAddress();
339dda28197Spatrick 
340dda28197Spatrick   if (request_path)
341dda28197Spatrick     object.try_emplace("source", CreateSource(*request_path));
342dda28197Spatrick 
343061da546Spatrick   if (bp_addr.IsValid()) {
344061da546Spatrick     auto line_entry = bp_addr.GetLineEntry();
345061da546Spatrick     const auto line = line_entry.GetLine();
346061da546Spatrick     if (line != UINT32_MAX)
347061da546Spatrick       object.try_emplace("line", line);
348061da546Spatrick     object.try_emplace("source", CreateSource(line_entry));
349061da546Spatrick   }
350dda28197Spatrick   // We try to add request_line as a fallback
351dda28197Spatrick   if (request_line)
352dda28197Spatrick     object.try_emplace("line", *request_line);
353061da546Spatrick   return llvm::json::Value(std::move(object));
354061da546Spatrick }
355061da546Spatrick 
GetDebugInfoSizeInSection(lldb::SBSection section)356be691f3bSpatrick static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
357be691f3bSpatrick   uint64_t debug_info_size = 0;
358be691f3bSpatrick   llvm::StringRef section_name(section.GetName());
359be691f3bSpatrick   if (section_name.startswith(".debug") || section_name.startswith("__debug") ||
360be691f3bSpatrick       section_name.startswith(".apple") || section_name.startswith("__apple"))
361be691f3bSpatrick     debug_info_size += section.GetFileByteSize();
362be691f3bSpatrick   size_t num_sub_sections = section.GetNumSubSections();
363be691f3bSpatrick   for (size_t i = 0; i < num_sub_sections; i++) {
364be691f3bSpatrick     debug_info_size +=
365be691f3bSpatrick         GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
366be691f3bSpatrick   }
367be691f3bSpatrick   return debug_info_size;
368be691f3bSpatrick }
369be691f3bSpatrick 
GetDebugInfoSize(lldb::SBModule module)370be691f3bSpatrick static uint64_t GetDebugInfoSize(lldb::SBModule module) {
371be691f3bSpatrick   uint64_t debug_info_size = 0;
372be691f3bSpatrick   size_t num_sections = module.GetNumSections();
373be691f3bSpatrick   for (size_t i = 0; i < num_sections; i++) {
374be691f3bSpatrick     debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
375be691f3bSpatrick   }
376be691f3bSpatrick   return debug_info_size;
377be691f3bSpatrick }
378be691f3bSpatrick 
ConvertDebugInfoSizeToString(uint64_t debug_info)379be691f3bSpatrick static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
380be691f3bSpatrick   std::ostringstream oss;
381be691f3bSpatrick   oss << std::fixed << std::setprecision(1);
382be691f3bSpatrick   if (debug_info < 1024) {
383be691f3bSpatrick     oss << debug_info << "B";
384be691f3bSpatrick   } else if (debug_info < 1024 * 1024) {
385be691f3bSpatrick     double kb = double(debug_info) / 1024.0;
386be691f3bSpatrick     oss << kb << "KB";
387be691f3bSpatrick   } else if (debug_info < 1024 * 1024 * 1024) {
388be691f3bSpatrick     double mb = double(debug_info) / (1024.0 * 1024.0);
389be691f3bSpatrick     oss << mb << "MB";
390be691f3bSpatrick   } else {
391be691f3bSpatrick     double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
392be691f3bSpatrick     oss << gb << "GB";
393be691f3bSpatrick   }
394be691f3bSpatrick   return oss.str();
395be691f3bSpatrick }
CreateModule(lldb::SBModule & module)396dda28197Spatrick llvm::json::Value CreateModule(lldb::SBModule &module) {
397dda28197Spatrick   llvm::json::Object object;
398dda28197Spatrick   if (!module.IsValid())
399dda28197Spatrick     return llvm::json::Value(std::move(object));
400dda28197Spatrick   const char *uuid = module.GetUUIDString();
401dda28197Spatrick   object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
402dda28197Spatrick   object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
403dda28197Spatrick   char module_path_arr[PATH_MAX];
404dda28197Spatrick   module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
405dda28197Spatrick   std::string module_path(module_path_arr);
406dda28197Spatrick   object.try_emplace("path", module_path);
407dda28197Spatrick   if (module.GetNumCompileUnits() > 0) {
408be691f3bSpatrick     std::string symbol_str = "Symbols loaded.";
409be691f3bSpatrick     std::string debug_info_size;
410be691f3bSpatrick     uint64_t debug_info = GetDebugInfoSize(module);
411be691f3bSpatrick     if (debug_info > 0) {
412be691f3bSpatrick       debug_info_size = ConvertDebugInfoSizeToString(debug_info);
413be691f3bSpatrick     }
414be691f3bSpatrick     object.try_emplace("symbolStatus", symbol_str);
415be691f3bSpatrick     object.try_emplace("debugInfoSize", debug_info_size);
416dda28197Spatrick     char symbol_path_arr[PATH_MAX];
417be691f3bSpatrick     module.GetSymbolFileSpec().GetPath(symbol_path_arr,
418be691f3bSpatrick                                        sizeof(symbol_path_arr));
419dda28197Spatrick     std::string symbol_path(symbol_path_arr);
420dda28197Spatrick     object.try_emplace("symbolFilePath", symbol_path);
421dda28197Spatrick   } else {
422dda28197Spatrick     object.try_emplace("symbolStatus", "Symbols not found.");
423061da546Spatrick   }
424dda28197Spatrick   std::string loaded_addr = std::to_string(
425dda28197Spatrick       module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target));
426dda28197Spatrick   object.try_emplace("addressRange", loaded_addr);
427dda28197Spatrick   std::string version_str;
428dda28197Spatrick   uint32_t version_nums[3];
429be691f3bSpatrick   uint32_t num_versions =
430be691f3bSpatrick       module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
431dda28197Spatrick   for (uint32_t i = 0; i < num_versions; ++i) {
432dda28197Spatrick     if (!version_str.empty())
433dda28197Spatrick       version_str += ".";
434dda28197Spatrick     version_str += std::to_string(version_nums[i]);
435dda28197Spatrick   }
436dda28197Spatrick   if (!version_str.empty())
437dda28197Spatrick     object.try_emplace("version", version_str);
438dda28197Spatrick   return llvm::json::Value(std::move(object));
439dda28197Spatrick }
440dda28197Spatrick 
AppendBreakpoint(lldb::SBBreakpoint & bp,llvm::json::Array & breakpoints,std::optional<llvm::StringRef> request_path,std::optional<uint32_t> request_line)441dda28197Spatrick void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints,
442*f6aab3d8Srobert                       std::optional<llvm::StringRef> request_path,
443*f6aab3d8Srobert                       std::optional<uint32_t> request_line) {
444dda28197Spatrick   breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
445061da546Spatrick }
446061da546Spatrick 
447061da546Spatrick // "Event": {
448061da546Spatrick //   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
449061da546Spatrick //     "type": "object",
450061da546Spatrick //     "description": "Server-initiated event.",
451061da546Spatrick //     "properties": {
452061da546Spatrick //       "type": {
453061da546Spatrick //         "type": "string",
454061da546Spatrick //         "enum": [ "event" ]
455061da546Spatrick //       },
456061da546Spatrick //       "event": {
457061da546Spatrick //         "type": "string",
458061da546Spatrick //         "description": "Type of event."
459061da546Spatrick //       },
460061da546Spatrick //       "body": {
461061da546Spatrick //         "type": [ "array", "boolean", "integer", "null", "number" ,
462061da546Spatrick //                   "object", "string" ],
463061da546Spatrick //         "description": "Event-specific information."
464061da546Spatrick //       }
465061da546Spatrick //     },
466061da546Spatrick //     "required": [ "type", "event" ]
467061da546Spatrick //   }]
468061da546Spatrick // },
469061da546Spatrick // "ProtocolMessage": {
470061da546Spatrick //   "type": "object",
471061da546Spatrick //   "description": "Base class of requests, responses, and events.",
472061da546Spatrick //   "properties": {
473061da546Spatrick //         "seq": {
474061da546Spatrick //           "type": "integer",
475061da546Spatrick //           "description": "Sequence number."
476061da546Spatrick //         },
477061da546Spatrick //         "type": {
478061da546Spatrick //           "type": "string",
479061da546Spatrick //           "description": "Message type.",
480061da546Spatrick //           "_enum": [ "request", "response", "event" ]
481061da546Spatrick //         }
482061da546Spatrick //   },
483061da546Spatrick //   "required": [ "seq", "type" ]
484061da546Spatrick // }
CreateEventObject(const llvm::StringRef event_name)485061da546Spatrick llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
486061da546Spatrick   llvm::json::Object event;
487061da546Spatrick   event.try_emplace("seq", 0);
488061da546Spatrick   event.try_emplace("type", "event");
489061da546Spatrick   EmplaceSafeString(event, "event", event_name);
490061da546Spatrick   return event;
491061da546Spatrick }
492061da546Spatrick 
493061da546Spatrick // "ExceptionBreakpointsFilter": {
494061da546Spatrick //   "type": "object",
495061da546Spatrick //   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
496061da546Spatrick //                   option for configuring how exceptions are dealt with.",
497061da546Spatrick //   "properties": {
498061da546Spatrick //     "filter": {
499061da546Spatrick //       "type": "string",
500061da546Spatrick //       "description": "The internal ID of the filter. This value is passed
501061da546Spatrick //                       to the setExceptionBreakpoints request."
502061da546Spatrick //     },
503061da546Spatrick //     "label": {
504061da546Spatrick //       "type": "string",
505061da546Spatrick //       "description": "The name of the filter. This will be shown in the UI."
506061da546Spatrick //     },
507061da546Spatrick //     "default": {
508061da546Spatrick //       "type": "boolean",
509061da546Spatrick //       "description": "Initial value of the filter. If not specified a value
510061da546Spatrick //                       'false' is assumed."
511061da546Spatrick //     }
512061da546Spatrick //   },
513061da546Spatrick //   "required": [ "filter", "label" ]
514061da546Spatrick // }
515061da546Spatrick llvm::json::Value
CreateExceptionBreakpointFilter(const ExceptionBreakpoint & bp)516061da546Spatrick CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
517061da546Spatrick   llvm::json::Object object;
518061da546Spatrick   EmplaceSafeString(object, "filter", bp.filter);
519061da546Spatrick   EmplaceSafeString(object, "label", bp.label);
520061da546Spatrick   object.try_emplace("default", bp.default_value);
521061da546Spatrick   return llvm::json::Value(std::move(object));
522061da546Spatrick }
523061da546Spatrick 
524061da546Spatrick // "Source": {
525061da546Spatrick //   "type": "object",
526061da546Spatrick //   "description": "A Source is a descriptor for source code. It is returned
527061da546Spatrick //                   from the debug adapter as part of a StackFrame and it is
528061da546Spatrick //                   used by clients when specifying breakpoints.",
529061da546Spatrick //   "properties": {
530061da546Spatrick //     "name": {
531061da546Spatrick //       "type": "string",
532061da546Spatrick //       "description": "The short name of the source. Every source returned
533061da546Spatrick //                       from the debug adapter has a name. When sending a
534061da546Spatrick //                       source to the debug adapter this name is optional."
535061da546Spatrick //     },
536061da546Spatrick //     "path": {
537061da546Spatrick //       "type": "string",
538061da546Spatrick //       "description": "The path of the source to be shown in the UI. It is
539061da546Spatrick //                       only used to locate and load the content of the
540061da546Spatrick //                       source if no sourceReference is specified (or its
541061da546Spatrick //                       value is 0)."
542061da546Spatrick //     },
543061da546Spatrick //     "sourceReference": {
544061da546Spatrick //       "type": "number",
545061da546Spatrick //       "description": "If sourceReference > 0 the contents of the source must
546061da546Spatrick //                       be retrieved through the SourceRequest (even if a path
547061da546Spatrick //                       is specified). A sourceReference is only valid for a
548061da546Spatrick //                       session, so it must not be used to persist a source."
549061da546Spatrick //     },
550061da546Spatrick //     "presentationHint": {
551061da546Spatrick //       "type": "string",
552061da546Spatrick //       "description": "An optional hint for how to present the source in the
553061da546Spatrick //                       UI. A value of 'deemphasize' can be used to indicate
554061da546Spatrick //                       that the source is not available or that it is
555061da546Spatrick //                       skipped on stepping.",
556061da546Spatrick //       "enum": [ "normal", "emphasize", "deemphasize" ]
557061da546Spatrick //     },
558061da546Spatrick //     "origin": {
559061da546Spatrick //       "type": "string",
560061da546Spatrick //       "description": "The (optional) origin of this source: possible values
561061da546Spatrick //                       'internal module', 'inlined content from source map',
562061da546Spatrick //                       etc."
563061da546Spatrick //     },
564061da546Spatrick //     "sources": {
565061da546Spatrick //       "type": "array",
566061da546Spatrick //       "items": {
567061da546Spatrick //         "$ref": "#/definitions/Source"
568061da546Spatrick //       },
569061da546Spatrick //       "description": "An optional list of sources that are related to this
570061da546Spatrick //                       source. These may be the source that generated this
571061da546Spatrick //                       source."
572061da546Spatrick //     },
573061da546Spatrick //     "adapterData": {
574061da546Spatrick //       "type":["array","boolean","integer","null","number","object","string"],
575061da546Spatrick //       "description": "Optional data that a debug adapter might want to loop
576061da546Spatrick //                       through the client. The client should leave the data
577061da546Spatrick //                       intact and persist it across sessions. The client
578061da546Spatrick //                       should not interpret the data."
579061da546Spatrick //     },
580061da546Spatrick //     "checksums": {
581061da546Spatrick //       "type": "array",
582061da546Spatrick //       "items": {
583061da546Spatrick //         "$ref": "#/definitions/Checksum"
584061da546Spatrick //       },
585061da546Spatrick //       "description": "The checksums associated with this file."
586061da546Spatrick //     }
587061da546Spatrick //   }
588061da546Spatrick // }
CreateSource(lldb::SBLineEntry & line_entry)589061da546Spatrick llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
590061da546Spatrick   llvm::json::Object object;
591061da546Spatrick   lldb::SBFileSpec file = line_entry.GetFileSpec();
592061da546Spatrick   if (file.IsValid()) {
593061da546Spatrick     const char *name = file.GetFilename();
594061da546Spatrick     if (name)
595061da546Spatrick       EmplaceSafeString(object, "name", name);
596061da546Spatrick     char path[PATH_MAX] = "";
597061da546Spatrick     file.GetPath(path, sizeof(path));
598061da546Spatrick     if (path[0]) {
599061da546Spatrick       EmplaceSafeString(object, "path", std::string(path));
600061da546Spatrick     }
601061da546Spatrick   }
602061da546Spatrick   return llvm::json::Value(std::move(object));
603061da546Spatrick }
604061da546Spatrick 
CreateSource(llvm::StringRef source_path)605dda28197Spatrick llvm::json::Value CreateSource(llvm::StringRef source_path) {
606dda28197Spatrick   llvm::json::Object source;
607dda28197Spatrick   llvm::StringRef name = llvm::sys::path::filename(source_path);
608dda28197Spatrick   EmplaceSafeString(source, "name", name);
609dda28197Spatrick   EmplaceSafeString(source, "path", source_path);
610dda28197Spatrick   return llvm::json::Value(std::move(source));
611dda28197Spatrick }
612dda28197Spatrick 
CreateSource(lldb::SBFrame & frame,int64_t & disasm_line)613061da546Spatrick llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
614061da546Spatrick   disasm_line = 0;
615061da546Spatrick   auto line_entry = frame.GetLineEntry();
616061da546Spatrick   if (line_entry.GetFileSpec().IsValid())
617061da546Spatrick     return CreateSource(line_entry);
618061da546Spatrick 
619061da546Spatrick   llvm::json::Object object;
620061da546Spatrick   const auto pc = frame.GetPC();
621061da546Spatrick 
622061da546Spatrick   lldb::SBInstructionList insts;
623061da546Spatrick   lldb::SBFunction function = frame.GetFunction();
624061da546Spatrick   lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
625061da546Spatrick   if (function.IsValid()) {
626061da546Spatrick     low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
627061da546Spatrick     auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
628061da546Spatrick     if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
629061da546Spatrick       // We have this disassembly cached already, return the existing
630061da546Spatrick       // sourceReference
631061da546Spatrick       object.try_emplace("sourceReference", addr_srcref->second);
632061da546Spatrick       disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
633061da546Spatrick     } else {
634061da546Spatrick       insts = function.GetInstructions(g_vsc.target);
635061da546Spatrick     }
636061da546Spatrick   } else {
637061da546Spatrick     lldb::SBSymbol symbol = frame.GetSymbol();
638061da546Spatrick     if (symbol.IsValid()) {
639061da546Spatrick       low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
640061da546Spatrick       auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
641061da546Spatrick       if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
642061da546Spatrick         // We have this disassembly cached already, return the existing
643061da546Spatrick         // sourceReference
644061da546Spatrick         object.try_emplace("sourceReference", addr_srcref->second);
645061da546Spatrick         disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
646061da546Spatrick       } else {
647061da546Spatrick         insts = symbol.GetInstructions(g_vsc.target);
648061da546Spatrick       }
649061da546Spatrick     }
650061da546Spatrick   }
651061da546Spatrick   const auto num_insts = insts.GetSize();
652061da546Spatrick   if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
653061da546Spatrick     EmplaceSafeString(object, "name", frame.GetFunctionName());
654061da546Spatrick     SourceReference source;
655061da546Spatrick     llvm::raw_string_ostream src_strm(source.content);
656061da546Spatrick     std::string line;
657061da546Spatrick     for (size_t i = 0; i < num_insts; ++i) {
658061da546Spatrick       lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
659061da546Spatrick       const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
660061da546Spatrick       const char *m = inst.GetMnemonic(g_vsc.target);
661061da546Spatrick       const char *o = inst.GetOperands(g_vsc.target);
662061da546Spatrick       const char *c = inst.GetComment(g_vsc.target);
663061da546Spatrick       if (pc == inst_addr)
664061da546Spatrick         disasm_line = i + 1;
665061da546Spatrick       const auto inst_offset = inst_addr - low_pc;
666061da546Spatrick       int spaces = 0;
667061da546Spatrick       if (inst_offset < 10)
668061da546Spatrick         spaces = 3;
669061da546Spatrick       else if (inst_offset < 100)
670061da546Spatrick         spaces = 2;
671061da546Spatrick       else if (inst_offset < 1000)
672061da546Spatrick         spaces = 1;
673061da546Spatrick       line.clear();
674061da546Spatrick       llvm::raw_string_ostream line_strm(line);
675061da546Spatrick       line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
676061da546Spatrick                                  inst_offset, llvm::fmt_repeat(' ', spaces), m,
677061da546Spatrick                                  o);
678061da546Spatrick 
679061da546Spatrick       // If there is a comment append it starting at column 60 or after one
680061da546Spatrick       // space past the last char
681061da546Spatrick       const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
682061da546Spatrick       if (c && c[0]) {
683061da546Spatrick         if (line.size() < comment_row)
684061da546Spatrick           line_strm.indent(comment_row - line_strm.str().size());
685061da546Spatrick         line_strm << " # " << c;
686061da546Spatrick       }
687061da546Spatrick       src_strm << line_strm.str() << "\n";
688061da546Spatrick       source.addr_to_line[inst_addr] = i + 1;
689061da546Spatrick     }
690061da546Spatrick     // Flush the source stream
691061da546Spatrick     src_strm.str();
692061da546Spatrick     auto sourceReference = VSCode::GetNextSourceReference();
693061da546Spatrick     g_vsc.source_map[sourceReference] = std::move(source);
694061da546Spatrick     g_vsc.addr_to_source_ref[low_pc] = sourceReference;
695061da546Spatrick     object.try_emplace("sourceReference", sourceReference);
696061da546Spatrick   }
697061da546Spatrick   return llvm::json::Value(std::move(object));
698061da546Spatrick }
699061da546Spatrick 
700061da546Spatrick // "StackFrame": {
701061da546Spatrick //   "type": "object",
702061da546Spatrick //   "description": "A Stackframe contains the source location.",
703061da546Spatrick //   "properties": {
704061da546Spatrick //     "id": {
705061da546Spatrick //       "type": "integer",
706061da546Spatrick //       "description": "An identifier for the stack frame. It must be unique
707061da546Spatrick //                       across all threads. This id can be used to retrieve
708061da546Spatrick //                       the scopes of the frame with the 'scopesRequest' or
709061da546Spatrick //                       to restart the execution of a stackframe."
710061da546Spatrick //     },
711061da546Spatrick //     "name": {
712061da546Spatrick //       "type": "string",
713061da546Spatrick //       "description": "The name of the stack frame, typically a method name."
714061da546Spatrick //     },
715061da546Spatrick //     "source": {
716061da546Spatrick //       "$ref": "#/definitions/Source",
717061da546Spatrick //       "description": "The optional source of the frame."
718061da546Spatrick //     },
719061da546Spatrick //     "line": {
720061da546Spatrick //       "type": "integer",
721061da546Spatrick //       "description": "The line within the file of the frame. If source is
722061da546Spatrick //                       null or doesn't exist, line is 0 and must be ignored."
723061da546Spatrick //     },
724061da546Spatrick //     "column": {
725061da546Spatrick //       "type": "integer",
726061da546Spatrick //       "description": "The column within the line. If source is null or
727061da546Spatrick //                       doesn't exist, column is 0 and must be ignored."
728061da546Spatrick //     },
729061da546Spatrick //     "endLine": {
730061da546Spatrick //       "type": "integer",
731061da546Spatrick //       "description": "An optional end line of the range covered by the
732061da546Spatrick //                       stack frame."
733061da546Spatrick //     },
734061da546Spatrick //     "endColumn": {
735061da546Spatrick //       "type": "integer",
736061da546Spatrick //       "description": "An optional end column of the range covered by the
737061da546Spatrick //                       stack frame."
738061da546Spatrick //     },
739061da546Spatrick //     "moduleId": {
740061da546Spatrick //       "type": ["integer", "string"],
741061da546Spatrick //       "description": "The module associated with this frame, if any."
742061da546Spatrick //     },
743061da546Spatrick //     "presentationHint": {
744061da546Spatrick //       "type": "string",
745061da546Spatrick //       "enum": [ "normal", "label", "subtle" ],
746061da546Spatrick //       "description": "An optional hint for how to present this frame in
747061da546Spatrick //                       the UI. A value of 'label' can be used to indicate
748061da546Spatrick //                       that the frame is an artificial frame that is used
749061da546Spatrick //                       as a visual label or separator. A value of 'subtle'
750061da546Spatrick //                       can be used to change the appearance of a frame in
751061da546Spatrick //                       a 'subtle' way."
752061da546Spatrick //     }
753061da546Spatrick //   },
754061da546Spatrick //   "required": [ "id", "name", "line", "column" ]
755061da546Spatrick // }
CreateStackFrame(lldb::SBFrame & frame)756061da546Spatrick llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
757061da546Spatrick   llvm::json::Object object;
758061da546Spatrick   int64_t frame_id = MakeVSCodeFrameID(frame);
759061da546Spatrick   object.try_emplace("id", frame_id);
760*f6aab3d8Srobert 
761*f6aab3d8Srobert   std::string frame_name;
762*f6aab3d8Srobert   const char *func_name = frame.GetFunctionName();
763*f6aab3d8Srobert   if (func_name)
764*f6aab3d8Srobert     frame_name = func_name;
765*f6aab3d8Srobert   else
766*f6aab3d8Srobert     frame_name = "<unknown>";
767*f6aab3d8Srobert   bool is_optimized = frame.GetFunction().GetIsOptimized();
768*f6aab3d8Srobert   if (is_optimized)
769*f6aab3d8Srobert     frame_name += " [opt]";
770*f6aab3d8Srobert   EmplaceSafeString(object, "name", frame_name);
771*f6aab3d8Srobert 
772061da546Spatrick   int64_t disasm_line = 0;
773061da546Spatrick   object.try_emplace("source", CreateSource(frame, disasm_line));
774061da546Spatrick 
775061da546Spatrick   auto line_entry = frame.GetLineEntry();
776061da546Spatrick   if (disasm_line > 0) {
777061da546Spatrick     object.try_emplace("line", disasm_line);
778061da546Spatrick   } else {
779061da546Spatrick     auto line = line_entry.GetLine();
780061da546Spatrick     if (line == UINT32_MAX)
781061da546Spatrick       line = 0;
782061da546Spatrick     object.try_emplace("line", line);
783061da546Spatrick   }
784061da546Spatrick   object.try_emplace("column", line_entry.GetColumn());
785061da546Spatrick   return llvm::json::Value(std::move(object));
786061da546Spatrick }
787061da546Spatrick 
788061da546Spatrick // "Thread": {
789061da546Spatrick //   "type": "object",
790061da546Spatrick //   "description": "A Thread",
791061da546Spatrick //   "properties": {
792061da546Spatrick //     "id": {
793061da546Spatrick //       "type": "integer",
794061da546Spatrick //       "description": "Unique identifier for the thread."
795061da546Spatrick //     },
796061da546Spatrick //     "name": {
797061da546Spatrick //       "type": "string",
798061da546Spatrick //       "description": "A name of the thread."
799061da546Spatrick //     }
800061da546Spatrick //   },
801061da546Spatrick //   "required": [ "id", "name" ]
802061da546Spatrick // }
CreateThread(lldb::SBThread & thread)803061da546Spatrick llvm::json::Value CreateThread(lldb::SBThread &thread) {
804061da546Spatrick   llvm::json::Object object;
805061da546Spatrick   object.try_emplace("id", (int64_t)thread.GetThreadID());
806061da546Spatrick   char thread_str[64];
807061da546Spatrick   snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
808061da546Spatrick   const char *name = thread.GetName();
809061da546Spatrick   if (name) {
810061da546Spatrick     std::string thread_with_name(thread_str);
811061da546Spatrick     thread_with_name += ' ';
812061da546Spatrick     thread_with_name += name;
813061da546Spatrick     EmplaceSafeString(object, "name", thread_with_name);
814061da546Spatrick   } else {
815061da546Spatrick     EmplaceSafeString(object, "name", std::string(thread_str));
816061da546Spatrick   }
817061da546Spatrick   return llvm::json::Value(std::move(object));
818061da546Spatrick }
819061da546Spatrick 
820061da546Spatrick // "StoppedEvent": {
821061da546Spatrick //   "allOf": [ { "$ref": "#/definitions/Event" }, {
822061da546Spatrick //     "type": "object",
823061da546Spatrick //     "description": "Event message for 'stopped' event type. The event
824061da546Spatrick //                     indicates that the execution of the debuggee has stopped
825061da546Spatrick //                     due to some condition. This can be caused by a break
826061da546Spatrick //                     point previously set, a stepping action has completed,
827061da546Spatrick //                     by executing a debugger statement etc.",
828061da546Spatrick //     "properties": {
829061da546Spatrick //       "event": {
830061da546Spatrick //         "type": "string",
831061da546Spatrick //         "enum": [ "stopped" ]
832061da546Spatrick //       },
833061da546Spatrick //       "body": {
834061da546Spatrick //         "type": "object",
835061da546Spatrick //         "properties": {
836061da546Spatrick //           "reason": {
837061da546Spatrick //             "type": "string",
838061da546Spatrick //             "description": "The reason for the event. For backward
839061da546Spatrick //                             compatibility this string is shown in the UI if
840061da546Spatrick //                             the 'description' attribute is missing (but it
841061da546Spatrick //                             must not be translated).",
842061da546Spatrick //             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
843061da546Spatrick //           },
844061da546Spatrick //           "description": {
845061da546Spatrick //             "type": "string",
846061da546Spatrick //             "description": "The full reason for the event, e.g. 'Paused
847061da546Spatrick //                             on exception'. This string is shown in the UI
848061da546Spatrick //                             as is."
849061da546Spatrick //           },
850061da546Spatrick //           "threadId": {
851061da546Spatrick //             "type": "integer",
852061da546Spatrick //             "description": "The thread which was stopped."
853061da546Spatrick //           },
854061da546Spatrick //           "text": {
855061da546Spatrick //             "type": "string",
856061da546Spatrick //             "description": "Additional information. E.g. if reason is
857061da546Spatrick //                             'exception', text contains the exception name.
858061da546Spatrick //                             This string is shown in the UI."
859061da546Spatrick //           },
860061da546Spatrick //           "allThreadsStopped": {
861061da546Spatrick //             "type": "boolean",
862061da546Spatrick //             "description": "If allThreadsStopped is true, a debug adapter
863061da546Spatrick //                             can announce that all threads have stopped.
864061da546Spatrick //                             The client should use this information to
865061da546Spatrick //                             enable that all threads can be expanded to
866061da546Spatrick //                             access their stacktraces. If the attribute
867061da546Spatrick //                             is missing or false, only the thread with the
868061da546Spatrick //                             given threadId can be expanded."
869061da546Spatrick //           }
870061da546Spatrick //         },
871061da546Spatrick //         "required": [ "reason" ]
872061da546Spatrick //       }
873061da546Spatrick //     },
874061da546Spatrick //     "required": [ "event", "body" ]
875061da546Spatrick //   }]
876061da546Spatrick // }
CreateThreadStopped(lldb::SBThread & thread,uint32_t stop_id)877061da546Spatrick llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
878061da546Spatrick                                       uint32_t stop_id) {
879061da546Spatrick   llvm::json::Object event(CreateEventObject("stopped"));
880061da546Spatrick   llvm::json::Object body;
881061da546Spatrick   switch (thread.GetStopReason()) {
882061da546Spatrick   case lldb::eStopReasonTrace:
883061da546Spatrick   case lldb::eStopReasonPlanComplete:
884061da546Spatrick     body.try_emplace("reason", "step");
885061da546Spatrick     break;
886061da546Spatrick   case lldb::eStopReasonBreakpoint: {
887061da546Spatrick     ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
888061da546Spatrick     if (exc_bp) {
889061da546Spatrick       body.try_emplace("reason", "exception");
890061da546Spatrick       EmplaceSafeString(body, "description", exc_bp->label);
891061da546Spatrick     } else {
892061da546Spatrick       body.try_emplace("reason", "breakpoint");
893dda28197Spatrick       char desc_str[64];
894dda28197Spatrick       uint64_t bp_id = thread.GetStopReasonDataAtIndex(0);
895dda28197Spatrick       uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
896dda28197Spatrick       snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64,
897dda28197Spatrick                bp_id, bp_loc_id);
898dda28197Spatrick       EmplaceSafeString(body, "description", desc_str);
899061da546Spatrick     }
900061da546Spatrick   } break;
901061da546Spatrick   case lldb::eStopReasonWatchpoint:
902061da546Spatrick   case lldb::eStopReasonInstrumentation:
903061da546Spatrick     body.try_emplace("reason", "breakpoint");
904061da546Spatrick     break;
905be691f3bSpatrick   case lldb::eStopReasonProcessorTrace:
906be691f3bSpatrick     body.try_emplace("reason", "processor trace");
907061da546Spatrick     break;
908be691f3bSpatrick   case lldb::eStopReasonSignal:
909061da546Spatrick   case lldb::eStopReasonException:
910061da546Spatrick     body.try_emplace("reason", "exception");
911061da546Spatrick     break;
912061da546Spatrick   case lldb::eStopReasonExec:
913061da546Spatrick     body.try_emplace("reason", "entry");
914061da546Spatrick     break;
915be691f3bSpatrick   case lldb::eStopReasonFork:
916be691f3bSpatrick     body.try_emplace("reason", "fork");
917be691f3bSpatrick     break;
918be691f3bSpatrick   case lldb::eStopReasonVFork:
919be691f3bSpatrick     body.try_emplace("reason", "vfork");
920be691f3bSpatrick     break;
921be691f3bSpatrick   case lldb::eStopReasonVForkDone:
922be691f3bSpatrick     body.try_emplace("reason", "vforkdone");
923be691f3bSpatrick     break;
924061da546Spatrick   case lldb::eStopReasonThreadExiting:
925061da546Spatrick   case lldb::eStopReasonInvalid:
926061da546Spatrick   case lldb::eStopReasonNone:
927061da546Spatrick     break;
928061da546Spatrick   }
929061da546Spatrick   if (stop_id == 0)
930061da546Spatrick     body.try_emplace("reason", "entry");
931061da546Spatrick   const lldb::tid_t tid = thread.GetThreadID();
932061da546Spatrick   body.try_emplace("threadId", (int64_t)tid);
933061da546Spatrick   // If no description has been set, then set it to the default thread stopped
934061da546Spatrick   // description. If we have breakpoints that get hit and shouldn't be reported
935061da546Spatrick   // as breakpoints, then they will set the description above.
936*f6aab3d8Srobert   if (!ObjectContainsKey(body, "description")) {
937061da546Spatrick     char description[1024];
938061da546Spatrick     if (thread.GetStopDescription(description, sizeof(description))) {
939061da546Spatrick       EmplaceSafeString(body, "description", std::string(description));
940061da546Spatrick     }
941061da546Spatrick   }
942061da546Spatrick   if (tid == g_vsc.focus_tid) {
943061da546Spatrick     body.try_emplace("threadCausedFocus", true);
944061da546Spatrick   }
945061da546Spatrick   body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
946061da546Spatrick   body.try_emplace("allThreadsStopped", true);
947061da546Spatrick   event.try_emplace("body", std::move(body));
948061da546Spatrick   return llvm::json::Value(std::move(event));
949061da546Spatrick }
950061da546Spatrick 
GetNonNullVariableName(lldb::SBValue v)951be691f3bSpatrick const char *GetNonNullVariableName(lldb::SBValue v) {
952be691f3bSpatrick   const char *name = v.GetName();
953be691f3bSpatrick   return name ? name : "<null>";
954be691f3bSpatrick }
955be691f3bSpatrick 
CreateUniqueVariableNameForDisplay(lldb::SBValue v,bool is_name_duplicated)956be691f3bSpatrick std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
957be691f3bSpatrick                                                bool is_name_duplicated) {
958be691f3bSpatrick   lldb::SBStream name_builder;
959be691f3bSpatrick   name_builder.Print(GetNonNullVariableName(v));
960be691f3bSpatrick   if (is_name_duplicated) {
961be691f3bSpatrick     lldb::SBDeclaration declaration = v.GetDeclaration();
962be691f3bSpatrick     const char *file_name = declaration.GetFileSpec().GetFilename();
963be691f3bSpatrick     const uint32_t line = declaration.GetLine();
964be691f3bSpatrick 
965be691f3bSpatrick     if (file_name != nullptr && line > 0)
966be691f3bSpatrick       name_builder.Printf(" @ %s:%u", file_name, line);
967be691f3bSpatrick     else if (const char *location = v.GetLocation())
968be691f3bSpatrick       name_builder.Printf(" @ %s", location);
969be691f3bSpatrick   }
970be691f3bSpatrick   return name_builder.GetData();
971be691f3bSpatrick }
972be691f3bSpatrick 
973061da546Spatrick // "Variable": {
974061da546Spatrick //   "type": "object",
975061da546Spatrick //   "description": "A Variable is a name/value pair. Optionally a variable
976061da546Spatrick //                   can have a 'type' that is shown if space permits or when
977061da546Spatrick //                   hovering over the variable's name. An optional 'kind' is
978061da546Spatrick //                   used to render additional properties of the variable,
979061da546Spatrick //                   e.g. different icons can be used to indicate that a
980061da546Spatrick //                   variable is public or private. If the value is
981061da546Spatrick //                   structured (has children), a handle is provided to
982061da546Spatrick //                   retrieve the children with the VariablesRequest. If
983061da546Spatrick //                   the number of named or indexed children is large, the
984061da546Spatrick //                   numbers should be returned via the optional
985061da546Spatrick //                   'namedVariables' and 'indexedVariables' attributes. The
986061da546Spatrick //                   client can use this optional information to present the
987061da546Spatrick //                   children in a paged UI and fetch them in chunks.",
988061da546Spatrick //   "properties": {
989061da546Spatrick //     "name": {
990061da546Spatrick //       "type": "string",
991061da546Spatrick //       "description": "The variable's name."
992061da546Spatrick //     },
993061da546Spatrick //     "value": {
994061da546Spatrick //       "type": "string",
995061da546Spatrick //       "description": "The variable's value. This can be a multi-line text,
996061da546Spatrick //                       e.g. for a function the body of a function."
997061da546Spatrick //     },
998061da546Spatrick //     "type": {
999061da546Spatrick //       "type": "string",
1000061da546Spatrick //       "description": "The type of the variable's value. Typically shown in
1001061da546Spatrick //                       the UI when hovering over the value."
1002061da546Spatrick //     },
1003061da546Spatrick //     "presentationHint": {
1004061da546Spatrick //       "$ref": "#/definitions/VariablePresentationHint",
1005061da546Spatrick //       "description": "Properties of a variable that can be used to determine
1006061da546Spatrick //                       how to render the variable in the UI."
1007061da546Spatrick //     },
1008061da546Spatrick //     "evaluateName": {
1009061da546Spatrick //       "type": "string",
1010061da546Spatrick //       "description": "Optional evaluatable name of this variable which can
1011061da546Spatrick //                       be passed to the 'EvaluateRequest' to fetch the
1012061da546Spatrick //                       variable's value."
1013061da546Spatrick //     },
1014061da546Spatrick //     "variablesReference": {
1015061da546Spatrick //       "type": "integer",
1016061da546Spatrick //       "description": "If variablesReference is > 0, the variable is
1017061da546Spatrick //                       structured and its children can be retrieved by
1018061da546Spatrick //                       passing variablesReference to the VariablesRequest."
1019061da546Spatrick //     },
1020061da546Spatrick //     "namedVariables": {
1021061da546Spatrick //       "type": "integer",
1022061da546Spatrick //       "description": "The number of named child variables. The client can
1023061da546Spatrick //                       use this optional information to present the children
1024061da546Spatrick //                       in a paged UI and fetch them in chunks."
1025061da546Spatrick //     },
1026061da546Spatrick //     "indexedVariables": {
1027061da546Spatrick //       "type": "integer",
1028061da546Spatrick //       "description": "The number of indexed child variables. The client
1029061da546Spatrick //                       can use this optional information to present the
1030061da546Spatrick //                       children in a paged UI and fetch them in chunks."
1031061da546Spatrick //     }
1032061da546Spatrick //   },
1033061da546Spatrick //   "required": [ "name", "value", "variablesReference" ]
1034061da546Spatrick // }
CreateVariable(lldb::SBValue v,int64_t variablesReference,int64_t varID,bool format_hex,bool is_name_duplicated)1035061da546Spatrick llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1036be691f3bSpatrick                                  int64_t varID, bool format_hex,
1037be691f3bSpatrick                                  bool is_name_duplicated) {
1038061da546Spatrick   llvm::json::Object object;
1039be691f3bSpatrick   EmplaceSafeString(object, "name",
1040be691f3bSpatrick                     CreateUniqueVariableNameForDisplay(v, is_name_duplicated));
1041be691f3bSpatrick 
1042061da546Spatrick   if (format_hex)
1043061da546Spatrick     v.SetFormat(lldb::eFormatHex);
1044061da546Spatrick   SetValueForKey(v, object, "value");
1045*f6aab3d8Srobert   auto type_obj = v.GetType();
1046*f6aab3d8Srobert   auto type_cstr = type_obj.GetDisplayTypeName();
1047*f6aab3d8Srobert   // If we have a type with many many children, we would like to be able to
1048*f6aab3d8Srobert   // give a hint to the IDE that the type has indexed children so that the
1049*f6aab3d8Srobert   // request can be broken up in grabbing only a few children at a time. We want
1050*f6aab3d8Srobert   // to be careful and only call "v.GetNumChildren()" if we have an array type
1051*f6aab3d8Srobert   // or if we have a synthetic child provider. We don't want to call
1052*f6aab3d8Srobert   // "v.GetNumChildren()" on all objects as class, struct and union types don't
1053*f6aab3d8Srobert   // need to be completed if they are never expanded. So we want to avoid
1054*f6aab3d8Srobert   // calling this to only cases where we it makes sense to keep performance high
1055*f6aab3d8Srobert   // during normal debugging.
1056*f6aab3d8Srobert 
1057*f6aab3d8Srobert   // If we have an array type, say that it is indexed and provide the number of
1058*f6aab3d8Srobert   // children in case we have a huge array. If we don't do this, then we might
1059*f6aab3d8Srobert   // take a while to produce all children at onces which can delay your debug
1060*f6aab3d8Srobert   // session.
1061*f6aab3d8Srobert   const bool is_array = type_obj.IsArrayType();
1062*f6aab3d8Srobert   const bool is_synthetic = v.IsSynthetic();
1063*f6aab3d8Srobert   if (is_array || is_synthetic) {
1064*f6aab3d8Srobert     const auto num_children = v.GetNumChildren();
1065*f6aab3d8Srobert     if (is_array) {
1066*f6aab3d8Srobert       object.try_emplace("indexedVariables", num_children);
1067*f6aab3d8Srobert     } else {
1068*f6aab3d8Srobert       // If a type has a synthetic child provider, then the SBType of "v" won't
1069*f6aab3d8Srobert       // tell us anything about what might be displayed. So we can check if the
1070*f6aab3d8Srobert       // first child's name is "[0]" and then we can say it is indexed.
1071*f6aab3d8Srobert       const char *first_child_name = v.GetChildAtIndex(0).GetName();
1072*f6aab3d8Srobert       if (first_child_name && strcmp(first_child_name, "[0]") == 0)
1073*f6aab3d8Srobert         object.try_emplace("indexedVariables", num_children);
1074*f6aab3d8Srobert     }
1075*f6aab3d8Srobert   }
1076061da546Spatrick   EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
1077061da546Spatrick   if (varID != INT64_MAX)
1078061da546Spatrick     object.try_emplace("id", varID);
1079061da546Spatrick   if (v.MightHaveChildren())
1080061da546Spatrick     object.try_emplace("variablesReference", variablesReference);
1081061da546Spatrick   else
1082061da546Spatrick     object.try_emplace("variablesReference", (int64_t)0);
1083061da546Spatrick   lldb::SBStream evaluateStream;
1084061da546Spatrick   v.GetExpressionPath(evaluateStream);
1085061da546Spatrick   const char *evaluateName = evaluateStream.GetData();
1086061da546Spatrick   if (evaluateName && evaluateName[0])
1087061da546Spatrick     EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
1088061da546Spatrick   return llvm::json::Value(std::move(object));
1089061da546Spatrick }
1090061da546Spatrick 
CreateCompileUnit(lldb::SBCompileUnit unit)1091dda28197Spatrick llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) {
1092dda28197Spatrick   llvm::json::Object object;
1093dda28197Spatrick   char unit_path_arr[PATH_MAX];
1094dda28197Spatrick   unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
1095dda28197Spatrick   std::string unit_path(unit_path_arr);
1096dda28197Spatrick   object.try_emplace("compileUnitPath", unit_path);
1097dda28197Spatrick   return llvm::json::Value(std::move(object));
1098dda28197Spatrick }
1099061da546Spatrick 
1100be691f3bSpatrick /// See
1101be691f3bSpatrick /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
1102be691f3bSpatrick llvm::json::Object
CreateRunInTerminalReverseRequest(const llvm::json::Object & launch_request,llvm::StringRef debug_adaptor_path,llvm::StringRef comm_file)1103be691f3bSpatrick CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
1104be691f3bSpatrick                                   llvm::StringRef debug_adaptor_path,
1105be691f3bSpatrick                                   llvm::StringRef comm_file) {
1106be691f3bSpatrick   llvm::json::Object reverse_request;
1107be691f3bSpatrick   reverse_request.try_emplace("type", "request");
1108be691f3bSpatrick   reverse_request.try_emplace("command", "runInTerminal");
1109be691f3bSpatrick 
1110be691f3bSpatrick   llvm::json::Object run_in_terminal_args;
1111be691f3bSpatrick   // This indicates the IDE to open an embedded terminal, instead of opening the
1112be691f3bSpatrick   // terminal in a new window.
1113be691f3bSpatrick   run_in_terminal_args.try_emplace("kind", "integrated");
1114be691f3bSpatrick 
1115be691f3bSpatrick   auto launch_request_arguments = launch_request.getObject("arguments");
1116be691f3bSpatrick   // The program path must be the first entry in the "args" field
1117be691f3bSpatrick   std::vector<std::string> args = {
1118be691f3bSpatrick       debug_adaptor_path.str(), "--comm-file", comm_file.str(),
1119be691f3bSpatrick       "--launch-target", GetString(launch_request_arguments, "program").str()};
1120be691f3bSpatrick   std::vector<std::string> target_args =
1121be691f3bSpatrick       GetStrings(launch_request_arguments, "args");
1122be691f3bSpatrick   args.insert(args.end(), target_args.begin(), target_args.end());
1123be691f3bSpatrick   run_in_terminal_args.try_emplace("args", args);
1124be691f3bSpatrick 
1125be691f3bSpatrick   const auto cwd = GetString(launch_request_arguments, "cwd");
1126be691f3bSpatrick   if (!cwd.empty())
1127be691f3bSpatrick     run_in_terminal_args.try_emplace("cwd", cwd);
1128be691f3bSpatrick 
1129be691f3bSpatrick   // We need to convert the input list of environments variables into a
1130be691f3bSpatrick   // dictionary
1131be691f3bSpatrick   std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1132be691f3bSpatrick   llvm::json::Object environment;
1133be691f3bSpatrick   for (const std::string &env : envs) {
1134be691f3bSpatrick     size_t index = env.find('=');
1135be691f3bSpatrick     environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1136be691f3bSpatrick   }
1137be691f3bSpatrick   run_in_terminal_args.try_emplace("env",
1138be691f3bSpatrick                                    llvm::json::Value(std::move(environment)));
1139be691f3bSpatrick 
1140be691f3bSpatrick   reverse_request.try_emplace(
1141be691f3bSpatrick       "arguments", llvm::json::Value(std::move(run_in_terminal_args)));
1142be691f3bSpatrick   return reverse_request;
1143be691f3bSpatrick }
1144be691f3bSpatrick 
1145*f6aab3d8Srobert // Keep all the top level items from the statistics dump, except for the
1146*f6aab3d8Srobert // "modules" array. It can be huge and cause delay
1147*f6aab3d8Srobert // Array and dictionary value will return as <key, JSON string> pairs
FilterAndGetValueForKey(const lldb::SBStructuredData data,const char * key,llvm::json::Object & out)1148*f6aab3d8Srobert void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key,
1149*f6aab3d8Srobert                              llvm::json::Object &out) {
1150*f6aab3d8Srobert   lldb::SBStructuredData value = data.GetValueForKey(key);
1151*f6aab3d8Srobert   std::string key_utf8 = llvm::json::fixUTF8(key);
1152*f6aab3d8Srobert   if (strcmp(key, "modules") == 0)
1153*f6aab3d8Srobert     return;
1154*f6aab3d8Srobert   switch (value.GetType()) {
1155*f6aab3d8Srobert   case lldb::eStructuredDataTypeFloat:
1156*f6aab3d8Srobert     out.try_emplace(key_utf8, value.GetFloatValue());
1157*f6aab3d8Srobert     break;
1158*f6aab3d8Srobert   case lldb::eStructuredDataTypeInteger:
1159*f6aab3d8Srobert     out.try_emplace(key_utf8, value.GetIntegerValue());
1160*f6aab3d8Srobert     break;
1161*f6aab3d8Srobert   case lldb::eStructuredDataTypeArray: {
1162*f6aab3d8Srobert     lldb::SBStream contents;
1163*f6aab3d8Srobert     value.GetAsJSON(contents);
1164*f6aab3d8Srobert     out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1165*f6aab3d8Srobert   } break;
1166*f6aab3d8Srobert   case lldb::eStructuredDataTypeBoolean:
1167*f6aab3d8Srobert     out.try_emplace(key_utf8, value.GetBooleanValue());
1168*f6aab3d8Srobert     break;
1169*f6aab3d8Srobert   case lldb::eStructuredDataTypeString: {
1170*f6aab3d8Srobert     // Get the string size before reading
1171*f6aab3d8Srobert     const size_t str_length = value.GetStringValue(nullptr, 0);
1172*f6aab3d8Srobert     std::string str(str_length + 1, 0);
1173*f6aab3d8Srobert     value.GetStringValue(&str[0], str_length);
1174*f6aab3d8Srobert     out.try_emplace(key_utf8, llvm::json::fixUTF8(str));
1175*f6aab3d8Srobert   } break;
1176*f6aab3d8Srobert   case lldb::eStructuredDataTypeDictionary: {
1177*f6aab3d8Srobert     lldb::SBStream contents;
1178*f6aab3d8Srobert     value.GetAsJSON(contents);
1179*f6aab3d8Srobert     out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
1180*f6aab3d8Srobert   } break;
1181*f6aab3d8Srobert   case lldb::eStructuredDataTypeNull:
1182*f6aab3d8Srobert   case lldb::eStructuredDataTypeGeneric:
1183*f6aab3d8Srobert   case lldb::eStructuredDataTypeInvalid:
1184*f6aab3d8Srobert     break;
1185*f6aab3d8Srobert   }
1186*f6aab3d8Srobert }
1187*f6aab3d8Srobert 
addStatistic(llvm::json::Object & event)1188*f6aab3d8Srobert void addStatistic(llvm::json::Object &event) {
1189*f6aab3d8Srobert   lldb::SBStructuredData statistics = g_vsc.target.GetStatistics();
1190*f6aab3d8Srobert   bool is_dictionary =
1191*f6aab3d8Srobert       statistics.GetType() == lldb::eStructuredDataTypeDictionary;
1192*f6aab3d8Srobert   if (!is_dictionary)
1193*f6aab3d8Srobert     return;
1194*f6aab3d8Srobert   llvm::json::Object stats_body;
1195*f6aab3d8Srobert 
1196*f6aab3d8Srobert   lldb::SBStringList keys;
1197*f6aab3d8Srobert   if (!statistics.GetKeys(keys))
1198*f6aab3d8Srobert     return;
1199*f6aab3d8Srobert   for (size_t i = 0; i < keys.GetSize(); i++) {
1200*f6aab3d8Srobert     const char *key = keys.GetStringAtIndex(i);
1201*f6aab3d8Srobert     FilterAndGetValueForKey(statistics, key, stats_body);
1202*f6aab3d8Srobert   }
1203*f6aab3d8Srobert   event.try_emplace("statistics", std::move(stats_body));
1204*f6aab3d8Srobert }
1205*f6aab3d8Srobert 
CreateTerminatedEventObject()1206*f6aab3d8Srobert llvm::json::Object CreateTerminatedEventObject() {
1207*f6aab3d8Srobert   llvm::json::Object event(CreateEventObject("terminated"));
1208*f6aab3d8Srobert   addStatistic(event);
1209*f6aab3d8Srobert   return event;
1210*f6aab3d8Srobert }
1211*f6aab3d8Srobert 
JSONToString(const llvm::json::Value & json)1212be691f3bSpatrick std::string JSONToString(const llvm::json::Value &json) {
1213be691f3bSpatrick   std::string data;
1214be691f3bSpatrick   llvm::raw_string_ostream os(data);
1215be691f3bSpatrick   os << json;
1216be691f3bSpatrick   os.flush();
1217be691f3bSpatrick   return data;
1218be691f3bSpatrick }
1219be691f3bSpatrick 
1220dda28197Spatrick } // namespace lldb_vscode
1221