brintos

brintos / llvm-project-archived public Read only

0
0
Text · 54.6 KiB · 465d85a Raw
1592 lines · cpp
1//===-- DAP.cpp -------------------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "DAP.h"10#include "CommandPlugins.h"11#include "DAPLog.h"12#include "EventHelper.h"13#include "ExceptionBreakpoint.h"14#include "Handler/RequestHandler.h"15#include "Handler/ResponseHandler.h"16#include "JSONUtils.h"17#include "LLDBUtils.h"18#include "OutputRedirector.h"19#include "Protocol/ProtocolBase.h"20#include "Protocol/ProtocolEvents.h"21#include "Protocol/ProtocolRequests.h"22#include "Protocol/ProtocolTypes.h"23#include "ProtocolUtils.h"24#include "Transport.h"25#include "lldb/API/SBBreakpoint.h"26#include "lldb/API/SBCommandInterpreter.h"27#include "lldb/API/SBEvent.h"28#include "lldb/API/SBLanguageRuntime.h"29#include "lldb/API/SBListener.h"30#include "lldb/API/SBMutex.h"31#include "lldb/API/SBProcess.h"32#include "lldb/API/SBStream.h"33#include "lldb/Host/JSONTransport.h"34#include "lldb/Host/MainLoop.h"35#include "lldb/Host/MainLoopBase.h"36#include "lldb/Utility/Status.h"37#include "lldb/lldb-defines.h"38#include "lldb/lldb-enumerations.h"39#include "lldb/lldb-types.h"40#include "llvm/ADT/ArrayRef.h"41#include "llvm/ADT/STLExtras.h"42#include "llvm/ADT/ScopeExit.h"43#include "llvm/ADT/StringExtras.h"44#include "llvm/ADT/StringRef.h"45#include "llvm/ADT/Twine.h"46#include "llvm/Support/Chrono.h"47#include "llvm/Support/Error.h"48#include "llvm/Support/ErrorHandling.h"49#include "llvm/Support/FormatVariadic.h"50#include "llvm/Support/raw_ostream.h"51#include <algorithm>52#include <cassert>53#include <chrono>54#include <condition_variable>55#include <cstdarg>56#include <cstdint>57#include <cstdio>58#include <functional>59#include <future>60#include <memory>61#include <mutex>62#include <optional>63#include <string>64#include <thread>65#include <utility>66#include <variant>67 68#if defined(_WIN32)69#define NOMINMAX70#include <fcntl.h>71#include <io.h>72#include <windows.h>73#else74#include <unistd.h>75#endif76 77using namespace lldb_dap;78using namespace lldb_dap::protocol;79using namespace lldb_private;80 81namespace {82#ifdef _WIN3283const char DEV_NULL[] = "nul";84#else85const char DEV_NULL[] = "/dev/null";86#endif87} // namespace88 89namespace lldb_dap {90 91static std::string GetStringFromStructuredData(lldb::SBStructuredData &data,92                                               const char *key) {93  lldb::SBStructuredData keyValue = data.GetValueForKey(key);94  if (!keyValue)95    return std::string();96 97  const size_t length = keyValue.GetStringValue(nullptr, 0);98 99  if (length == 0)100    return std::string();101 102  std::string str(length + 1, 0);103  keyValue.GetStringValue(&str[0], length + 1);104  return str;105}106 107static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data,108                                          const char *key) {109  lldb::SBStructuredData keyValue = data.GetValueForKey(key);110 111  if (!keyValue.IsValid())112    return 0;113  return keyValue.GetUnsignedIntegerValue();114}115 116/// Return string with first character capitalized.117static std::string capitalize(llvm::StringRef str) {118  if (str.empty())119    return "";120  return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();121}122 123llvm::StringRef DAP::debug_adapter_path = "";124 125DAP::DAP(Log *log, const ReplMode default_repl_mode,126         std::vector<std::string> pre_init_commands, bool no_lldbinit,127         llvm::StringRef client_name, DAPTransport &transport, MainLoop &loop)128    : log(log), transport(transport), broadcaster("lldb-dap"),129      progress_event_reporter(130          [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),131      repl_mode(default_repl_mode), no_lldbinit(no_lldbinit),132      m_client_name(client_name), m_loop(loop) {133  configuration.preInitCommands = std::move(pre_init_commands);134  RegisterRequests();135}136 137DAP::~DAP() = default;138 139void DAP::PopulateExceptionBreakpoints() {140  if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {141    exception_breakpoints.emplace_back(*this, "cpp_catch", "C++ Catch",142                                       lldb::eLanguageTypeC_plus_plus,143                                       eExceptionKindCatch);144    exception_breakpoints.emplace_back(*this, "cpp_throw", "C++ Throw",145                                       lldb::eLanguageTypeC_plus_plus,146                                       eExceptionKindThrow);147  }148 149  if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {150    exception_breakpoints.emplace_back(*this, "objc_catch", "Objective-C Catch",151                                       lldb::eLanguageTypeObjC,152                                       eExceptionKindCatch);153    exception_breakpoints.emplace_back(*this, "objc_throw", "Objective-C Throw",154                                       lldb::eLanguageTypeObjC,155                                       eExceptionKindThrow);156  }157 158  if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {159    exception_breakpoints.emplace_back(*this, "swift_catch", "Swift Catch",160                                       lldb::eLanguageTypeSwift,161                                       eExceptionKindCatch);162    exception_breakpoints.emplace_back(*this, "swift_throw", "Swift Throw",163                                       lldb::eLanguageTypeSwift,164                                       eExceptionKindThrow);165  }166 167  // Besides handling the hardcoded list of languages from above, we try to find168  // any other languages that support exception breakpoints using the SB API.169  for (int raw_lang = lldb::eLanguageTypeUnknown;170       raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {171    lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);172 173    // We first discard any languages already handled above.174    if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||175        lang == lldb::eLanguageTypeSwift)176      continue;177 178    if (!lldb::SBDebugger::SupportsLanguage(lang))179      continue;180 181    const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);182    if (!name)183      continue;184    std::string raw_lang_name = name;185    std::string capitalized_lang_name = capitalize(name);186 187    if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {188      const char *raw_throw_keyword =189          lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);190      std::string throw_keyword =191          raw_throw_keyword ? raw_throw_keyword : "throw";192 193      exception_breakpoints.emplace_back(194          *this, raw_lang_name + "_" + throw_keyword,195          capitalized_lang_name + " " + capitalize(throw_keyword), lang,196          eExceptionKindThrow);197    }198 199    if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {200      const char *raw_catch_keyword =201          lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);202      std::string catch_keyword =203          raw_catch_keyword ? raw_catch_keyword : "catch";204 205      exception_breakpoints.emplace_back(206          *this, raw_lang_name + "_" + catch_keyword,207          capitalized_lang_name + " " + capitalize(catch_keyword), lang,208          eExceptionKindCatch);209    }210  }211}212 213ExceptionBreakpoint *DAP::GetExceptionBreakpoint(llvm::StringRef filter) {214  for (auto &bp : exception_breakpoints) {215    if (bp.GetFilter() == filter)216      return &bp;217  }218  return nullptr;219}220 221ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {222  for (auto &bp : exception_breakpoints) {223    if (bp.GetID() == bp_id)224      return &bp;225  }226  return nullptr;227}228 229llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {230  in = lldb::SBFile(std::fopen(DEV_NULL, "r"), /*transfer_ownership=*/true);231 232  if (auto Error = out.RedirectTo(overrideOut, [this](llvm::StringRef output) {233        SendOutput(OutputType::Console, output);234      }))235    return Error;236 237  if (auto Error = err.RedirectTo(overrideErr, [this](llvm::StringRef output) {238        SendOutput(OutputType::Console, output);239      }))240    return Error;241 242  return llvm::Error::success();243}244 245void DAP::StopEventHandlers() {246  event_thread_sp.reset();247 248  // Clean up expired event threads from the session manager.249  DAPSessionManager::GetInstance().ReleaseExpiredEventThreads();250 251  // Still handle the progress thread normally since it's per-DAP instance.252  if (progress_event_thread.joinable()) {253    broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);254    progress_event_thread.join();255  }256}257 258// Serialize the JSON value into a string and send the JSON packet to259// the "out" stream.260void DAP::SendJSON(const llvm::json::Value &json) {261  // FIXME: Instead of parsing the output message from JSON, pass the `Message`262  // as parameter to `SendJSON`.263  Message message;264  llvm::json::Path::Root root;265  if (!fromJSON(json, message, root)) {266    DAP_LOG_ERROR(log, root.getError(), "({1}) encoding failed: {0}",267                  m_client_name);268    return;269  }270  Send(message);271}272 273Id DAP::Send(const Message &message) {274  std::lock_guard<std::mutex> guard(call_mutex);275  Message msg = std::visit(276      [this](auto &&msg) -> Message {277        if (msg.seq == kCalculateSeq)278          msg.seq = seq++;279        return msg;280      },281      Message(message));282 283  if (const protocol::Event *event = std::get_if<protocol::Event>(&msg)) {284    if (llvm::Error err = transport.Send(*event))285      DAP_LOG_ERROR(log, std::move(err), "({0}) sending event failed",286                    m_client_name);287    return event->seq;288  }289 290  if (const Request *req = std::get_if<Request>(&msg)) {291    if (llvm::Error err = transport.Send(*req))292      DAP_LOG_ERROR(log, std::move(err), "({0}) sending request failed",293                    m_client_name);294    return req->seq;295  }296 297  if (const Response *resp = std::get_if<Response>(&msg)) {298    // FIXME: After all the requests have migrated from LegacyRequestHandler >299    // RequestHandler<> this should be handled in RequestHandler<>::operator().300    // If the debugger was interrupted, convert this response into a301    // 'cancelled' response because we might have a partial result.302    llvm::Error err = (debugger.InterruptRequested())303                          ? transport.Send({304                                /*request_seq=*/resp->request_seq,305                                /*command=*/resp->command,306                                /*success=*/false,307                                /*message=*/eResponseMessageCancelled,308                                /*body=*/std::nullopt,309                                /*seq=*/resp->seq,310                            })311                          : transport.Send(*resp);312    if (err)313      DAP_LOG_ERROR(log, std::move(err), "({0}) sending response failed",314                    m_client_name);315    return resp->seq;316  }317 318  llvm_unreachable("Unexpected message type");319}320 321// "OutputEvent": {322//   "allOf": [ { "$ref": "#/definitions/Event" }, {323//     "type": "object",324//     "description": "Event message for 'output' event type. The event325//                     indicates that the target has produced some output.",326//     "properties": {327//       "event": {328//         "type": "string",329//         "enum": [ "output" ]330//       },331//       "body": {332//         "type": "object",333//         "properties": {334//           "category": {335//             "type": "string",336//             "description": "The output category. If not specified,337//                             'console' is assumed.",338//             "_enum": [ "console", "stdout", "stderr", "telemetry" ]339//           },340//           "output": {341//             "type": "string",342//             "description": "The output to report."343//           },344//           "variablesReference": {345//             "type": "number",346//             "description": "If an attribute 'variablesReference' exists347//                             and its value is > 0, the output contains348//                             objects which can be retrieved by passing349//                             variablesReference to the VariablesRequest."350//           },351//           "source": {352//             "$ref": "#/definitions/Source",353//             "description": "An optional source location where the output354//                             was produced."355//           },356//           "line": {357//             "type": "integer",358//             "description": "An optional source location line where the359//                             output was produced."360//           },361//           "column": {362//             "type": "integer",363//             "description": "An optional source location column where the364//                             output was produced."365//           },366//           "data": {367//             "type":["array","boolean","integer","null","number","object",368//                     "string"],369//             "description": "Optional data to report. For the 'telemetry'370//                             category the data will be sent to telemetry, for371//                             the other categories the data is shown in JSON372//                             format."373//           }374//         },375//         "required": ["output"]376//       }377//     },378//     "required": [ "event", "body" ]379//   }]380// }381void DAP::SendOutput(OutputType o, const llvm::StringRef output) {382  if (output.empty())383    return;384 385  const char *category = nullptr;386  switch (o) {387  case OutputType::Console:388    category = "console";389    break;390  case OutputType::Important:391    category = "important";392    break;393  case OutputType::Stdout:394    category = "stdout";395    break;396  case OutputType::Stderr:397    category = "stderr";398    break;399  case OutputType::Telemetry:400    category = "telemetry";401    break;402  }403 404  // Send each line of output as an individual event, including the newline if405  // present.406  ::size_t idx = 0;407  do {408    ::size_t end = output.find('\n', idx);409    if (end == llvm::StringRef::npos)410      end = output.size() - 1;411    llvm::json::Object event(CreateEventObject("output"));412    llvm::json::Object body;413    body.try_emplace("category", category);414    EmplaceSafeString(body, "output", output.slice(idx, end + 1).str());415    event.try_emplace("body", std::move(body));416    SendJSON(llvm::json::Value(std::move(event)));417    idx = end + 1;418  } while (idx < output.size());419}420 421// interface ProgressStartEvent extends Event {422//   event: 'progressStart';423//424//   body: {425//     /**426//      * An ID that must be used in subsequent 'progressUpdate' and427//      'progressEnd'428//      * events to make them refer to the same progress reporting.429//      * IDs must be unique within a debug session.430//      */431//     progressId: string;432//433//     /**434//      * Mandatory (short) title of the progress reporting. Shown in the UI to435//      * describe the long running operation.436//      */437//     title: string;438//439//     /**440//      * The request ID that this progress report is related to. If specified a441//      * debug adapter is expected to emit442//      * progress events for the long running request until the request has443//      been444//      * either completed or cancelled.445//      * If the request ID is omitted, the progress report is assumed to be446//      * related to some general activity of the debug adapter.447//      */448//     requestId?: number;449//450//     /**451//      * If true, the request that reports progress may be canceled with a452//      * 'cancel' request.453//      * So this property basically controls whether the client should use UX454//      that455//      * supports cancellation.456//      * Clients that don't support cancellation are allowed to ignore the457//      * setting.458//      */459//     cancellable?: boolean;460//461//     /**462//      * Optional, more detailed progress message.463//      */464//     message?: string;465//466//     /**467//      * Optional progress percentage to display (value range: 0 to 100). If468//      * omitted no percentage will be shown.469//      */470//     percentage?: number;471//   };472// }473//474// interface ProgressUpdateEvent extends Event {475//   event: 'progressUpdate';476//477//   body: {478//     /**479//      * The ID that was introduced in the initial 'progressStart' event.480//      */481//     progressId: string;482//483//     /**484//      * Optional, more detailed progress message. If omitted, the previous485//      * message (if any) is used.486//      */487//     message?: string;488//489//     /**490//      * Optional progress percentage to display (value range: 0 to 100). If491//      * omitted no percentage will be shown.492//      */493//     percentage?: number;494//   };495// }496//497// interface ProgressEndEvent extends Event {498//   event: 'progressEnd';499//500//   body: {501//     /**502//      * The ID that was introduced in the initial 'ProgressStartEvent'.503//      */504//     progressId: string;505//506//     /**507//      * Optional, more detailed progress message. If omitted, the previous508//      * message (if any) is used.509//      */510//     message?: string;511//   };512// }513 514void DAP::SendProgressEvent(uint64_t progress_id, const char *message,515                            uint64_t completed, uint64_t total) {516  progress_event_reporter.Push(progress_id, message, completed, total);517}518 519void __attribute__((format(printf, 3, 4)))520DAP::SendFormattedOutput(OutputType o, const char *format, ...) {521  char buffer[1024];522  va_list args;523  va_start(args, format);524  int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);525  va_end(args);526  SendOutput(527      o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));528}529 530int32_t DAP::CreateSourceReference(lldb::addr_t address) {531  std::lock_guard<std::mutex> guard(m_source_references_mutex);532  auto iter = llvm::find(m_source_references, address);533  if (iter != m_source_references.end())534    return std::distance(m_source_references.begin(), iter) + 1;535 536  m_source_references.emplace_back(address);537  return static_cast<int32_t>(m_source_references.size());538}539 540std::optional<lldb::addr_t> DAP::GetSourceReferenceAddress(int32_t reference) {541  std::lock_guard<std::mutex> guard(m_source_references_mutex);542  if (reference <= LLDB_DAP_INVALID_SRC_REF)543    return std::nullopt;544 545  if (static_cast<size_t>(reference) > m_source_references.size())546    return std::nullopt;547 548  return m_source_references[reference - 1];549}550 551ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {552  const auto num = thread.GetStopReasonDataCount();553  // Check to see if have hit an exception breakpoint and change the554  // reason to "exception", but only do so if all breakpoints that were555  // hit are exception breakpoints.556  ExceptionBreakpoint *exc_bp = nullptr;557  for (size_t i = 0; i < num; i += 2) {558    // thread.GetStopReasonDataAtIndex(i) will return the bp ID and559    // thread.GetStopReasonDataAtIndex(i+1) will return the location560    // within that breakpoint. We only care about the bp ID so we can561    // see if this is an exception breakpoint that is getting hit.562    lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);563    exc_bp = GetExceptionBreakpoint(bp_id);564    // If any breakpoint is not an exception breakpoint, then stop and565    // report this as a normal breakpoint566    if (exc_bp == nullptr)567      return nullptr;568  }569  return exc_bp;570}571 572lldb::SBThread DAP::GetLLDBThread(lldb::tid_t tid) {573  return target.GetProcess().GetThreadByID(tid);574}575 576lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {577  auto tid = GetInteger<int64_t>(arguments, "threadId")578                 .value_or(LLDB_INVALID_THREAD_ID);579  return target.GetProcess().GetThreadByID(tid);580}581 582lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {583  if (frame_id == LLDB_DAP_INVALID_FRAME_ID)584    return lldb::SBFrame();585 586  lldb::SBProcess process = target.GetProcess();587  // Upper 32 bits is the thread index ID588  lldb::SBThread thread =589      process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));590  // Lower 32 bits is the frame index591  return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));592}593 594lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {595  const auto frame_id = GetInteger<uint64_t>(arguments, "frameId")596                            .value_or(LLDB_DAP_INVALID_FRAME_ID);597  return GetLLDBFrame(frame_id);598}599 600ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,601                             bool partial_expression) {602  // Check for the escape hatch prefix.603  if (!expression.empty() &&604      llvm::StringRef(expression)605          .starts_with(configuration.commandEscapePrefix)) {606    expression = expression.substr(configuration.commandEscapePrefix.size());607    return ReplMode::Command;608  }609 610  switch (repl_mode) {611  case ReplMode::Variable:612    return ReplMode::Variable;613  case ReplMode::Command:614    return ReplMode::Command;615  case ReplMode::Auto:616    // To determine if the expression is a command or not, check if the first617    // term is a variable or command. If it's a variable in scope we will prefer618    // that behavior and give a warning to the user if they meant to invoke the619    // operation as a command.620    //621    // Example use case:622    //   int p and expression "p + 1" > variable623    //   int i and expression "i" > variable624    //   int var and expression "va" > command625    std::pair<llvm::StringRef, llvm::StringRef> token =626        llvm::getToken(expression);627 628    // If the first token is not fully finished yet, we can't629    // determine whether this will be a variable or a lldb command.630    if (partial_expression && token.second.empty())631      return ReplMode::Auto;632 633    std::string term = token.first.str();634    lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();635    bool term_is_command = interpreter.CommandExists(term.c_str()) ||636                           interpreter.UserCommandExists(term.c_str()) ||637                           interpreter.AliasExists(term.c_str());638    bool term_is_variable = frame.FindVariable(term.c_str()).IsValid();639 640    // If we have both a variable and command, warn the user about the conflict.641    if (term_is_command && term_is_variable) {642      llvm::errs()643          << "Warning: Expression '" << term644          << "' is both an LLDB command and variable. It will be evaluated as "645             "a variable. To evaluate the expression as an LLDB command, use '"646          << configuration.commandEscapePrefix << "' as a prefix.\n";647    }648 649    // Variables take preference to commands in auto, since commands can always650    // be called using the command_escape_prefix651    return term_is_variable  ? ReplMode::Variable652           : term_is_command ? ReplMode::Command653                             : ReplMode::Variable;654  }655 656  llvm_unreachable("enum cases exhausted.");657}658 659std::optional<protocol::Source> DAP::ResolveSource(const lldb::SBFrame &frame) {660  if (!frame.IsValid())661    return std::nullopt;662 663  const lldb::SBLineEntry frame_line_entry = frame.GetLineEntry();664  if (DisplayAssemblySource(debugger, frame_line_entry)) {665    const lldb::SBAddress frame_pc = frame.GetPCAddress();666    return ResolveAssemblySource(frame_pc);667  }668 669  return CreateSource(frame_line_entry.GetFileSpec());670}671 672std::optional<protocol::Source> DAP::ResolveSource(lldb::SBAddress address) {673  lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address);674  if (DisplayAssemblySource(debugger, line_entry))675    return ResolveAssemblySource(address);676 677  if (!line_entry.IsValid())678    return std::nullopt;679 680  return CreateSource(line_entry.GetFileSpec());681}682 683std::optional<protocol::Source>684DAP::ResolveAssemblySource(lldb::SBAddress address) {685  lldb::SBSymbol symbol = address.GetSymbol();686  lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;687  std::string name;688  if (symbol.IsValid()) {689    load_addr = symbol.GetStartAddress().GetLoadAddress(target);690    name = symbol.GetName();691  } else {692    load_addr = address.GetLoadAddress(target);693    name = GetLoadAddressString(load_addr);694  }695 696  if (load_addr == LLDB_INVALID_ADDRESS)697    return std::nullopt;698 699  protocol::Source source;700  source.sourceReference = CreateSourceReference(load_addr);701  lldb::SBModule module = address.GetModule();702  if (module.IsValid()) {703    lldb::SBFileSpec file_spec = module.GetFileSpec();704    if (file_spec.IsValid()) {705      std::string path = GetSBFileSpecPath(file_spec);706      if (!path.empty())707        source.path = path + '`' + name;708    }709  }710 711  source.name = std::move(name);712 713  // Mark the source as deemphasized since users will only be able to view714  // assembly for these frames.715  source.presentationHint =716      protocol::Source::eSourcePresentationHintDeemphasize;717 718  return source;719}720 721bool DAP::RunLLDBCommands(llvm::StringRef prefix,722                          llvm::ArrayRef<std::string> commands) {723  bool required_command_failed = false;724  std::string output = ::RunLLDBCommands(725      debugger, prefix, commands, required_command_failed,726      /*parse_command_directives*/ true, /*echo_commands*/ true);727  SendOutput(OutputType::Console, output);728  return !required_command_failed;729}730 731static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) {732  return llvm::createStringError(733      llvm::inconvertibleErrorCode(),734      llvm::formatv(735          "Failed to run {0} commands. See the Debug Console for more details.",736          category)737          .str()738          .c_str());739}740 741llvm::Error742DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) {743  if (!RunLLDBCommands("Running attachCommands:", attach_commands))744    return createRunLLDBCommandsErrorMessage("attach");745  return llvm::Error::success();746}747 748llvm::Error749DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {750  if (!RunLLDBCommands("Running launchCommands:", launch_commands))751    return createRunLLDBCommandsErrorMessage("launch");752  return llvm::Error::success();753}754 755llvm::Error DAP::RunInitCommands() {756  if (!RunLLDBCommands("Running initCommands:", configuration.initCommands))757    return createRunLLDBCommandsErrorMessage("initCommands");758  return llvm::Error::success();759}760 761llvm::Error DAP::RunPreInitCommands() {762  if (!RunLLDBCommands("Running preInitCommands:",763                       configuration.preInitCommands))764    return createRunLLDBCommandsErrorMessage("preInitCommands");765  return llvm::Error::success();766}767 768llvm::Error DAP::RunPreRunCommands() {769  if (!RunLLDBCommands("Running preRunCommands:", configuration.preRunCommands))770    return createRunLLDBCommandsErrorMessage("preRunCommands");771  return llvm::Error::success();772}773 774void DAP::RunPostRunCommands() {775  RunLLDBCommands("Running postRunCommands:", configuration.postRunCommands);776}777void DAP::RunStopCommands() {778  RunLLDBCommands("Running stopCommands:", configuration.stopCommands);779}780 781void DAP::RunExitCommands() {782  RunLLDBCommands("Running exitCommands:", configuration.exitCommands);783}784 785void DAP::RunTerminateCommands() {786  RunLLDBCommands("Running terminateCommands:",787                  configuration.terminateCommands);788}789 790lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) {791  // the given program as an argument. Executable file can be a source of target792  // architecture and platform, if they differ from the host. Setting exe path793  // in launch info is useless because Target.Launch() will not change794  // architecture and platform, therefore they should be known at the target795  // creation. We also use target triple and platform from the launch796  // configuration, if given, since in some cases ELF file doesn't contain797  // enough information to determine correct arch and platform (or ELF can be798  // omitted at all), so it is good to leave the user an opportunity to specify799  // those. Any of those three can be left empty.800  auto target = this->debugger.CreateTarget(801      /*filename=*/configuration.program.data(),802      /*target_triple=*/configuration.targetTriple.data(),803      /*platform_name=*/configuration.platformName.data(),804      /*add_dependent_modules=*/true, // Add dependent modules.805      error);806 807  return target;808}809 810void DAP::SetTarget(const lldb::SBTarget target) {811  this->target = target;812 813  if (target.IsValid()) {814    // Configure breakpoint event listeners for the target.815    lldb::SBListener listener = this->debugger.GetListener();816    listener.StartListeningForEvents(817        this->target.GetBroadcaster(),818        lldb::SBTarget::eBroadcastBitBreakpointChanged |819            lldb::SBTarget::eBroadcastBitModulesLoaded |820            lldb::SBTarget::eBroadcastBitModulesUnloaded |821            lldb::SBTarget::eBroadcastBitSymbolsLoaded |822            lldb::SBTarget::eBroadcastBitSymbolsChanged |823            lldb::SBTarget::eBroadcastBitNewTargetCreated);824    listener.StartListeningForEvents(this->broadcaster,825                                     eBroadcastBitStopEventThread);826  }827}828 829bool DAP::HandleObject(const Message &M) {830  TelemetryDispatcher dispatcher(&debugger);831  dispatcher.Set("client_name", m_client_name.str());832  if (const auto *req = std::get_if<Request>(&M)) {833    {834      std::lock_guard<std::mutex> guard(m_active_request_mutex);835      m_active_request = req;836 837      // Clear the interrupt request prior to invoking a handler.838      if (debugger.InterruptRequested())839        debugger.CancelInterruptRequest();840    }841 842    auto cleanup = llvm::make_scope_exit([&]() {843      std::scoped_lock<std::mutex> active_request_lock(m_active_request_mutex);844      m_active_request = nullptr;845    });846 847    auto handler_pos = request_handlers.find(req->command);848    dispatcher.Set("client_data",849                   llvm::Twine("request_command:", req->command).str());850    if (handler_pos != request_handlers.end()) {851      handler_pos->second->Run(*req);852      return true; // Success853    }854 855    dispatcher.Set("error",856                   llvm::Twine("unhandled-command:" + req->command).str());857    DAP_LOG(log, "({0}) error: unhandled command '{1}'", m_client_name,858            req->command);859    return false; // Fail860  }861 862  if (const auto *resp = std::get_if<Response>(&M)) {863    std::unique_ptr<ResponseHandler> response_handler;864    {865      std::lock_guard<std::mutex> guard(call_mutex);866      auto inflight = inflight_reverse_requests.find(resp->request_seq);867      if (inflight != inflight_reverse_requests.end()) {868        response_handler = std::move(inflight->second);869        inflight_reverse_requests.erase(inflight);870      }871    }872 873    if (!response_handler)874      response_handler =875          std::make_unique<UnknownResponseHandler>("", resp->request_seq);876 877    // Result should be given, use null if not.878    if (resp->success) {879      (*response_handler)(resp->body);880      dispatcher.Set("client_data",881                     llvm::Twine("response_command:", resp->command).str());882    } else {883      llvm::StringRef message = "Unknown error, response failed";884      if (resp->message) {885        message =886            std::visit(llvm::makeVisitor(887                           [](const std::string &message) -> llvm::StringRef {888                             return message;889                           },890                           [](const protocol::ResponseMessage &message)891                               -> llvm::StringRef {892                             switch (message) {893                             case protocol::eResponseMessageCancelled:894                               return "cancelled";895                             case protocol::eResponseMessageNotStopped:896                               return "notStopped";897                             }898                             llvm_unreachable("unknown response message kind.");899                           }),900                       *resp->message);901      }902      dispatcher.Set("error", message.str());903 904      (*response_handler)(llvm::createStringError(905          std::error_code(-1, std::generic_category()), message));906    }907 908    return true;909  }910 911  dispatcher.Set("error", "Unsupported protocol message");912  DAP_LOG(log, "Unsupported protocol message");913 914  return false;915}916 917void DAP::SendTerminatedEvent() {918  // Prevent races if the process exits while we're being asked to disconnect.919  llvm::call_once(terminated_event_flag, [&] {920    RunTerminateCommands();921    // Send a "terminated" event922    llvm::json::Object event(CreateTerminatedEventObject(target));923    SendJSON(llvm::json::Value(std::move(event)));924  });925}926 927llvm::Error DAP::Disconnect() { return Disconnect(!is_attach); }928 929llvm::Error DAP::Disconnect(bool terminateDebuggee) {930  lldb::SBError error;931  lldb::SBProcess process = target.GetProcess();932  auto state = process.GetState();933  switch (state) {934  case lldb::eStateInvalid:935  case lldb::eStateUnloaded:936  case lldb::eStateDetached:937  case lldb::eStateExited:938    break;939  case lldb::eStateConnected:940  case lldb::eStateAttaching:941  case lldb::eStateLaunching:942  case lldb::eStateStepping:943  case lldb::eStateCrashed:944  case lldb::eStateSuspended:945  case lldb::eStateStopped:946  case lldb::eStateRunning: {947    ScopeSyncMode scope_sync_mode(debugger);948    error = terminateDebuggee ? process.Kill() : process.Detach();949    break;950  }951  }952 953  SendTerminatedEvent();954  TerminateLoop();955  return ToError(error);956}957 958bool DAP::IsCancelled(const protocol::Request &req) {959  std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);960  return m_cancelled_requests.contains(req.seq);961}962 963void DAP::ClearCancelRequest(const CancelArguments &args) {964  std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);965  if (args.requestId)966    m_cancelled_requests.erase(*args.requestId);967}968 969template <typename T>970static std::optional<T> getArgumentsIfRequest(const Request &req,971                                              llvm::StringLiteral command) {972  if (req.command != command)973    return std::nullopt;974 975  T args;976  llvm::json::Path::Root root;977  if (!fromJSON(req.arguments, args, root))978    return std::nullopt;979 980  return args;981}982 983void DAP::Received(const protocol::Event &event) {984  // no-op, no supported events from the client to the server as of DAP v1.68.985}986 987void DAP::Received(const protocol::Request &request) {988  if (request.command == "disconnect")989    m_disconnecting = true;990 991  const std::optional<CancelArguments> cancel_args =992      getArgumentsIfRequest<CancelArguments>(request, "cancel");993  if (cancel_args) {994    {995      std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);996      if (cancel_args->requestId)997        m_cancelled_requests.insert(*cancel_args->requestId);998    }999 1000    // If a cancel is requested for the active request, make a best1001    // effort attempt to interrupt.1002    std::lock_guard<std::mutex> guard(m_active_request_mutex);1003    if (m_active_request && cancel_args->requestId == m_active_request->seq) {1004      DAP_LOG(log, "({0}) interrupting inflight request (command={1} seq={2})",1005              m_client_name, m_active_request->command, m_active_request->seq);1006      debugger.RequestInterrupt();1007    }1008  }1009 1010  std::lock_guard<std::mutex> guard(m_queue_mutex);1011  DAP_LOG(log, "({0}) queued (command={1} seq={2})", m_client_name,1012          request.command, request.seq);1013  m_queue.push_back(request);1014  m_queue_cv.notify_one();1015}1016 1017void DAP::Received(const protocol::Response &response) {1018  std::lock_guard<std::mutex> guard(m_queue_mutex);1019  DAP_LOG(log, "({0}) queued (command={1} seq={2})", m_client_name,1020          response.command, response.request_seq);1021  m_queue.push_back(response);1022  m_queue_cv.notify_one();1023}1024 1025void DAP::OnError(llvm::Error error) {1026  DAP_LOG_ERROR(log, std::move(error), "({1}) received error: {0}",1027                m_client_name);1028  TerminateLoop(/*failed=*/true);1029}1030 1031void DAP::OnClosed() {1032  DAP_LOG(log, "({0}) received EOF", m_client_name);1033  TerminateLoop();1034}1035 1036void DAP::TerminateLoop(bool failed) {1037  std::lock_guard<std::mutex> guard(m_queue_mutex);1038  if (m_disconnecting)1039    return; // Already disconnecting.1040 1041  m_error_occurred = failed;1042  m_disconnecting = true;1043  m_loop.AddPendingCallback(1044      [](MainLoopBase &loop) { loop.RequestTermination(); });1045}1046 1047void DAP::TransportHandler() {1048  auto scope_guard = llvm::make_scope_exit([this] {1049    std::lock_guard<std::mutex> guard(m_queue_mutex);1050    // Ensure we're marked as disconnecting when the reader exits.1051    m_disconnecting = true;1052    m_queue_cv.notify_all();1053  });1054 1055  auto handle = transport.RegisterMessageHandler(m_loop, *this);1056  if (!handle) {1057    DAP_LOG_ERROR(log, handle.takeError(),1058                  "({1}) registering message handler failed: {0}",1059                  m_client_name);1060    std::lock_guard<std::mutex> guard(m_queue_mutex);1061    m_error_occurred = true;1062    return;1063  }1064 1065  if (Status status = m_loop.Run(); status.Fail()) {1066    DAP_LOG_ERROR(log, status.takeError(), "({1}) MainLoop run failed: {0}",1067                  m_client_name);1068    std::lock_guard<std::mutex> guard(m_queue_mutex);1069    m_error_occurred = true;1070    return;1071  }1072}1073 1074llvm::Error DAP::Loop() {1075  {1076    // Reset disconnect flag once we start the loop.1077    std::lock_guard<std::mutex> guard(m_queue_mutex);1078    m_disconnecting = false;1079  }1080 1081  auto thread = std::thread(std::bind(&DAP::TransportHandler, this));1082 1083  auto cleanup = llvm::make_scope_exit([this]() {1084    // FIXME: Merge these into the MainLoop handler.1085    out.Stop();1086    err.Stop();1087    StopEventHandlers();1088 1089    // Destroy the debugger when the session ends. This will trigger the1090    // debugger's destroy callbacks for earlier logging and clean-ups, rather1091    // than waiting for the termination of the lldb-dap process.1092    lldb::SBDebugger::Destroy(debugger);1093  });1094 1095  while (true) {1096    std::unique_lock<std::mutex> lock(m_queue_mutex);1097    m_queue_cv.wait(lock, [&] { return m_disconnecting || !m_queue.empty(); });1098 1099    if (m_disconnecting && m_queue.empty())1100      break;1101 1102    Message next = m_queue.front();1103    m_queue.pop_front();1104 1105    // Unlock while we're processing the event.1106    lock.unlock();1107 1108    if (!HandleObject(next))1109      return llvm::createStringError(llvm::inconvertibleErrorCode(),1110                                     "unhandled packet");1111  }1112 1113  // Don't wait to join the mainloop thread if our callback wasn't added1114  // successfully, or we'll wait forever.1115  if (m_loop.AddPendingCallback(1116          [](MainLoopBase &loop) { loop.RequestTermination(); }))1117    thread.join();1118 1119  if (m_error_occurred)1120    return llvm::createStringError(llvm::inconvertibleErrorCode(),1121                                   "DAP Loop terminated due to an internal "1122                                   "error, see DAP Logs for more information.");1123  return llvm::Error::success();1124}1125 1126lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) {1127  lldb::SBError error;1128  lldb::SBProcess process = target.GetProcess();1129  if (!process.IsValid()) {1130    error.SetErrorString("invalid process");1131    return error;1132  }1133  auto timeout_time =1134      std::chrono::steady_clock::now() + std::chrono::seconds(seconds);1135  while (std::chrono::steady_clock::now() < timeout_time) {1136    const auto state = process.GetState();1137    switch (state) {1138    case lldb::eStateUnloaded:1139    case lldb::eStateAttaching:1140    case lldb::eStateConnected:1141    case lldb::eStateInvalid:1142    case lldb::eStateLaunching:1143    case lldb::eStateRunning:1144    case lldb::eStateStepping:1145    case lldb::eStateSuspended:1146      break;1147    case lldb::eStateDetached:1148      error.SetErrorString("process detached during launch or attach");1149      return error;1150    case lldb::eStateExited:1151      error.SetErrorString("process exited during launch or attach");1152      return error;1153    case lldb::eStateCrashed:1154    case lldb::eStateStopped:1155      return lldb::SBError(); // Success!1156    }1157    std::this_thread::sleep_for(std::chrono::microseconds(250));1158  }1159  error.SetErrorString(1160      llvm::formatv("process failed to stop within {0}", seconds)1161          .str()1162          .c_str());1163  return error;1164}1165 1166void DAP::ConfigureSourceMaps() {1167  if (configuration.sourceMap.empty() && configuration.sourcePath.empty())1168    return;1169 1170  std::string sourceMapCommand;1171  llvm::raw_string_ostream strm(sourceMapCommand);1172  strm << "settings set target.source-map ";1173 1174  if (!configuration.sourceMap.empty()) {1175    for (const auto &kv : configuration.sourceMap) {1176      strm << "\"" << kv.first << "\" \"" << kv.second << "\" ";1177    }1178  } else if (!configuration.sourcePath.empty()) {1179    strm << "\".\" \"" << configuration.sourcePath << "\"";1180  }1181 1182  RunLLDBCommands("Setting source map:", {sourceMapCommand});1183}1184 1185void DAP::SetConfiguration(const protocol::Configuration &config,1186                           bool is_attach) {1187  configuration = config;1188  stop_at_entry = config.stopOnEntry;1189  this->is_attach = is_attach;1190 1191  if (configuration.customFrameFormat)1192    SetFrameFormat(*configuration.customFrameFormat);1193  if (configuration.customThreadFormat)1194    SetThreadFormat(*configuration.customThreadFormat);1195}1196 1197void DAP::SetFrameFormat(llvm::StringRef format) {1198  lldb::SBError error;1199  frame_format = lldb::SBFormat(format.str().c_str(), error);1200  if (error.Fail()) {1201    SendOutput(OutputType::Console,1202               llvm::formatv(1203                   "The provided frame format '{0}' couldn't be parsed: {1}\n",1204                   format, error.GetCString())1205                   .str());1206  }1207}1208 1209void DAP::SetThreadFormat(llvm::StringRef format) {1210  lldb::SBError error;1211  thread_format = lldb::SBFormat(format.str().c_str(), error);1212  if (error.Fail()) {1213    SendOutput(OutputType::Console,1214               llvm::formatv(1215                   "The provided thread format '{0}' couldn't be parsed: {1}\n",1216                   format, error.GetCString())1217                   .str());1218  }1219}1220 1221InstructionBreakpoint *1222DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {1223  for (auto &bp : instruction_breakpoints) {1224    if (bp.second.GetID() == bp_id)1225      return &bp.second;1226  }1227  return nullptr;1228}1229 1230InstructionBreakpoint *1231DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {1232  const auto num = thread.GetStopReasonDataCount();1233  InstructionBreakpoint *inst_bp = nullptr;1234  for (size_t i = 0; i < num; i += 2) {1235    // thread.GetStopReasonDataAtIndex(i) will return the bp ID and1236    // thread.GetStopReasonDataAtIndex(i+1) will return the location1237    // within that breakpoint. We only care about the bp ID so we can1238    // see if this is an instruction breakpoint that is getting hit.1239    lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);1240    inst_bp = GetInstructionBreakpoint(bp_id);1241    // If any breakpoint is not an instruction breakpoint, then stop and1242    // report this as a normal breakpoint1243    if (inst_bp == nullptr)1244      return nullptr;1245  }1246  return inst_bp;1247}1248 1249protocol::Capabilities DAP::GetCapabilities() {1250  protocol::Capabilities capabilities;1251 1252  // Supported capabilities that are not specific to a single request.1253  capabilities.supportedFeatures = {1254      protocol::eAdapterFeatureLogPoints,1255      protocol::eAdapterFeatureSteppingGranularity,1256      protocol::eAdapterFeatureValueFormattingOptions,1257  };1258 1259  // Capabilities associated with specific requests.1260  for (auto &kv : request_handlers) {1261    llvm::SmallDenseSet<AdapterFeature, 1> features =1262        kv.second->GetSupportedFeatures();1263    capabilities.supportedFeatures.insert(features.begin(), features.end());1264  }1265 1266  // Available filters or options for the setExceptionBreakpoints request.1267  PopulateExceptionBreakpoints();1268  std::vector<protocol::ExceptionBreakpointsFilter> filters;1269  for (const auto &exc_bp : exception_breakpoints)1270    filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));1271  capabilities.exceptionBreakpointFilters = std::move(filters);1272 1273  // FIXME: This should be registered based on the supported languages?1274  std::vector<std::string> completion_characters;1275  completion_characters.emplace_back(".");1276  // FIXME: I wonder if we should remove this key... its very aggressive1277  // triggering and accepting completions.1278  completion_characters.emplace_back(" ");1279  completion_characters.emplace_back("\t");1280  capabilities.completionTriggerCharacters = std::move(completion_characters);1281 1282  // Put in non-DAP specification lldb specific information.1283  capabilities.lldbExtVersion = debugger.GetVersionString();1284 1285  return capabilities;1286}1287 1288protocol::Capabilities DAP::GetCustomCapabilities() {1289  protocol::Capabilities capabilities;1290 1291  // Add all custom capabilities here.1292  const llvm::DenseSet<AdapterFeature> all_custom_features = {1293      protocol::eAdapterFeatureSupportsModuleSymbolsRequest,1294  };1295 1296  for (auto &kv : request_handlers) {1297    llvm::SmallDenseSet<AdapterFeature, 1> features =1298        kv.second->GetSupportedFeatures();1299 1300    for (auto &feature : features) {1301      if (all_custom_features.contains(feature))1302        capabilities.supportedFeatures.insert(feature);1303    }1304  }1305 1306  return capabilities;1307}1308 1309void DAP::StartEventThread() {1310  // Get event thread for this debugger (creates it if it doesn't exist).1311  event_thread_sp = DAPSessionManager::GetInstance().GetEventThreadForDebugger(1312      debugger, this);1313}1314 1315void DAP::StartProgressEventThread() {1316  progress_event_thread = std::thread(&DAP::ProgressEventThread, this);1317}1318 1319void DAP::StartEventThreads() {1320  if (clientFeatures.contains(eClientFeatureProgressReporting))1321    StartProgressEventThread();1322 1323  StartEventThread();1324}1325 1326llvm::Error DAP::InitializeDebugger(int debugger_id,1327                                    lldb::user_id_t target_id) {1328  // Find the existing debugger by ID1329  debugger = lldb::SBDebugger::FindDebuggerWithID(debugger_id);1330  if (!debugger.IsValid()) {1331    return llvm::createStringError(1332        "Unable to find existing debugger for debugger ID");1333  }1334 1335  // Find the target within the debugger by its globally unique ID1336  lldb::SBTarget target = debugger.FindTargetByGloballyUniqueID(target_id);1337  if (!target.IsValid()) {1338    return llvm::createStringError(1339        "Unable to find existing target for target ID");1340  }1341 1342  // Set the target for this DAP session.1343  SetTarget(target);1344  StartEventThreads();1345  return llvm::Error::success();1346}1347 1348llvm::Error DAP::InitializeDebugger() {1349  debugger = lldb::SBDebugger::Create(/*argument_name=*/false);1350 1351  // Configure input/output/error file descriptors.1352  debugger.SetInputFile(in);1353  target = debugger.GetDummyTarget();1354 1355  llvm::Expected<int> out_fd = out.GetWriteFileDescriptor();1356  if (!out_fd)1357    return out_fd.takeError();1358  debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false));1359 1360  llvm::Expected<int> err_fd = err.GetWriteFileDescriptor();1361  if (!err_fd)1362    return err_fd.takeError();1363  debugger.SetErrorFile(lldb::SBFile(*err_fd, "w", false));1364 1365  // The sourceInitFile option is not part of the DAP specification. It is an1366  // extension used by the test suite to prevent sourcing `.lldbinit` and1367  // changing its behavior. The CLI flag --no-lldbinit takes precedence over1368  // the DAP parameter.1369  bool should_source_init_files = !no_lldbinit && sourceInitFile;1370  if (should_source_init_files) {1371    debugger.SkipLLDBInitFiles(false);1372    debugger.SkipAppInitFiles(false);1373    lldb::SBCommandReturnObject init;1374    auto interp = debugger.GetCommandInterpreter();1375    interp.SourceInitFileInGlobalDirectory(init);1376    interp.SourceInitFileInHomeDirectory(init);1377  }1378 1379  // Run initialization commands.1380  if (llvm::Error err = RunPreInitCommands())1381    return err;1382 1383  auto cmd = debugger.GetCommandInterpreter().AddMultiwordCommand(1384      "lldb-dap", "Commands for managing lldb-dap.");1385 1386  if (clientFeatures.contains(eClientFeatureStartDebuggingRequest)) {1387    cmd.AddCommand(1388        "start-debugging", new StartDebuggingCommand(*this),1389        "Sends a startDebugging request from the debug adapter to the client "1390        "to start a child debug session of the same type as the caller.");1391  }1392 1393  cmd.AddCommand(1394      "repl-mode", new ReplModeCommand(*this),1395      "Get or set the repl behavior of lldb-dap evaluation requests.");1396  cmd.AddCommand("send-event", new SendEventCommand(*this),1397                 "Sends an DAP event to the client.");1398 1399  StartEventThreads();1400  return llvm::Error::success();1401}1402 1403void DAP::ProgressEventThread() {1404  lldb::SBListener listener("lldb-dap.progress.listener");1405  debugger.GetBroadcaster().AddListener(1406      listener, lldb::SBDebugger::eBroadcastBitProgress |1407                    lldb::SBDebugger::eBroadcastBitExternalProgress);1408  broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);1409  lldb::SBEvent event;1410  bool done = false;1411  while (!done) {1412    if (listener.WaitForEvent(UINT32_MAX, event)) {1413      const auto event_mask = event.GetType();1414      if (event.BroadcasterMatchesRef(broadcaster)) {1415        if (event_mask & eBroadcastBitStopProgressThread) {1416          done = true;1417        }1418      } else {1419        lldb::SBStructuredData data =1420            lldb::SBDebugger::GetProgressDataFromEvent(event);1421 1422        const uint64_t progress_id =1423            GetUintFromStructuredData(data, "progress_id");1424        const uint64_t completed = GetUintFromStructuredData(data, "completed");1425        const uint64_t total = GetUintFromStructuredData(data, "total");1426        const std::string details =1427            GetStringFromStructuredData(data, "details");1428 1429        if (completed == 0) {1430          if (total == UINT64_MAX) {1431            // This progress is non deterministic and won't get updated until it1432            // is completed. Send the "message" which will be the combined title1433            // and detail. The only other progress event for thus1434            // non-deterministic progress will be the completed event So there1435            // will be no need to update the detail.1436            const std::string message =1437                GetStringFromStructuredData(data, "message");1438            SendProgressEvent(progress_id, message.c_str(), completed, total);1439          } else {1440            // This progress is deterministic and will receive updates,1441            // on the progress creation event VSCode will save the message in1442            // the create packet and use that as the title, so we send just the1443            // title in the progressCreate packet followed immediately by a1444            // detail packet, if there is any detail.1445            const std::string title =1446                GetStringFromStructuredData(data, "title");1447            SendProgressEvent(progress_id, title.c_str(), completed, total);1448            if (!details.empty())1449              SendProgressEvent(progress_id, details.c_str(), completed, total);1450          }1451        } else {1452          // This progress event is either the end of the progress dialog, or an1453          // update with possible detail. The "detail" string we send to VS Code1454          // will be appended to the progress dialog's initial text from when it1455          // was created.1456          SendProgressEvent(progress_id, details.c_str(), completed, total);1457        }1458      }1459    }1460  }1461}1462 1463std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(1464    const protocol::Source &source,1465    const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints) {1466  std::vector<protocol::Breakpoint> response_breakpoints;1467  if (source.sourceReference) {1468    // Breakpoint set by assembly source.1469    auto &existing_breakpoints =1470        m_source_assembly_breakpoints[*source.sourceReference];1471    response_breakpoints =1472        SetSourceBreakpoints(source, breakpoints, existing_breakpoints);1473  } else {1474    // Breakpoint set by a regular source file.1475    const auto path = source.path.value_or("");1476    auto &existing_breakpoints = m_source_breakpoints[path];1477    response_breakpoints =1478        SetSourceBreakpoints(source, breakpoints, existing_breakpoints);1479  }1480 1481  return response_breakpoints;1482}1483 1484std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(1485    const protocol::Source &source,1486    const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints,1487    SourceBreakpointMap &existing_breakpoints) {1488  std::vector<protocol::Breakpoint> response_breakpoints;1489 1490  SourceBreakpointMap request_breakpoints;1491  if (breakpoints) {1492    for (const auto &bp : *breakpoints) {1493      SourceBreakpoint src_bp(*this, bp);1494      std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(),1495                                           src_bp.GetColumn());1496      request_breakpoints.try_emplace(bp_pos, src_bp);1497 1498      const auto [iv, inserted] =1499          existing_breakpoints.try_emplace(bp_pos, src_bp);1500      // We check if this breakpoint already exists to update it.1501      if (inserted) {1502        if (llvm::Error error = iv->second.SetBreakpoint(source)) {1503          protocol::Breakpoint invalid_breakpoint;1504          invalid_breakpoint.message = llvm::toString(std::move(error));1505          invalid_breakpoint.verified = false;1506          response_breakpoints.push_back(std::move(invalid_breakpoint));1507          existing_breakpoints.erase(iv);1508          continue;1509        }1510      } else {1511        iv->second.UpdateBreakpoint(src_bp);1512      }1513 1514      protocol::Breakpoint response_breakpoint =1515          iv->second.ToProtocolBreakpoint();1516 1517      if (!response_breakpoint.source)1518        response_breakpoint.source = source;1519      if (!response_breakpoint.line &&1520          src_bp.GetLine() != LLDB_INVALID_LINE_NUMBER)1521        response_breakpoint.line = src_bp.GetLine();1522      if (!response_breakpoint.column &&1523          src_bp.GetColumn() != LLDB_INVALID_COLUMN_NUMBER)1524        response_breakpoint.column = src_bp.GetColumn();1525      response_breakpoints.push_back(std::move(response_breakpoint));1526    }1527  }1528 1529  // Delete any breakpoints in this source file that aren't in the1530  // request_bps set. There is no call to remove breakpoints other than1531  // calling this function with a smaller or empty "breakpoints" list.1532  for (auto it = existing_breakpoints.begin();1533       it != existing_breakpoints.end();) {1534    auto request_pos = request_breakpoints.find(it->first);1535    if (request_pos == request_breakpoints.end()) {1536      // This breakpoint no longer exists in this source file, delete it1537      target.BreakpointDelete(it->second.GetID());1538      it = existing_breakpoints.erase(it);1539    } else {1540      ++it;1541    }1542  }1543 1544  return response_breakpoints;1545}1546 1547void DAP::RegisterRequests() {1548  RegisterRequest<AttachRequestHandler>();1549  RegisterRequest<BreakpointLocationsRequestHandler>();1550  RegisterRequest<CancelRequestHandler>();1551  RegisterRequest<CompletionsRequestHandler>();1552  RegisterRequest<ConfigurationDoneRequestHandler>();1553  RegisterRequest<ContinueRequestHandler>();1554  RegisterRequest<DataBreakpointInfoRequestHandler>();1555  RegisterRequest<DisassembleRequestHandler>();1556  RegisterRequest<DisconnectRequestHandler>();1557  RegisterRequest<EvaluateRequestHandler>();1558  RegisterRequest<ExceptionInfoRequestHandler>();1559  RegisterRequest<InitializeRequestHandler>();1560  RegisterRequest<LaunchRequestHandler>();1561  RegisterRequest<LocationsRequestHandler>();1562  RegisterRequest<NextRequestHandler>();1563  RegisterRequest<PauseRequestHandler>();1564  RegisterRequest<ReadMemoryRequestHandler>();1565  RegisterRequest<RestartRequestHandler>();1566  RegisterRequest<ScopesRequestHandler>();1567  RegisterRequest<SetBreakpointsRequestHandler>();1568  RegisterRequest<SetDataBreakpointsRequestHandler>();1569  RegisterRequest<SetExceptionBreakpointsRequestHandler>();1570  RegisterRequest<SetFunctionBreakpointsRequestHandler>();1571  RegisterRequest<SetInstructionBreakpointsRequestHandler>();1572  RegisterRequest<SetVariableRequestHandler>();1573  RegisterRequest<SourceRequestHandler>();1574  RegisterRequest<StackTraceRequestHandler>();1575  RegisterRequest<StepInRequestHandler>();1576  RegisterRequest<StepInTargetsRequestHandler>();1577  RegisterRequest<StepOutRequestHandler>();1578  RegisterRequest<ThreadsRequestHandler>();1579  RegisterRequest<VariablesRequestHandler>();1580  RegisterRequest<WriteMemoryRequestHandler>();1581 1582  // Custom requests1583  RegisterRequest<CompileUnitsRequestHandler>();1584  RegisterRequest<ModulesRequestHandler>();1585  RegisterRequest<ModuleSymbolsRequestHandler>();1586 1587  // Testing requests1588  RegisterRequest<TestGetTargetBreakpointsRequestHandler>();1589}1590 1591} // namespace lldb_dap1592