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