xref: /llvm-project/lldb/tools/lldb-vscode/DAP.cpp (revision 873426bea3dd67d80dd10650e64e91c69796614f)
101263c6cSJonas Devlieghere //===-- DAP.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 
901263c6cSJonas Devlieghere #include "DAP.h"
1009c258efSAdrian Vogelsgesang #include "JSONUtils.h"
1101263c6cSJonas Devlieghere #include "LLDBUtils.h"
12*873426beSJohn Harrison #include "OutputRedirector.h"
13*873426beSJohn Harrison #include "lldb/API/SBBreakpoint.h"
1433bf08ecSPavel Labath #include "lldb/API/SBCommandInterpreter.h"
15*873426beSJohn Harrison #include "lldb/API/SBCommandReturnObject.h"
1609c258efSAdrian Vogelsgesang #include "lldb/API/SBLanguageRuntime.h"
1709c258efSAdrian Vogelsgesang #include "lldb/API/SBListener.h"
18*873426beSJohn Harrison #include "lldb/API/SBProcess.h"
1909c258efSAdrian Vogelsgesang #include "lldb/API/SBStream.h"
20*873426beSJohn Harrison #include "lldb/Host/FileSystem.h"
21*873426beSJohn Harrison #include "lldb/Utility/Status.h"
22*873426beSJohn Harrison #include "lldb/lldb-defines.h"
23*873426beSJohn Harrison #include "lldb/lldb-enumerations.h"
24*873426beSJohn Harrison #include "llvm/ADT/ArrayRef.h"
2501263c6cSJonas Devlieghere #include "llvm/ADT/StringExtras.h"
26*873426beSJohn Harrison #include "llvm/ADT/Twine.h"
27*873426beSJohn Harrison #include "llvm/Support/Error.h"
28*873426beSJohn Harrison #include "llvm/Support/ErrorHandling.h"
2901263c6cSJonas Devlieghere #include "llvm/Support/FormatVariadic.h"
30*873426beSJohn Harrison #include "llvm/Support/raw_ostream.h"
31*873426beSJohn Harrison #include <algorithm>
32*873426beSJohn Harrison #include <cassert>
33*873426beSJohn Harrison #include <chrono>
34*873426beSJohn Harrison #include <cstdarg>
35*873426beSJohn Harrison #include <cstdio>
36*873426beSJohn Harrison #include <fstream>
37*873426beSJohn Harrison #include <mutex>
38*873426beSJohn Harrison #include <utility>
3901263c6cSJonas Devlieghere 
4001263c6cSJonas Devlieghere #if defined(_WIN32)
4101263c6cSJonas Devlieghere #define NOMINMAX
4201263c6cSJonas Devlieghere #include <fcntl.h>
4301263c6cSJonas Devlieghere #include <io.h>
4401263c6cSJonas Devlieghere #include <windows.h>
45*873426beSJohn Harrison #else
46*873426beSJohn Harrison #include <unistd.h>
4701263c6cSJonas Devlieghere #endif
4801263c6cSJonas Devlieghere 
4901263c6cSJonas Devlieghere using namespace lldb_dap;
5001263c6cSJonas Devlieghere 
51*873426beSJohn Harrison namespace {
52*873426beSJohn Harrison #ifdef _WIN32
53*873426beSJohn Harrison const char DEV_NULL[] = "nul";
54*873426beSJohn Harrison #else
55*873426beSJohn Harrison const char DEV_NULL[] = "/dev/null";
56*873426beSJohn Harrison #endif
57*873426beSJohn Harrison } // namespace
58*873426beSJohn Harrison 
5901263c6cSJonas Devlieghere namespace lldb_dap {
6001263c6cSJonas Devlieghere 
61*873426beSJohn Harrison DAP::DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
62*873426beSJohn Harrison          StreamDescriptor input, StreamDescriptor output)
63*873426beSJohn Harrison     : debug_adaptor_path(path), log(log), input(std::move(input)),
64*873426beSJohn Harrison       output(std::move(output)), broadcaster("lldb-dap"),
653121f752SJohn Harrison       exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
663121f752SJohn Harrison       stop_at_entry(false), is_attach(false),
6701263c6cSJonas Devlieghere       enable_auto_variable_summaries(false),
6801263c6cSJonas Devlieghere       enable_synthetic_child_debugging(false),
6919ecdedcSAdrian Vogelsgesang       display_extended_backtrace(false),
7001263c6cSJonas Devlieghere       restarting_process_id(LLDB_INVALID_PROCESS_ID),
7101263c6cSJonas Devlieghere       configuration_done_sent(false), waiting_for_run_in_terminal(false),
7201263c6cSJonas Devlieghere       progress_event_reporter(
7301263c6cSJonas Devlieghere           [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
74*873426beSJohn Harrison       reverse_request_seq(0), repl_mode(repl_mode) {}
7501263c6cSJonas Devlieghere 
7601263c6cSJonas Devlieghere DAP::~DAP() = default;
7701263c6cSJonas Devlieghere 
78541f22eeSWalter Erquinigo /// Return string with first character capitalized.
79541f22eeSWalter Erquinigo static std::string capitalize(llvm::StringRef str) {
80541f22eeSWalter Erquinigo   if (str.empty())
81541f22eeSWalter Erquinigo     return "";
82541f22eeSWalter Erquinigo   return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();
83541f22eeSWalter Erquinigo }
84541f22eeSWalter Erquinigo 
85e951bd0fSVy Nguyen void DAP::PopulateExceptionBreakpoints() {
86e951bd0fSVy Nguyen   llvm::call_once(init_exception_breakpoints_flag, [this]() {
87e951bd0fSVy Nguyen     exception_breakpoints = std::vector<ExceptionBreakpoint>{};
88e951bd0fSVy Nguyen 
89e951bd0fSVy Nguyen     if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
90b99d4112SJohn Harrison       exception_breakpoints->emplace_back(*this, "cpp_catch", "C++ Catch",
91e951bd0fSVy Nguyen                                           lldb::eLanguageTypeC_plus_plus);
92b99d4112SJohn Harrison       exception_breakpoints->emplace_back(*this, "cpp_throw", "C++ Throw",
93e951bd0fSVy Nguyen                                           lldb::eLanguageTypeC_plus_plus);
94e951bd0fSVy Nguyen     }
95e951bd0fSVy Nguyen     if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {
96b99d4112SJohn Harrison       exception_breakpoints->emplace_back(
97b99d4112SJohn Harrison           *this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC);
98b99d4112SJohn Harrison       exception_breakpoints->emplace_back(
99b99d4112SJohn Harrison           *this, "objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC);
100e951bd0fSVy Nguyen     }
101e951bd0fSVy Nguyen     if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {
102b99d4112SJohn Harrison       exception_breakpoints->emplace_back(*this, "swift_catch", "Swift Catch",
103e951bd0fSVy Nguyen                                           lldb::eLanguageTypeSwift);
104b99d4112SJohn Harrison       exception_breakpoints->emplace_back(*this, "swift_throw", "Swift Throw",
105e951bd0fSVy Nguyen                                           lldb::eLanguageTypeSwift);
106e951bd0fSVy Nguyen     }
107541f22eeSWalter Erquinigo     // Besides handling the hardcoded list of languages from above, we try to
108541f22eeSWalter Erquinigo     // find any other languages that support exception breakpoints using the
109541f22eeSWalter Erquinigo     // SB API.
110541f22eeSWalter Erquinigo     for (int raw_lang = lldb::eLanguageTypeUnknown;
111541f22eeSWalter Erquinigo          raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
112541f22eeSWalter Erquinigo       lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
113541f22eeSWalter Erquinigo 
114541f22eeSWalter Erquinigo       // We first discard any languages already handled above.
115541f22eeSWalter Erquinigo       if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||
116541f22eeSWalter Erquinigo           lang == lldb::eLanguageTypeSwift)
117541f22eeSWalter Erquinigo         continue;
118541f22eeSWalter Erquinigo 
119541f22eeSWalter Erquinigo       if (!lldb::SBDebugger::SupportsLanguage(lang))
120541f22eeSWalter Erquinigo         continue;
121541f22eeSWalter Erquinigo 
122541f22eeSWalter Erquinigo       const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);
123541f22eeSWalter Erquinigo       if (!name)
124541f22eeSWalter Erquinigo         continue;
125541f22eeSWalter Erquinigo       std::string raw_lang_name = name;
126541f22eeSWalter Erquinigo       std::string capitalized_lang_name = capitalize(name);
127541f22eeSWalter Erquinigo 
128541f22eeSWalter Erquinigo       if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {
129541f22eeSWalter Erquinigo         const char *raw_throw_keyword =
130541f22eeSWalter Erquinigo             lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
131541f22eeSWalter Erquinigo         std::string throw_keyword =
132541f22eeSWalter Erquinigo             raw_throw_keyword ? raw_throw_keyword : "throw";
133541f22eeSWalter Erquinigo 
134541f22eeSWalter Erquinigo         exception_breakpoints->emplace_back(
135b99d4112SJohn Harrison             *this, raw_lang_name + "_" + throw_keyword,
136541f22eeSWalter Erquinigo             capitalized_lang_name + " " + capitalize(throw_keyword), lang);
137541f22eeSWalter Erquinigo       }
138541f22eeSWalter Erquinigo 
139541f22eeSWalter Erquinigo       if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {
140541f22eeSWalter Erquinigo         const char *raw_catch_keyword =
141541f22eeSWalter Erquinigo             lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
142541f22eeSWalter Erquinigo         std::string catch_keyword =
143541f22eeSWalter Erquinigo             raw_catch_keyword ? raw_catch_keyword : "catch";
144541f22eeSWalter Erquinigo 
145541f22eeSWalter Erquinigo         exception_breakpoints->emplace_back(
146b99d4112SJohn Harrison             *this, raw_lang_name + "_" + catch_keyword,
147541f22eeSWalter Erquinigo             capitalized_lang_name + " " + capitalize(catch_keyword), lang);
148541f22eeSWalter Erquinigo       }
149541f22eeSWalter Erquinigo     }
150e951bd0fSVy Nguyen     assert(!exception_breakpoints->empty() && "should not be empty");
151e951bd0fSVy Nguyen   });
152e951bd0fSVy Nguyen }
153e951bd0fSVy Nguyen 
15401263c6cSJonas Devlieghere ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
155e951bd0fSVy Nguyen   // PopulateExceptionBreakpoints() is called after g_dap.debugger is created
156e951bd0fSVy Nguyen   // in a request-initialize.
157e951bd0fSVy Nguyen   //
158e951bd0fSVy Nguyen   // But this GetExceptionBreakpoint() method may be called before attaching, in
159e951bd0fSVy Nguyen   // which case, we may not have populated the filter yet.
160e951bd0fSVy Nguyen   //
161e951bd0fSVy Nguyen   // We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
162e951bd0fSVy Nguyen   // we need SBDebugger::Initialize() to have been called before this.
163e951bd0fSVy Nguyen   //
164e951bd0fSVy Nguyen   // So just calling PopulateExceptionBreakoints(),which does lazy-populating
165e951bd0fSVy Nguyen   // seems easiest. Two other options include:
166e951bd0fSVy Nguyen   //  + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
167e951bd0fSVy Nguyen   //    right after the call to SBDebugger::Initialize()
168e951bd0fSVy Nguyen   //  + Just call PopulateExceptionBreakpoints() to get a fresh list  everytime
169e951bd0fSVy Nguyen   //    we query (a bit overkill since it's not likely to change?)
170e951bd0fSVy Nguyen   PopulateExceptionBreakpoints();
171e951bd0fSVy Nguyen 
172e951bd0fSVy Nguyen   for (auto &bp : *exception_breakpoints) {
17301263c6cSJonas Devlieghere     if (bp.filter == filter)
17401263c6cSJonas Devlieghere       return &bp;
17501263c6cSJonas Devlieghere   }
17601263c6cSJonas Devlieghere   return nullptr;
17701263c6cSJonas Devlieghere }
17801263c6cSJonas Devlieghere 
17901263c6cSJonas Devlieghere ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
180e951bd0fSVy Nguyen   // See comment in the other GetExceptionBreakpoint().
181e951bd0fSVy Nguyen   PopulateExceptionBreakpoints();
182e951bd0fSVy Nguyen 
183e951bd0fSVy Nguyen   for (auto &bp : *exception_breakpoints) {
18401263c6cSJonas Devlieghere     if (bp.bp.GetID() == bp_id)
18501263c6cSJonas Devlieghere       return &bp;
18601263c6cSJonas Devlieghere   }
18701263c6cSJonas Devlieghere   return nullptr;
18801263c6cSJonas Devlieghere }
18901263c6cSJonas Devlieghere 
190*873426beSJohn Harrison llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
191*873426beSJohn Harrison   in = lldb::SBFile(std::fopen(DEV_NULL, "r"), /*transfer_ownership=*/true);
192*873426beSJohn Harrison 
193*873426beSJohn Harrison   if (auto Error = out.RedirectTo([this](llvm::StringRef output) {
194*873426beSJohn Harrison         SendOutput(OutputType::Stdout, output);
195*873426beSJohn Harrison       }))
196*873426beSJohn Harrison     return Error;
197*873426beSJohn Harrison 
198*873426beSJohn Harrison   if (overrideOut) {
199*873426beSJohn Harrison     auto fd = out.GetWriteFileDescriptor();
200*873426beSJohn Harrison     if (auto Error = fd.takeError())
201*873426beSJohn Harrison       return Error;
202*873426beSJohn Harrison 
203*873426beSJohn Harrison     if (dup2(*fd, fileno(overrideOut)) == -1)
204*873426beSJohn Harrison       return llvm::errorCodeToError(llvm::errnoAsErrorCode());
205*873426beSJohn Harrison   }
206*873426beSJohn Harrison 
207*873426beSJohn Harrison   if (auto Error = err.RedirectTo([this](llvm::StringRef output) {
208*873426beSJohn Harrison         SendOutput(OutputType::Stderr, output);
209*873426beSJohn Harrison       }))
210*873426beSJohn Harrison     return Error;
211*873426beSJohn Harrison 
212*873426beSJohn Harrison   if (overrideErr) {
213*873426beSJohn Harrison     auto fd = err.GetWriteFileDescriptor();
214*873426beSJohn Harrison     if (auto Error = fd.takeError())
215*873426beSJohn Harrison       return Error;
216*873426beSJohn Harrison 
217*873426beSJohn Harrison     if (dup2(*fd, fileno(overrideErr)) == -1)
218*873426beSJohn Harrison       return llvm::errorCodeToError(llvm::errnoAsErrorCode());
219*873426beSJohn Harrison   }
220*873426beSJohn Harrison 
221*873426beSJohn Harrison   return llvm::Error::success();
222*873426beSJohn Harrison }
223*873426beSJohn Harrison 
224*873426beSJohn Harrison void DAP::StopIO() {
225*873426beSJohn Harrison   out.Stop();
226*873426beSJohn Harrison   err.Stop();
227*873426beSJohn Harrison }
228*873426beSJohn Harrison 
22901263c6cSJonas Devlieghere // Send the JSON in "json_str" to the "out" stream. Correctly send the
23001263c6cSJonas Devlieghere // "Content-Length:" field followed by the length, followed by the raw
23101263c6cSJonas Devlieghere // JSON bytes.
23201263c6cSJonas Devlieghere void DAP::SendJSON(const std::string &json_str) {
23301263c6cSJonas Devlieghere   output.write_full("Content-Length: ");
23401263c6cSJonas Devlieghere   output.write_full(llvm::utostr(json_str.size()));
23501263c6cSJonas Devlieghere   output.write_full("\r\n\r\n");
23601263c6cSJonas Devlieghere   output.write_full(json_str);
23701263c6cSJonas Devlieghere }
23801263c6cSJonas Devlieghere 
23901263c6cSJonas Devlieghere // Serialize the JSON value into a string and send the JSON packet to
24001263c6cSJonas Devlieghere // the "out" stream.
24101263c6cSJonas Devlieghere void DAP::SendJSON(const llvm::json::Value &json) {
242d7796855SYoungsuk Kim   std::string json_str;
243d7796855SYoungsuk Kim   llvm::raw_string_ostream strm(json_str);
24401263c6cSJonas Devlieghere   strm << json;
24501263c6cSJonas Devlieghere   static std::mutex mutex;
24601263c6cSJonas Devlieghere   std::lock_guard<std::mutex> locker(mutex);
24701263c6cSJonas Devlieghere   SendJSON(json_str);
24801263c6cSJonas Devlieghere 
24901263c6cSJonas Devlieghere   if (log) {
2502cfea14aSPavel Labath     auto now = std::chrono::duration<double>(
2512cfea14aSPavel Labath         std::chrono::system_clock::now().time_since_epoch());
2522cfea14aSPavel Labath     *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << std::endl
25301263c6cSJonas Devlieghere          << "Content-Length: " << json_str.size() << "\r\n\r\n"
25401263c6cSJonas Devlieghere          << llvm::formatv("{0:2}", json).str() << std::endl;
25501263c6cSJonas Devlieghere   }
25601263c6cSJonas Devlieghere }
25701263c6cSJonas Devlieghere 
25801263c6cSJonas Devlieghere // Read a JSON packet from the "in" stream.
25901263c6cSJonas Devlieghere std::string DAP::ReadJSON() {
26001263c6cSJonas Devlieghere   std::string length_str;
26101263c6cSJonas Devlieghere   std::string json_str;
26201263c6cSJonas Devlieghere   int length;
26301263c6cSJonas Devlieghere 
264*873426beSJohn Harrison   if (!input.read_expected(log, "Content-Length: "))
26501263c6cSJonas Devlieghere     return json_str;
26601263c6cSJonas Devlieghere 
267*873426beSJohn Harrison   if (!input.read_line(log, length_str))
26801263c6cSJonas Devlieghere     return json_str;
26901263c6cSJonas Devlieghere 
27001263c6cSJonas Devlieghere   if (!llvm::to_integer(length_str, length))
27101263c6cSJonas Devlieghere     return json_str;
27201263c6cSJonas Devlieghere 
273*873426beSJohn Harrison   if (!input.read_expected(log, "\r\n"))
27401263c6cSJonas Devlieghere     return json_str;
27501263c6cSJonas Devlieghere 
276*873426beSJohn Harrison   if (!input.read_full(log, length, json_str))
27701263c6cSJonas Devlieghere     return json_str;
27801263c6cSJonas Devlieghere 
2792cfea14aSPavel Labath   if (log) {
2802cfea14aSPavel Labath     auto now = std::chrono::duration<double>(
2812cfea14aSPavel Labath         std::chrono::system_clock::now().time_since_epoch());
2822cfea14aSPavel Labath     *log << llvm::formatv("{0:f9} --> ", now.count()).str() << std::endl
2832cfea14aSPavel Labath          << "Content-Length: " << length << "\r\n\r\n";
2842cfea14aSPavel Labath   }
28501263c6cSJonas Devlieghere   return json_str;
28601263c6cSJonas Devlieghere }
28701263c6cSJonas Devlieghere 
28801263c6cSJonas Devlieghere // "OutputEvent": {
28901263c6cSJonas Devlieghere //   "allOf": [ { "$ref": "#/definitions/Event" }, {
29001263c6cSJonas Devlieghere //     "type": "object",
29101263c6cSJonas Devlieghere //     "description": "Event message for 'output' event type. The event
29201263c6cSJonas Devlieghere //                     indicates that the target has produced some output.",
29301263c6cSJonas Devlieghere //     "properties": {
29401263c6cSJonas Devlieghere //       "event": {
29501263c6cSJonas Devlieghere //         "type": "string",
29601263c6cSJonas Devlieghere //         "enum": [ "output" ]
29701263c6cSJonas Devlieghere //       },
29801263c6cSJonas Devlieghere //       "body": {
29901263c6cSJonas Devlieghere //         "type": "object",
30001263c6cSJonas Devlieghere //         "properties": {
30101263c6cSJonas Devlieghere //           "category": {
30201263c6cSJonas Devlieghere //             "type": "string",
30301263c6cSJonas Devlieghere //             "description": "The output category. If not specified,
30401263c6cSJonas Devlieghere //                             'console' is assumed.",
30501263c6cSJonas Devlieghere //             "_enum": [ "console", "stdout", "stderr", "telemetry" ]
30601263c6cSJonas Devlieghere //           },
30701263c6cSJonas Devlieghere //           "output": {
30801263c6cSJonas Devlieghere //             "type": "string",
30901263c6cSJonas Devlieghere //             "description": "The output to report."
31001263c6cSJonas Devlieghere //           },
31101263c6cSJonas Devlieghere //           "variablesReference": {
31201263c6cSJonas Devlieghere //             "type": "number",
31301263c6cSJonas Devlieghere //             "description": "If an attribute 'variablesReference' exists
31401263c6cSJonas Devlieghere //                             and its value is > 0, the output contains
31501263c6cSJonas Devlieghere //                             objects which can be retrieved by passing
31601263c6cSJonas Devlieghere //                             variablesReference to the VariablesRequest."
31701263c6cSJonas Devlieghere //           },
31801263c6cSJonas Devlieghere //           "source": {
31901263c6cSJonas Devlieghere //             "$ref": "#/definitions/Source",
32001263c6cSJonas Devlieghere //             "description": "An optional source location where the output
32101263c6cSJonas Devlieghere //                             was produced."
32201263c6cSJonas Devlieghere //           },
32301263c6cSJonas Devlieghere //           "line": {
32401263c6cSJonas Devlieghere //             "type": "integer",
32501263c6cSJonas Devlieghere //             "description": "An optional source location line where the
32601263c6cSJonas Devlieghere //                             output was produced."
32701263c6cSJonas Devlieghere //           },
32801263c6cSJonas Devlieghere //           "column": {
32901263c6cSJonas Devlieghere //             "type": "integer",
33001263c6cSJonas Devlieghere //             "description": "An optional source location column where the
33101263c6cSJonas Devlieghere //                             output was produced."
33201263c6cSJonas Devlieghere //           },
33301263c6cSJonas Devlieghere //           "data": {
33401263c6cSJonas Devlieghere //             "type":["array","boolean","integer","null","number","object",
33501263c6cSJonas Devlieghere //                     "string"],
33601263c6cSJonas Devlieghere //             "description": "Optional data to report. For the 'telemetry'
33701263c6cSJonas Devlieghere //                             category the data will be sent to telemetry, for
33801263c6cSJonas Devlieghere //                             the other categories the data is shown in JSON
33901263c6cSJonas Devlieghere //                             format."
34001263c6cSJonas Devlieghere //           }
34101263c6cSJonas Devlieghere //         },
34201263c6cSJonas Devlieghere //         "required": ["output"]
34301263c6cSJonas Devlieghere //       }
34401263c6cSJonas Devlieghere //     },
34501263c6cSJonas Devlieghere //     "required": [ "event", "body" ]
34601263c6cSJonas Devlieghere //   }]
34701263c6cSJonas Devlieghere // }
34801263c6cSJonas Devlieghere void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
34901263c6cSJonas Devlieghere   if (output.empty())
35001263c6cSJonas Devlieghere     return;
35101263c6cSJonas Devlieghere 
35201263c6cSJonas Devlieghere   const char *category = nullptr;
35301263c6cSJonas Devlieghere   switch (o) {
35401263c6cSJonas Devlieghere   case OutputType::Console:
35501263c6cSJonas Devlieghere     category = "console";
35601263c6cSJonas Devlieghere     break;
35701263c6cSJonas Devlieghere   case OutputType::Stdout:
35801263c6cSJonas Devlieghere     category = "stdout";
35901263c6cSJonas Devlieghere     break;
36001263c6cSJonas Devlieghere   case OutputType::Stderr:
36101263c6cSJonas Devlieghere     category = "stderr";
36201263c6cSJonas Devlieghere     break;
36301263c6cSJonas Devlieghere   case OutputType::Telemetry:
36401263c6cSJonas Devlieghere     category = "telemetry";
36501263c6cSJonas Devlieghere     break;
36601263c6cSJonas Devlieghere   }
36730ca06c4SJohn Harrison 
36830ca06c4SJohn Harrison   // Send each line of output as an individual event, including the newline if
36930ca06c4SJohn Harrison   // present.
37030ca06c4SJohn Harrison   ::size_t idx = 0;
37130ca06c4SJohn Harrison   do {
37230ca06c4SJohn Harrison     ::size_t end = output.find('\n', idx);
37330ca06c4SJohn Harrison     if (end == llvm::StringRef::npos)
37430ca06c4SJohn Harrison       end = output.size() - 1;
37530ca06c4SJohn Harrison     llvm::json::Object event(CreateEventObject("output"));
37630ca06c4SJohn Harrison     llvm::json::Object body;
37701263c6cSJonas Devlieghere     body.try_emplace("category", category);
37830ca06c4SJohn Harrison     EmplaceSafeString(body, "output", output.slice(idx, end + 1).str());
37901263c6cSJonas Devlieghere     event.try_emplace("body", std::move(body));
38001263c6cSJonas Devlieghere     SendJSON(llvm::json::Value(std::move(event)));
38130ca06c4SJohn Harrison     idx = end + 1;
38230ca06c4SJohn Harrison   } while (idx < output.size());
38301263c6cSJonas Devlieghere }
38401263c6cSJonas Devlieghere 
38501263c6cSJonas Devlieghere // interface ProgressStartEvent extends Event {
38601263c6cSJonas Devlieghere //   event: 'progressStart';
38701263c6cSJonas Devlieghere //
38801263c6cSJonas Devlieghere //   body: {
38901263c6cSJonas Devlieghere //     /**
39001263c6cSJonas Devlieghere //      * An ID that must be used in subsequent 'progressUpdate' and
39101263c6cSJonas Devlieghere //      'progressEnd'
39201263c6cSJonas Devlieghere //      * events to make them refer to the same progress reporting.
39301263c6cSJonas Devlieghere //      * IDs must be unique within a debug session.
39401263c6cSJonas Devlieghere //      */
39501263c6cSJonas Devlieghere //     progressId: string;
39601263c6cSJonas Devlieghere //
39701263c6cSJonas Devlieghere //     /**
39801263c6cSJonas Devlieghere //      * Mandatory (short) title of the progress reporting. Shown in the UI to
39901263c6cSJonas Devlieghere //      * describe the long running operation.
40001263c6cSJonas Devlieghere //      */
40101263c6cSJonas Devlieghere //     title: string;
40201263c6cSJonas Devlieghere //
40301263c6cSJonas Devlieghere //     /**
40401263c6cSJonas Devlieghere //      * The request ID that this progress report is related to. If specified a
40501263c6cSJonas Devlieghere //      * debug adapter is expected to emit
40601263c6cSJonas Devlieghere //      * progress events for the long running request until the request has
40701263c6cSJonas Devlieghere //      been
40801263c6cSJonas Devlieghere //      * either completed or cancelled.
40901263c6cSJonas Devlieghere //      * If the request ID is omitted, the progress report is assumed to be
41001263c6cSJonas Devlieghere //      * related to some general activity of the debug adapter.
41101263c6cSJonas Devlieghere //      */
41201263c6cSJonas Devlieghere //     requestId?: number;
41301263c6cSJonas Devlieghere //
41401263c6cSJonas Devlieghere //     /**
41501263c6cSJonas Devlieghere //      * If true, the request that reports progress may be canceled with a
41601263c6cSJonas Devlieghere //      * 'cancel' request.
41701263c6cSJonas Devlieghere //      * So this property basically controls whether the client should use UX
41801263c6cSJonas Devlieghere //      that
41901263c6cSJonas Devlieghere //      * supports cancellation.
42001263c6cSJonas Devlieghere //      * Clients that don't support cancellation are allowed to ignore the
42101263c6cSJonas Devlieghere //      * setting.
42201263c6cSJonas Devlieghere //      */
42301263c6cSJonas Devlieghere //     cancellable?: boolean;
42401263c6cSJonas Devlieghere //
42501263c6cSJonas Devlieghere //     /**
42601263c6cSJonas Devlieghere //      * Optional, more detailed progress message.
42701263c6cSJonas Devlieghere //      */
42801263c6cSJonas Devlieghere //     message?: string;
42901263c6cSJonas Devlieghere //
43001263c6cSJonas Devlieghere //     /**
43101263c6cSJonas Devlieghere //      * Optional progress percentage to display (value range: 0 to 100). If
43201263c6cSJonas Devlieghere //      * omitted no percentage will be shown.
43301263c6cSJonas Devlieghere //      */
43401263c6cSJonas Devlieghere //     percentage?: number;
43501263c6cSJonas Devlieghere //   };
43601263c6cSJonas Devlieghere // }
43701263c6cSJonas Devlieghere //
43801263c6cSJonas Devlieghere // interface ProgressUpdateEvent extends Event {
43901263c6cSJonas Devlieghere //   event: 'progressUpdate';
44001263c6cSJonas Devlieghere //
44101263c6cSJonas Devlieghere //   body: {
44201263c6cSJonas Devlieghere //     /**
44301263c6cSJonas Devlieghere //      * The ID that was introduced in the initial 'progressStart' event.
44401263c6cSJonas Devlieghere //      */
44501263c6cSJonas Devlieghere //     progressId: string;
44601263c6cSJonas Devlieghere //
44701263c6cSJonas Devlieghere //     /**
44801263c6cSJonas Devlieghere //      * Optional, more detailed progress message. If omitted, the previous
44901263c6cSJonas Devlieghere //      * message (if any) is used.
45001263c6cSJonas Devlieghere //      */
45101263c6cSJonas Devlieghere //     message?: string;
45201263c6cSJonas Devlieghere //
45301263c6cSJonas Devlieghere //     /**
45401263c6cSJonas Devlieghere //      * Optional progress percentage to display (value range: 0 to 100). If
45501263c6cSJonas Devlieghere //      * omitted no percentage will be shown.
45601263c6cSJonas Devlieghere //      */
45701263c6cSJonas Devlieghere //     percentage?: number;
45801263c6cSJonas Devlieghere //   };
45901263c6cSJonas Devlieghere // }
46001263c6cSJonas Devlieghere //
46101263c6cSJonas Devlieghere // interface ProgressEndEvent extends Event {
46201263c6cSJonas Devlieghere //   event: 'progressEnd';
46301263c6cSJonas Devlieghere //
46401263c6cSJonas Devlieghere //   body: {
46501263c6cSJonas Devlieghere //     /**
46601263c6cSJonas Devlieghere //      * The ID that was introduced in the initial 'ProgressStartEvent'.
46701263c6cSJonas Devlieghere //      */
46801263c6cSJonas Devlieghere //     progressId: string;
46901263c6cSJonas Devlieghere //
47001263c6cSJonas Devlieghere //     /**
47101263c6cSJonas Devlieghere //      * Optional, more detailed progress message. If omitted, the previous
47201263c6cSJonas Devlieghere //      * message (if any) is used.
47301263c6cSJonas Devlieghere //      */
47401263c6cSJonas Devlieghere //     message?: string;
47501263c6cSJonas Devlieghere //   };
47601263c6cSJonas Devlieghere // }
47701263c6cSJonas Devlieghere 
47801263c6cSJonas Devlieghere void DAP::SendProgressEvent(uint64_t progress_id, const char *message,
47901263c6cSJonas Devlieghere                             uint64_t completed, uint64_t total) {
48001263c6cSJonas Devlieghere   progress_event_reporter.Push(progress_id, message, completed, total);
48101263c6cSJonas Devlieghere }
48201263c6cSJonas Devlieghere 
48301263c6cSJonas Devlieghere void __attribute__((format(printf, 3, 4)))
48401263c6cSJonas Devlieghere DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
48501263c6cSJonas Devlieghere   char buffer[1024];
48601263c6cSJonas Devlieghere   va_list args;
48701263c6cSJonas Devlieghere   va_start(args, format);
48801263c6cSJonas Devlieghere   int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
48901263c6cSJonas Devlieghere   va_end(args);
49001263c6cSJonas Devlieghere   SendOutput(
49101263c6cSJonas Devlieghere       o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
49201263c6cSJonas Devlieghere }
49301263c6cSJonas Devlieghere 
49401263c6cSJonas Devlieghere ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
49501263c6cSJonas Devlieghere   const auto num = thread.GetStopReasonDataCount();
49601263c6cSJonas Devlieghere   // Check to see if have hit an exception breakpoint and change the
49701263c6cSJonas Devlieghere   // reason to "exception", but only do so if all breakpoints that were
49801263c6cSJonas Devlieghere   // hit are exception breakpoints.
49901263c6cSJonas Devlieghere   ExceptionBreakpoint *exc_bp = nullptr;
50001263c6cSJonas Devlieghere   for (size_t i = 0; i < num; i += 2) {
50101263c6cSJonas Devlieghere     // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
50201263c6cSJonas Devlieghere     // thread.GetStopReasonDataAtIndex(i+1) will return the location
50301263c6cSJonas Devlieghere     // within that breakpoint. We only care about the bp ID so we can
50401263c6cSJonas Devlieghere     // see if this is an exception breakpoint that is getting hit.
50501263c6cSJonas Devlieghere     lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
50601263c6cSJonas Devlieghere     exc_bp = GetExceptionBreakpoint(bp_id);
50701263c6cSJonas Devlieghere     // If any breakpoint is not an exception breakpoint, then stop and
50801263c6cSJonas Devlieghere     // report this as a normal breakpoint
50901263c6cSJonas Devlieghere     if (exc_bp == nullptr)
51001263c6cSJonas Devlieghere       return nullptr;
51101263c6cSJonas Devlieghere   }
51201263c6cSJonas Devlieghere   return exc_bp;
51301263c6cSJonas Devlieghere }
51401263c6cSJonas Devlieghere 
51501263c6cSJonas Devlieghere lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
51601263c6cSJonas Devlieghere   auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
51701263c6cSJonas Devlieghere   return target.GetProcess().GetThreadByID(tid);
51801263c6cSJonas Devlieghere }
51901263c6cSJonas Devlieghere 
52001263c6cSJonas Devlieghere lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
52101263c6cSJonas Devlieghere   const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
52201263c6cSJonas Devlieghere   lldb::SBProcess process = target.GetProcess();
52301263c6cSJonas Devlieghere   // Upper 32 bits is the thread index ID
52401263c6cSJonas Devlieghere   lldb::SBThread thread =
52501263c6cSJonas Devlieghere       process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
52601263c6cSJonas Devlieghere   // Lower 32 bits is the frame index
52701263c6cSJonas Devlieghere   return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
52801263c6cSJonas Devlieghere }
52901263c6cSJonas Devlieghere 
53001263c6cSJonas Devlieghere llvm::json::Value DAP::CreateTopLevelScopes() {
53101263c6cSJonas Devlieghere   llvm::json::Array scopes;
532a6d299ddSJohn Harrison   scopes.emplace_back(
533a6d299ddSJohn Harrison       CreateScope("Locals", VARREF_LOCALS, variables.locals.GetSize(), false));
53401263c6cSJonas Devlieghere   scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
535a6d299ddSJohn Harrison                                   variables.globals.GetSize(), false));
53601263c6cSJonas Devlieghere   scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
537a6d299ddSJohn Harrison                                   variables.registers.GetSize(), false));
53801263c6cSJonas Devlieghere   return llvm::json::Value(std::move(scopes));
53901263c6cSJonas Devlieghere }
54001263c6cSJonas Devlieghere 
541a5876befSAdrian Vogelsgesang ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
542a5876befSAdrian Vogelsgesang                              bool partial_expression) {
543a5876befSAdrian Vogelsgesang   // Check for the escape hatch prefix.
5444ea1994aSJohn Harrison   if (!expression.empty() &&
545a6d299ddSJohn Harrison       llvm::StringRef(expression).starts_with(command_escape_prefix)) {
546a6d299ddSJohn Harrison     expression = expression.substr(command_escape_prefix.size());
547a5876befSAdrian Vogelsgesang     return ReplMode::Command;
54801263c6cSJonas Devlieghere   }
54901263c6cSJonas Devlieghere 
55001263c6cSJonas Devlieghere   switch (repl_mode) {
55101263c6cSJonas Devlieghere   case ReplMode::Variable:
552a5876befSAdrian Vogelsgesang     return ReplMode::Variable;
55301263c6cSJonas Devlieghere   case ReplMode::Command:
554a5876befSAdrian Vogelsgesang     return ReplMode::Command;
55501263c6cSJonas Devlieghere   case ReplMode::Auto:
5564ea1994aSJohn Harrison     // To determine if the expression is a command or not, check if the first
5574ea1994aSJohn Harrison     // term is a variable or command. If it's a variable in scope we will prefer
5584ea1994aSJohn Harrison     // that behavior and give a warning to the user if they meant to invoke the
5594ea1994aSJohn Harrison     // operation as a command.
5604ea1994aSJohn Harrison     //
5614ea1994aSJohn Harrison     // Example use case:
5624ea1994aSJohn Harrison     //   int p and expression "p + 1" > variable
5634ea1994aSJohn Harrison     //   int i and expression "i" > variable
5644ea1994aSJohn Harrison     //   int var and expression "va" > command
5654ea1994aSJohn Harrison     std::pair<llvm::StringRef, llvm::StringRef> token =
5664ea1994aSJohn Harrison         llvm::getToken(expression);
567a5876befSAdrian Vogelsgesang 
568a5876befSAdrian Vogelsgesang     // If the first token is not fully finished yet, we can't
569a5876befSAdrian Vogelsgesang     // determine whether this will be a variable or a lldb command.
570a5876befSAdrian Vogelsgesang     if (partial_expression && token.second.empty())
571a5876befSAdrian Vogelsgesang       return ReplMode::Auto;
572a5876befSAdrian Vogelsgesang 
5734ea1994aSJohn Harrison     std::string term = token.first.str();
57433bf08ecSPavel Labath     lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
57533bf08ecSPavel Labath     bool term_is_command = interpreter.CommandExists(term.c_str()) ||
57633bf08ecSPavel Labath                            interpreter.UserCommandExists(term.c_str()) ||
57733bf08ecSPavel Labath                            interpreter.AliasExists(term.c_str());
5784ea1994aSJohn Harrison     bool term_is_variable = frame.FindVariable(term.c_str()).IsValid();
57901263c6cSJonas Devlieghere 
5804ea1994aSJohn Harrison     // If we have both a variable and command, warn the user about the conflict.
5814ea1994aSJohn Harrison     if (term_is_command && term_is_variable) {
5824ea1994aSJohn Harrison       llvm::errs()
5834ea1994aSJohn Harrison           << "Warning: Expression '" << term
5844ea1994aSJohn Harrison           << "' is both an LLDB command and variable. It will be evaluated as "
5854ea1994aSJohn Harrison              "a variable. To evaluate the expression as an LLDB command, use '"
586a6d299ddSJohn Harrison           << command_escape_prefix << "' as a prefix.\n";
58701263c6cSJonas Devlieghere     }
58801263c6cSJonas Devlieghere 
5894ea1994aSJohn Harrison     // Variables take preference to commands in auto, since commands can always
5904ea1994aSJohn Harrison     // be called using the command_escape_prefix
591a5876befSAdrian Vogelsgesang     return term_is_variable  ? ReplMode::Variable
592a5876befSAdrian Vogelsgesang            : term_is_command ? ReplMode::Command
593a5876befSAdrian Vogelsgesang                              : ReplMode::Variable;
59401263c6cSJonas Devlieghere   }
59501263c6cSJonas Devlieghere 
5964ea1994aSJohn Harrison   llvm_unreachable("enum cases exhausted.");
59701263c6cSJonas Devlieghere }
59801263c6cSJonas Devlieghere 
599aa207674SWalter Erquinigo bool DAP::RunLLDBCommands(llvm::StringRef prefix,
600aa207674SWalter Erquinigo                           llvm::ArrayRef<std::string> commands) {
601aa207674SWalter Erquinigo   bool required_command_failed = false;
602aa207674SWalter Erquinigo   std::string output =
603e5ba1172SJohn Harrison       ::RunLLDBCommands(debugger, prefix, commands, required_command_failed);
604aa207674SWalter Erquinigo   SendOutput(OutputType::Console, output);
605aa207674SWalter Erquinigo   return !required_command_failed;
60601263c6cSJonas Devlieghere }
60701263c6cSJonas Devlieghere 
608aa207674SWalter Erquinigo static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) {
609aa207674SWalter Erquinigo   return llvm::createStringError(
610aa207674SWalter Erquinigo       llvm::inconvertibleErrorCode(),
611aa207674SWalter Erquinigo       llvm::formatv(
612aa207674SWalter Erquinigo           "Failed to run {0} commands. See the Debug Console for more details.",
613aa207674SWalter Erquinigo           category)
614aa207674SWalter Erquinigo           .str()
615aa207674SWalter Erquinigo           .c_str());
61601263c6cSJonas Devlieghere }
61701263c6cSJonas Devlieghere 
618aa207674SWalter Erquinigo llvm::Error
619aa207674SWalter Erquinigo DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) {
620aa207674SWalter Erquinigo   if (!RunLLDBCommands("Running attachCommands:", attach_commands))
621aa207674SWalter Erquinigo     return createRunLLDBCommandsErrorMessage("attach");
622aa207674SWalter Erquinigo   return llvm::Error::success();
62301263c6cSJonas Devlieghere }
62401263c6cSJonas Devlieghere 
625aa207674SWalter Erquinigo llvm::Error
626aa207674SWalter Erquinigo DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {
627aa207674SWalter Erquinigo   if (!RunLLDBCommands("Running launchCommands:", launch_commands))
628aa207674SWalter Erquinigo     return createRunLLDBCommandsErrorMessage("launch");
629aa207674SWalter Erquinigo   return llvm::Error::success();
630aa207674SWalter Erquinigo }
631aa207674SWalter Erquinigo 
632aa207674SWalter Erquinigo llvm::Error DAP::RunInitCommands() {
633aa207674SWalter Erquinigo   if (!RunLLDBCommands("Running initCommands:", init_commands))
634aa207674SWalter Erquinigo     return createRunLLDBCommandsErrorMessage("initCommands");
635aa207674SWalter Erquinigo   return llvm::Error::success();
636aa207674SWalter Erquinigo }
637aa207674SWalter Erquinigo 
638541f22eeSWalter Erquinigo llvm::Error DAP::RunPreInitCommands() {
639541f22eeSWalter Erquinigo   if (!RunLLDBCommands("Running preInitCommands:", pre_init_commands))
640541f22eeSWalter Erquinigo     return createRunLLDBCommandsErrorMessage("preInitCommands");
641541f22eeSWalter Erquinigo   return llvm::Error::success();
642541f22eeSWalter Erquinigo }
643541f22eeSWalter Erquinigo 
644aa207674SWalter Erquinigo llvm::Error DAP::RunPreRunCommands() {
645aa207674SWalter Erquinigo   if (!RunLLDBCommands("Running preRunCommands:", pre_run_commands))
646aa207674SWalter Erquinigo     return createRunLLDBCommandsErrorMessage("preRunCommands");
647aa207674SWalter Erquinigo   return llvm::Error::success();
648aa207674SWalter Erquinigo }
649aa207674SWalter Erquinigo 
650aa207674SWalter Erquinigo void DAP::RunPostRunCommands() {
651aa207674SWalter Erquinigo   RunLLDBCommands("Running postRunCommands:", post_run_commands);
652aa207674SWalter Erquinigo }
65301263c6cSJonas Devlieghere void DAP::RunStopCommands() {
65401263c6cSJonas Devlieghere   RunLLDBCommands("Running stopCommands:", stop_commands);
65501263c6cSJonas Devlieghere }
65601263c6cSJonas Devlieghere 
65701263c6cSJonas Devlieghere void DAP::RunExitCommands() {
65801263c6cSJonas Devlieghere   RunLLDBCommands("Running exitCommands:", exit_commands);
65901263c6cSJonas Devlieghere }
66001263c6cSJonas Devlieghere 
66101263c6cSJonas Devlieghere void DAP::RunTerminateCommands() {
66201263c6cSJonas Devlieghere   RunLLDBCommands("Running terminateCommands:", terminate_commands);
66301263c6cSJonas Devlieghere }
66401263c6cSJonas Devlieghere 
66501263c6cSJonas Devlieghere lldb::SBTarget
66601263c6cSJonas Devlieghere DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
66701263c6cSJonas Devlieghere                                lldb::SBError &error) {
66801263c6cSJonas Devlieghere   // Grab the name of the program we need to debug and create a target using
66901263c6cSJonas Devlieghere   // the given program as an argument. Executable file can be a source of target
67001263c6cSJonas Devlieghere   // architecture and platform, if they differ from the host. Setting exe path
67101263c6cSJonas Devlieghere   // in launch info is useless because Target.Launch() will not change
67201263c6cSJonas Devlieghere   // architecture and platform, therefore they should be known at the target
67301263c6cSJonas Devlieghere   // creation. We also use target triple and platform from the launch
67401263c6cSJonas Devlieghere   // configuration, if given, since in some cases ELF file doesn't contain
67501263c6cSJonas Devlieghere   // enough information to determine correct arch and platform (or ELF can be
67601263c6cSJonas Devlieghere   // omitted at all), so it is good to leave the user an apportunity to specify
67701263c6cSJonas Devlieghere   // those. Any of those three can be left empty.
67801263c6cSJonas Devlieghere   llvm::StringRef target_triple = GetString(arguments, "targetTriple");
67901263c6cSJonas Devlieghere   llvm::StringRef platform_name = GetString(arguments, "platformName");
68001263c6cSJonas Devlieghere   llvm::StringRef program = GetString(arguments, "program");
68101263c6cSJonas Devlieghere   auto target = this->debugger.CreateTarget(
68201263c6cSJonas Devlieghere       program.data(), target_triple.data(), platform_name.data(),
68301263c6cSJonas Devlieghere       true, // Add dependent modules.
68401263c6cSJonas Devlieghere       error);
68501263c6cSJonas Devlieghere 
68601263c6cSJonas Devlieghere   if (error.Fail()) {
68701263c6cSJonas Devlieghere     // Update message if there was an error.
68801263c6cSJonas Devlieghere     error.SetErrorStringWithFormat(
68901263c6cSJonas Devlieghere         "Could not create a target for a program '%s': %s.", program.data(),
69001263c6cSJonas Devlieghere         error.GetCString());
69101263c6cSJonas Devlieghere   }
69201263c6cSJonas Devlieghere 
69301263c6cSJonas Devlieghere   return target;
69401263c6cSJonas Devlieghere }
69501263c6cSJonas Devlieghere 
69601263c6cSJonas Devlieghere void DAP::SetTarget(const lldb::SBTarget target) {
69701263c6cSJonas Devlieghere   this->target = target;
69801263c6cSJonas Devlieghere 
69901263c6cSJonas Devlieghere   if (target.IsValid()) {
70001263c6cSJonas Devlieghere     // Configure breakpoint event listeners for the target.
70101263c6cSJonas Devlieghere     lldb::SBListener listener = this->debugger.GetListener();
70201263c6cSJonas Devlieghere     listener.StartListeningForEvents(
70301263c6cSJonas Devlieghere         this->target.GetBroadcaster(),
70401263c6cSJonas Devlieghere         lldb::SBTarget::eBroadcastBitBreakpointChanged);
70501263c6cSJonas Devlieghere     listener.StartListeningForEvents(this->broadcaster,
70601263c6cSJonas Devlieghere                                      eBroadcastBitStopEventThread);
70701263c6cSJonas Devlieghere   }
70801263c6cSJonas Devlieghere }
70901263c6cSJonas Devlieghere 
71001263c6cSJonas Devlieghere PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
71101263c6cSJonas Devlieghere   std::string json = ReadJSON();
71201263c6cSJonas Devlieghere   if (json.empty())
71301263c6cSJonas Devlieghere     return PacketStatus::EndOfFile;
71401263c6cSJonas Devlieghere 
71501263c6cSJonas Devlieghere   llvm::StringRef json_sref(json);
71601263c6cSJonas Devlieghere   llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
71701263c6cSJonas Devlieghere   if (!json_value) {
71801263c6cSJonas Devlieghere     auto error = json_value.takeError();
71901263c6cSJonas Devlieghere     if (log) {
72001263c6cSJonas Devlieghere       std::string error_str;
72101263c6cSJonas Devlieghere       llvm::raw_string_ostream strm(error_str);
72201263c6cSJonas Devlieghere       strm << error;
72301263c6cSJonas Devlieghere       *log << "error: failed to parse JSON: " << error_str << std::endl
72401263c6cSJonas Devlieghere            << json << std::endl;
72501263c6cSJonas Devlieghere     }
72601263c6cSJonas Devlieghere     return PacketStatus::JSONMalformed;
72701263c6cSJonas Devlieghere   }
72801263c6cSJonas Devlieghere 
72901263c6cSJonas Devlieghere   if (log) {
73001263c6cSJonas Devlieghere     *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
73101263c6cSJonas Devlieghere   }
73201263c6cSJonas Devlieghere 
73301263c6cSJonas Devlieghere   llvm::json::Object *object_ptr = json_value->getAsObject();
73401263c6cSJonas Devlieghere   if (!object_ptr) {
73501263c6cSJonas Devlieghere     if (log)
73601263c6cSJonas Devlieghere       *log << "error: json packet isn't a object" << std::endl;
73701263c6cSJonas Devlieghere     return PacketStatus::JSONNotObject;
73801263c6cSJonas Devlieghere   }
73901263c6cSJonas Devlieghere   object = *object_ptr;
74001263c6cSJonas Devlieghere   return PacketStatus::Success;
74101263c6cSJonas Devlieghere }
74201263c6cSJonas Devlieghere 
74301263c6cSJonas Devlieghere bool DAP::HandleObject(const llvm::json::Object &object) {
74401263c6cSJonas Devlieghere   const auto packet_type = GetString(object, "type");
74501263c6cSJonas Devlieghere   if (packet_type == "request") {
74601263c6cSJonas Devlieghere     const auto command = GetString(object, "command");
747c3c424d2SKazu Hirata     auto handler_pos = request_handlers.find(command);
7483121f752SJohn Harrison     if (handler_pos == request_handlers.end()) {
74901263c6cSJonas Devlieghere       if (log)
75001263c6cSJonas Devlieghere         *log << "error: unhandled command \"" << command.data() << "\""
75101263c6cSJonas Devlieghere              << std::endl;
75201263c6cSJonas Devlieghere       return false; // Fail
75301263c6cSJonas Devlieghere     }
7543121f752SJohn Harrison 
7553121f752SJohn Harrison     handler_pos->second(*this, object);
7563121f752SJohn Harrison     return true; // Success
75701263c6cSJonas Devlieghere   }
75801263c6cSJonas Devlieghere 
75901263c6cSJonas Devlieghere   if (packet_type == "response") {
76001263c6cSJonas Devlieghere     auto id = GetSigned(object, "request_seq", 0);
76101263c6cSJonas Devlieghere     ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) {
76201263c6cSJonas Devlieghere       llvm::errs() << "Unhandled response\n";
76301263c6cSJonas Devlieghere     };
76401263c6cSJonas Devlieghere 
76501263c6cSJonas Devlieghere     {
76601263c6cSJonas Devlieghere       std::lock_guard<std::mutex> locker(call_mutex);
76701263c6cSJonas Devlieghere       auto inflight = inflight_reverse_requests.find(id);
76801263c6cSJonas Devlieghere       if (inflight != inflight_reverse_requests.end()) {
76901263c6cSJonas Devlieghere         response_handler = std::move(inflight->second);
77001263c6cSJonas Devlieghere         inflight_reverse_requests.erase(inflight);
77101263c6cSJonas Devlieghere       }
77201263c6cSJonas Devlieghere     }
77301263c6cSJonas Devlieghere 
77401263c6cSJonas Devlieghere     // Result should be given, use null if not.
77501263c6cSJonas Devlieghere     if (GetBoolean(object, "success", false)) {
77601263c6cSJonas Devlieghere       llvm::json::Value Result = nullptr;
77701263c6cSJonas Devlieghere       if (auto *B = object.get("body")) {
77801263c6cSJonas Devlieghere         Result = std::move(*B);
77901263c6cSJonas Devlieghere       }
78001263c6cSJonas Devlieghere       response_handler(Result);
78101263c6cSJonas Devlieghere     } else {
78201263c6cSJonas Devlieghere       llvm::StringRef message = GetString(object, "message");
78301263c6cSJonas Devlieghere       if (message.empty()) {
78401263c6cSJonas Devlieghere         message = "Unknown error, response failed";
78501263c6cSJonas Devlieghere       }
78601263c6cSJonas Devlieghere       response_handler(llvm::createStringError(
78701263c6cSJonas Devlieghere           std::error_code(-1, std::generic_category()), message));
78801263c6cSJonas Devlieghere     }
78901263c6cSJonas Devlieghere 
79001263c6cSJonas Devlieghere     return true;
79101263c6cSJonas Devlieghere   }
79201263c6cSJonas Devlieghere 
79301263c6cSJonas Devlieghere   return false;
79401263c6cSJonas Devlieghere }
79501263c6cSJonas Devlieghere 
79601263c6cSJonas Devlieghere llvm::Error DAP::Loop() {
797871f4839SPavel Labath   while (!disconnecting) {
79801263c6cSJonas Devlieghere     llvm::json::Object object;
79901263c6cSJonas Devlieghere     lldb_dap::PacketStatus status = GetNextObject(object);
80001263c6cSJonas Devlieghere 
80101263c6cSJonas Devlieghere     if (status == lldb_dap::PacketStatus::EndOfFile) {
80201263c6cSJonas Devlieghere       break;
80301263c6cSJonas Devlieghere     }
80401263c6cSJonas Devlieghere 
80501263c6cSJonas Devlieghere     if (status != lldb_dap::PacketStatus::Success) {
80601263c6cSJonas Devlieghere       return llvm::createStringError(llvm::inconvertibleErrorCode(),
80701263c6cSJonas Devlieghere                                      "failed to send packet");
80801263c6cSJonas Devlieghere     }
80901263c6cSJonas Devlieghere 
81001263c6cSJonas Devlieghere     if (!HandleObject(object)) {
81101263c6cSJonas Devlieghere       return llvm::createStringError(llvm::inconvertibleErrorCode(),
81201263c6cSJonas Devlieghere                                      "unhandled packet");
81301263c6cSJonas Devlieghere     }
81401263c6cSJonas Devlieghere   }
81501263c6cSJonas Devlieghere 
81601263c6cSJonas Devlieghere   return llvm::Error::success();
81701263c6cSJonas Devlieghere }
81801263c6cSJonas Devlieghere 
81901263c6cSJonas Devlieghere void DAP::SendReverseRequest(llvm::StringRef command,
82001263c6cSJonas Devlieghere                              llvm::json::Value arguments,
82101263c6cSJonas Devlieghere                              ResponseCallback callback) {
82201263c6cSJonas Devlieghere   int64_t id;
82301263c6cSJonas Devlieghere   {
82401263c6cSJonas Devlieghere     std::lock_guard<std::mutex> locker(call_mutex);
82501263c6cSJonas Devlieghere     id = ++reverse_request_seq;
82601263c6cSJonas Devlieghere     inflight_reverse_requests.emplace(id, std::move(callback));
82701263c6cSJonas Devlieghere   }
82801263c6cSJonas Devlieghere 
82901263c6cSJonas Devlieghere   SendJSON(llvm::json::Object{
83001263c6cSJonas Devlieghere       {"type", "request"},
83101263c6cSJonas Devlieghere       {"seq", id},
83201263c6cSJonas Devlieghere       {"command", command},
83301263c6cSJonas Devlieghere       {"arguments", std::move(arguments)},
83401263c6cSJonas Devlieghere   });
83501263c6cSJonas Devlieghere }
83601263c6cSJonas Devlieghere 
83701263c6cSJonas Devlieghere void DAP::RegisterRequestCallback(std::string request,
83801263c6cSJonas Devlieghere                                   RequestCallback callback) {
83901263c6cSJonas Devlieghere   request_handlers[request] = callback;
84001263c6cSJonas Devlieghere }
84101263c6cSJonas Devlieghere 
84201263c6cSJonas Devlieghere lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
84301263c6cSJonas Devlieghere   lldb::SBError error;
84401263c6cSJonas Devlieghere   lldb::SBProcess process = target.GetProcess();
84501263c6cSJonas Devlieghere   if (!process.IsValid()) {
84601263c6cSJonas Devlieghere     error.SetErrorString("invalid process");
84701263c6cSJonas Devlieghere     return error;
84801263c6cSJonas Devlieghere   }
84901263c6cSJonas Devlieghere   auto timeout_time =
85001263c6cSJonas Devlieghere       std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
85101263c6cSJonas Devlieghere   while (std::chrono::steady_clock::now() < timeout_time) {
85201263c6cSJonas Devlieghere     const auto state = process.GetState();
85301263c6cSJonas Devlieghere     switch (state) {
85401263c6cSJonas Devlieghere     case lldb::eStateAttaching:
85501263c6cSJonas Devlieghere     case lldb::eStateConnected:
85601263c6cSJonas Devlieghere     case lldb::eStateInvalid:
85701263c6cSJonas Devlieghere     case lldb::eStateLaunching:
85801263c6cSJonas Devlieghere     case lldb::eStateRunning:
85901263c6cSJonas Devlieghere     case lldb::eStateStepping:
86001263c6cSJonas Devlieghere     case lldb::eStateSuspended:
86101263c6cSJonas Devlieghere       break;
86201263c6cSJonas Devlieghere     case lldb::eStateDetached:
86301263c6cSJonas Devlieghere       error.SetErrorString("process detached during launch or attach");
86401263c6cSJonas Devlieghere       return error;
86501263c6cSJonas Devlieghere     case lldb::eStateExited:
86601263c6cSJonas Devlieghere       error.SetErrorString("process exited during launch or attach");
86701263c6cSJonas Devlieghere       return error;
86801263c6cSJonas Devlieghere     case lldb::eStateUnloaded:
86901263c6cSJonas Devlieghere       error.SetErrorString("process unloaded during launch or attach");
87001263c6cSJonas Devlieghere       return error;
87101263c6cSJonas Devlieghere     case lldb::eStateCrashed:
87201263c6cSJonas Devlieghere     case lldb::eStateStopped:
87301263c6cSJonas Devlieghere       return lldb::SBError(); // Success!
87401263c6cSJonas Devlieghere     }
87501263c6cSJonas Devlieghere     std::this_thread::sleep_for(std::chrono::microseconds(250));
87601263c6cSJonas Devlieghere   }
87701263c6cSJonas Devlieghere   error.SetErrorStringWithFormat("process failed to stop within %u seconds",
87801263c6cSJonas Devlieghere                                  seconds);
87901263c6cSJonas Devlieghere   return error;
88001263c6cSJonas Devlieghere }
88101263c6cSJonas Devlieghere 
88201263c6cSJonas Devlieghere void Variables::Clear() {
88301263c6cSJonas Devlieghere   locals.Clear();
88401263c6cSJonas Devlieghere   globals.Clear();
88501263c6cSJonas Devlieghere   registers.Clear();
8860cc2cd78SAdrian Vogelsgesang   referenced_variables.clear();
88701263c6cSJonas Devlieghere }
88801263c6cSJonas Devlieghere 
88901263c6cSJonas Devlieghere int64_t Variables::GetNewVariableReference(bool is_permanent) {
89001263c6cSJonas Devlieghere   if (is_permanent)
89101263c6cSJonas Devlieghere     return next_permanent_var_ref++;
89201263c6cSJonas Devlieghere   return next_temporary_var_ref++;
89301263c6cSJonas Devlieghere }
89401263c6cSJonas Devlieghere 
89501263c6cSJonas Devlieghere bool Variables::IsPermanentVariableReference(int64_t var_ref) {
89601263c6cSJonas Devlieghere   return var_ref >= PermanentVariableStartIndex;
89701263c6cSJonas Devlieghere }
89801263c6cSJonas Devlieghere 
89901263c6cSJonas Devlieghere lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
90001263c6cSJonas Devlieghere   if (IsPermanentVariableReference(var_ref)) {
9010cc2cd78SAdrian Vogelsgesang     auto pos = referenced_permanent_variables.find(var_ref);
9020cc2cd78SAdrian Vogelsgesang     if (pos != referenced_permanent_variables.end())
90301263c6cSJonas Devlieghere       return pos->second;
90401263c6cSJonas Devlieghere   } else {
9050cc2cd78SAdrian Vogelsgesang     auto pos = referenced_variables.find(var_ref);
9060cc2cd78SAdrian Vogelsgesang     if (pos != referenced_variables.end())
90701263c6cSJonas Devlieghere       return pos->second;
90801263c6cSJonas Devlieghere   }
90901263c6cSJonas Devlieghere   return lldb::SBValue();
91001263c6cSJonas Devlieghere }
91101263c6cSJonas Devlieghere 
9120cc2cd78SAdrian Vogelsgesang int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
91301263c6cSJonas Devlieghere   int64_t var_ref = GetNewVariableReference(is_permanent);
91401263c6cSJonas Devlieghere   if (is_permanent)
9150cc2cd78SAdrian Vogelsgesang     referenced_permanent_variables.insert(std::make_pair(var_ref, variable));
91601263c6cSJonas Devlieghere   else
9170cc2cd78SAdrian Vogelsgesang     referenced_variables.insert(std::make_pair(var_ref, variable));
91801263c6cSJonas Devlieghere   return var_ref;
91901263c6cSJonas Devlieghere }
92001263c6cSJonas Devlieghere 
92101263c6cSJonas Devlieghere bool StartDebuggingRequestHandler::DoExecute(
92201263c6cSJonas Devlieghere     lldb::SBDebugger debugger, char **command,
92301263c6cSJonas Devlieghere     lldb::SBCommandReturnObject &result) {
924224f62deSJohn Harrison   // Command format like: `start-debugging <launch|attach> <configuration>`
92501263c6cSJonas Devlieghere   if (!command) {
926224f62deSJohn Harrison     result.SetError("Invalid use of start-debugging, expected format "
927224f62deSJohn Harrison                     "`start-debugging <launch|attach> <configuration>`.");
92801263c6cSJonas Devlieghere     return false;
92901263c6cSJonas Devlieghere   }
93001263c6cSJonas Devlieghere 
93101263c6cSJonas Devlieghere   if (!command[0] || llvm::StringRef(command[0]).empty()) {
932224f62deSJohn Harrison     result.SetError("start-debugging request type missing.");
93301263c6cSJonas Devlieghere     return false;
93401263c6cSJonas Devlieghere   }
93501263c6cSJonas Devlieghere 
93601263c6cSJonas Devlieghere   if (!command[1] || llvm::StringRef(command[1]).empty()) {
937224f62deSJohn Harrison     result.SetError("start-debugging debug configuration missing.");
93801263c6cSJonas Devlieghere     return false;
93901263c6cSJonas Devlieghere   }
94001263c6cSJonas Devlieghere 
94101263c6cSJonas Devlieghere   llvm::StringRef request{command[0]};
94201263c6cSJonas Devlieghere   std::string raw_configuration{command[1]};
94301263c6cSJonas Devlieghere 
94401263c6cSJonas Devlieghere   llvm::Expected<llvm::json::Value> configuration =
94501263c6cSJonas Devlieghere       llvm::json::parse(raw_configuration);
94601263c6cSJonas Devlieghere 
94701263c6cSJonas Devlieghere   if (!configuration) {
94801263c6cSJonas Devlieghere     llvm::Error err = configuration.takeError();
949224f62deSJohn Harrison     std::string msg = "Failed to parse json configuration: " +
950224f62deSJohn Harrison                       llvm::toString(std::move(err)) + "\n\n" +
951224f62deSJohn Harrison                       raw_configuration;
95201263c6cSJonas Devlieghere     result.SetError(msg.c_str());
95301263c6cSJonas Devlieghere     return false;
95401263c6cSJonas Devlieghere   }
95501263c6cSJonas Devlieghere 
956a6d299ddSJohn Harrison   dap.SendReverseRequest(
95701263c6cSJonas Devlieghere       "startDebugging",
95801263c6cSJonas Devlieghere       llvm::json::Object{{"request", request},
95901263c6cSJonas Devlieghere                          {"configuration", std::move(*configuration)}},
96001263c6cSJonas Devlieghere       [](llvm::Expected<llvm::json::Value> value) {
96101263c6cSJonas Devlieghere         if (!value) {
96201263c6cSJonas Devlieghere           llvm::Error err = value.takeError();
96301263c6cSJonas Devlieghere           llvm::errs() << "reverse start debugging request failed: "
96401263c6cSJonas Devlieghere                        << llvm::toString(std::move(err)) << "\n";
96501263c6cSJonas Devlieghere         }
96601263c6cSJonas Devlieghere       });
96701263c6cSJonas Devlieghere 
96801263c6cSJonas Devlieghere   result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
96901263c6cSJonas Devlieghere 
97001263c6cSJonas Devlieghere   return true;
97101263c6cSJonas Devlieghere }
97201263c6cSJonas Devlieghere 
97301263c6cSJonas Devlieghere bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
97401263c6cSJonas Devlieghere                                        char **command,
97501263c6cSJonas Devlieghere                                        lldb::SBCommandReturnObject &result) {
97601263c6cSJonas Devlieghere   // Command format like: `repl-mode <variable|command|auto>?`
97701263c6cSJonas Devlieghere   // If a new mode is not specified report the current mode.
97801263c6cSJonas Devlieghere   if (!command || llvm::StringRef(command[0]).empty()) {
97901263c6cSJonas Devlieghere     std::string mode;
980a6d299ddSJohn Harrison     switch (dap.repl_mode) {
98101263c6cSJonas Devlieghere     case ReplMode::Variable:
98201263c6cSJonas Devlieghere       mode = "variable";
98301263c6cSJonas Devlieghere       break;
98401263c6cSJonas Devlieghere     case ReplMode::Command:
98501263c6cSJonas Devlieghere       mode = "command";
98601263c6cSJonas Devlieghere       break;
98701263c6cSJonas Devlieghere     case ReplMode::Auto:
98801263c6cSJonas Devlieghere       mode = "auto";
98901263c6cSJonas Devlieghere       break;
99001263c6cSJonas Devlieghere     }
99101263c6cSJonas Devlieghere 
99201263c6cSJonas Devlieghere     result.Printf("lldb-dap repl-mode %s.\n", mode.c_str());
99301263c6cSJonas Devlieghere     result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
99401263c6cSJonas Devlieghere 
99501263c6cSJonas Devlieghere     return true;
99601263c6cSJonas Devlieghere   }
99701263c6cSJonas Devlieghere 
99801263c6cSJonas Devlieghere   llvm::StringRef new_mode{command[0]};
99901263c6cSJonas Devlieghere 
100001263c6cSJonas Devlieghere   if (new_mode == "variable") {
1001a6d299ddSJohn Harrison     dap.repl_mode = ReplMode::Variable;
100201263c6cSJonas Devlieghere   } else if (new_mode == "command") {
1003a6d299ddSJohn Harrison     dap.repl_mode = ReplMode::Command;
100401263c6cSJonas Devlieghere   } else if (new_mode == "auto") {
1005a6d299ddSJohn Harrison     dap.repl_mode = ReplMode::Auto;
100601263c6cSJonas Devlieghere   } else {
100701263c6cSJonas Devlieghere     lldb::SBStream error_message;
100801263c6cSJonas Devlieghere     error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', "
100901263c6cSJonas Devlieghere                          "'command' or 'auto'.\n",
101001263c6cSJonas Devlieghere                          new_mode.data());
101101263c6cSJonas Devlieghere     result.SetError(error_message.GetData());
101201263c6cSJonas Devlieghere     return false;
101301263c6cSJonas Devlieghere   }
101401263c6cSJonas Devlieghere 
101501263c6cSJonas Devlieghere   result.Printf("lldb-dap repl-mode %s set.\n", new_mode.data());
101601263c6cSJonas Devlieghere   result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
101701263c6cSJonas Devlieghere   return true;
101801263c6cSJonas Devlieghere }
101901263c6cSJonas Devlieghere 
1020c5c11f34SJohn Harrison // Sends a DAP event with an optional body.
1021c5c11f34SJohn Harrison //
1022c5c11f34SJohn Harrison // See
1023c5c11f34SJohn Harrison // https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent
1024c5c11f34SJohn Harrison bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
1025c5c11f34SJohn Harrison                                         char **command,
1026c5c11f34SJohn Harrison                                         lldb::SBCommandReturnObject &result) {
1027c5c11f34SJohn Harrison   // Command format like: `send-event <name> <body>?`
1028c5c11f34SJohn Harrison   if (!command || !command[0] || llvm::StringRef(command[0]).empty()) {
1029c5c11f34SJohn Harrison     result.SetError("Not enough arguments found, expected format "
1030c5c11f34SJohn Harrison                     "`lldb-dap send-event <name> <body>?`.");
1031c5c11f34SJohn Harrison     return false;
1032c5c11f34SJohn Harrison   }
1033c5c11f34SJohn Harrison 
1034c5c11f34SJohn Harrison   llvm::StringRef name{command[0]};
1035c5c11f34SJohn Harrison   // Events that are stateful and should be handled by lldb-dap internally.
1036c5c11f34SJohn Harrison   const std::array internal_events{"breakpoint", "capabilities", "continued",
1037c5c11f34SJohn Harrison                                    "exited",     "initialize",   "loadedSource",
1038c5c11f34SJohn Harrison                                    "module",     "process",      "stopped",
1039c5c11f34SJohn Harrison                                    "terminated", "thread"};
1040c5c11f34SJohn Harrison   if (std::find(internal_events.begin(), internal_events.end(), name) !=
1041c5c11f34SJohn Harrison       std::end(internal_events)) {
1042c5c11f34SJohn Harrison     std::string msg =
1043c5c11f34SJohn Harrison         llvm::formatv("Invalid use of lldb-dap send-event, event \"{0}\" "
1044c5c11f34SJohn Harrison                       "should be handled by lldb-dap internally.",
1045c5c11f34SJohn Harrison                       name)
1046c5c11f34SJohn Harrison             .str();
1047c5c11f34SJohn Harrison     result.SetError(msg.c_str());
1048c5c11f34SJohn Harrison     return false;
1049c5c11f34SJohn Harrison   }
1050c5c11f34SJohn Harrison 
1051c5c11f34SJohn Harrison   llvm::json::Object event(CreateEventObject(name));
1052c5c11f34SJohn Harrison 
1053c5c11f34SJohn Harrison   if (command[1] && !llvm::StringRef(command[1]).empty()) {
1054c5c11f34SJohn Harrison     // See if we have unused arguments.
1055c5c11f34SJohn Harrison     if (command[2]) {
1056c5c11f34SJohn Harrison       result.SetError(
1057c5c11f34SJohn Harrison           "Additional arguments found, expected `lldb-dap send-event "
1058c5c11f34SJohn Harrison           "<name> <body>?`.");
1059c5c11f34SJohn Harrison       return false;
1060c5c11f34SJohn Harrison     }
1061c5c11f34SJohn Harrison 
1062c5c11f34SJohn Harrison     llvm::StringRef raw_body{command[1]};
1063c5c11f34SJohn Harrison 
1064c5c11f34SJohn Harrison     llvm::Expected<llvm::json::Value> body = llvm::json::parse(raw_body);
1065c5c11f34SJohn Harrison 
1066c5c11f34SJohn Harrison     if (!body) {
1067c5c11f34SJohn Harrison       llvm::Error err = body.takeError();
1068c5c11f34SJohn Harrison       std::string msg = "Failed to parse custom event body: " +
1069c5c11f34SJohn Harrison                         llvm::toString(std::move(err));
1070c5c11f34SJohn Harrison       result.SetError(msg.c_str());
1071c5c11f34SJohn Harrison       return false;
1072c5c11f34SJohn Harrison     }
1073c5c11f34SJohn Harrison 
1074c5c11f34SJohn Harrison     event.try_emplace("body", std::move(*body));
1075c5c11f34SJohn Harrison   }
1076c5c11f34SJohn Harrison 
1077a6d299ddSJohn Harrison   dap.SendJSON(llvm::json::Value(std::move(event)));
1078c5c11f34SJohn Harrison   result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
1079c5c11f34SJohn Harrison   return true;
1080c5c11f34SJohn Harrison }
1081c5c11f34SJohn Harrison 
1082d9ec4b24SWalter Erquinigo void DAP::SetFrameFormat(llvm::StringRef format) {
1083d9ec4b24SWalter Erquinigo   if (format.empty())
1084d9ec4b24SWalter Erquinigo     return;
1085d9ec4b24SWalter Erquinigo   lldb::SBError error;
1086a6d299ddSJohn Harrison   frame_format = lldb::SBFormat(format.str().c_str(), error);
1087d9ec4b24SWalter Erquinigo   if (error.Fail()) {
1088a6d299ddSJohn Harrison     SendOutput(OutputType::Console,
1089d9ec4b24SWalter Erquinigo                llvm::formatv(
1090a6d299ddSJohn Harrison                    "The provided frame format '{0}' couldn't be parsed: {1}\n",
1091a6d299ddSJohn Harrison                    format, error.GetCString())
1092d9ec4b24SWalter Erquinigo                    .str());
1093d9ec4b24SWalter Erquinigo   }
1094d9ec4b24SWalter Erquinigo }
1095d9ec4b24SWalter Erquinigo 
10961654d7dcSWalter Erquinigo void DAP::SetThreadFormat(llvm::StringRef format) {
10971654d7dcSWalter Erquinigo   if (format.empty())
10981654d7dcSWalter Erquinigo     return;
10991654d7dcSWalter Erquinigo   lldb::SBError error;
1100a6d299ddSJohn Harrison   thread_format = lldb::SBFormat(format.str().c_str(), error);
11011654d7dcSWalter Erquinigo   if (error.Fail()) {
1102a6d299ddSJohn Harrison     SendOutput(OutputType::Console,
11031654d7dcSWalter Erquinigo                llvm::formatv(
11041654d7dcSWalter Erquinigo                    "The provided thread format '{0}' couldn't be parsed: {1}\n",
11051654d7dcSWalter Erquinigo                    format, error.GetCString())
11061654d7dcSWalter Erquinigo                    .str());
11071654d7dcSWalter Erquinigo   }
110889c27d6bSSanthosh Kumar Ellendula }
110989c27d6bSSanthosh Kumar Ellendula 
111089c27d6bSSanthosh Kumar Ellendula InstructionBreakpoint *
111189c27d6bSSanthosh Kumar Ellendula DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {
111289c27d6bSSanthosh Kumar Ellendula   for (auto &bp : instruction_breakpoints) {
1113b99d4112SJohn Harrison     if (bp.second.bp.GetID() == bp_id)
111489c27d6bSSanthosh Kumar Ellendula       return &bp.second;
111589c27d6bSSanthosh Kumar Ellendula   }
111689c27d6bSSanthosh Kumar Ellendula   return nullptr;
111789c27d6bSSanthosh Kumar Ellendula }
111889c27d6bSSanthosh Kumar Ellendula 
111989c27d6bSSanthosh Kumar Ellendula InstructionBreakpoint *
112089c27d6bSSanthosh Kumar Ellendula DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
112189c27d6bSSanthosh Kumar Ellendula   const auto num = thread.GetStopReasonDataCount();
112289c27d6bSSanthosh Kumar Ellendula   InstructionBreakpoint *inst_bp = nullptr;
112389c27d6bSSanthosh Kumar Ellendula   for (size_t i = 0; i < num; i += 2) {
112489c27d6bSSanthosh Kumar Ellendula     // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
112589c27d6bSSanthosh Kumar Ellendula     // thread.GetStopReasonDataAtIndex(i+1) will return the location
112689c27d6bSSanthosh Kumar Ellendula     // within that breakpoint. We only care about the bp ID so we can
112789c27d6bSSanthosh Kumar Ellendula     // see if this is an instruction breakpoint that is getting hit.
112889c27d6bSSanthosh Kumar Ellendula     lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
112989c27d6bSSanthosh Kumar Ellendula     inst_bp = GetInstructionBreakpoint(bp_id);
113089c27d6bSSanthosh Kumar Ellendula     // If any breakpoint is not an instruction breakpoint, then stop and
113189c27d6bSSanthosh Kumar Ellendula     // report this as a normal breakpoint
113289c27d6bSSanthosh Kumar Ellendula     if (inst_bp == nullptr)
113389c27d6bSSanthosh Kumar Ellendula       return nullptr;
113489c27d6bSSanthosh Kumar Ellendula   }
113589c27d6bSSanthosh Kumar Ellendula   return inst_bp;
11361654d7dcSWalter Erquinigo }
11371654d7dcSWalter Erquinigo 
113801263c6cSJonas Devlieghere } // namespace lldb_dap
1139