xref: /llvm-project/lldb/tools/lldb-dap/JSONUtils.cpp (revision 22561cfb443267905d4190f0e2a738e6b412457f)
101263c6cSJonas Devlieghere //===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
201263c6cSJonas Devlieghere //
301263c6cSJonas Devlieghere // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
401263c6cSJonas Devlieghere // See https://llvm.org/LICENSE.txt for license information.
501263c6cSJonas Devlieghere // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
601263c6cSJonas Devlieghere //
701263c6cSJonas Devlieghere //===----------------------------------------------------------------------===//
801263c6cSJonas Devlieghere 
9b99d4112SJohn Harrison #include "JSONUtils.h"
1001263c6cSJonas Devlieghere 
11b99d4112SJohn Harrison #include "BreakpointBase.h"
12b99d4112SJohn Harrison #include "DAP.h"
13b99d4112SJohn Harrison #include "ExceptionBreakpoint.h"
14b99d4112SJohn Harrison #include "LLDBUtils.h"
15b99d4112SJohn Harrison #include "lldb/API/SBAddress.h"
16b99d4112SJohn Harrison #include "lldb/API/SBCompileUnit.h"
1701263c6cSJonas Devlieghere #include "lldb/API/SBDeclaration.h"
18b99d4112SJohn Harrison #include "lldb/API/SBEnvironment.h"
19b99d4112SJohn Harrison #include "lldb/API/SBError.h"
20b99d4112SJohn Harrison #include "lldb/API/SBFileSpec.h"
21b99d4112SJohn Harrison #include "lldb/API/SBFrame.h"
22b99d4112SJohn Harrison #include "lldb/API/SBFunction.h"
23b99d4112SJohn Harrison #include "lldb/API/SBLineEntry.h"
24b99d4112SJohn Harrison #include "lldb/API/SBModule.h"
25b99d4112SJohn Harrison #include "lldb/API/SBQueue.h"
26b99d4112SJohn Harrison #include "lldb/API/SBSection.h"
2709c258efSAdrian Vogelsgesang #include "lldb/API/SBStream.h"
2801263c6cSJonas Devlieghere #include "lldb/API/SBStringList.h"
2901263c6cSJonas Devlieghere #include "lldb/API/SBStructuredData.h"
30b99d4112SJohn Harrison #include "lldb/API/SBTarget.h"
31b99d4112SJohn Harrison #include "lldb/API/SBThread.h"
32b99d4112SJohn Harrison #include "lldb/API/SBType.h"
3301263c6cSJonas Devlieghere #include "lldb/API/SBValue.h"
348d8d9f0eSJohn Harrison #include "lldb/Host/PosixApi.h" // IWYU pragma: keep
35b99d4112SJohn Harrison #include "lldb/lldb-defines.h"
36b99d4112SJohn Harrison #include "lldb/lldb-enumerations.h"
37b99d4112SJohn Harrison #include "lldb/lldb-types.h"
38b99d4112SJohn Harrison #include "llvm/ADT/DenseMap.h"
39b99d4112SJohn Harrison #include "llvm/ADT/StringExtras.h"
40b99d4112SJohn Harrison #include "llvm/ADT/StringRef.h"
41b99d4112SJohn Harrison #include "llvm/Support/Compiler.h"
42b99d4112SJohn Harrison #include "llvm/Support/Format.h"
43b99d4112SJohn Harrison #include "llvm/Support/FormatVariadic.h"
44b99d4112SJohn Harrison #include "llvm/Support/Path.h"
45b99d4112SJohn Harrison #include "llvm/Support/ScopedPrinter.h"
46b99d4112SJohn Harrison #include "llvm/Support/raw_ostream.h"
47b99d4112SJohn Harrison #include <chrono>
48b99d4112SJohn Harrison #include <climits>
49b99d4112SJohn Harrison #include <cstddef>
50b99d4112SJohn Harrison #include <iomanip>
51b99d4112SJohn Harrison #include <optional>
52b99d4112SJohn Harrison #include <sstream>
53b99d4112SJohn Harrison #include <string>
54b99d4112SJohn Harrison #include <utility>
55b99d4112SJohn Harrison #include <vector>
5601263c6cSJonas Devlieghere 
5701263c6cSJonas Devlieghere namespace lldb_dap {
5801263c6cSJonas Devlieghere 
5901263c6cSJonas Devlieghere void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
6001263c6cSJonas Devlieghere                        llvm::StringRef str) {
6101263c6cSJonas Devlieghere   if (LLVM_LIKELY(llvm::json::isUTF8(str)))
6201263c6cSJonas Devlieghere     obj.try_emplace(key, str.str());
6301263c6cSJonas Devlieghere   else
6401263c6cSJonas Devlieghere     obj.try_emplace(key, llvm::json::fixUTF8(str));
6501263c6cSJonas Devlieghere }
6601263c6cSJonas Devlieghere 
6701263c6cSJonas Devlieghere llvm::StringRef GetAsString(const llvm::json::Value &value) {
6801263c6cSJonas Devlieghere   if (auto s = value.getAsString())
6901263c6cSJonas Devlieghere     return *s;
7001263c6cSJonas Devlieghere   return llvm::StringRef();
7101263c6cSJonas Devlieghere }
7201263c6cSJonas Devlieghere 
7301263c6cSJonas Devlieghere // Gets a string from a JSON object using the key, or returns an empty string.
7410664813SWalter Erquinigo llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key,
7510664813SWalter Erquinigo                           llvm::StringRef defaultValue) {
7601263c6cSJonas Devlieghere   if (std::optional<llvm::StringRef> value = obj.getString(key))
7701263c6cSJonas Devlieghere     return *value;
7810664813SWalter Erquinigo   return defaultValue;
7901263c6cSJonas Devlieghere }
8001263c6cSJonas Devlieghere 
8110664813SWalter Erquinigo llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key,
8210664813SWalter Erquinigo                           llvm::StringRef defaultValue) {
8301263c6cSJonas Devlieghere   if (obj == nullptr)
8410664813SWalter Erquinigo     return defaultValue;
8585ee3fc7Sjeffreytan81   return GetString(*obj, key, defaultValue);
8601263c6cSJonas Devlieghere }
8701263c6cSJonas Devlieghere 
8801263c6cSJonas Devlieghere // Gets an unsigned integer from a JSON object using the key, or returns the
8901263c6cSJonas Devlieghere // specified fail value.
9001263c6cSJonas Devlieghere uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
9101263c6cSJonas Devlieghere                      uint64_t fail_value) {
9201263c6cSJonas Devlieghere   if (auto value = obj.getInteger(key))
9301263c6cSJonas Devlieghere     return (uint64_t)*value;
9401263c6cSJonas Devlieghere   return fail_value;
9501263c6cSJonas Devlieghere }
9601263c6cSJonas Devlieghere 
9701263c6cSJonas Devlieghere uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
9801263c6cSJonas Devlieghere                      uint64_t fail_value) {
9901263c6cSJonas Devlieghere   if (obj == nullptr)
10001263c6cSJonas Devlieghere     return fail_value;
10101263c6cSJonas Devlieghere   return GetUnsigned(*obj, key, fail_value);
10201263c6cSJonas Devlieghere }
10301263c6cSJonas Devlieghere 
10401263c6cSJonas Devlieghere bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
10501263c6cSJonas Devlieghere                 bool fail_value) {
10601263c6cSJonas Devlieghere   if (auto value = obj.getBoolean(key))
10701263c6cSJonas Devlieghere     return *value;
10801263c6cSJonas Devlieghere   if (auto value = obj.getInteger(key))
10901263c6cSJonas Devlieghere     return *value != 0;
11001263c6cSJonas Devlieghere   return fail_value;
11101263c6cSJonas Devlieghere }
11201263c6cSJonas Devlieghere 
11301263c6cSJonas Devlieghere bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
11401263c6cSJonas Devlieghere                 bool fail_value) {
11501263c6cSJonas Devlieghere   if (obj == nullptr)
11601263c6cSJonas Devlieghere     return fail_value;
11701263c6cSJonas Devlieghere   return GetBoolean(*obj, key, fail_value);
11801263c6cSJonas Devlieghere }
11901263c6cSJonas Devlieghere 
12001263c6cSJonas Devlieghere int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
12101263c6cSJonas Devlieghere                   int64_t fail_value) {
12201263c6cSJonas Devlieghere   if (auto value = obj.getInteger(key))
12301263c6cSJonas Devlieghere     return *value;
12401263c6cSJonas Devlieghere   return fail_value;
12501263c6cSJonas Devlieghere }
12601263c6cSJonas Devlieghere 
12701263c6cSJonas Devlieghere int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
12801263c6cSJonas Devlieghere                   int64_t fail_value) {
12901263c6cSJonas Devlieghere   if (obj == nullptr)
13001263c6cSJonas Devlieghere     return fail_value;
13101263c6cSJonas Devlieghere   return GetSigned(*obj, key, fail_value);
13201263c6cSJonas Devlieghere }
13301263c6cSJonas Devlieghere 
13401263c6cSJonas Devlieghere bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
13501263c6cSJonas Devlieghere   return obj.find(key) != obj.end();
13601263c6cSJonas Devlieghere }
13701263c6cSJonas Devlieghere 
1383acb1eacSAdrian Vogelsgesang std::string EncodeMemoryReference(lldb::addr_t addr) {
1393acb1eacSAdrian Vogelsgesang   return "0x" + llvm::utohexstr(addr);
1403acb1eacSAdrian Vogelsgesang }
1413acb1eacSAdrian Vogelsgesang 
1423acb1eacSAdrian Vogelsgesang std::optional<lldb::addr_t>
1433acb1eacSAdrian Vogelsgesang DecodeMemoryReference(llvm::StringRef memoryReference) {
1443acb1eacSAdrian Vogelsgesang   if (!memoryReference.starts_with("0x"))
1453acb1eacSAdrian Vogelsgesang     return std::nullopt;
1463acb1eacSAdrian Vogelsgesang 
1473acb1eacSAdrian Vogelsgesang   lldb::addr_t addr;
1483acb1eacSAdrian Vogelsgesang   if (memoryReference.consumeInteger(0, addr))
1493acb1eacSAdrian Vogelsgesang     return std::nullopt;
1503acb1eacSAdrian Vogelsgesang 
1513acb1eacSAdrian Vogelsgesang   return addr;
1523acb1eacSAdrian Vogelsgesang }
1533acb1eacSAdrian Vogelsgesang 
15401263c6cSJonas Devlieghere std::vector<std::string> GetStrings(const llvm::json::Object *obj,
15501263c6cSJonas Devlieghere                                     llvm::StringRef key) {
15601263c6cSJonas Devlieghere   std::vector<std::string> strs;
157*faaf2dbfSJohn Harrison   const auto *json_array = obj->getArray(key);
15801263c6cSJonas Devlieghere   if (!json_array)
15901263c6cSJonas Devlieghere     return strs;
16001263c6cSJonas Devlieghere   for (const auto &value : *json_array) {
16101263c6cSJonas Devlieghere     switch (value.kind()) {
16201263c6cSJonas Devlieghere     case llvm::json::Value::String:
16301263c6cSJonas Devlieghere       strs.push_back(value.getAsString()->str());
16401263c6cSJonas Devlieghere       break;
16501263c6cSJonas Devlieghere     case llvm::json::Value::Number:
16601263c6cSJonas Devlieghere     case llvm::json::Value::Boolean:
16701263c6cSJonas Devlieghere       strs.push_back(llvm::to_string(value));
16801263c6cSJonas Devlieghere       break;
16901263c6cSJonas Devlieghere     case llvm::json::Value::Null:
17001263c6cSJonas Devlieghere     case llvm::json::Value::Object:
17101263c6cSJonas Devlieghere     case llvm::json::Value::Array:
17201263c6cSJonas Devlieghere       break;
17301263c6cSJonas Devlieghere     }
17401263c6cSJonas Devlieghere   }
17501263c6cSJonas Devlieghere   return strs;
17601263c6cSJonas Devlieghere }
17701263c6cSJonas Devlieghere 
178d4c17891SDa-Viper std::unordered_map<std::string, std::string>
179d4c17891SDa-Viper GetStringMap(const llvm::json::Object &obj, llvm::StringRef key) {
180d4c17891SDa-Viper   std::unordered_map<std::string, std::string> strs;
181d4c17891SDa-Viper   const auto *const json_object = obj.getObject(key);
182d4c17891SDa-Viper   if (!json_object)
183d4c17891SDa-Viper     return strs;
184d4c17891SDa-Viper 
185d4c17891SDa-Viper   for (const auto &[key, value] : *json_object) {
186d4c17891SDa-Viper     switch (value.kind()) {
187d4c17891SDa-Viper     case llvm::json::Value::String:
188d4c17891SDa-Viper       strs.emplace(key.str(), value.getAsString()->str());
189d4c17891SDa-Viper       break;
190d4c17891SDa-Viper     case llvm::json::Value::Number:
191d4c17891SDa-Viper     case llvm::json::Value::Boolean:
192d4c17891SDa-Viper       strs.emplace(key.str(), llvm::to_string(value));
193d4c17891SDa-Viper       break;
194d4c17891SDa-Viper     case llvm::json::Value::Null:
195d4c17891SDa-Viper     case llvm::json::Value::Object:
196d4c17891SDa-Viper     case llvm::json::Value::Array:
197d4c17891SDa-Viper       break;
198d4c17891SDa-Viper     }
199d4c17891SDa-Viper   }
200d4c17891SDa-Viper   return strs;
201d4c17891SDa-Viper }
202d4c17891SDa-Viper 
20340a361acSJohn Harrison static bool IsClassStructOrUnionType(lldb::SBType t) {
20440a361acSJohn Harrison   return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct |
205ee63f287SZequan Wu                               lldb::eTypeClassArray)) != 0;
20640a361acSJohn Harrison }
20740a361acSJohn Harrison 
20801263c6cSJonas Devlieghere /// Create a short summary for a container that contains the summary of its
20901263c6cSJonas Devlieghere /// first children, so that the user can get a glimpse of its contents at a
21001263c6cSJonas Devlieghere /// glance.
21101263c6cSJonas Devlieghere static std::optional<std::string>
21201263c6cSJonas Devlieghere TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
21301263c6cSJonas Devlieghere   if (!v.MightHaveChildren())
21401263c6cSJonas Devlieghere     return std::nullopt;
21501263c6cSJonas Devlieghere   /// As this operation can be potentially slow, we limit the total time spent
21601263c6cSJonas Devlieghere   /// fetching children to a few ms.
21701263c6cSJonas Devlieghere   const auto max_evaluation_time = std::chrono::milliseconds(10);
21801263c6cSJonas Devlieghere   /// We don't want to generate a extremely long summary string, so we limit its
21901263c6cSJonas Devlieghere   /// length.
22001263c6cSJonas Devlieghere   const size_t max_length = 32;
22101263c6cSJonas Devlieghere 
22201263c6cSJonas Devlieghere   auto start = std::chrono::steady_clock::now();
22301263c6cSJonas Devlieghere   std::string summary;
22401263c6cSJonas Devlieghere   llvm::raw_string_ostream os(summary);
22501263c6cSJonas Devlieghere   os << "{";
22601263c6cSJonas Devlieghere 
22701263c6cSJonas Devlieghere   llvm::StringRef separator = "";
22801263c6cSJonas Devlieghere 
22901263c6cSJonas Devlieghere   for (size_t i = 0, e = v.GetNumChildren(); i < e; ++i) {
23001263c6cSJonas Devlieghere     // If we reached the time limit or exceeded the number of characters, we
23101263c6cSJonas Devlieghere     // dump `...` to signal that there are more elements in the collection.
23201263c6cSJonas Devlieghere     if (summary.size() > max_length ||
23301263c6cSJonas Devlieghere         (std::chrono::steady_clock::now() - start) > max_evaluation_time) {
23401263c6cSJonas Devlieghere       os << separator << "...";
23501263c6cSJonas Devlieghere       break;
23601263c6cSJonas Devlieghere     }
23701263c6cSJonas Devlieghere     lldb::SBValue child = v.GetChildAtIndex(i);
23801263c6cSJonas Devlieghere 
23901263c6cSJonas Devlieghere     if (llvm::StringRef name = child.GetName(); !name.empty()) {
24040a361acSJohn Harrison       llvm::StringRef desc;
24101263c6cSJonas Devlieghere       if (llvm::StringRef summary = child.GetSummary(); !summary.empty())
24240a361acSJohn Harrison         desc = summary;
24340a361acSJohn Harrison       else if (llvm::StringRef value = child.GetValue(); !value.empty())
24440a361acSJohn Harrison         desc = value;
24540a361acSJohn Harrison       else if (IsClassStructOrUnionType(child.GetType()))
24640a361acSJohn Harrison         desc = "{...}";
24701263c6cSJonas Devlieghere       else
24840a361acSJohn Harrison         continue;
24901263c6cSJonas Devlieghere 
25001263c6cSJonas Devlieghere       // If the child is an indexed entry, we don't show its index to save
25101263c6cSJonas Devlieghere       // characters.
25201263c6cSJonas Devlieghere       if (name.starts_with("["))
25340a361acSJohn Harrison         os << separator << desc;
25401263c6cSJonas Devlieghere       else
25540a361acSJohn Harrison         os << separator << name << ":" << desc;
25601263c6cSJonas Devlieghere       separator = ", ";
25701263c6cSJonas Devlieghere     }
25801263c6cSJonas Devlieghere   }
25901263c6cSJonas Devlieghere   os << "}";
26001263c6cSJonas Devlieghere 
26101263c6cSJonas Devlieghere   if (summary == "{...}" || summary == "{}")
26201263c6cSJonas Devlieghere     return std::nullopt;
26301263c6cSJonas Devlieghere   return summary;
26401263c6cSJonas Devlieghere }
26501263c6cSJonas Devlieghere 
26601263c6cSJonas Devlieghere /// Try to create a summary string for the given value that doesn't have a
26701263c6cSJonas Devlieghere /// summary of its own.
268*faaf2dbfSJohn Harrison static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue &value) {
26901263c6cSJonas Devlieghere   // We use the dereferenced value for generating the summary.
27001263c6cSJonas Devlieghere   if (value.GetType().IsPointerType() || value.GetType().IsReferenceType())
27101263c6cSJonas Devlieghere     value = value.Dereference();
27201263c6cSJonas Devlieghere 
27301263c6cSJonas Devlieghere   // We only support auto summaries for containers.
27401263c6cSJonas Devlieghere   return TryCreateAutoSummaryForContainer(value);
27501263c6cSJonas Devlieghere }
27601263c6cSJonas Devlieghere 
27701263c6cSJonas Devlieghere void FillResponse(const llvm::json::Object &request,
27801263c6cSJonas Devlieghere                   llvm::json::Object &response) {
27901263c6cSJonas Devlieghere   // Fill in all of the needed response fields to a "request" and set "success"
28001263c6cSJonas Devlieghere   // to true by default.
28101263c6cSJonas Devlieghere   response.try_emplace("type", "response");
28201263c6cSJonas Devlieghere   response.try_emplace("seq", (int64_t)0);
28301263c6cSJonas Devlieghere   EmplaceSafeString(response, "command", GetString(request, "command"));
28401263c6cSJonas Devlieghere   const int64_t seq = GetSigned(request, "seq", 0);
28501263c6cSJonas Devlieghere   response.try_emplace("request_seq", seq);
28601263c6cSJonas Devlieghere   response.try_emplace("success", true);
28701263c6cSJonas Devlieghere }
28801263c6cSJonas Devlieghere 
28901263c6cSJonas Devlieghere // "Scope": {
29001263c6cSJonas Devlieghere //   "type": "object",
29101263c6cSJonas Devlieghere //   "description": "A Scope is a named container for variables. Optionally
29201263c6cSJonas Devlieghere //                   a scope can map to a source or a range within a source.",
29301263c6cSJonas Devlieghere //   "properties": {
29401263c6cSJonas Devlieghere //     "name": {
29501263c6cSJonas Devlieghere //       "type": "string",
29601263c6cSJonas Devlieghere //       "description": "Name of the scope such as 'Arguments', 'Locals'."
29701263c6cSJonas Devlieghere //     },
29801263c6cSJonas Devlieghere //     "presentationHint": {
29901263c6cSJonas Devlieghere //       "type": "string",
30001263c6cSJonas Devlieghere //       "description": "An optional hint for how to present this scope in the
30101263c6cSJonas Devlieghere //                       UI. If this attribute is missing, the scope is shown
30201263c6cSJonas Devlieghere //                       with a generic UI.",
30301263c6cSJonas Devlieghere //       "_enum": [ "arguments", "locals", "registers" ],
30401263c6cSJonas Devlieghere //     },
30501263c6cSJonas Devlieghere //     "variablesReference": {
30601263c6cSJonas Devlieghere //       "type": "integer",
30701263c6cSJonas Devlieghere //       "description": "The variables of this scope can be retrieved by
30801263c6cSJonas Devlieghere //                       passing the value of variablesReference to the
30901263c6cSJonas Devlieghere //                       VariablesRequest."
31001263c6cSJonas Devlieghere //     },
31101263c6cSJonas Devlieghere //     "namedVariables": {
31201263c6cSJonas Devlieghere //       "type": "integer",
31301263c6cSJonas Devlieghere //       "description": "The number of named variables in this scope. The
31401263c6cSJonas Devlieghere //                       client can use this optional information to present
31501263c6cSJonas Devlieghere //                       the variables in a paged UI and fetch them in chunks."
31601263c6cSJonas Devlieghere //     },
31701263c6cSJonas Devlieghere //     "indexedVariables": {
31801263c6cSJonas Devlieghere //       "type": "integer",
31901263c6cSJonas Devlieghere //       "description": "The number of indexed variables in this scope. The
32001263c6cSJonas Devlieghere //                       client can use this optional information to present
32101263c6cSJonas Devlieghere //                       the variables in a paged UI and fetch them in chunks."
32201263c6cSJonas Devlieghere //     },
32301263c6cSJonas Devlieghere //     "expensive": {
32401263c6cSJonas Devlieghere //       "type": "boolean",
32501263c6cSJonas Devlieghere //       "description": "If true, the number of variables in this scope is
32601263c6cSJonas Devlieghere //                       large or expensive to retrieve."
32701263c6cSJonas Devlieghere //     },
32801263c6cSJonas Devlieghere //     "source": {
32901263c6cSJonas Devlieghere //       "$ref": "#/definitions/Source",
33001263c6cSJonas Devlieghere //       "description": "Optional source for this scope."
33101263c6cSJonas Devlieghere //     },
33201263c6cSJonas Devlieghere //     "line": {
33301263c6cSJonas Devlieghere //       "type": "integer",
33401263c6cSJonas Devlieghere //       "description": "Optional start line of the range covered by this
33501263c6cSJonas Devlieghere //                       scope."
33601263c6cSJonas Devlieghere //     },
33701263c6cSJonas Devlieghere //     "column": {
33801263c6cSJonas Devlieghere //       "type": "integer",
33901263c6cSJonas Devlieghere //       "description": "Optional start column of the range covered by this
34001263c6cSJonas Devlieghere //                       scope."
34101263c6cSJonas Devlieghere //     },
34201263c6cSJonas Devlieghere //     "endLine": {
34301263c6cSJonas Devlieghere //       "type": "integer",
34401263c6cSJonas Devlieghere //       "description": "Optional end line of the range covered by this scope."
34501263c6cSJonas Devlieghere //     },
34601263c6cSJonas Devlieghere //     "endColumn": {
34701263c6cSJonas Devlieghere //       "type": "integer",
34801263c6cSJonas Devlieghere //       "description": "Optional end column of the range covered by this
34901263c6cSJonas Devlieghere //                       scope."
35001263c6cSJonas Devlieghere //     }
35101263c6cSJonas Devlieghere //   },
35201263c6cSJonas Devlieghere //   "required": [ "name", "variablesReference", "expensive" ]
35301263c6cSJonas Devlieghere // }
35401263c6cSJonas Devlieghere llvm::json::Value CreateScope(const llvm::StringRef name,
35501263c6cSJonas Devlieghere                               int64_t variablesReference,
35601263c6cSJonas Devlieghere                               int64_t namedVariables, bool expensive) {
35701263c6cSJonas Devlieghere   llvm::json::Object object;
35801263c6cSJonas Devlieghere   EmplaceSafeString(object, "name", name.str());
35901263c6cSJonas Devlieghere 
36001263c6cSJonas Devlieghere   // TODO: Support "arguments" scope. At the moment lldb-dap includes the
36101263c6cSJonas Devlieghere   // arguments into the "locals" scope.
36201263c6cSJonas Devlieghere   if (variablesReference == VARREF_LOCALS) {
36301263c6cSJonas Devlieghere     object.try_emplace("presentationHint", "locals");
36401263c6cSJonas Devlieghere   } else if (variablesReference == VARREF_REGS) {
36501263c6cSJonas Devlieghere     object.try_emplace("presentationHint", "registers");
36601263c6cSJonas Devlieghere   }
36701263c6cSJonas Devlieghere 
36801263c6cSJonas Devlieghere   object.try_emplace("variablesReference", variablesReference);
36901263c6cSJonas Devlieghere   object.try_emplace("expensive", expensive);
37001263c6cSJonas Devlieghere   object.try_emplace("namedVariables", namedVariables);
37101263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
37201263c6cSJonas Devlieghere }
37301263c6cSJonas Devlieghere 
37401263c6cSJonas Devlieghere // "Breakpoint": {
37501263c6cSJonas Devlieghere //   "type": "object",
37601263c6cSJonas Devlieghere //   "description": "Information about a Breakpoint created in setBreakpoints
37701263c6cSJonas Devlieghere //                   or setFunctionBreakpoints.",
37801263c6cSJonas Devlieghere //   "properties": {
37901263c6cSJonas Devlieghere //     "id": {
38001263c6cSJonas Devlieghere //       "type": "integer",
38101263c6cSJonas Devlieghere //       "description": "An optional unique identifier for the breakpoint."
38201263c6cSJonas Devlieghere //     },
38301263c6cSJonas Devlieghere //     "verified": {
38401263c6cSJonas Devlieghere //       "type": "boolean",
38501263c6cSJonas Devlieghere //       "description": "If true breakpoint could be set (but not necessarily
38601263c6cSJonas Devlieghere //                       at the desired location)."
38701263c6cSJonas Devlieghere //     },
38801263c6cSJonas Devlieghere //     "message": {
38901263c6cSJonas Devlieghere //       "type": "string",
39001263c6cSJonas Devlieghere //       "description": "An optional message about the state of the breakpoint.
39101263c6cSJonas Devlieghere //                       This is shown to the user and can be used to explain
39201263c6cSJonas Devlieghere //                       why a breakpoint could not be verified."
39301263c6cSJonas Devlieghere //     },
39401263c6cSJonas Devlieghere //     "source": {
39501263c6cSJonas Devlieghere //       "$ref": "#/definitions/Source",
39601263c6cSJonas Devlieghere //       "description": "The source where the breakpoint is located."
39701263c6cSJonas Devlieghere //     },
39801263c6cSJonas Devlieghere //     "line": {
39901263c6cSJonas Devlieghere //       "type": "integer",
40001263c6cSJonas Devlieghere //       "description": "The start line of the actual range covered by the
40101263c6cSJonas Devlieghere //                       breakpoint."
40201263c6cSJonas Devlieghere //     },
40301263c6cSJonas Devlieghere //     "column": {
40401263c6cSJonas Devlieghere //       "type": "integer",
40501263c6cSJonas Devlieghere //       "description": "An optional start column of the actual range covered
40601263c6cSJonas Devlieghere //                       by the breakpoint."
40701263c6cSJonas Devlieghere //     },
40801263c6cSJonas Devlieghere //     "endLine": {
40901263c6cSJonas Devlieghere //       "type": "integer",
41001263c6cSJonas Devlieghere //       "description": "An optional end line of the actual range covered by
41101263c6cSJonas Devlieghere //                       the breakpoint."
41201263c6cSJonas Devlieghere //     },
41301263c6cSJonas Devlieghere //     "endColumn": {
41401263c6cSJonas Devlieghere //       "type": "integer",
41501263c6cSJonas Devlieghere //       "description": "An optional end column of the actual range covered by
41601263c6cSJonas Devlieghere //                       the breakpoint. If no end line is given, then the end
41701263c6cSJonas Devlieghere //                       column is assumed to be in the start line."
41801263c6cSJonas Devlieghere //     }
41901263c6cSJonas Devlieghere //   },
42001263c6cSJonas Devlieghere //   "required": [ "verified" ]
42101263c6cSJonas Devlieghere // }
422d58c128bSZequan Wu llvm::json::Value CreateBreakpoint(BreakpointBase *bp,
42301263c6cSJonas Devlieghere                                    std::optional<llvm::StringRef> request_path,
42401263c6cSJonas Devlieghere                                    std::optional<uint32_t> request_line,
42501263c6cSJonas Devlieghere                                    std::optional<uint32_t> request_column) {
42601263c6cSJonas Devlieghere   llvm::json::Object object;
42701263c6cSJonas Devlieghere   if (request_path)
42801263c6cSJonas Devlieghere     object.try_emplace("source", CreateSource(*request_path));
429d58c128bSZequan Wu   bp->CreateJsonObject(object);
43001263c6cSJonas Devlieghere   // We try to add request_line as a fallback
43101263c6cSJonas Devlieghere   if (request_line)
43201263c6cSJonas Devlieghere     object.try_emplace("line", *request_line);
43301263c6cSJonas Devlieghere   if (request_column)
43401263c6cSJonas Devlieghere     object.try_emplace("column", *request_column);
43501263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
43601263c6cSJonas Devlieghere }
43701263c6cSJonas Devlieghere 
43801263c6cSJonas Devlieghere static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
43901263c6cSJonas Devlieghere   uint64_t debug_info_size = 0;
44001263c6cSJonas Devlieghere   llvm::StringRef section_name(section.GetName());
441744f3891SKazu Hirata   if (section_name.starts_with(".debug") ||
442744f3891SKazu Hirata       section_name.starts_with("__debug") ||
443744f3891SKazu Hirata       section_name.starts_with(".apple") || section_name.starts_with("__apple"))
44401263c6cSJonas Devlieghere     debug_info_size += section.GetFileByteSize();
44501263c6cSJonas Devlieghere   size_t num_sub_sections = section.GetNumSubSections();
44601263c6cSJonas Devlieghere   for (size_t i = 0; i < num_sub_sections; i++) {
44701263c6cSJonas Devlieghere     debug_info_size +=
44801263c6cSJonas Devlieghere         GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
44901263c6cSJonas Devlieghere   }
45001263c6cSJonas Devlieghere   return debug_info_size;
45101263c6cSJonas Devlieghere }
45201263c6cSJonas Devlieghere 
45301263c6cSJonas Devlieghere static uint64_t GetDebugInfoSize(lldb::SBModule module) {
45401263c6cSJonas Devlieghere   uint64_t debug_info_size = 0;
45501263c6cSJonas Devlieghere   size_t num_sections = module.GetNumSections();
45601263c6cSJonas Devlieghere   for (size_t i = 0; i < num_sections; i++) {
45701263c6cSJonas Devlieghere     debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
45801263c6cSJonas Devlieghere   }
45901263c6cSJonas Devlieghere   return debug_info_size;
46001263c6cSJonas Devlieghere }
46101263c6cSJonas Devlieghere 
46201263c6cSJonas Devlieghere static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
46301263c6cSJonas Devlieghere   std::ostringstream oss;
46401263c6cSJonas Devlieghere   oss << std::fixed << std::setprecision(1);
46501263c6cSJonas Devlieghere   if (debug_info < 1024) {
46601263c6cSJonas Devlieghere     oss << debug_info << "B";
46701263c6cSJonas Devlieghere   } else if (debug_info < 1024 * 1024) {
46801263c6cSJonas Devlieghere     double kb = double(debug_info) / 1024.0;
46901263c6cSJonas Devlieghere     oss << kb << "KB";
47001263c6cSJonas Devlieghere   } else if (debug_info < 1024 * 1024 * 1024) {
47101263c6cSJonas Devlieghere     double mb = double(debug_info) / (1024.0 * 1024.0);
47201263c6cSJonas Devlieghere     oss << mb << "MB";
47301263c6cSJonas Devlieghere   } else {
47401263c6cSJonas Devlieghere     double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
47501263c6cSJonas Devlieghere     oss << gb << "GB";
47601263c6cSJonas Devlieghere   }
47701263c6cSJonas Devlieghere   return oss.str();
47801263c6cSJonas Devlieghere }
479*faaf2dbfSJohn Harrison 
480*faaf2dbfSJohn Harrison llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) {
48101263c6cSJonas Devlieghere   llvm::json::Object object;
482*faaf2dbfSJohn Harrison   if (!target.IsValid() || !module.IsValid())
48301263c6cSJonas Devlieghere     return llvm::json::Value(std::move(object));
484*faaf2dbfSJohn Harrison 
48501263c6cSJonas Devlieghere   const char *uuid = module.GetUUIDString();
48601263c6cSJonas Devlieghere   object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
48701263c6cSJonas Devlieghere   object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
48801263c6cSJonas Devlieghere   char module_path_arr[PATH_MAX];
48901263c6cSJonas Devlieghere   module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
49001263c6cSJonas Devlieghere   std::string module_path(module_path_arr);
49101263c6cSJonas Devlieghere   object.try_emplace("path", module_path);
49201263c6cSJonas Devlieghere   if (module.GetNumCompileUnits() > 0) {
49301263c6cSJonas Devlieghere     std::string symbol_str = "Symbols loaded.";
49401263c6cSJonas Devlieghere     std::string debug_info_size;
49501263c6cSJonas Devlieghere     uint64_t debug_info = GetDebugInfoSize(module);
49601263c6cSJonas Devlieghere     if (debug_info > 0) {
49701263c6cSJonas Devlieghere       debug_info_size = ConvertDebugInfoSizeToString(debug_info);
49801263c6cSJonas Devlieghere     }
49901263c6cSJonas Devlieghere     object.try_emplace("symbolStatus", symbol_str);
50001263c6cSJonas Devlieghere     object.try_emplace("debugInfoSize", debug_info_size);
50101263c6cSJonas Devlieghere     char symbol_path_arr[PATH_MAX];
50201263c6cSJonas Devlieghere     module.GetSymbolFileSpec().GetPath(symbol_path_arr,
50301263c6cSJonas Devlieghere                                        sizeof(symbol_path_arr));
50401263c6cSJonas Devlieghere     std::string symbol_path(symbol_path_arr);
50501263c6cSJonas Devlieghere     object.try_emplace("symbolFilePath", symbol_path);
50601263c6cSJonas Devlieghere   } else {
50701263c6cSJonas Devlieghere     object.try_emplace("symbolStatus", "Symbols not found.");
50801263c6cSJonas Devlieghere   }
50901263c6cSJonas Devlieghere   std::string loaded_addr = std::to_string(
510*faaf2dbfSJohn Harrison       module.GetObjectFileHeaderAddress().GetLoadAddress(target));
51101263c6cSJonas Devlieghere   object.try_emplace("addressRange", loaded_addr);
51201263c6cSJonas Devlieghere   std::string version_str;
51301263c6cSJonas Devlieghere   uint32_t version_nums[3];
51401263c6cSJonas Devlieghere   uint32_t num_versions =
51501263c6cSJonas Devlieghere       module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
51601263c6cSJonas Devlieghere   for (uint32_t i = 0; i < num_versions; ++i) {
51701263c6cSJonas Devlieghere     if (!version_str.empty())
51801263c6cSJonas Devlieghere       version_str += ".";
51901263c6cSJonas Devlieghere     version_str += std::to_string(version_nums[i]);
52001263c6cSJonas Devlieghere   }
52101263c6cSJonas Devlieghere   if (!version_str.empty())
52201263c6cSJonas Devlieghere     object.try_emplace("version", version_str);
52301263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
52401263c6cSJonas Devlieghere }
52501263c6cSJonas Devlieghere 
526d58c128bSZequan Wu void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints,
52701263c6cSJonas Devlieghere                       std::optional<llvm::StringRef> request_path,
52801263c6cSJonas Devlieghere                       std::optional<uint32_t> request_line) {
52901263c6cSJonas Devlieghere   breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
53001263c6cSJonas Devlieghere }
53101263c6cSJonas Devlieghere 
53201263c6cSJonas Devlieghere // "Event": {
53301263c6cSJonas Devlieghere //   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
53401263c6cSJonas Devlieghere //     "type": "object",
53501263c6cSJonas Devlieghere //     "description": "Server-initiated event.",
53601263c6cSJonas Devlieghere //     "properties": {
53701263c6cSJonas Devlieghere //       "type": {
53801263c6cSJonas Devlieghere //         "type": "string",
53901263c6cSJonas Devlieghere //         "enum": [ "event" ]
54001263c6cSJonas Devlieghere //       },
54101263c6cSJonas Devlieghere //       "event": {
54201263c6cSJonas Devlieghere //         "type": "string",
54301263c6cSJonas Devlieghere //         "description": "Type of event."
54401263c6cSJonas Devlieghere //       },
54501263c6cSJonas Devlieghere //       "body": {
54601263c6cSJonas Devlieghere //         "type": [ "array", "boolean", "integer", "null", "number" ,
54701263c6cSJonas Devlieghere //                   "object", "string" ],
54801263c6cSJonas Devlieghere //         "description": "Event-specific information."
54901263c6cSJonas Devlieghere //       }
55001263c6cSJonas Devlieghere //     },
55101263c6cSJonas Devlieghere //     "required": [ "type", "event" ]
55201263c6cSJonas Devlieghere //   }]
55301263c6cSJonas Devlieghere // },
55401263c6cSJonas Devlieghere // "ProtocolMessage": {
55501263c6cSJonas Devlieghere //   "type": "object",
55601263c6cSJonas Devlieghere //   "description": "Base class of requests, responses, and events.",
55701263c6cSJonas Devlieghere //   "properties": {
55801263c6cSJonas Devlieghere //         "seq": {
55901263c6cSJonas Devlieghere //           "type": "integer",
56001263c6cSJonas Devlieghere //           "description": "Sequence number."
56101263c6cSJonas Devlieghere //         },
56201263c6cSJonas Devlieghere //         "type": {
56301263c6cSJonas Devlieghere //           "type": "string",
56401263c6cSJonas Devlieghere //           "description": "Message type.",
56501263c6cSJonas Devlieghere //           "_enum": [ "request", "response", "event" ]
56601263c6cSJonas Devlieghere //         }
56701263c6cSJonas Devlieghere //   },
56801263c6cSJonas Devlieghere //   "required": [ "seq", "type" ]
56901263c6cSJonas Devlieghere // }
57001263c6cSJonas Devlieghere llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
57101263c6cSJonas Devlieghere   llvm::json::Object event;
57201263c6cSJonas Devlieghere   event.try_emplace("seq", 0);
57301263c6cSJonas Devlieghere   event.try_emplace("type", "event");
57401263c6cSJonas Devlieghere   EmplaceSafeString(event, "event", event_name);
57501263c6cSJonas Devlieghere   return event;
57601263c6cSJonas Devlieghere }
57701263c6cSJonas Devlieghere 
57801263c6cSJonas Devlieghere // "ExceptionBreakpointsFilter": {
57901263c6cSJonas Devlieghere //   "type": "object",
58001263c6cSJonas Devlieghere //   "description": "An ExceptionBreakpointsFilter is shown in the UI as an
58101263c6cSJonas Devlieghere //                   option for configuring how exceptions are dealt with.",
58201263c6cSJonas Devlieghere //   "properties": {
58301263c6cSJonas Devlieghere //     "filter": {
58401263c6cSJonas Devlieghere //       "type": "string",
58501263c6cSJonas Devlieghere //       "description": "The internal ID of the filter. This value is passed
58601263c6cSJonas Devlieghere //                       to the setExceptionBreakpoints request."
58701263c6cSJonas Devlieghere //     },
58801263c6cSJonas Devlieghere //     "label": {
58901263c6cSJonas Devlieghere //       "type": "string",
59001263c6cSJonas Devlieghere //       "description": "The name of the filter. This will be shown in the UI."
59101263c6cSJonas Devlieghere //     },
59201263c6cSJonas Devlieghere //     "default": {
59301263c6cSJonas Devlieghere //       "type": "boolean",
59401263c6cSJonas Devlieghere //       "description": "Initial value of the filter. If not specified a value
59501263c6cSJonas Devlieghere //                       'false' is assumed."
59601263c6cSJonas Devlieghere //     }
59701263c6cSJonas Devlieghere //   },
59801263c6cSJonas Devlieghere //   "required": [ "filter", "label" ]
59901263c6cSJonas Devlieghere // }
60001263c6cSJonas Devlieghere llvm::json::Value
60101263c6cSJonas Devlieghere CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
60201263c6cSJonas Devlieghere   llvm::json::Object object;
60301263c6cSJonas Devlieghere   EmplaceSafeString(object, "filter", bp.filter);
60401263c6cSJonas Devlieghere   EmplaceSafeString(object, "label", bp.label);
60501263c6cSJonas Devlieghere   object.try_emplace("default", bp.default_value);
60601263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
60701263c6cSJonas Devlieghere }
60801263c6cSJonas Devlieghere 
60901263c6cSJonas Devlieghere // "Source": {
61001263c6cSJonas Devlieghere //   "type": "object",
61101263c6cSJonas Devlieghere //   "description": "A Source is a descriptor for source code. It is returned
61201263c6cSJonas Devlieghere //                   from the debug adapter as part of a StackFrame and it is
61301263c6cSJonas Devlieghere //                   used by clients when specifying breakpoints.",
61401263c6cSJonas Devlieghere //   "properties": {
61501263c6cSJonas Devlieghere //     "name": {
61601263c6cSJonas Devlieghere //       "type": "string",
61701263c6cSJonas Devlieghere //       "description": "The short name of the source. Every source returned
61801263c6cSJonas Devlieghere //                       from the debug adapter has a name. When sending a
61901263c6cSJonas Devlieghere //                       source to the debug adapter this name is optional."
62001263c6cSJonas Devlieghere //     },
62101263c6cSJonas Devlieghere //     "path": {
62201263c6cSJonas Devlieghere //       "type": "string",
62301263c6cSJonas Devlieghere //       "description": "The path of the source to be shown in the UI. It is
62401263c6cSJonas Devlieghere //                       only used to locate and load the content of the
62501263c6cSJonas Devlieghere //                       source if no sourceReference is specified (or its
62601263c6cSJonas Devlieghere //                       value is 0)."
62701263c6cSJonas Devlieghere //     },
62801263c6cSJonas Devlieghere //     "sourceReference": {
62901263c6cSJonas Devlieghere //       "type": "number",
63001263c6cSJonas Devlieghere //       "description": "If sourceReference > 0 the contents of the source must
63101263c6cSJonas Devlieghere //                       be retrieved through the SourceRequest (even if a path
63201263c6cSJonas Devlieghere //                       is specified). A sourceReference is only valid for a
63301263c6cSJonas Devlieghere //                       session, so it must not be used to persist a source."
63401263c6cSJonas Devlieghere //     },
63501263c6cSJonas Devlieghere //     "presentationHint": {
63601263c6cSJonas Devlieghere //       "type": "string",
63701263c6cSJonas Devlieghere //       "description": "An optional hint for how to present the source in the
63801263c6cSJonas Devlieghere //                       UI. A value of 'deemphasize' can be used to indicate
63901263c6cSJonas Devlieghere //                       that the source is not available or that it is
64001263c6cSJonas Devlieghere //                       skipped on stepping.",
64101263c6cSJonas Devlieghere //       "enum": [ "normal", "emphasize", "deemphasize" ]
64201263c6cSJonas Devlieghere //     },
64301263c6cSJonas Devlieghere //     "origin": {
64401263c6cSJonas Devlieghere //       "type": "string",
64501263c6cSJonas Devlieghere //       "description": "The (optional) origin of this source: possible values
64601263c6cSJonas Devlieghere //                       'internal module', 'inlined content from source map',
64701263c6cSJonas Devlieghere //                       etc."
64801263c6cSJonas Devlieghere //     },
64901263c6cSJonas Devlieghere //     "sources": {
65001263c6cSJonas Devlieghere //       "type": "array",
65101263c6cSJonas Devlieghere //       "items": {
65201263c6cSJonas Devlieghere //         "$ref": "#/definitions/Source"
65301263c6cSJonas Devlieghere //       },
65401263c6cSJonas Devlieghere //       "description": "An optional list of sources that are related to this
65501263c6cSJonas Devlieghere //                       source. These may be the source that generated this
65601263c6cSJonas Devlieghere //                       source."
65701263c6cSJonas Devlieghere //     },
65801263c6cSJonas Devlieghere //     "adapterData": {
65901263c6cSJonas Devlieghere //       "type":["array","boolean","integer","null","number","object","string"],
66001263c6cSJonas Devlieghere //       "description": "Optional data that a debug adapter might want to loop
66101263c6cSJonas Devlieghere //                       through the client. The client should leave the data
66201263c6cSJonas Devlieghere //                       intact and persist it across sessions. The client
66301263c6cSJonas Devlieghere //                       should not interpret the data."
66401263c6cSJonas Devlieghere //     },
66501263c6cSJonas Devlieghere //     "checksums": {
66601263c6cSJonas Devlieghere //       "type": "array",
66701263c6cSJonas Devlieghere //       "items": {
66801263c6cSJonas Devlieghere //         "$ref": "#/definitions/Checksum"
66901263c6cSJonas Devlieghere //       },
67001263c6cSJonas Devlieghere //       "description": "The checksums associated with this file."
67101263c6cSJonas Devlieghere //     }
67201263c6cSJonas Devlieghere //   }
67301263c6cSJonas Devlieghere // }
6740cc2cd78SAdrian Vogelsgesang llvm::json::Value CreateSource(const lldb::SBFileSpec &file) {
67501263c6cSJonas Devlieghere   llvm::json::Object object;
67601263c6cSJonas Devlieghere   if (file.IsValid()) {
67701263c6cSJonas Devlieghere     const char *name = file.GetFilename();
67801263c6cSJonas Devlieghere     if (name)
67901263c6cSJonas Devlieghere       EmplaceSafeString(object, "name", name);
68001263c6cSJonas Devlieghere     char path[PATH_MAX] = "";
68101263c6cSJonas Devlieghere     if (file.GetPath(path, sizeof(path)) &&
68201263c6cSJonas Devlieghere         lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
68301263c6cSJonas Devlieghere       EmplaceSafeString(object, "path", std::string(path));
68401263c6cSJonas Devlieghere     }
68501263c6cSJonas Devlieghere   }
68601263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
68701263c6cSJonas Devlieghere }
68801263c6cSJonas Devlieghere 
6890cc2cd78SAdrian Vogelsgesang llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) {
6900cc2cd78SAdrian Vogelsgesang   return CreateSource(line_entry.GetFileSpec());
6910cc2cd78SAdrian Vogelsgesang }
6920cc2cd78SAdrian Vogelsgesang 
69301263c6cSJonas Devlieghere llvm::json::Value CreateSource(llvm::StringRef source_path) {
69401263c6cSJonas Devlieghere   llvm::json::Object source;
69501263c6cSJonas Devlieghere   llvm::StringRef name = llvm::sys::path::filename(source_path);
69601263c6cSJonas Devlieghere   EmplaceSafeString(source, "name", name);
69701263c6cSJonas Devlieghere   EmplaceSafeString(source, "path", source_path);
69801263c6cSJonas Devlieghere   return llvm::json::Value(std::move(source));
69901263c6cSJonas Devlieghere }
70001263c6cSJonas Devlieghere 
701*faaf2dbfSJohn Harrison static std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
70201263c6cSJonas Devlieghere   auto line_entry = frame.GetLineEntry();
70301263c6cSJonas Devlieghere   // A line entry of 0 indicates the line is compiler generated i.e. no source
70401263c6cSJonas Devlieghere   // file is associated with the frame.
70501263c6cSJonas Devlieghere   if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0)
70601263c6cSJonas Devlieghere     return CreateSource(line_entry);
70701263c6cSJonas Devlieghere 
70801263c6cSJonas Devlieghere   return {};
70901263c6cSJonas Devlieghere }
71001263c6cSJonas Devlieghere 
71101263c6cSJonas Devlieghere // "StackFrame": {
71201263c6cSJonas Devlieghere //   "type": "object",
71301263c6cSJonas Devlieghere //   "description": "A Stackframe contains the source location.",
71401263c6cSJonas Devlieghere //   "properties": {
71501263c6cSJonas Devlieghere //     "id": {
71601263c6cSJonas Devlieghere //       "type": "integer",
71701263c6cSJonas Devlieghere //       "description": "An identifier for the stack frame. It must be unique
71801263c6cSJonas Devlieghere //                       across all threads. This id can be used to retrieve
71901263c6cSJonas Devlieghere //                       the scopes of the frame with the 'scopesRequest' or
72001263c6cSJonas Devlieghere //                       to restart the execution of a stackframe."
72101263c6cSJonas Devlieghere //     },
72201263c6cSJonas Devlieghere //     "name": {
72301263c6cSJonas Devlieghere //       "type": "string",
72401263c6cSJonas Devlieghere //       "description": "The name of the stack frame, typically a method name."
72501263c6cSJonas Devlieghere //     },
72601263c6cSJonas Devlieghere //     "source": {
72701263c6cSJonas Devlieghere //       "$ref": "#/definitions/Source",
72801263c6cSJonas Devlieghere //       "description": "The optional source of the frame."
72901263c6cSJonas Devlieghere //     },
73001263c6cSJonas Devlieghere //     "line": {
73101263c6cSJonas Devlieghere //       "type": "integer",
73201263c6cSJonas Devlieghere //       "description": "The line within the file of the frame. If source is
73301263c6cSJonas Devlieghere //                       null or doesn't exist, line is 0 and must be ignored."
73401263c6cSJonas Devlieghere //     },
73501263c6cSJonas Devlieghere //     "column": {
73601263c6cSJonas Devlieghere //       "type": "integer",
73701263c6cSJonas Devlieghere //       "description": "The column within the line. If source is null or
73801263c6cSJonas Devlieghere //                       doesn't exist, column is 0 and must be ignored."
73901263c6cSJonas Devlieghere //     },
74001263c6cSJonas Devlieghere //     "endLine": {
74101263c6cSJonas Devlieghere //       "type": "integer",
74201263c6cSJonas Devlieghere //       "description": "An optional end line of the range covered by the
74301263c6cSJonas Devlieghere //                       stack frame."
74401263c6cSJonas Devlieghere //     },
74501263c6cSJonas Devlieghere //     "endColumn": {
74601263c6cSJonas Devlieghere //       "type": "integer",
74701263c6cSJonas Devlieghere //       "description": "An optional end column of the range covered by the
74801263c6cSJonas Devlieghere //                       stack frame."
74901263c6cSJonas Devlieghere //     },
75001263c6cSJonas Devlieghere //     "instructionPointerReference": {
75101263c6cSJonas Devlieghere // 	     "type": "string",
75201263c6cSJonas Devlieghere // 	     "description": "A memory reference for the current instruction
7533acb1eacSAdrian Vogelsgesang //                         pointer in this frame."
75401263c6cSJonas Devlieghere //     },
75501263c6cSJonas Devlieghere //     "moduleId": {
75601263c6cSJonas Devlieghere //       "type": ["integer", "string"],
75701263c6cSJonas Devlieghere //       "description": "The module associated with this frame, if any."
75801263c6cSJonas Devlieghere //     },
75901263c6cSJonas Devlieghere //     "presentationHint": {
76001263c6cSJonas Devlieghere //       "type": "string",
76101263c6cSJonas Devlieghere //       "enum": [ "normal", "label", "subtle" ],
76201263c6cSJonas Devlieghere //       "description": "An optional hint for how to present this frame in
76301263c6cSJonas Devlieghere //                       the UI. A value of 'label' can be used to indicate
76401263c6cSJonas Devlieghere //                       that the frame is an artificial frame that is used
76501263c6cSJonas Devlieghere //                       as a visual label or separator. A value of 'subtle'
76601263c6cSJonas Devlieghere //                       can be used to change the appearance of a frame in
76701263c6cSJonas Devlieghere //                       a 'subtle' way."
76801263c6cSJonas Devlieghere //     }
76901263c6cSJonas Devlieghere //   },
77001263c6cSJonas Devlieghere //   "required": [ "id", "name", "line", "column" ]
77101263c6cSJonas Devlieghere // }
772*faaf2dbfSJohn Harrison llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
773*faaf2dbfSJohn Harrison                                    lldb::SBFormat &format) {
77401263c6cSJonas Devlieghere   llvm::json::Object object;
77501263c6cSJonas Devlieghere   int64_t frame_id = MakeDAPFrameID(frame);
77601263c6cSJonas Devlieghere   object.try_emplace("id", frame_id);
77701263c6cSJonas Devlieghere 
778d9ec4b24SWalter Erquinigo   std::string frame_name;
779d9ec4b24SWalter Erquinigo   lldb::SBStream stream;
780*faaf2dbfSJohn Harrison   if (format && frame.GetDescriptionWithFormat(format, stream).Success()) {
781d9ec4b24SWalter Erquinigo     frame_name = stream.GetData();
782d9ec4b24SWalter Erquinigo 
783d9ec4b24SWalter Erquinigo     // `function_name` can be a nullptr, which throws an error when assigned to
784d9ec4b24SWalter Erquinigo     // an `std::string`.
785d9ec4b24SWalter Erquinigo   } else if (const char *name = frame.GetDisplayFunctionName()) {
786d9ec4b24SWalter Erquinigo     frame_name = name;
787d9ec4b24SWalter Erquinigo   }
788d9ec4b24SWalter Erquinigo 
78901263c6cSJonas Devlieghere   if (frame_name.empty()) {
79001263c6cSJonas Devlieghere     // If the function name is unavailable, display the pc address as a 16-digit
79101263c6cSJonas Devlieghere     // hex string, e.g. "0x0000000000012345"
79201263c6cSJonas Devlieghere     llvm::raw_string_ostream os(frame_name);
79301263c6cSJonas Devlieghere     os << llvm::format_hex(frame.GetPC(), 18);
79401263c6cSJonas Devlieghere   }
79507ed3258SWalter Erquinigo 
79607ed3258SWalter Erquinigo   // We only include `[opt]` if a custom frame format is not specified.
797*faaf2dbfSJohn Harrison   if (!format && frame.GetFunction().GetIsOptimized())
79801263c6cSJonas Devlieghere     frame_name += " [opt]";
79907ed3258SWalter Erquinigo 
80001263c6cSJonas Devlieghere   EmplaceSafeString(object, "name", frame_name);
80101263c6cSJonas Devlieghere 
80201263c6cSJonas Devlieghere   auto source = CreateSource(frame);
80301263c6cSJonas Devlieghere 
80401263c6cSJonas Devlieghere   if (source) {
80501263c6cSJonas Devlieghere     object.try_emplace("source", *source);
80601263c6cSJonas Devlieghere     auto line_entry = frame.GetLineEntry();
80701263c6cSJonas Devlieghere     auto line = line_entry.GetLine();
80801263c6cSJonas Devlieghere     if (line && line != LLDB_INVALID_LINE_NUMBER)
80901263c6cSJonas Devlieghere       object.try_emplace("line", line);
81099f42e6bSXu Jun     else
81199f42e6bSXu Jun       object.try_emplace("line", 0);
81201263c6cSJonas Devlieghere     auto column = line_entry.GetColumn();
81301263c6cSJonas Devlieghere     object.try_emplace("column", column);
81401263c6cSJonas Devlieghere   } else {
81501263c6cSJonas Devlieghere     object.try_emplace("line", 0);
81601263c6cSJonas Devlieghere     object.try_emplace("column", 0);
81701263c6cSJonas Devlieghere   }
81801263c6cSJonas Devlieghere 
81901263c6cSJonas Devlieghere   const auto pc = frame.GetPC();
82001263c6cSJonas Devlieghere   if (pc != LLDB_INVALID_ADDRESS) {
82101263c6cSJonas Devlieghere     std::string formatted_addr = "0x" + llvm::utohexstr(pc);
82201263c6cSJonas Devlieghere     object.try_emplace("instructionPointerReference", formatted_addr);
82301263c6cSJonas Devlieghere   }
82401263c6cSJonas Devlieghere 
8259e9e8238SAdrian Prantl   if (frame.IsArtificial() || frame.IsHidden())
8269e9e8238SAdrian Prantl     object.try_emplace("presentationHint", "subtle");
8279e9e8238SAdrian Prantl 
82801263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
82901263c6cSJonas Devlieghere }
83001263c6cSJonas Devlieghere 
831*faaf2dbfSJohn Harrison llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread,
832*faaf2dbfSJohn Harrison                                                 lldb::SBFormat &format) {
8335b4100ccSJohn Harrison   std::string name;
8345b4100ccSJohn Harrison   lldb::SBStream stream;
835*faaf2dbfSJohn Harrison   if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
8365b4100ccSJohn Harrison     name = stream.GetData();
8375b4100ccSJohn Harrison   } else {
8385b4100ccSJohn Harrison     const uint32_t thread_idx = thread.GetExtendedBacktraceOriginatingIndexID();
8395b4100ccSJohn Harrison     const char *queue_name = thread.GetQueueName();
8405b4100ccSJohn Harrison     if (queue_name != nullptr) {
8415b4100ccSJohn Harrison       name = llvm::formatv("Enqueued from {0} (Thread {1})", queue_name,
8425b4100ccSJohn Harrison                            thread_idx);
8435b4100ccSJohn Harrison     } else {
8445b4100ccSJohn Harrison       name = llvm::formatv("Thread {0}", thread_idx);
8455b4100ccSJohn Harrison     }
8465b4100ccSJohn Harrison   }
8475b4100ccSJohn Harrison 
8485b4100ccSJohn Harrison   return llvm::json::Value(llvm::json::Object{{"id", thread.GetThreadID() + 1},
8495b4100ccSJohn Harrison                                               {"name", name},
8505b4100ccSJohn Harrison                                               {"presentationHint", "label"}});
8515b4100ccSJohn Harrison }
8525b4100ccSJohn Harrison 
85301263c6cSJonas Devlieghere // "Thread": {
85401263c6cSJonas Devlieghere //   "type": "object",
85501263c6cSJonas Devlieghere //   "description": "A Thread",
85601263c6cSJonas Devlieghere //   "properties": {
85701263c6cSJonas Devlieghere //     "id": {
85801263c6cSJonas Devlieghere //       "type": "integer",
85901263c6cSJonas Devlieghere //       "description": "Unique identifier for the thread."
86001263c6cSJonas Devlieghere //     },
86101263c6cSJonas Devlieghere //     "name": {
86201263c6cSJonas Devlieghere //       "type": "string",
86301263c6cSJonas Devlieghere //       "description": "A name of the thread."
86401263c6cSJonas Devlieghere //     }
86501263c6cSJonas Devlieghere //   },
86601263c6cSJonas Devlieghere //   "required": [ "id", "name" ]
86701263c6cSJonas Devlieghere // }
868*faaf2dbfSJohn Harrison llvm::json::Value CreateThread(lldb::SBThread &thread, lldb::SBFormat &format) {
86901263c6cSJonas Devlieghere   llvm::json::Object object;
87001263c6cSJonas Devlieghere   object.try_emplace("id", (int64_t)thread.GetThreadID());
8711654d7dcSWalter Erquinigo   std::string thread_str;
8721654d7dcSWalter Erquinigo   lldb::SBStream stream;
873*faaf2dbfSJohn Harrison   if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
8741654d7dcSWalter Erquinigo     thread_str = stream.GetData();
8751654d7dcSWalter Erquinigo   } else {
87601263c6cSJonas Devlieghere     const char *thread_name = thread.GetName();
87701263c6cSJonas Devlieghere     const char *queue_name = thread.GetQueueName();
87801263c6cSJonas Devlieghere 
87901263c6cSJonas Devlieghere     if (thread_name) {
88001263c6cSJonas Devlieghere       thread_str = std::string(thread_name);
88101263c6cSJonas Devlieghere     } else if (queue_name) {
88201263c6cSJonas Devlieghere       auto kind = thread.GetQueue().GetKind();
88301263c6cSJonas Devlieghere       std::string queue_kind_label = "";
88401263c6cSJonas Devlieghere       if (kind == lldb::eQueueKindSerial) {
88501263c6cSJonas Devlieghere         queue_kind_label = " (serial)";
88601263c6cSJonas Devlieghere       } else if (kind == lldb::eQueueKindConcurrent) {
88701263c6cSJonas Devlieghere         queue_kind_label = " (concurrent)";
88801263c6cSJonas Devlieghere       }
88901263c6cSJonas Devlieghere 
8901654d7dcSWalter Erquinigo       thread_str =
8911654d7dcSWalter Erquinigo           llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
89201263c6cSJonas Devlieghere                         queue_name, queue_kind_label)
89301263c6cSJonas Devlieghere               .str();
89401263c6cSJonas Devlieghere     } else {
89501263c6cSJonas Devlieghere       thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
89601263c6cSJonas Devlieghere     }
8971654d7dcSWalter Erquinigo   }
89801263c6cSJonas Devlieghere 
89901263c6cSJonas Devlieghere   EmplaceSafeString(object, "name", thread_str);
90001263c6cSJonas Devlieghere 
90101263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
90201263c6cSJonas Devlieghere }
90301263c6cSJonas Devlieghere 
90401263c6cSJonas Devlieghere // "StoppedEvent": {
90501263c6cSJonas Devlieghere //   "allOf": [ { "$ref": "#/definitions/Event" }, {
90601263c6cSJonas Devlieghere //     "type": "object",
90701263c6cSJonas Devlieghere //     "description": "Event message for 'stopped' event type. The event
90801263c6cSJonas Devlieghere //                     indicates that the execution of the debuggee has stopped
90901263c6cSJonas Devlieghere //                     due to some condition. This can be caused by a break
91001263c6cSJonas Devlieghere //                     point previously set, a stepping action has completed,
91101263c6cSJonas Devlieghere //                     by executing a debugger statement etc.",
91201263c6cSJonas Devlieghere //     "properties": {
91301263c6cSJonas Devlieghere //       "event": {
91401263c6cSJonas Devlieghere //         "type": "string",
91501263c6cSJonas Devlieghere //         "enum": [ "stopped" ]
91601263c6cSJonas Devlieghere //       },
91701263c6cSJonas Devlieghere //       "body": {
91801263c6cSJonas Devlieghere //         "type": "object",
91901263c6cSJonas Devlieghere //         "properties": {
92001263c6cSJonas Devlieghere //           "reason": {
92101263c6cSJonas Devlieghere //             "type": "string",
92201263c6cSJonas Devlieghere //             "description": "The reason for the event. For backward
92301263c6cSJonas Devlieghere //                             compatibility this string is shown in the UI if
92401263c6cSJonas Devlieghere //                             the 'description' attribute is missing (but it
92501263c6cSJonas Devlieghere //                             must not be translated).",
92601263c6cSJonas Devlieghere //             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
92701263c6cSJonas Devlieghere //           },
92801263c6cSJonas Devlieghere //           "description": {
92901263c6cSJonas Devlieghere //             "type": "string",
93001263c6cSJonas Devlieghere //             "description": "The full reason for the event, e.g. 'Paused
93101263c6cSJonas Devlieghere //                             on exception'. This string is shown in the UI
93201263c6cSJonas Devlieghere //                             as is."
93301263c6cSJonas Devlieghere //           },
93401263c6cSJonas Devlieghere //           "threadId": {
93501263c6cSJonas Devlieghere //             "type": "integer",
93601263c6cSJonas Devlieghere //             "description": "The thread which was stopped."
93701263c6cSJonas Devlieghere //           },
93801263c6cSJonas Devlieghere //           "text": {
93901263c6cSJonas Devlieghere //             "type": "string",
94001263c6cSJonas Devlieghere //             "description": "Additional information. E.g. if reason is
94101263c6cSJonas Devlieghere //                             'exception', text contains the exception name.
94201263c6cSJonas Devlieghere //                             This string is shown in the UI."
94301263c6cSJonas Devlieghere //           },
94401263c6cSJonas Devlieghere //           "allThreadsStopped": {
94501263c6cSJonas Devlieghere //             "type": "boolean",
94601263c6cSJonas Devlieghere //             "description": "If allThreadsStopped is true, a debug adapter
94701263c6cSJonas Devlieghere //                             can announce that all threads have stopped.
94801263c6cSJonas Devlieghere //                             The client should use this information to
94901263c6cSJonas Devlieghere //                             enable that all threads can be expanded to
95001263c6cSJonas Devlieghere //                             access their stacktraces. If the attribute
95101263c6cSJonas Devlieghere //                             is missing or false, only the thread with the
95201263c6cSJonas Devlieghere //                             given threadId can be expanded."
95301263c6cSJonas Devlieghere //           }
95401263c6cSJonas Devlieghere //         },
95501263c6cSJonas Devlieghere //         "required": [ "reason" ]
95601263c6cSJonas Devlieghere //       }
95701263c6cSJonas Devlieghere //     },
95801263c6cSJonas Devlieghere //     "required": [ "event", "body" ]
95901263c6cSJonas Devlieghere //   }]
96001263c6cSJonas Devlieghere // }
961*faaf2dbfSJohn Harrison llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread,
96201263c6cSJonas Devlieghere                                       uint32_t stop_id) {
96301263c6cSJonas Devlieghere   llvm::json::Object event(CreateEventObject("stopped"));
96401263c6cSJonas Devlieghere   llvm::json::Object body;
96501263c6cSJonas Devlieghere   switch (thread.GetStopReason()) {
96601263c6cSJonas Devlieghere   case lldb::eStopReasonTrace:
96701263c6cSJonas Devlieghere   case lldb::eStopReasonPlanComplete:
96801263c6cSJonas Devlieghere     body.try_emplace("reason", "step");
96901263c6cSJonas Devlieghere     break;
97001263c6cSJonas Devlieghere   case lldb::eStopReasonBreakpoint: {
971*faaf2dbfSJohn Harrison     ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
97201263c6cSJonas Devlieghere     if (exc_bp) {
97301263c6cSJonas Devlieghere       body.try_emplace("reason", "exception");
97401263c6cSJonas Devlieghere       EmplaceSafeString(body, "description", exc_bp->label);
97501263c6cSJonas Devlieghere     } else {
97689c27d6bSSanthosh Kumar Ellendula       InstructionBreakpoint *inst_bp =
977*faaf2dbfSJohn Harrison           dap.GetInstructionBPFromStopReason(thread);
97889c27d6bSSanthosh Kumar Ellendula       if (inst_bp) {
97989c27d6bSSanthosh Kumar Ellendula         body.try_emplace("reason", "instruction breakpoint");
98089c27d6bSSanthosh Kumar Ellendula       } else {
98101263c6cSJonas Devlieghere         body.try_emplace("reason", "breakpoint");
98289c27d6bSSanthosh Kumar Ellendula       }
983c8f72856SJohn Harrison       lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
984c8f72856SJohn Harrison       lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
985c8f72856SJohn Harrison       std::string desc_str =
986c8f72856SJohn Harrison           llvm::formatv("breakpoint {0}.{1}", bp_id, bp_loc_id);
98701263c6cSJonas Devlieghere       body.try_emplace("hitBreakpointIds",
98801263c6cSJonas Devlieghere                        llvm::json::Array{llvm::json::Value(bp_id)});
98901263c6cSJonas Devlieghere       EmplaceSafeString(body, "description", desc_str);
99001263c6cSJonas Devlieghere     }
99101263c6cSJonas Devlieghere   } break;
99201263c6cSJonas Devlieghere   case lldb::eStopReasonWatchpoint:
99301263c6cSJonas Devlieghere   case lldb::eStopReasonInstrumentation:
99401263c6cSJonas Devlieghere     body.try_emplace("reason", "breakpoint");
99501263c6cSJonas Devlieghere     break;
99601263c6cSJonas Devlieghere   case lldb::eStopReasonProcessorTrace:
99701263c6cSJonas Devlieghere     body.try_emplace("reason", "processor trace");
99801263c6cSJonas Devlieghere     break;
99901263c6cSJonas Devlieghere   case lldb::eStopReasonSignal:
100001263c6cSJonas Devlieghere   case lldb::eStopReasonException:
100101263c6cSJonas Devlieghere     body.try_emplace("reason", "exception");
100201263c6cSJonas Devlieghere     break;
100301263c6cSJonas Devlieghere   case lldb::eStopReasonExec:
100401263c6cSJonas Devlieghere     body.try_emplace("reason", "entry");
100501263c6cSJonas Devlieghere     break;
100601263c6cSJonas Devlieghere   case lldb::eStopReasonFork:
100701263c6cSJonas Devlieghere     body.try_emplace("reason", "fork");
100801263c6cSJonas Devlieghere     break;
100901263c6cSJonas Devlieghere   case lldb::eStopReasonVFork:
101001263c6cSJonas Devlieghere     body.try_emplace("reason", "vfork");
101101263c6cSJonas Devlieghere     break;
101201263c6cSJonas Devlieghere   case lldb::eStopReasonVForkDone:
101301263c6cSJonas Devlieghere     body.try_emplace("reason", "vforkdone");
101401263c6cSJonas Devlieghere     break;
1015f838fa82Sjeffreytan81   case lldb::eStopReasonInterrupt:
1016f838fa82Sjeffreytan81     body.try_emplace("reason", "async interrupt");
1017f838fa82Sjeffreytan81     break;
101801263c6cSJonas Devlieghere   case lldb::eStopReasonThreadExiting:
101901263c6cSJonas Devlieghere   case lldb::eStopReasonInvalid:
102001263c6cSJonas Devlieghere   case lldb::eStopReasonNone:
102101263c6cSJonas Devlieghere     break;
102201263c6cSJonas Devlieghere   }
102301263c6cSJonas Devlieghere   if (stop_id == 0)
102401263c6cSJonas Devlieghere     body.try_emplace("reason", "entry");
102501263c6cSJonas Devlieghere   const lldb::tid_t tid = thread.GetThreadID();
102601263c6cSJonas Devlieghere   body.try_emplace("threadId", (int64_t)tid);
102701263c6cSJonas Devlieghere   // If no description has been set, then set it to the default thread stopped
102801263c6cSJonas Devlieghere   // description. If we have breakpoints that get hit and shouldn't be reported
102901263c6cSJonas Devlieghere   // as breakpoints, then they will set the description above.
103001263c6cSJonas Devlieghere   if (!ObjectContainsKey(body, "description")) {
103101263c6cSJonas Devlieghere     char description[1024];
103201263c6cSJonas Devlieghere     if (thread.GetStopDescription(description, sizeof(description))) {
103301263c6cSJonas Devlieghere       EmplaceSafeString(body, "description", std::string(description));
103401263c6cSJonas Devlieghere     }
103501263c6cSJonas Devlieghere   }
103601263c6cSJonas Devlieghere   // "threadCausedFocus" is used in tests to validate breaking behavior.
1037*faaf2dbfSJohn Harrison   if (tid == dap.focus_tid) {
103801263c6cSJonas Devlieghere     body.try_emplace("threadCausedFocus", true);
103901263c6cSJonas Devlieghere   }
1040*faaf2dbfSJohn Harrison   body.try_emplace("preserveFocusHint", tid != dap.focus_tid);
104101263c6cSJonas Devlieghere   body.try_emplace("allThreadsStopped", true);
104201263c6cSJonas Devlieghere   event.try_emplace("body", std::move(body));
104301263c6cSJonas Devlieghere   return llvm::json::Value(std::move(event));
104401263c6cSJonas Devlieghere }
104501263c6cSJonas Devlieghere 
1046*faaf2dbfSJohn Harrison const char *GetNonNullVariableName(lldb::SBValue &v) {
104701263c6cSJonas Devlieghere   const char *name = v.GetName();
104801263c6cSJonas Devlieghere   return name ? name : "<null>";
104901263c6cSJonas Devlieghere }
105001263c6cSJonas Devlieghere 
1051*faaf2dbfSJohn Harrison std::string CreateUniqueVariableNameForDisplay(lldb::SBValue &v,
105201263c6cSJonas Devlieghere                                                bool is_name_duplicated) {
105301263c6cSJonas Devlieghere   lldb::SBStream name_builder;
105401263c6cSJonas Devlieghere   name_builder.Print(GetNonNullVariableName(v));
105501263c6cSJonas Devlieghere   if (is_name_duplicated) {
105601263c6cSJonas Devlieghere     lldb::SBDeclaration declaration = v.GetDeclaration();
105701263c6cSJonas Devlieghere     const char *file_name = declaration.GetFileSpec().GetFilename();
105801263c6cSJonas Devlieghere     const uint32_t line = declaration.GetLine();
105901263c6cSJonas Devlieghere 
106001263c6cSJonas Devlieghere     if (file_name != nullptr && line > 0)
106101263c6cSJonas Devlieghere       name_builder.Printf(" @ %s:%u", file_name, line);
106201263c6cSJonas Devlieghere     else if (const char *location = v.GetLocation())
106301263c6cSJonas Devlieghere       name_builder.Printf(" @ %s", location);
106401263c6cSJonas Devlieghere   }
106501263c6cSJonas Devlieghere   return name_builder.GetData();
106601263c6cSJonas Devlieghere }
106701263c6cSJonas Devlieghere 
1068*faaf2dbfSJohn Harrison VariableDescription::VariableDescription(lldb::SBValue v,
1069*faaf2dbfSJohn Harrison                                          bool auto_variable_summaries,
1070*faaf2dbfSJohn Harrison                                          bool format_hex,
1071ffd173baSWalter Erquinigo                                          bool is_name_duplicated,
1072ffd173baSWalter Erquinigo                                          std::optional<std::string> custom_name)
1073ffd173baSWalter Erquinigo     : v(v) {
1074ffd173baSWalter Erquinigo   name = custom_name
1075ffd173baSWalter Erquinigo              ? *custom_name
1076ffd173baSWalter Erquinigo              : CreateUniqueVariableNameForDisplay(v, is_name_duplicated);
1077ffd173baSWalter Erquinigo 
1078ffd173baSWalter Erquinigo   type_obj = v.GetType();
1079ffd173baSWalter Erquinigo   std::string raw_display_type_name =
1080ffd173baSWalter Erquinigo       llvm::StringRef(type_obj.GetDisplayTypeName()).str();
1081ffd173baSWalter Erquinigo   display_type_name =
1082ffd173baSWalter Erquinigo       !raw_display_type_name.empty() ? raw_display_type_name : NO_TYPENAME;
1083ffd173baSWalter Erquinigo 
1084b8d38bb5Sjeffreytan81   // Only format hex/default if there is no existing special format.
1085b8d38bb5Sjeffreytan81   if (v.GetFormat() == lldb::eFormatDefault ||
1086b8d38bb5Sjeffreytan81       v.GetFormat() == lldb::eFormatHex) {
1087ffd173baSWalter Erquinigo     if (format_hex)
1088ffd173baSWalter Erquinigo       v.SetFormat(lldb::eFormatHex);
1089b8d38bb5Sjeffreytan81     else
1090b8d38bb5Sjeffreytan81       v.SetFormat(lldb::eFormatDefault);
1091b8d38bb5Sjeffreytan81   }
1092ffd173baSWalter Erquinigo 
1093ffd173baSWalter Erquinigo   llvm::raw_string_ostream os_display_value(display_value);
1094ffd173baSWalter Erquinigo 
1095ffd173baSWalter Erquinigo   if (lldb::SBError sb_error = v.GetError(); sb_error.Fail()) {
1096ffd173baSWalter Erquinigo     error = sb_error.GetCString();
1097ffd173baSWalter Erquinigo     os_display_value << "<error: " << error << ">";
1098ffd173baSWalter Erquinigo   } else {
1099ffd173baSWalter Erquinigo     value = llvm::StringRef(v.GetValue()).str();
1100ffd173baSWalter Erquinigo     summary = llvm::StringRef(v.GetSummary()).str();
1101*faaf2dbfSJohn Harrison     if (summary.empty() && auto_variable_summaries)
1102ffd173baSWalter Erquinigo       auto_summary = TryCreateAutoSummary(v);
1103ffd173baSWalter Erquinigo 
1104ffd173baSWalter Erquinigo     std::optional<std::string> effective_summary =
1105ffd173baSWalter Erquinigo         !summary.empty() ? summary : auto_summary;
1106ffd173baSWalter Erquinigo 
1107ffd173baSWalter Erquinigo     if (!value.empty()) {
1108ffd173baSWalter Erquinigo       os_display_value << value;
1109ffd173baSWalter Erquinigo       if (effective_summary)
1110ffd173baSWalter Erquinigo         os_display_value << " " << *effective_summary;
1111ffd173baSWalter Erquinigo     } else if (effective_summary) {
1112ffd173baSWalter Erquinigo       os_display_value << *effective_summary;
1113ffd173baSWalter Erquinigo 
1114ffd173baSWalter Erquinigo       // As last resort, we print its type and address if available.
1115ffd173baSWalter Erquinigo     } else {
1116ffd173baSWalter Erquinigo       if (!raw_display_type_name.empty()) {
1117ffd173baSWalter Erquinigo         os_display_value << raw_display_type_name;
1118ffd173baSWalter Erquinigo         lldb::addr_t address = v.GetLoadAddress();
1119ffd173baSWalter Erquinigo         if (address != LLDB_INVALID_ADDRESS)
1120ffd173baSWalter Erquinigo           os_display_value << " @ " << llvm::format_hex(address, 0);
1121ffd173baSWalter Erquinigo       }
1122ffd173baSWalter Erquinigo     }
1123ffd173baSWalter Erquinigo   }
1124ffd173baSWalter Erquinigo 
1125ffd173baSWalter Erquinigo   lldb::SBStream evaluateStream;
1126ffd173baSWalter Erquinigo   v.GetExpressionPath(evaluateStream);
1127ffd173baSWalter Erquinigo   evaluate_name = llvm::StringRef(evaluateStream.GetData()).str();
1128ffd173baSWalter Erquinigo }
1129ffd173baSWalter Erquinigo 
1130ffd173baSWalter Erquinigo llvm::json::Object VariableDescription::GetVariableExtensionsJSON() {
1131ffd173baSWalter Erquinigo   llvm::json::Object extensions;
1132ffd173baSWalter Erquinigo   if (error)
1133ffd173baSWalter Erquinigo     EmplaceSafeString(extensions, "error", *error);
1134ffd173baSWalter Erquinigo   if (!value.empty())
1135ffd173baSWalter Erquinigo     EmplaceSafeString(extensions, "value", value);
1136ffd173baSWalter Erquinigo   if (!summary.empty())
1137ffd173baSWalter Erquinigo     EmplaceSafeString(extensions, "summary", summary);
1138ffd173baSWalter Erquinigo   if (auto_summary)
1139ffd173baSWalter Erquinigo     EmplaceSafeString(extensions, "autoSummary", *auto_summary);
1140ffd173baSWalter Erquinigo 
1141ffd173baSWalter Erquinigo   if (lldb::SBDeclaration decl = v.GetDeclaration(); decl.IsValid()) {
1142ffd173baSWalter Erquinigo     llvm::json::Object decl_obj;
1143ffd173baSWalter Erquinigo     if (lldb::SBFileSpec file = decl.GetFileSpec(); file.IsValid()) {
1144ffd173baSWalter Erquinigo       char path[PATH_MAX] = "";
1145ffd173baSWalter Erquinigo       if (file.GetPath(path, sizeof(path)) &&
1146ffd173baSWalter Erquinigo           lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
1147ffd173baSWalter Erquinigo         decl_obj.try_emplace("path", std::string(path));
1148ffd173baSWalter Erquinigo       }
1149ffd173baSWalter Erquinigo     }
1150ffd173baSWalter Erquinigo 
1151ffd173baSWalter Erquinigo     if (int line = decl.GetLine())
1152ffd173baSWalter Erquinigo       decl_obj.try_emplace("line", line);
1153ffd173baSWalter Erquinigo     if (int column = decl.GetColumn())
1154ffd173baSWalter Erquinigo       decl_obj.try_emplace("column", column);
1155ffd173baSWalter Erquinigo 
1156ffd173baSWalter Erquinigo     if (!decl_obj.empty())
1157ffd173baSWalter Erquinigo       extensions.try_emplace("declaration", std::move(decl_obj));
1158ffd173baSWalter Erquinigo   }
1159ffd173baSWalter Erquinigo   return extensions;
1160ffd173baSWalter Erquinigo }
1161ffd173baSWalter Erquinigo 
116240a361acSJohn Harrison std::string VariableDescription::GetResult(llvm::StringRef context) {
1163af8f1554SPavel Labath   // In repl context, the results can be displayed as multiple lines so more
1164af8f1554SPavel Labath   // detailed descriptions can be returned.
1165af8f1554SPavel Labath   if (context != "repl")
116640a361acSJohn Harrison     return display_value;
116740a361acSJohn Harrison 
116840a361acSJohn Harrison   if (!v.IsValid())
116940a361acSJohn Harrison     return display_value;
117040a361acSJohn Harrison 
117140a361acSJohn Harrison   // Try the SBValue::GetDescription(), which may call into language runtime
117240a361acSJohn Harrison   // specific formatters (see ValueObjectPrinter).
117340a361acSJohn Harrison   lldb::SBStream stream;
117440a361acSJohn Harrison   v.GetDescription(stream);
117540a361acSJohn Harrison   llvm::StringRef description = stream.GetData();
117640a361acSJohn Harrison   return description.trim().str();
117740a361acSJohn Harrison }
117840a361acSJohn Harrison 
11799f8ae784SAdrian Vogelsgesang bool ValuePointsToCode(lldb::SBValue v) {
11809f8ae784SAdrian Vogelsgesang   if (!v.GetType().GetPointeeType().IsFunctionType())
11819f8ae784SAdrian Vogelsgesang     return false;
11829f8ae784SAdrian Vogelsgesang 
11839f8ae784SAdrian Vogelsgesang   lldb::addr_t addr = v.GetValueAsAddress();
11849f8ae784SAdrian Vogelsgesang   lldb::SBLineEntry line_entry =
1185*faaf2dbfSJohn Harrison       v.GetTarget().ResolveLoadAddress(addr).GetLineEntry();
11869f8ae784SAdrian Vogelsgesang 
11879f8ae784SAdrian Vogelsgesang   return line_entry.IsValid();
11889f8ae784SAdrian Vogelsgesang }
11899f8ae784SAdrian Vogelsgesang 
11909f8ae784SAdrian Vogelsgesang int64_t PackLocation(int64_t var_ref, bool is_value_location) {
11919f8ae784SAdrian Vogelsgesang   return var_ref << 1 | is_value_location;
11929f8ae784SAdrian Vogelsgesang }
11939f8ae784SAdrian Vogelsgesang 
11949f8ae784SAdrian Vogelsgesang std::pair<int64_t, bool> UnpackLocation(int64_t location_id) {
11959f8ae784SAdrian Vogelsgesang   return std::pair{location_id >> 1, location_id & 1};
11969f8ae784SAdrian Vogelsgesang }
11979f8ae784SAdrian Vogelsgesang 
119801263c6cSJonas Devlieghere // "Variable": {
119901263c6cSJonas Devlieghere //   "type": "object",
120001263c6cSJonas Devlieghere //   "description": "A Variable is a name/value pair. Optionally a variable
120101263c6cSJonas Devlieghere //                   can have a 'type' that is shown if space permits or when
120201263c6cSJonas Devlieghere //                   hovering over the variable's name. An optional 'kind' is
120301263c6cSJonas Devlieghere //                   used to render additional properties of the variable,
120401263c6cSJonas Devlieghere //                   e.g. different icons can be used to indicate that a
120501263c6cSJonas Devlieghere //                   variable is public or private. If the value is
120601263c6cSJonas Devlieghere //                   structured (has children), a handle is provided to
120701263c6cSJonas Devlieghere //                   retrieve the children with the VariablesRequest. If
120801263c6cSJonas Devlieghere //                   the number of named or indexed children is large, the
120901263c6cSJonas Devlieghere //                   numbers should be returned via the optional
121001263c6cSJonas Devlieghere //                   'namedVariables' and 'indexedVariables' attributes. The
121101263c6cSJonas Devlieghere //                   client can use this optional information to present the
121201263c6cSJonas Devlieghere //                   children in a paged UI and fetch them in chunks.",
121301263c6cSJonas Devlieghere //   "properties": {
121401263c6cSJonas Devlieghere //     "name": {
121501263c6cSJonas Devlieghere //       "type": "string",
121601263c6cSJonas Devlieghere //       "description": "The variable's name."
121701263c6cSJonas Devlieghere //     },
121801263c6cSJonas Devlieghere //     "value": {
121901263c6cSJonas Devlieghere //       "type": "string",
122001263c6cSJonas Devlieghere //       "description": "The variable's value. This can be a multi-line text,
122101263c6cSJonas Devlieghere //                       e.g. for a function the body of a function."
122201263c6cSJonas Devlieghere //     },
122301263c6cSJonas Devlieghere //     "type": {
122401263c6cSJonas Devlieghere //       "type": "string",
122501263c6cSJonas Devlieghere //       "description": "The type of the variable's value. Typically shown in
122601263c6cSJonas Devlieghere //                       the UI when hovering over the value."
122701263c6cSJonas Devlieghere //     },
122801263c6cSJonas Devlieghere //     "presentationHint": {
122901263c6cSJonas Devlieghere //       "$ref": "#/definitions/VariablePresentationHint",
123001263c6cSJonas Devlieghere //       "description": "Properties of a variable that can be used to determine
123101263c6cSJonas Devlieghere //                       how to render the variable in the UI."
123201263c6cSJonas Devlieghere //     },
123301263c6cSJonas Devlieghere //     "evaluateName": {
123401263c6cSJonas Devlieghere //       "type": "string",
123501263c6cSJonas Devlieghere //       "description": "Optional evaluatable name of this variable which can
123601263c6cSJonas Devlieghere //                       be passed to the 'EvaluateRequest' to fetch the
123701263c6cSJonas Devlieghere //                       variable's value."
123801263c6cSJonas Devlieghere //     },
123901263c6cSJonas Devlieghere //     "variablesReference": {
124001263c6cSJonas Devlieghere //       "type": "integer",
124101263c6cSJonas Devlieghere //       "description": "If variablesReference is > 0, the variable is
124201263c6cSJonas Devlieghere //                       structured and its children can be retrieved by
124301263c6cSJonas Devlieghere //                       passing variablesReference to the VariablesRequest."
124401263c6cSJonas Devlieghere //     },
124501263c6cSJonas Devlieghere //     "namedVariables": {
124601263c6cSJonas Devlieghere //       "type": "integer",
124701263c6cSJonas Devlieghere //       "description": "The number of named child variables. The client can
124801263c6cSJonas Devlieghere //                       use this optional information to present the children
124901263c6cSJonas Devlieghere //                       in a paged UI and fetch them in chunks."
125001263c6cSJonas Devlieghere //     },
125101263c6cSJonas Devlieghere //     "indexedVariables": {
125201263c6cSJonas Devlieghere //       "type": "integer",
125301263c6cSJonas Devlieghere //       "description": "The number of indexed child variables. The client
125401263c6cSJonas Devlieghere //                       can use this optional information to present the
125501263c6cSJonas Devlieghere //                       children in a paged UI and fetch them in chunks."
12560cc2cd78SAdrian Vogelsgesang //     },
12573acb1eacSAdrian Vogelsgesang //     "memoryReference": {
12583acb1eacSAdrian Vogelsgesang //        "type": "string",
12593acb1eacSAdrian Vogelsgesang //        "description": "A memory reference associated with this variable.
12603acb1eacSAdrian Vogelsgesang //                        For pointer type variables, this is generally a
12613acb1eacSAdrian Vogelsgesang //                        reference to the memory address contained in the
12623acb1eacSAdrian Vogelsgesang //                        pointer. For executable data, this reference may later
12633acb1eacSAdrian Vogelsgesang //                        be used in a `disassemble` request. This attribute may
12643acb1eacSAdrian Vogelsgesang //                        be returned by a debug adapter if corresponding
12653acb1eacSAdrian Vogelsgesang //                        capability `supportsMemoryReferences` is true."
12663acb1eacSAdrian Vogelsgesang //     },
12670cc2cd78SAdrian Vogelsgesang //     "declarationLocationReference": {
12680cc2cd78SAdrian Vogelsgesang //       "type": "integer",
12690cc2cd78SAdrian Vogelsgesang //       "description": "A reference that allows the client to request the
12700cc2cd78SAdrian Vogelsgesang //                       location where the variable is declared. This should be
12710cc2cd78SAdrian Vogelsgesang //                       present only if the adapter is likely to be able to
12720cc2cd78SAdrian Vogelsgesang //                       resolve the location.\n\nThis reference shares the same
12730cc2cd78SAdrian Vogelsgesang //                       lifetime as the `variablesReference`. See 'Lifetime of
12740cc2cd78SAdrian Vogelsgesang //                       Object References' in the Overview section for
12750cc2cd78SAdrian Vogelsgesang //                       details."
12760cc2cd78SAdrian Vogelsgesang //     },
12779f8ae784SAdrian Vogelsgesang //     "valueLocationReference": {
12789f8ae784SAdrian Vogelsgesang //       "type": "integer",
12799f8ae784SAdrian Vogelsgesang //       "description": "A reference that allows the client to request the
12809f8ae784SAdrian Vogelsgesang //                       location where the variable's value is declared. For
12819f8ae784SAdrian Vogelsgesang //                       example, if the variable contains a function pointer,
12829f8ae784SAdrian Vogelsgesang //                       the adapter may be able to look up the function's
12839f8ae784SAdrian Vogelsgesang //                       location. This should be present only if the adapter
12849f8ae784SAdrian Vogelsgesang //                       is likely to be able to resolve the location.\n\nThis
12859f8ae784SAdrian Vogelsgesang //                       reference shares the same lifetime as the
12869f8ae784SAdrian Vogelsgesang //                       `variablesReference`. See 'Lifetime of Object
12879f8ae784SAdrian Vogelsgesang //                       References' in the Overview section for details."
12889f8ae784SAdrian Vogelsgesang //     },
12890cc2cd78SAdrian Vogelsgesang //
1290ffd173baSWalter Erquinigo //     "$__lldb_extensions": {
1291ffd173baSWalter Erquinigo //       "description": "Unofficial extensions to the protocol",
1292ffd173baSWalter Erquinigo //       "properties": {
12930ea19bd3SWalter Erquinigo //         "declaration": {
1294ffd173baSWalter Erquinigo //           "type": "object",
12950cc2cd78SAdrian Vogelsgesang //           "description": "The source location where the variable was
12960cc2cd78SAdrian Vogelsgesang //                           declared. This value won't be present if no
12970cc2cd78SAdrian Vogelsgesang //                           declaration is available.
12980cc2cd78SAdrian Vogelsgesang //                           Superseded by `declarationLocationReference`",
12990ea19bd3SWalter Erquinigo //           "properties": {
13000ea19bd3SWalter Erquinigo //             "path": {
1301ffd173baSWalter Erquinigo //               "type": "string",
13020ea19bd3SWalter Erquinigo //               "description": "The source file path where the variable was
13030ea19bd3SWalter Erquinigo //                              declared."
13040ea19bd3SWalter Erquinigo //             },
13050ea19bd3SWalter Erquinigo //             "line": {
1306ffd173baSWalter Erquinigo //               "type": "number",
13070cc2cd78SAdrian Vogelsgesang //               "description": "The 1-indexed source line where the variable
13080cc2cd78SAdrian Vogelsgesang //                               was declared."
13090ea19bd3SWalter Erquinigo //             },
13100ea19bd3SWalter Erquinigo //             "column": {
1311ffd173baSWalter Erquinigo //               "type": "number",
1312ffd173baSWalter Erquinigo //               "description": "The 1-indexed source column where the variable
1313ffd173baSWalter Erquinigo //                               was declared."
13140ea19bd3SWalter Erquinigo //             }
13150ea19bd3SWalter Erquinigo //           }
1316ffd173baSWalter Erquinigo //         },
13170cc2cd78SAdrian Vogelsgesang //         "value": {
1318ffd173baSWalter Erquinigo //           "type": "string",
1319ffd173baSWalter Erquinigo //           "description": "The internal value of the variable as returned by
1320ffd173baSWalter Erquinigo //                            This is effectively SBValue.GetValue(). The other
13210cc2cd78SAdrian Vogelsgesang //                            `value` entry in the top-level variable response
13220cc2cd78SAdrian Vogelsgesang //                            is, on the other hand, just a display string for
13230cc2cd78SAdrian Vogelsgesang //                            the variable."
1324ffd173baSWalter Erquinigo //         },
13250cc2cd78SAdrian Vogelsgesang //         "summary": {
1326ffd173baSWalter Erquinigo //           "type": "string",
1327ffd173baSWalter Erquinigo //           "description": "The summary string of the variable. This is
1328ffd173baSWalter Erquinigo //                           effectively SBValue.GetSummary()."
1329ffd173baSWalter Erquinigo //         },
13300cc2cd78SAdrian Vogelsgesang //         "autoSummary": {
1331ffd173baSWalter Erquinigo //           "type": "string",
1332ffd173baSWalter Erquinigo //           "description": "The auto generated summary if using
1333ffd173baSWalter Erquinigo //                           `enableAutoVariableSummaries`."
1334ffd173baSWalter Erquinigo //         },
13350cc2cd78SAdrian Vogelsgesang //         "error": {
1336ffd173baSWalter Erquinigo //           "type": "string",
1337ffd173baSWalter Erquinigo //           "description": "An error message generated if LLDB couldn't inspect
1338ffd173baSWalter Erquinigo //                           the variable."
1339ffd173baSWalter Erquinigo //         }
13400ea19bd3SWalter Erquinigo //       }
13410cc2cd78SAdrian Vogelsgesang //     }
134201263c6cSJonas Devlieghere //   },
134301263c6cSJonas Devlieghere //   "required": [ "name", "value", "variablesReference" ]
134401263c6cSJonas Devlieghere // }
13450cc2cd78SAdrian Vogelsgesang llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
1346*faaf2dbfSJohn Harrison                                  bool format_hex, bool auto_variable_summaries,
1347*faaf2dbfSJohn Harrison                                  bool synthetic_child_debugging,
1348*faaf2dbfSJohn Harrison                                  bool is_name_duplicated,
134901263c6cSJonas Devlieghere                                  std::optional<std::string> custom_name) {
1350*faaf2dbfSJohn Harrison   VariableDescription desc(v, auto_variable_summaries, format_hex,
1351*faaf2dbfSJohn Harrison                            is_name_duplicated, custom_name);
135201263c6cSJonas Devlieghere   llvm::json::Object object;
1353ffd173baSWalter Erquinigo   EmplaceSafeString(object, "name", desc.name);
1354ffd173baSWalter Erquinigo   EmplaceSafeString(object, "value", desc.display_value);
135501263c6cSJonas Devlieghere 
1356ffd173baSWalter Erquinigo   if (!desc.evaluate_name.empty())
1357ffd173baSWalter Erquinigo     EmplaceSafeString(object, "evaluateName", desc.evaluate_name);
1358ffd173baSWalter Erquinigo 
135901263c6cSJonas Devlieghere   // If we have a type with many children, we would like to be able to
136001263c6cSJonas Devlieghere   // give a hint to the IDE that the type has indexed children so that the
1361ffd173baSWalter Erquinigo   // request can be broken up in grabbing only a few children at a time. We
1362ffd173baSWalter Erquinigo   // want to be careful and only call "v.GetNumChildren()" if we have an array
1363f38ebec7SPavel Labath   // type or if we have a synthetic child provider producing indexed children.
1364f38ebec7SPavel Labath   // We don't want to call "v.GetNumChildren()" on all objects as class, struct
1365f38ebec7SPavel Labath   // and union types don't need to be completed if they are never expanded. So
1366f38ebec7SPavel Labath   // we want to avoid calling this to only cases where we it makes sense to keep
1367ffd173baSWalter Erquinigo   // performance high during normal debugging.
136801263c6cSJonas Devlieghere 
1369ffd173baSWalter Erquinigo   // If we have an array type, say that it is indexed and provide the number
1370ffd173baSWalter Erquinigo   // of children in case we have a huge array. If we don't do this, then we
1371ffd173baSWalter Erquinigo   // might take a while to produce all children at onces which can delay your
1372ffd173baSWalter Erquinigo   // debug session.
1373f38ebec7SPavel Labath   if (desc.type_obj.IsArrayType()) {
1374f38ebec7SPavel Labath     object.try_emplace("indexedVariables", v.GetNumChildren());
1375f38ebec7SPavel Labath   } else if (v.IsSynthetic()) {
1376f38ebec7SPavel Labath     // For a type with a synthetic child provider, the SBType of "v" won't tell
1377f38ebec7SPavel Labath     // us anything about what might be displayed. Instead, we check if the first
1378f38ebec7SPavel Labath     // child's name is "[0]" and then say it is indexed. We call
1379f38ebec7SPavel Labath     // GetNumChildren() only if the child name matches to avoid a potentially
1380f38ebec7SPavel Labath     // expensive operation.
1381f38ebec7SPavel Labath     if (lldb::SBValue first_child = v.GetChildAtIndex(0)) {
1382f38ebec7SPavel Labath       llvm::StringRef first_child_name = first_child.GetName();
1383f38ebec7SPavel Labath       if (first_child_name == "[0]") {
1384f38ebec7SPavel Labath         size_t num_children = v.GetNumChildren();
1385f38ebec7SPavel Labath         // If we are creating a "[raw]" fake child for each synthetic type, we
1386f38ebec7SPavel Labath         // have to account for it when returning indexed variables.
1387*faaf2dbfSJohn Harrison         if (synthetic_child_debugging)
1388f38ebec7SPavel Labath           ++num_children;
1389f38ebec7SPavel Labath         object.try_emplace("indexedVariables", num_children);
1390f38ebec7SPavel Labath       }
139101263c6cSJonas Devlieghere     }
139201263c6cSJonas Devlieghere   }
1393ffd173baSWalter Erquinigo   EmplaceSafeString(object, "type", desc.display_type_name);
13940cc2cd78SAdrian Vogelsgesang 
13950cc2cd78SAdrian Vogelsgesang   // A unique variable identifier to help in properly identifying variables with
13960cc2cd78SAdrian Vogelsgesang   // the same name. This is an extension to the VS protocol.
13970cc2cd78SAdrian Vogelsgesang   object.try_emplace("id", var_ref);
13980cc2cd78SAdrian Vogelsgesang 
139901263c6cSJonas Devlieghere   if (v.MightHaveChildren())
14000cc2cd78SAdrian Vogelsgesang     object.try_emplace("variablesReference", var_ref);
140101263c6cSJonas Devlieghere   else
14020cc2cd78SAdrian Vogelsgesang     object.try_emplace("variablesReference", 0);
14030cc2cd78SAdrian Vogelsgesang 
14040cc2cd78SAdrian Vogelsgesang   if (v.GetDeclaration().IsValid())
14059f8ae784SAdrian Vogelsgesang     object.try_emplace("declarationLocationReference",
14069f8ae784SAdrian Vogelsgesang                        PackLocation(var_ref, false));
14079f8ae784SAdrian Vogelsgesang 
14089f8ae784SAdrian Vogelsgesang   if (ValuePointsToCode(v))
14099f8ae784SAdrian Vogelsgesang     object.try_emplace("valueLocationReference", PackLocation(var_ref, true));
14100ea19bd3SWalter Erquinigo 
14113acb1eacSAdrian Vogelsgesang   if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
14123acb1eacSAdrian Vogelsgesang     object.try_emplace("memoryReference", EncodeMemoryReference(addr));
14133acb1eacSAdrian Vogelsgesang 
1414ffd173baSWalter Erquinigo   object.try_emplace("$__lldb_extensions", desc.GetVariableExtensionsJSON());
141501263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
141601263c6cSJonas Devlieghere }
141701263c6cSJonas Devlieghere 
1418*faaf2dbfSJohn Harrison llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) {
141901263c6cSJonas Devlieghere   llvm::json::Object object;
142001263c6cSJonas Devlieghere   char unit_path_arr[PATH_MAX];
142101263c6cSJonas Devlieghere   unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr));
142201263c6cSJonas Devlieghere   std::string unit_path(unit_path_arr);
142301263c6cSJonas Devlieghere   object.try_emplace("compileUnitPath", unit_path);
142401263c6cSJonas Devlieghere   return llvm::json::Value(std::move(object));
142501263c6cSJonas Devlieghere }
142601263c6cSJonas Devlieghere 
142701263c6cSJonas Devlieghere /// See
142801263c6cSJonas Devlieghere /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
142901263c6cSJonas Devlieghere llvm::json::Object
143001263c6cSJonas Devlieghere CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
143101263c6cSJonas Devlieghere                                   llvm::StringRef debug_adaptor_path,
143201263c6cSJonas Devlieghere                                   llvm::StringRef comm_file,
143301263c6cSJonas Devlieghere                                   lldb::pid_t debugger_pid) {
143401263c6cSJonas Devlieghere   llvm::json::Object run_in_terminal_args;
14359f8ae784SAdrian Vogelsgesang   // This indicates the IDE to open an embedded terminal, instead of opening
14369f8ae784SAdrian Vogelsgesang   // the terminal in a new window.
143701263c6cSJonas Devlieghere   run_in_terminal_args.try_emplace("kind", "integrated");
143801263c6cSJonas Devlieghere 
1439*faaf2dbfSJohn Harrison   const auto *launch_request_arguments = launch_request.getObject("arguments");
144001263c6cSJonas Devlieghere   // The program path must be the first entry in the "args" field
144101263c6cSJonas Devlieghere   std::vector<std::string> args = {debug_adaptor_path.str(), "--comm-file",
144201263c6cSJonas Devlieghere                                    comm_file.str()};
144301263c6cSJonas Devlieghere   if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
144401263c6cSJonas Devlieghere     args.push_back("--debugger-pid");
144501263c6cSJonas Devlieghere     args.push_back(std::to_string(debugger_pid));
144601263c6cSJonas Devlieghere   }
144701263c6cSJonas Devlieghere   args.push_back("--launch-target");
144801263c6cSJonas Devlieghere   args.push_back(GetString(launch_request_arguments, "program").str());
144901263c6cSJonas Devlieghere   std::vector<std::string> target_args =
145001263c6cSJonas Devlieghere       GetStrings(launch_request_arguments, "args");
145101263c6cSJonas Devlieghere   args.insert(args.end(), target_args.begin(), target_args.end());
145201263c6cSJonas Devlieghere   run_in_terminal_args.try_emplace("args", args);
145301263c6cSJonas Devlieghere 
145401263c6cSJonas Devlieghere   const auto cwd = GetString(launch_request_arguments, "cwd");
145501263c6cSJonas Devlieghere   if (!cwd.empty())
145601263c6cSJonas Devlieghere     run_in_terminal_args.try_emplace("cwd", cwd);
145701263c6cSJonas Devlieghere 
1458d4c17891SDa-Viper   auto envs = GetEnvironmentFromArguments(*launch_request_arguments);
1459d4c17891SDa-Viper   llvm::json::Object env_json;
1460d4c17891SDa-Viper   for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count;
1461d4c17891SDa-Viper        index++) {
1462d4c17891SDa-Viper     llvm::StringRef key = envs.GetNameAtIndex(index);
1463d4c17891SDa-Viper     llvm::StringRef value = envs.GetValueAtIndex(index);
1464d4c17891SDa-Viper 
1465*faaf2dbfSJohn Harrison     if (!key.empty())
1466d4c17891SDa-Viper       env_json.try_emplace(key, value);
146701263c6cSJonas Devlieghere   }
146801263c6cSJonas Devlieghere   run_in_terminal_args.try_emplace("env",
1469d4c17891SDa-Viper                                    llvm::json::Value(std::move(env_json)));
147001263c6cSJonas Devlieghere 
147101263c6cSJonas Devlieghere   return run_in_terminal_args;
147201263c6cSJonas Devlieghere }
147301263c6cSJonas Devlieghere 
147401263c6cSJonas Devlieghere // Keep all the top level items from the statistics dump, except for the
147501263c6cSJonas Devlieghere // "modules" array. It can be huge and cause delay
147601263c6cSJonas Devlieghere // Array and dictionary value will return as <key, JSON string> pairs
1477*faaf2dbfSJohn Harrison static void FilterAndGetValueForKey(const lldb::SBStructuredData data,
1478*faaf2dbfSJohn Harrison                                     const char *key, llvm::json::Object &out) {
147901263c6cSJonas Devlieghere   lldb::SBStructuredData value = data.GetValueForKey(key);
148001263c6cSJonas Devlieghere   std::string key_utf8 = llvm::json::fixUTF8(key);
1481b99d4112SJohn Harrison   if (llvm::StringRef(key) == "modules")
148201263c6cSJonas Devlieghere     return;
148301263c6cSJonas Devlieghere   switch (value.GetType()) {
148401263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeFloat:
148501263c6cSJonas Devlieghere     out.try_emplace(key_utf8, value.GetFloatValue());
148601263c6cSJonas Devlieghere     break;
148701263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeUnsignedInteger:
148801263c6cSJonas Devlieghere     out.try_emplace(key_utf8, value.GetIntegerValue((uint64_t)0));
148901263c6cSJonas Devlieghere     break;
149001263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeSignedInteger:
149101263c6cSJonas Devlieghere     out.try_emplace(key_utf8, value.GetIntegerValue((int64_t)0));
149201263c6cSJonas Devlieghere     break;
149301263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeArray: {
149401263c6cSJonas Devlieghere     lldb::SBStream contents;
149501263c6cSJonas Devlieghere     value.GetAsJSON(contents);
149601263c6cSJonas Devlieghere     out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
149701263c6cSJonas Devlieghere   } break;
149801263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeBoolean:
149901263c6cSJonas Devlieghere     out.try_emplace(key_utf8, value.GetBooleanValue());
150001263c6cSJonas Devlieghere     break;
150101263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeString: {
150201263c6cSJonas Devlieghere     // Get the string size before reading
150301263c6cSJonas Devlieghere     const size_t str_length = value.GetStringValue(nullptr, 0);
150401263c6cSJonas Devlieghere     std::string str(str_length + 1, 0);
150501263c6cSJonas Devlieghere     value.GetStringValue(&str[0], str_length);
150601263c6cSJonas Devlieghere     out.try_emplace(key_utf8, llvm::json::fixUTF8(str));
150701263c6cSJonas Devlieghere   } break;
150801263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeDictionary: {
150901263c6cSJonas Devlieghere     lldb::SBStream contents;
151001263c6cSJonas Devlieghere     value.GetAsJSON(contents);
151101263c6cSJonas Devlieghere     out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData()));
151201263c6cSJonas Devlieghere   } break;
151301263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeNull:
151401263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeGeneric:
151501263c6cSJonas Devlieghere   case lldb::eStructuredDataTypeInvalid:
151601263c6cSJonas Devlieghere     break;
151701263c6cSJonas Devlieghere   }
151801263c6cSJonas Devlieghere }
151901263c6cSJonas Devlieghere 
1520*faaf2dbfSJohn Harrison static void addStatistic(lldb::SBTarget &target, llvm::json::Object &event) {
1521*faaf2dbfSJohn Harrison   lldb::SBStructuredData statistics = target.GetStatistics();
152201263c6cSJonas Devlieghere   bool is_dictionary =
152301263c6cSJonas Devlieghere       statistics.GetType() == lldb::eStructuredDataTypeDictionary;
152401263c6cSJonas Devlieghere   if (!is_dictionary)
152501263c6cSJonas Devlieghere     return;
152601263c6cSJonas Devlieghere   llvm::json::Object stats_body;
152701263c6cSJonas Devlieghere 
152801263c6cSJonas Devlieghere   lldb::SBStringList keys;
152901263c6cSJonas Devlieghere   if (!statistics.GetKeys(keys))
153001263c6cSJonas Devlieghere     return;
153101263c6cSJonas Devlieghere   for (size_t i = 0; i < keys.GetSize(); i++) {
153201263c6cSJonas Devlieghere     const char *key = keys.GetStringAtIndex(i);
153301263c6cSJonas Devlieghere     FilterAndGetValueForKey(statistics, key, stats_body);
153401263c6cSJonas Devlieghere   }
153501263c6cSJonas Devlieghere   event.try_emplace("statistics", std::move(stats_body));
153601263c6cSJonas Devlieghere }
153701263c6cSJonas Devlieghere 
1538*faaf2dbfSJohn Harrison llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target) {
153901263c6cSJonas Devlieghere   llvm::json::Object event(CreateEventObject("terminated"));
1540*faaf2dbfSJohn Harrison   addStatistic(target, event);
154101263c6cSJonas Devlieghere   return event;
154201263c6cSJonas Devlieghere }
154301263c6cSJonas Devlieghere 
154401263c6cSJonas Devlieghere std::string JSONToString(const llvm::json::Value &json) {
154501263c6cSJonas Devlieghere   std::string data;
154601263c6cSJonas Devlieghere   llvm::raw_string_ostream os(data);
154701263c6cSJonas Devlieghere   os << json;
154801263c6cSJonas Devlieghere   return data;
154901263c6cSJonas Devlieghere }
155001263c6cSJonas Devlieghere 
155101263c6cSJonas Devlieghere } // namespace lldb_dap
1552