diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index bdb24fc78cfb6..35250d9eef608 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -32,11 +32,10 @@ using namespace lldb_dap; namespace lldb_dap { -DAP g_dap; - -DAP::DAP() - : broadcaster("lldb-dap"), exception_breakpoints(), - focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), +DAP::DAP(llvm::StringRef path, ReplMode repl_mode) + : debug_adaptor_path(path), broadcaster("lldb-dap"), + exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), + stop_at_entry(false), is_attach(false), enable_auto_variable_summaries(false), enable_synthetic_child_debugging(false), display_extended_backtrace(false), @@ -44,7 +43,7 @@ DAP::DAP() configuration_done_sent(false), waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), - reverse_request_seq(0), repl_mode(ReplMode::Auto) { + reverse_request_seq(0), repl_mode(repl_mode) { const char *log_file_path = getenv("LLDBDAP_LOG"); #if defined(_WIN32) // Windows opens stdout and stdin in text mode which converts \n to 13,10 @@ -693,15 +692,15 @@ bool DAP::HandleObject(const llvm::json::Object &object) { if (packet_type == "request") { const auto command = GetString(object, "command"); auto handler_pos = request_handlers.find(command); - if (handler_pos != request_handlers.end()) { - handler_pos->second(object); - return true; // Success - } else { + if (handler_pos == request_handlers.end()) { if (log) *log << "error: unhandled command \"" << command.data() << "\"" << std::endl; return false; // Fail } + + handler_pos->second(*this, object); + return true; // Success } if (packet_type == "response") { diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 5381b3271ba96..ae496236f1336 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -63,7 +63,7 @@ enum DAPBroadcasterBits { eBroadcastBitStopProgressThread = 1u << 1 }; -typedef void (*RequestCallback)(const llvm::json::Object &command); +typedef void (*RequestCallback)(DAP &dap, const llvm::json::Object &command); typedef void (*ResponseCallback)(llvm::Expected value); enum class PacketStatus { @@ -137,7 +137,7 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface { }; struct DAP { - std::string debug_adaptor_path; + llvm::StringRef debug_adaptor_path; InputStream input; OutputStream output; lldb::SBDebugger debugger; @@ -198,7 +198,7 @@ struct DAP { // will contain that expression. std::string last_nonempty_var_expression; - DAP(); + DAP(llvm::StringRef path, ReplMode repl_mode); ~DAP(); DAP(const DAP &rhs) = delete; void operator=(const DAP &rhs) = delete; @@ -353,8 +353,6 @@ struct DAP { void SendJSON(const std::string &json_str); }; -extern DAP g_dap; - } // namespace lldb_dap #endif diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index fc22ec03b672e..3bfc578806021 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -24,7 +24,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/ScopeExit.h" -#include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -119,34 +118,34 @@ constexpr int StackPageSize = 20; /// Prints a welcome message on the editor if the preprocessor variable /// LLDB_DAP_WELCOME_MESSAGE is defined. -static void PrintWelcomeMessage() { +static void PrintWelcomeMessage(DAP &dap) { #ifdef LLDB_DAP_WELCOME_MESSAGE - g_dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE); + dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE); #endif } -lldb::SBValueList *GetTopLevelScope(int64_t variablesReference) { +lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) { switch (variablesReference) { case VARREF_LOCALS: - return &g_dap.variables.locals; + return &dap.variables.locals; case VARREF_GLOBALS: - return &g_dap.variables.globals; + return &dap.variables.globals; case VARREF_REGS: - return &g_dap.variables.registers; + return &dap.variables.registers; default: return nullptr; } } -SOCKET AcceptConnection(int portno) { +SOCKET AcceptConnection(DAP &dap, int portno) { // Accept a socket connection from any host on "portno". SOCKET newsockfd = -1; struct sockaddr_in serv_addr, cli_addr; SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { - if (g_dap.log) - *g_dap.log << "error: opening socket (" << strerror(errno) << ")" - << std::endl; + if (dap.log) + *dap.log << "error: opening socket (" << strerror(errno) << ")" + << std::endl; } else { memset((char *)&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; @@ -154,9 +153,9 @@ SOCKET AcceptConnection(int portno) { serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); serv_addr.sin_port = htons(portno); if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { - if (g_dap.log) - *g_dap.log << "error: binding socket (" << strerror(errno) << ")" - << std::endl; + if (dap.log) + *dap.log << "error: binding socket (" << strerror(errno) << ")" + << std::endl; } else { listen(sockfd, 5); socklen_t clilen = sizeof(cli_addr); @@ -164,9 +163,8 @@ SOCKET AcceptConnection(int portno) { llvm::sys::RetryAfterSignal(static_cast(-1), accept, sockfd, (struct sockaddr *)&cli_addr, &clilen); if (newsockfd < 0) - if (g_dap.log) - *g_dap.log << "error: accept (" << strerror(errno) << ")" - << std::endl; + if (dap.log) + *dap.log << "error: accept (" << strerror(errno) << ")" << std::endl; } #if defined(_WIN32) closesocket(sockfd); @@ -190,66 +188,65 @@ std::vector MakeArgv(const llvm::ArrayRef &strs) { } // Send a "exited" event to indicate the process has exited. -void SendProcessExitedEvent(lldb::SBProcess &process) { +void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) { llvm::json::Object event(CreateEventObject("exited")); llvm::json::Object body; body.try_emplace("exitCode", (int64_t)process.GetExitStatus()); event.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(event))); + dap.SendJSON(llvm::json::Value(std::move(event))); } -void SendThreadExitedEvent(lldb::tid_t tid) { +void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) { llvm::json::Object event(CreateEventObject("thread")); llvm::json::Object body; body.try_emplace("reason", "exited"); body.try_emplace("threadId", (int64_t)tid); event.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(event))); + dap.SendJSON(llvm::json::Value(std::move(event))); } // Send a "continued" event to indicate the process is in the running state. -void SendContinuedEvent() { - lldb::SBProcess process = g_dap.target.GetProcess(); +void SendContinuedEvent(DAP &dap) { + lldb::SBProcess process = dap.target.GetProcess(); if (!process.IsValid()) { return; } // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!g_dap.configuration_done_sent || - g_dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } llvm::json::Object event(CreateEventObject("continued")); llvm::json::Object body; - body.try_emplace("threadId", (int64_t)g_dap.focus_tid); + body.try_emplace("threadId", (int64_t)dap.focus_tid); body.try_emplace("allThreadsContinued", true); event.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(event))); + dap.SendJSON(llvm::json::Value(std::move(event))); } // Send a "terminated" event to indicate the process is done being // debugged. -void SendTerminatedEvent() { +void SendTerminatedEvent(DAP &dap) { // Prevent races if the process exits while we're being asked to disconnect. - llvm::call_once(g_dap.terminated_event_flag, [&] { - g_dap.RunTerminateCommands(); + llvm::call_once(dap.terminated_event_flag, [&] { + dap.RunTerminateCommands(); // Send a "terminated" event - llvm::json::Object event(CreateTerminatedEventObject(g_dap.target)); - g_dap.SendJSON(llvm::json::Value(std::move(event))); + llvm::json::Object event(CreateTerminatedEventObject(dap.target)); + dap.SendJSON(llvm::json::Value(std::move(event))); }); } // Send a thread stopped event for all threads as long as the process // is stopped. -void SendThreadStoppedEvent() { - lldb::SBProcess process = g_dap.target.GetProcess(); +void SendThreadStoppedEvent(DAP &dap) { + lldb::SBProcess process = dap.target.GetProcess(); if (process.IsValid()) { auto state = process.GetState(); if (state == lldb::eStateStopped) { llvm::DenseSet old_thread_ids; - old_thread_ids.swap(g_dap.thread_ids); + old_thread_ids.swap(dap.thread_ids); uint32_t stop_id = process.GetStopID(); const uint32_t num_threads = process.GetNumThreads(); @@ -265,10 +262,10 @@ void SendThreadStoppedEvent() { const lldb::tid_t tid = thread.GetThreadID(); const bool has_reason = ThreadHasStopReason(thread); // If the focus thread doesn't have a stop reason, clear the thread ID - if (tid == g_dap.focus_tid) { + if (tid == dap.focus_tid) { focus_thread_exists = true; if (!has_reason) - g_dap.focus_tid = LLDB_INVALID_THREAD_ID; + dap.focus_tid = LLDB_INVALID_THREAD_ID; } if (has_reason) { ++num_threads_with_reason; @@ -277,47 +274,46 @@ void SendThreadStoppedEvent() { } } - // We will have cleared g_dap.focus_tid if the focus thread doesn't have + // We will have cleared dap.focus_tid if the focus thread doesn't have // a stop reason, so if it was cleared, or wasn't set, or doesn't exist, // then set the focus thread to the first thread with a stop reason. - if (!focus_thread_exists || g_dap.focus_tid == LLDB_INVALID_THREAD_ID) - g_dap.focus_tid = first_tid_with_reason; + if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID) + dap.focus_tid = first_tid_with_reason; // If no threads stopped with a reason, then report the first one so // we at least let the UI know we stopped. if (num_threads_with_reason == 0) { lldb::SBThread thread = process.GetThreadAtIndex(0); - g_dap.focus_tid = thread.GetThreadID(); - g_dap.SendJSON(CreateThreadStopped(g_dap, thread, stop_id)); + dap.focus_tid = thread.GetThreadID(); + dap.SendJSON(CreateThreadStopped(dap, thread, stop_id)); } else { for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); - g_dap.thread_ids.insert(thread.GetThreadID()); + dap.thread_ids.insert(thread.GetThreadID()); if (ThreadHasStopReason(thread)) { - g_dap.SendJSON(CreateThreadStopped(g_dap, thread, stop_id)); + dap.SendJSON(CreateThreadStopped(dap, thread, stop_id)); } } } for (auto tid : old_thread_ids) { - auto end = g_dap.thread_ids.end(); - auto pos = g_dap.thread_ids.find(tid); + auto end = dap.thread_ids.end(); + auto pos = dap.thread_ids.find(tid); if (pos == end) - SendThreadExitedEvent(tid); + SendThreadExitedEvent(dap, tid); } } else { - if (g_dap.log) - *g_dap.log << "error: SendThreadStoppedEvent() when process" - " isn't stopped (" - << lldb::SBDebugger::StateAsCString(state) << ')' - << std::endl; + if (dap.log) + *dap.log << "error: SendThreadStoppedEvent() when process" + " isn't stopped (" + << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl; } } else { - if (g_dap.log) - *g_dap.log << "error: SendThreadStoppedEvent() invalid process" - << std::endl; + if (dap.log) + *dap.log << "error: SendThreadStoppedEvent() invalid process" + << std::endl; } - g_dap.RunStopCommands(); + dap.RunStopCommands(); } // "ProcessEvent": { @@ -374,14 +370,14 @@ void SendThreadStoppedEvent() { // } // ] // } -void SendProcessEvent(LaunchMethod launch_method) { - lldb::SBFileSpec exe_fspec = g_dap.target.GetExecutable(); +void SendProcessEvent(DAP &dap, LaunchMethod launch_method) { + lldb::SBFileSpec exe_fspec = dap.target.GetExecutable(); char exe_path[PATH_MAX]; exe_fspec.GetPath(exe_path, sizeof(exe_path)); llvm::json::Object event(CreateEventObject("process")); llvm::json::Object body; EmplaceSafeString(body, "name", std::string(exe_path)); - const auto pid = g_dap.target.GetProcess().GetProcessID(); + const auto pid = dap.target.GetProcess().GetProcessID(); body.try_emplace("systemProcessId", (int64_t)pid); body.try_emplace("isLocalProcess", true); const char *startMethod = nullptr; @@ -398,31 +394,31 @@ void SendProcessEvent(LaunchMethod launch_method) { } body.try_emplace("startMethod", startMethod); event.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(event))); + dap.SendJSON(llvm::json::Value(std::move(event))); } // Grab any STDOUT and STDERR from the process and send it up to VS Code // via an "output" event to the "stdout" and "stderr" categories. -void SendStdOutStdErr(lldb::SBProcess &process) { +void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) { char buffer[OutputBufferSize]; size_t count; while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0) - g_dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count)); + dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count)); while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0) - g_dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count)); + dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count)); } -void ProgressEventThreadFunction() { +void ProgressEventThreadFunction(DAP &dap) { lldb::SBListener listener("lldb-dap.progress.listener"); - g_dap.debugger.GetBroadcaster().AddListener( + dap.debugger.GetBroadcaster().AddListener( listener, lldb::SBDebugger::eBroadcastBitProgress); - g_dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); + dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); lldb::SBEvent event; bool done = false; while (!done) { if (listener.WaitForEvent(1, event)) { const auto event_mask = event.GetType(); - if (event.BroadcasterMatchesRef(g_dap.broadcaster)) { + if (event.BroadcasterMatchesRef(dap.broadcaster)) { if (event_mask & eBroadcastBitStopProgressThread) { done = true; } @@ -434,7 +430,7 @@ void ProgressEventThreadFunction() { const char *message = lldb::SBDebugger::GetProgressFromEvent( event, progress_id, completed, total, is_debugger_specific); if (message) - g_dap.SendProgressEvent(progress_id, message, completed, total); + dap.SendProgressEvent(progress_id, message, completed, total); } } } @@ -445,9 +441,9 @@ void ProgressEventThreadFunction() { // "FILE *" to output packets back to VS Code and they have mutexes in them // them prevent multiple threads from writing simultaneously so no locking // is required. -void EventThreadFunction() { +void EventThreadFunction(DAP &dap) { lldb::SBEvent event; - lldb::SBListener listener = g_dap.debugger.GetListener(); + lldb::SBListener listener = dap.debugger.GetListener(); bool done = false; while (!done) { if (listener.WaitForEvent(1, event)) { @@ -483,50 +479,50 @@ void EventThreadFunction() { // stop events which we do not want to send an event for. We will // manually send a stopped event in request_configurationDone(...) // so don't send any before then. - if (g_dap.configuration_done_sent) { + if (dap.configuration_done_sent) { // Only report a stopped event if the process was not // automatically restarted. if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(process); - SendThreadStoppedEvent(); + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } } break; case lldb::eStateRunning: - g_dap.WillContinue(); - SendContinuedEvent(); + dap.WillContinue(); + SendContinuedEvent(dap); break; case lldb::eStateExited: lldb::SBStream stream; process.GetStatus(stream); - g_dap.SendOutput(OutputType::Console, stream.GetData()); + dap.SendOutput(OutputType::Console, stream.GetData()); // When restarting, we can get an "exited" event for the process we // just killed with the old PID, or even with no PID. In that case // we don't have to terminate the session. if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || - process.GetProcessID() == g_dap.restarting_process_id) { - g_dap.restarting_process_id = LLDB_INVALID_PROCESS_ID; + process.GetProcessID() == dap.restarting_process_id) { + dap.restarting_process_id = LLDB_INVALID_PROCESS_ID; } else { // Run any exit LLDB commands the user specified in the // launch.json - g_dap.RunExitCommands(); - SendProcessExitedEvent(process); - SendTerminatedEvent(); + dap.RunExitCommands(); + SendProcessExitedEvent(dap, process); + SendTerminatedEvent(dap); done = true; } break; } } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { - SendStdOutStdErr(process); + SendStdOutStdErr(dap, process); } } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { auto event_type = lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); auto bp = Breakpoint( - g_dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); + dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); // If the breakpoint was originated from the IDE, it will have the // BreakpointBase::GetBreakpointLabel() label attached. Regardless // of wether the locations were added or removed, the breakpoint @@ -548,10 +544,10 @@ void EventThreadFunction() { body.try_emplace("breakpoint", source_bp); body.try_emplace("reason", "changed"); bp_event.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(bp_event))); + dap.SendJSON(llvm::json::Value(std::move(bp_event))); } } - } else if (event.BroadcasterMatchesRef(g_dap.broadcaster)) { + } else if (event.BroadcasterMatchesRef(dap.broadcaster)) { if (event_mask & eBroadcastBitStopEventThread) { done = true; } @@ -560,9 +556,11 @@ void EventThreadFunction() { } } -lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) { +lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference, + llvm::StringRef name) { lldb::SBValue variable; - if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) { + if (lldb::SBValueList *top_scope = + GetTopLevelScope(dap, variablesReference)) { bool is_duplicated_variable_name = name.contains(" @"); // variablesReference is one of our scopes, not an actual variable it is // asking for a variable in locals or globals or registers @@ -584,7 +582,7 @@ lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) { // We have a named item within an actual variable so we need to find it // withing the container variable by name. - lldb::SBValue container = g_dap.variables.GetVariable(variablesReference); + lldb::SBValue container = dap.variables.GetVariable(variablesReference); variable = container.GetChildMemberWithName(name.data()); if (!variable.IsValid()) { if (name.starts_with("[")) { @@ -602,7 +600,7 @@ lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) { // Both attach and launch take a either a sourcePath or sourceMap // argument (or neither), from which we need to set the target.source-map. -void SetSourceMapFromArguments(const llvm::json::Object &arguments) { +void SetSourceMapFromArguments(DAP &dap, const llvm::json::Object &arguments) { const char *sourceMapHelp = "source must be be an array of two-element arrays, " "each containing a source and replacement path string.\n"; @@ -621,7 +619,7 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) { if (mapping == nullptr || mapping->size() != 2 || (*mapping)[0].kind() != llvm::json::Value::String || (*mapping)[1].kind() != llvm::json::Value::String) { - g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); + dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); return; } const auto mapFrom = GetAsString((*mapping)[0]); @@ -636,7 +634,7 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) { } } else { if (ObjectContainsKey(arguments, sourceMapKey)) { - g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); + dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); return; } if (sourcePath.empty()) @@ -645,7 +643,7 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) { strm << "\".\" \"" << sourcePath << "\""; } if (!sourceMapCommand.empty()) { - g_dap.RunLLDBCommands("Setting source map:", {sourceMapCommand}); + dap.RunLLDBCommands("Setting source map:", {sourceMapCommand}); } } @@ -681,15 +679,15 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) { // 13. th3->s2 // // s=3,l=3 = [th0->s3, label1, th1->s0] -bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames, - int64_t &offset, const int64_t start_frame, - const int64_t levels) { +bool FillStackFrames(DAP &dap, lldb::SBThread &thread, + llvm::json::Array &stack_frames, int64_t &offset, + const int64_t start_frame, const int64_t levels) { bool reached_end_of_stack = false; for (int64_t i = start_frame; static_cast(stack_frames.size()) < levels; i++) { if (i == -1) { stack_frames.emplace_back( - CreateExtendedStackFrameLabel(thread, g_dap.frame_format)); + CreateExtendedStackFrameLabel(thread, dap.frame_format)); continue; } @@ -700,10 +698,10 @@ bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames, break; } - stack_frames.emplace_back(CreateStackFrame(frame, g_dap.frame_format)); + stack_frames.emplace_back(CreateStackFrame(frame, dap.frame_format)); } - if (g_dap.display_extended_backtrace && reached_end_of_stack) { + if (dap.display_extended_backtrace && reached_end_of_stack) { // Check for any extended backtraces. for (uint32_t bt = 0; bt < thread.GetProcess().GetNumExtendedBacktraceTypes(); bt++) { @@ -713,7 +711,7 @@ bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames, continue; reached_end_of_stack = FillStackFrames( - backtrace, stack_frames, offset, + dap, backtrace, stack_frames, offset, (start_frame - offset) > 0 ? start_frame - offset : -1, levels); if (static_cast(stack_frames.size()) >= levels) break; @@ -751,15 +749,15 @@ bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames, // acknowledgement, so no body field is required." // }] // } -void request_attach(const llvm::json::Object &request) { - g_dap.is_attach = true; - g_dap.last_launch_or_attach_request = request; +void request_attach(DAP &dap, const llvm::json::Object &request) { + dap.is_attach = true; + dap.last_launch_or_attach_request = request; llvm::json::Object response; lldb::SBError error; FillResponse(request, response); lldb::SBAttachInfo attach_info; const int invalid_port = 0; - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); const lldb::pid_t pid = GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID); const auto gdb_remote_port = @@ -770,30 +768,29 @@ void request_attach(const llvm::json::Object &request) { attach_info.SetProcessID(pid); const auto wait_for = GetBoolean(arguments, "waitFor", false); attach_info.SetWaitForLaunch(wait_for, false /*async*/); - g_dap.init_commands = GetStrings(arguments, "initCommands"); - g_dap.pre_run_commands = GetStrings(arguments, "preRunCommands"); - g_dap.stop_commands = GetStrings(arguments, "stopCommands"); - g_dap.exit_commands = GetStrings(arguments, "exitCommands"); - g_dap.terminate_commands = GetStrings(arguments, "terminateCommands"); + dap.init_commands = GetStrings(arguments, "initCommands"); + dap.pre_run_commands = GetStrings(arguments, "preRunCommands"); + dap.stop_commands = GetStrings(arguments, "stopCommands"); + dap.exit_commands = GetStrings(arguments, "exitCommands"); + dap.terminate_commands = GetStrings(arguments, "terminateCommands"); auto attachCommands = GetStrings(arguments, "attachCommands"); llvm::StringRef core_file = GetString(arguments, "coreFile"); const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30); - g_dap.stop_at_entry = + dap.stop_at_entry = core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true; - g_dap.post_run_commands = GetStrings(arguments, "postRunCommands"); + dap.post_run_commands = GetStrings(arguments, "postRunCommands"); const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot"); - g_dap.enable_auto_variable_summaries = + dap.enable_auto_variable_summaries = GetBoolean(arguments, "enableAutoVariableSummaries", false); - g_dap.enable_synthetic_child_debugging = + dap.enable_synthetic_child_debugging = GetBoolean(arguments, "enableSyntheticChildDebugging", false); - g_dap.display_extended_backtrace = + dap.display_extended_backtrace = GetBoolean(arguments, "displayExtendedBacktrace", false); - g_dap.command_escape_prefix = - GetString(arguments, "commandEscapePrefix", "`"); - g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat")); - g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat")); + dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`"); + dap.SetFrameFormat(GetString(arguments, "customFrameFormat")); + dap.SetThreadFormat(GetString(arguments, "customThreadFormat")); - PrintWelcomeMessage(); + PrintWelcomeMessage(dap); // This is a hack for loading DWARF in .o files on Mac where the .o files // in the debug map of the main executable have relative paths which require @@ -803,29 +800,29 @@ void request_attach(const llvm::json::Object &request) { llvm::sys::fs::set_current_path(debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json - if (llvm::Error err = g_dap.RunInitCommands()) { + if (llvm::Error err = dap.RunInitCommands()) { response["success"] = false; EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } - SetSourceMapFromArguments(*arguments); + SetSourceMapFromArguments(dap, *arguments); lldb::SBError status; - g_dap.SetTarget(g_dap.CreateTargetFromArguments(*arguments, status)); + dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status)); if (status.Fail()) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", status.GetCString()); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = g_dap.RunPreRunCommands()) { + if (llvm::Error err = dap.RunPreRunCommands()) { response["success"] = false; EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } @@ -834,15 +831,15 @@ void request_attach(const llvm::json::Object &request) { char attach_msg[256]; auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), "Waiting to attach to \"%s\"...", - g_dap.target.GetExecutable().GetFilename()); - g_dap.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); + dap.target.GetExecutable().GetFilename()); + dap.SendOutput(OutputType::Console, + llvm::StringRef(attach_msg, attach_msg_len)); } if (attachCommands.empty()) { // No "attachCommands", just attach normally. // Disable async events so the attach will be successful when we return from // the launch call and the launch will happen synchronously - g_dap.debugger.SetAsync(false); + dap.debugger.SetAsync(false); if (core_file.empty()) { if ((pid != LLDB_INVALID_PROCESS_ID) && (gdb_remote_port != invalid_port)) { @@ -850,44 +847,44 @@ void request_attach(const llvm::json::Object &request) { error.SetErrorString("The user can't specify both pid and port"); } else if (gdb_remote_port != invalid_port) { // If port is specified and pid is not. - lldb::SBListener listener = g_dap.debugger.GetListener(); + lldb::SBListener listener = dap.debugger.GetListener(); // If the user hasn't provided the hostname property, default localhost // being used. std::string connect_url = llvm::formatv("connect://{0}:", gdb_remote_hostname); connect_url += std::to_string(gdb_remote_port); - g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); } else { // Attach by process name or id. - g_dap.target.Attach(attach_info, error); + dap.target.Attach(attach_info, error); } } else - g_dap.target.LoadCore(core_file.data(), error); + dap.target.LoadCore(core_file.data(), error); // Reenable async events - g_dap.debugger.SetAsync(true); + dap.debugger.SetAsync(true); } else { // We have "attachCommands" that are a set of commands that are expected // to execute the commands after which a process should be created. If there // is no valid process after running these commands, we have failed. - if (llvm::Error err = g_dap.RunAttachCommands(attachCommands)) { + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { response["success"] = false; EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } // The custom commands might have created a new target so we should use the // selected target after these commands are run. - g_dap.target = g_dap.debugger.GetSelectedTarget(); + dap.target = dap.debugger.GetSelectedTarget(); // Make sure the process is attached and stopped before proceeding as the // the launch commands are not run using the synchronous mode. - error = g_dap.WaitForProcessToStop(timeout_seconds); + error = dap.WaitForProcessToStop(timeout_seconds); } if (error.Success() && core_file.empty()) { - auto attached_pid = g_dap.target.GetProcess().GetProcessID(); + auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { if (attachCommands.empty()) error.SetErrorString("failed to attach to a process"); @@ -900,13 +897,13 @@ void request_attach(const llvm::json::Object &request) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", std::string(error.GetCString())); } else { - g_dap.RunPostRunCommands(); + dap.RunPostRunCommands(); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); if (error.Success()) { - SendProcessEvent(Attach); - g_dap.SendJSON(CreateEventObject("initialized")); + SendProcessEvent(dap, Attach); + dap.SendJSON(CreateEventObject("initialized")); } } @@ -964,15 +961,15 @@ void request_attach(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_continue(const llvm::json::Object &request) { +void request_continue(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - lldb::SBProcess process = g_dap.target.GetProcess(); + lldb::SBProcess process = dap.target.GetProcess(); lldb::SBError error = process.Continue(); llvm::json::Object body; body.try_emplace("allThreadsContinued", true); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "ConfigurationDoneRequest": { @@ -1006,15 +1003,15 @@ void request_continue(const llvm::json::Object &request) { // just an acknowledgement, so no body field is required." // }] // }, -void request_configurationDone(const llvm::json::Object &request) { +void request_configurationDone(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - g_dap.SendJSON(llvm::json::Value(std::move(response))); - g_dap.configuration_done_sent = true; - if (g_dap.stop_at_entry) - SendThreadStoppedEvent(); + dap.SendJSON(llvm::json::Value(std::move(response))); + dap.configuration_done_sent = true; + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); else - g_dap.target.GetProcess().Continue(); + dap.target.GetProcess().Continue(); } // "DisconnectRequest": { @@ -1061,15 +1058,15 @@ void request_configurationDone(const llvm::json::Object &request) { // acknowledgement, so no body field is required." // }] // } -void request_disconnect(const llvm::json::Object &request) { +void request_disconnect(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); - bool defaultTerminateDebuggee = g_dap.is_attach ? false : true; + bool defaultTerminateDebuggee = dap.is_attach ? false : true; bool terminateDebuggee = GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee); - lldb::SBProcess process = g_dap.target.GetProcess(); + lldb::SBProcess process = dap.target.GetProcess(); auto state = process.GetState(); switch (state) { case lldb::eStateInvalid: @@ -1085,24 +1082,24 @@ void request_disconnect(const llvm::json::Object &request) { case lldb::eStateSuspended: case lldb::eStateStopped: case lldb::eStateRunning: - g_dap.debugger.SetAsync(false); + dap.debugger.SetAsync(false); lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach(); if (!error.Success()) EmplaceSafeString(response, "error", error.GetCString()); - g_dap.debugger.SetAsync(true); + dap.debugger.SetAsync(true); break; } - SendTerminatedEvent(); - g_dap.SendJSON(llvm::json::Value(std::move(response))); - if (g_dap.event_thread.joinable()) { - g_dap.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread); - g_dap.event_thread.join(); + SendTerminatedEvent(dap); + dap.SendJSON(llvm::json::Value(std::move(response))); + if (dap.event_thread.joinable()) { + dap.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread); + dap.event_thread.join(); } - if (g_dap.progress_event_thread.joinable()) { - g_dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread); - g_dap.progress_event_thread.join(); + if (dap.progress_event_thread.joinable()) { + dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread); + dap.progress_event_thread.join(); } - g_dap.disconnecting = true; + dap.disconnecting = true; } // "ExceptionInfoRequest": { @@ -1202,18 +1199,18 @@ void request_disconnect(const llvm::json::Object &request) { // } // } // }, -void request_exceptionInfo(const llvm::json::Object &request) { +void request_exceptionInfo(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); llvm::json::Object body; - lldb::SBThread thread = g_dap.GetLLDBThread(*arguments); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); if (thread.IsValid()) { auto stopReason = thread.GetStopReason(); if (stopReason == lldb::eStopReasonSignal) body.try_emplace("exceptionId", "signal"); else if (stopReason == lldb::eStopReasonBreakpoint) { - ExceptionBreakpoint *exc_bp = g_dap.GetExceptionBPFromStopReason(thread); + ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread); if (exc_bp) { EmplaceSafeString(body, "exceptionId", exc_bp->filter); EmplaceSafeString(body, "description", exc_bp->label); @@ -1259,7 +1256,7 @@ void request_exceptionInfo(const llvm::json::Object &request) { response["success"] = llvm::json::Value(false); } response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "CompletionsRequest": { @@ -1377,14 +1374,14 @@ void request_exceptionInfo(const llvm::json::Object &request) { // "interface", "module", "property", "unit", "value", "enum", "keyword", // "snippet", "text", "color", "file", "reference", "customcolor" ] // } -void request_completions(const llvm::json::Object &request) { +void request_completions(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body; - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); // If we have a frame, try to set the context for variable completions. - lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); + lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); if (frame.IsValid()) { frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread()); frame.GetThread().SetSelectedFrame(frame.GetFrameID()); @@ -1404,19 +1401,19 @@ void request_completions(const llvm::json::Object &request) { llvm::json::Array targets; bool had_escape_prefix = - llvm::StringRef(text).starts_with(g_dap.command_escape_prefix); - ReplMode completion_mode = g_dap.DetectReplMode(frame, text, true); + llvm::StringRef(text).starts_with(dap.command_escape_prefix); + ReplMode completion_mode = dap.DetectReplMode(frame, text, true); // Handle the offset change introduced by stripping out the // `command_escape_prefix`. if (had_escape_prefix) { - if (offset < static_cast(g_dap.command_escape_prefix.size())) { + if (offset < static_cast(dap.command_escape_prefix.size())) { body.try_emplace("targets", std::move(targets)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } - offset -= g_dap.command_escape_prefix.size(); + offset -= dap.command_escape_prefix.size(); } // While the user is typing then we likely have an incomplete input and cannot @@ -1433,9 +1430,8 @@ void request_completions(const llvm::json::Object &request) { lldb::SBStringList matches; lldb::SBStringList descriptions; - if (!g_dap.debugger.GetCommandInterpreter() - .HandleCompletionWithDescriptions(line.c_str(), cursor, 0, 100, - matches, descriptions)) + if (!dap.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions( + line.c_str(), cursor, 0, 100, matches, descriptions)) continue; // The first element is the common substring after the cursor position for @@ -1465,7 +1461,7 @@ void request_completions(const llvm::json::Object &request) { body.try_emplace("targets", std::move(targets)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "EvaluateRequest": { @@ -1592,30 +1588,30 @@ void request_completions(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_evaluate(const llvm::json::Object &request) { +void request_evaluate(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body; - auto arguments = request.getObject("arguments"); - lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); + const auto *arguments = request.getObject("arguments"); + lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); std::string expression = GetString(arguments, "expression").str(); llvm::StringRef context = GetString(arguments, "context"); bool repeat_last_command = - expression.empty() && g_dap.last_nonempty_var_expression.empty(); + expression.empty() && dap.last_nonempty_var_expression.empty(); if (context == "repl" && (repeat_last_command || (!expression.empty() && - g_dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) { + dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) { // Since the current expression is not for a variable, clear the // last_nonempty_var_expression field. - g_dap.last_nonempty_var_expression.clear(); + dap.last_nonempty_var_expression.clear(); // If we're evaluating a command relative to the current frame, set the // focus_tid to the current frame for any thread related events. if (frame.IsValid()) { - g_dap.focus_tid = frame.GetThread().GetThreadID(); + dap.focus_tid = frame.GetThread().GetThreadID(); } - auto result = RunLLDBCommandsVerbatim(g_dap.debugger, llvm::StringRef(), + auto result = RunLLDBCommandsVerbatim(dap.debugger, llvm::StringRef(), {std::string(expression)}); EmplaceSafeString(body, "result", result); body.try_emplace("variablesReference", (int64_t)0); @@ -1626,9 +1622,9 @@ void request_evaluate(const llvm::json::Object &request) { // evaluation); otherwise save the current non-empty expression for the // next (possibly empty) variable expression. if (expression.empty()) - expression = g_dap.last_nonempty_var_expression; + expression = dap.last_nonempty_var_expression; else - g_dap.last_nonempty_var_expression = expression; + dap.last_nonempty_var_expression = expression; } // Always try to get the answer from the local variables if possible. If // this fails, then if the context is not "hover", actually evaluate an @@ -1657,12 +1653,12 @@ void request_evaluate(const llvm::json::Object &request) { else EmplaceSafeString(response, "message", "evaluate failed"); } else { - VariableDescription desc(value, g_dap.enable_auto_variable_summaries); + VariableDescription desc(value, dap.enable_auto_variable_summaries); EmplaceSafeString(body, "result", desc.GetResult(context)); EmplaceSafeString(body, "type", desc.display_type_name); int64_t var_ref = 0; if (value.MightHaveChildren() || ValuePointsToCode(value)) - var_ref = g_dap.variables.InsertVariable( + var_ref = dap.variables.InsertVariable( value, /*is_permanent=*/context == "repl"); if (value.MightHaveChildren()) body.try_emplace("variablesReference", var_ref); @@ -1676,7 +1672,7 @@ void request_evaluate(const llvm::json::Object &request) { } } response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "compileUnitsRequest": { @@ -1719,16 +1715,16 @@ void request_evaluate(const llvm::json::Object &request) { // } // }] // } -void request_compileUnits(const llvm::json::Object &request) { +void request_compileUnits(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body; llvm::json::Array units; - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); std::string module_id = std::string(GetString(arguments, "moduleId")); - int num_modules = g_dap.target.GetNumModules(); + int num_modules = dap.target.GetNumModules(); for (int i = 0; i < num_modules; i++) { - auto curr_module = g_dap.target.GetModuleAtIndex(i); + auto curr_module = dap.target.GetModuleAtIndex(i); if (module_id == curr_module.GetUUIDString()) { int num_units = curr_module.GetNumCompileUnits(); for (int j = 0; j < num_units; j++) { @@ -1740,7 +1736,7 @@ void request_compileUnits(const llvm::json::Object &request) { } } response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "modulesRequest": { @@ -1769,20 +1765,20 @@ void request_compileUnits(const llvm::json::Object &request) { // } // }] // } -void request_modules(const llvm::json::Object &request) { +void request_modules(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Array modules; - for (size_t i = 0; i < g_dap.target.GetNumModules(); i++) { - lldb::SBModule module = g_dap.target.GetModuleAtIndex(i); - modules.emplace_back(CreateModule(g_dap.target, module)); + for (size_t i = 0; i < dap.target.GetNumModules(); i++) { + lldb::SBModule module = dap.target.GetModuleAtIndex(i); + modules.emplace_back(CreateModule(dap.target, module)); } llvm::json::Object body; body.try_emplace("modules", std::move(modules)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "InitializeRequest": { @@ -1861,49 +1857,46 @@ void request_modules(const llvm::json::Object &request) { // } // }] // } -void request_initialize(const llvm::json::Object &request) { +void request_initialize(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body; - auto log_cb = [](const char *buf, void *baton) -> void { - g_dap.SendOutput(OutputType::Console, llvm::StringRef{buf}); - }; - - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); // sourceInitFile option is not from formal DAP specification. It is only // used by unit tests to prevent sourcing .lldbinit files from environment // which may affect the outcome of tests. bool source_init_file = GetBoolean(arguments, "sourceInitFile", true); - g_dap.debugger = lldb::SBDebugger::Create(source_init_file, log_cb, nullptr); - if (llvm::Error err = g_dap.RunPreInitCommands()) { + dap.debugger = lldb::SBDebugger::Create(source_init_file); + if (llvm::Error err = dap.RunPreInitCommands()) { response["success"] = false; EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } - g_dap.PopulateExceptionBreakpoints(); - auto cmd = g_dap.debugger.GetCommandInterpreter().AddMultiwordCommand( + dap.PopulateExceptionBreakpoints(); + auto cmd = dap.debugger.GetCommandInterpreter().AddMultiwordCommand( "lldb-dap", "Commands for managing lldb-dap."); if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) { cmd.AddCommand( - "start-debugging", new StartDebuggingRequestHandler(g_dap), + "start-debugging", new StartDebuggingRequestHandler(dap), "Sends a startDebugging request from the debug adapter to the client " "to start a child debug session of the same type as the caller."); } cmd.AddCommand( - "repl-mode", new ReplModeRequestHandler(g_dap), + "repl-mode", new ReplModeRequestHandler(dap), "Get or set the repl behavior of lldb-dap evaluation requests."); - cmd.AddCommand("send-event", new SendEventRequestHandler(g_dap), + cmd.AddCommand("send-event", new SendEventRequestHandler(dap), "Sends an DAP event to the client."); - g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction); + dap.progress_event_thread = + std::thread(ProgressEventThreadFunction, std::ref(dap)); // Start our event thread so we can receive events from the debugger, target, // process and more. - g_dap.event_thread = std::thread(EventThreadFunction); + dap.event_thread = std::thread(EventThreadFunction, std::ref(dap)); // The debug adapter supports the configurationDoneRequest. body.try_emplace("supportsConfigurationDoneRequest", true); @@ -1919,7 +1912,7 @@ void request_initialize(const llvm::json::Object &request) { body.try_emplace("supportsEvaluateForHovers", true); // Available filters or options for the setExceptionBreakpoints request. llvm::json::Array filters; - for (const auto &exc_bp : *g_dap.exception_breakpoints) { + for (const auto &exc_bp : *dap.exception_breakpoints) { filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp)); } body.try_emplace("exceptionBreakpointFilters", std::move(filters)); @@ -1992,16 +1985,17 @@ void request_initialize(const llvm::json::Object &request) { // Put in non-DAP specification lldb specific information. llvm::json::Object lldb_json; - lldb_json.try_emplace("version", g_dap.debugger.GetVersionString()); + lldb_json.try_emplace("version", dap.debugger.GetVersionString()); body.try_emplace("__lldb", std::move(lldb_json)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } -llvm::Error request_runInTerminal(const llvm::json::Object &launch_request, +llvm::Error request_runInTerminal(DAP &dap, + const llvm::json::Object &launch_request, const uint64_t timeout_seconds) { - g_dap.is_attach = true; + dap.is_attach = true; lldb::SBAttachInfo attach_info; llvm::Expected> comm_file_or_err = @@ -2017,25 +2011,25 @@ llvm::Error request_runInTerminal(const llvm::json::Object &launch_request, debugger_pid = getpid(); #endif llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( - launch_request, g_dap.debug_adaptor_path, comm_file.m_path, debugger_pid); - g_dap.SendReverseRequest("runInTerminal", std::move(reverse_request), - [](llvm::Expected value) { - if (!value) { - llvm::Error err = value.takeError(); - llvm::errs() - << "runInTerminal request failed: " - << llvm::toString(std::move(err)) << "\n"; - } - }); + launch_request, dap.debug_adaptor_path, comm_file.m_path, debugger_pid); + dap.SendReverseRequest("runInTerminal", std::move(reverse_request), + [](llvm::Expected value) { + if (!value) { + llvm::Error err = value.takeError(); + llvm::errs() + << "runInTerminal request failed: " + << llvm::toString(std::move(err)) << "\n"; + } + }); if (llvm::Expected pid = comm_channel.GetLauncherPid()) attach_info.SetProcessID(*pid); else return pid.takeError(); - g_dap.debugger.SetAsync(false); + dap.debugger.SetAsync(false); lldb::SBError error; - g_dap.target.Attach(attach_info, error); + dap.target.Attach(attach_info, error); if (error.Fail()) return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -2053,11 +2047,11 @@ llvm::Error request_runInTerminal(const llvm::json::Object &launch_request, // process right in the middle of the exec. To the user, what we are doing is // transparent, as they will only be able to see the process since the exec, // completely unaware of the preparatory work. - g_dap.target.GetProcess().Continue(); + dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), // we return the debugger to its async state. - g_dap.debugger.SetAsync(true); + dap.debugger.SetAsync(true); // If sending the notification failed, the launcher should be dead by now and // the async didAttach notification should have an error message, so we @@ -2074,13 +2068,13 @@ llvm::Error request_runInTerminal(const llvm::json::Object &launch_request, // runInTerminal if applicable. It doesn't do any of the additional // initialization and bookkeeping stuff that is needed for `request_launch`. // This way we can reuse the process launching logic for RestartRequest too. -lldb::SBError LaunchProcess(const llvm::json::Object &request) { +lldb::SBError LaunchProcess(DAP &dap, const llvm::json::Object &request) { lldb::SBError error; - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); auto launchCommands = GetStrings(arguments, "launchCommands"); // Instantiate a launch info instance for the target. - auto launch_info = g_dap.target.GetLaunchInfo(); + auto launch_info = dap.target.GetLaunchInfo(); // Grab the current working directory if there is one and set it in the // launch info. @@ -2113,29 +2107,29 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) { const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30); if (GetBoolean(arguments, "runInTerminal", false)) { - if (llvm::Error err = request_runInTerminal(request, timeout_seconds)) + if (llvm::Error err = request_runInTerminal(dap, request, timeout_seconds)) error.SetErrorString(llvm::toString(std::move(err)).c_str()); } else if (launchCommands.empty()) { // Disable async events so the launch will be successful when we return from // the launch call and the launch will happen synchronously - g_dap.debugger.SetAsync(false); - g_dap.target.Launch(launch_info, error); - g_dap.debugger.SetAsync(true); + dap.debugger.SetAsync(false); + dap.target.Launch(launch_info, error); + dap.debugger.SetAsync(true); } else { // Set the launch info so that run commands can access the configured // launch details. - g_dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = g_dap.RunLaunchCommands(launchCommands)) { + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) { error.SetErrorString(llvm::toString(std::move(err)).c_str()); return error; } // The custom commands might have created a new target so we should use the // selected target after these commands are run. - g_dap.target = g_dap.debugger.GetSelectedTarget(); + dap.target = dap.debugger.GetSelectedTarget(); // Make sure the process is launched and stopped at the entry point before // proceeding as the launch commands are not run using the synchronous // mode. - error = g_dap.WaitForProcessToStop(timeout_seconds); + error = dap.WaitForProcessToStop(timeout_seconds); } return error; } @@ -2174,32 +2168,31 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) { // acknowledgement, so no body field is required." // }] // } -void request_launch(const llvm::json::Object &request) { - g_dap.is_attach = false; - g_dap.last_launch_or_attach_request = request; +void request_launch(DAP &dap, const llvm::json::Object &request) { + dap.is_attach = false; + dap.last_launch_or_attach_request = request; llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - g_dap.init_commands = GetStrings(arguments, "initCommands"); - g_dap.pre_run_commands = GetStrings(arguments, "preRunCommands"); - g_dap.stop_commands = GetStrings(arguments, "stopCommands"); - g_dap.exit_commands = GetStrings(arguments, "exitCommands"); - g_dap.terminate_commands = GetStrings(arguments, "terminateCommands"); - g_dap.post_run_commands = GetStrings(arguments, "postRunCommands"); - g_dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); + const auto *arguments = request.getObject("arguments"); + dap.init_commands = GetStrings(arguments, "initCommands"); + dap.pre_run_commands = GetStrings(arguments, "preRunCommands"); + dap.stop_commands = GetStrings(arguments, "stopCommands"); + dap.exit_commands = GetStrings(arguments, "exitCommands"); + dap.terminate_commands = GetStrings(arguments, "terminateCommands"); + dap.post_run_commands = GetStrings(arguments, "postRunCommands"); + dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot"); - g_dap.enable_auto_variable_summaries = + dap.enable_auto_variable_summaries = GetBoolean(arguments, "enableAutoVariableSummaries", false); - g_dap.enable_synthetic_child_debugging = + dap.enable_synthetic_child_debugging = GetBoolean(arguments, "enableSyntheticChildDebugging", false); - g_dap.display_extended_backtrace = + dap.display_extended_backtrace = GetBoolean(arguments, "displayExtendedBacktrace", false); - g_dap.command_escape_prefix = - GetString(arguments, "commandEscapePrefix", "`"); - g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat")); - g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat")); + dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`"); + dap.SetFrameFormat(GetString(arguments, "customFrameFormat")); + dap.SetThreadFormat(GetString(arguments, "customThreadFormat")); - PrintWelcomeMessage(); + PrintWelcomeMessage(dap); // This is a hack for loading DWARF in .o files on Mac where the .o files // in the debug map of the main executable have relative paths which @@ -2211,50 +2204,50 @@ void request_launch(const llvm::json::Object &request) { // Run any initialize LLDB commands the user specified in the launch.json. // This is run before target is created, so commands can't do anything with // the targets - preRunCommands are run with the target. - if (llvm::Error err = g_dap.RunInitCommands()) { + if (llvm::Error err = dap.RunInitCommands()) { response["success"] = false; EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } - SetSourceMapFromArguments(*arguments); + SetSourceMapFromArguments(dap, *arguments); lldb::SBError status; - g_dap.SetTarget(g_dap.CreateTargetFromArguments(*arguments, status)); + dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status)); if (status.Fail()) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", status.GetCString()); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = g_dap.RunPreRunCommands()) { + if (llvm::Error err = dap.RunPreRunCommands()) { response["success"] = false; EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } - status = LaunchProcess(request); + status = LaunchProcess(dap, request); if (status.Fail()) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", std::string(status.GetCString())); } else { - g_dap.RunPostRunCommands(); + dap.RunPostRunCommands(); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); if (!status.Fail()) { - if (g_dap.is_attach) - SendProcessEvent(Attach); // this happens when doing runInTerminal + if (dap.is_attach) + SendProcessEvent(dap, Attach); // this happens when doing runInTerminal else - SendProcessEvent(Launch); + SendProcessEvent(dap, Launch); } - g_dap.SendJSON(CreateEventObject("initialized")); + dap.SendJSON(CreateEventObject("initialized")); } // Check if the step-granularity is `instruction` @@ -2308,15 +2301,15 @@ static bool hasInstructionGranularity(const llvm::json::Object &requestArgs) { // acknowledgement, so no body field is required." // }] // } -void request_next(const llvm::json::Object &request) { +void request_next(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - lldb::SBThread thread = g_dap.GetLLDBThread(*arguments); + const auto *arguments = request.getObject("arguments"); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); if (thread.IsValid()) { // Remember the thread ID that caused the resume so we can set the // "threadCausedFocus" boolean value in the "stopped" events. - g_dap.focus_tid = thread.GetThreadID(); + dap.focus_tid = thread.GetThreadID(); if (hasInstructionGranularity(*arguments)) { thread.StepInstruction(/*step_over=*/true); } else { @@ -2325,7 +2318,7 @@ void request_next(const llvm::json::Object &request) { } else { response["success"] = llvm::json::Value(false); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "PauseRequest": { @@ -2364,12 +2357,12 @@ void request_next(const llvm::json::Object &request) { // acknowledgement, so no body field is required." // }] // } -void request_pause(const llvm::json::Object &request) { +void request_pause(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - lldb::SBProcess process = g_dap.target.GetProcess(); + lldb::SBProcess process = dap.target.GetProcess(); lldb::SBError error = process.Stop(); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "RestartRequest": { @@ -2413,14 +2406,14 @@ void request_pause(const llvm::json::Object &request) { // acknowledgement, so no body field is required." // }] // }, -void request_restart(const llvm::json::Object &request) { +void request_restart(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - if (!g_dap.last_launch_or_attach_request) { + if (!dap.last_launch_or_attach_request) { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", "Restart request received but no process was launched."); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } // Check if we were in a "launch" session or an "attach" session. @@ -2432,35 +2425,36 @@ void request_restart(const llvm::json::Object &request) { // Note that when using runInTerminal we're technically attached, but it's an // implementation detail. The adapter *did* launch the process in response to // a "launch" command, so we can still stop it and re-run it. This is why we - // don't just check `g_dap.is_attach`. - if (GetString(*g_dap.last_launch_or_attach_request, "command") == "attach") { + // don't just check `dap.is_attach`. + if (GetString(*dap.last_launch_or_attach_request, "command") == "attach") { response["success"] = llvm::json::Value(false); EmplaceSafeString(response, "message", "Restarting an \"attach\" session is not supported."); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } // The optional `arguments` field in RestartRequest can contain an updated // version of the launch arguments. If there's one, use it. - auto restart_arguments = request.getObject("arguments"); + const auto *restart_arguments = request.getObject("arguments"); if (restart_arguments) { - auto launch_request_arguments = restart_arguments->getObject("arguments"); + const auto *launch_request_arguments = + restart_arguments->getObject("arguments"); if (launch_request_arguments) { - (*g_dap.last_launch_or_attach_request)["arguments"] = + (*dap.last_launch_or_attach_request)["arguments"] = llvm::json::Value(llvm::json::Object(*launch_request_arguments)); } } // Keep track of the old PID so when we get a "process exited" event from the // killed process we can detect it and not shut down the whole session. - lldb::SBProcess process = g_dap.target.GetProcess(); - g_dap.restarting_process_id = process.GetProcessID(); + lldb::SBProcess process = dap.target.GetProcess(); + dap.restarting_process_id = process.GetProcessID(); // Stop the current process if necessary. The logic here is similar to // CommandObjectProcessLaunchOrAttach::StopProcessIfNecessary, except that // we don't ask the user for confirmation. - g_dap.debugger.SetAsync(false); + dap.debugger.SetAsync(false); if (process.IsValid()) { lldb::StateType state = process.GetState(); if (state != lldb::eStateConnected) { @@ -2468,21 +2462,21 @@ void request_restart(const llvm::json::Object &request) { } // Clear the list of thread ids to avoid sending "thread exited" events // for threads of the process we are terminating. - g_dap.thread_ids.clear(); + dap.thread_ids.clear(); } - g_dap.debugger.SetAsync(true); - LaunchProcess(*g_dap.last_launch_or_attach_request); + dap.debugger.SetAsync(true); + LaunchProcess(dap, *dap.last_launch_or_attach_request); // This is normally done after receiving a "configuration done" request. // Because we're restarting, configuration has already happened so we can // continue the process right away. - if (g_dap.stop_at_entry) { - SendThreadStoppedEvent(); + if (dap.stop_at_entry) { + SendThreadStoppedEvent(dap); } else { - g_dap.target.GetProcess().Continue(); + dap.target.GetProcess().Continue(); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "ScopesRequest": { @@ -2536,12 +2530,12 @@ void request_restart(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_scopes(const llvm::json::Object &request) { +void request_scopes(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body; - auto arguments = request.getObject("arguments"); - lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); + const auto *arguments = request.getObject("arguments"); + lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); // As the user selects different stack frames in the GUI, a "scopes" request // will be sent to the DAP. This is the only way we know that the user has // selected a frame in a thread. There are no other notifications that are @@ -2561,18 +2555,18 @@ void request_scopes(const llvm::json::Object &request) { frame.GetThread().SetSelectedFrame(frame.GetFrameID()); } - g_dap.variables.locals = frame.GetVariables(/*arguments=*/true, - /*locals=*/true, - /*statics=*/false, - /*in_scope_only=*/true); - g_dap.variables.globals = frame.GetVariables(/*arguments=*/false, - /*locals=*/false, - /*statics=*/true, - /*in_scope_only=*/true); - g_dap.variables.registers = frame.GetRegisters(); - body.try_emplace("scopes", g_dap.CreateTopLevelScopes()); + dap.variables.locals = frame.GetVariables(/*arguments=*/true, + /*locals=*/true, + /*statics=*/false, + /*in_scope_only=*/true); + dap.variables.globals = frame.GetVariables(/*arguments=*/false, + /*locals=*/false, + /*statics=*/true, + /*in_scope_only=*/true); + dap.variables.registers = frame.GetRegisters(); + body.try_emplace("scopes", dap.CreateTopLevelScopes()); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SetBreakpointsRequest": { @@ -2685,7 +2679,7 @@ void request_scopes(const llvm::json::Object &request) { // }, // "required": [ "line" ] // } -void request_setBreakpoints(const llvm::json::Object &request) { +void request_setBreakpoints(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); @@ -2703,10 +2697,10 @@ void request_setBreakpoints(const llvm::json::Object &request) { for (const auto &bp : *breakpoints) { const auto *bp_obj = bp.getAsObject(); if (bp_obj) { - SourceBreakpoint src_bp(g_dap, *bp_obj); + SourceBreakpoint src_bp(dap, *bp_obj); request_bps.try_emplace(src_bp.line, src_bp); const auto [iv, inserted] = - g_dap.source_breakpoints[path].try_emplace(src_bp.line, src_bp); + dap.source_breakpoints[path].try_emplace(src_bp.line, src_bp); // We check if this breakpoint already exists to update it if (inserted) iv->getSecond().SetBreakpoint(path.data()); @@ -2721,13 +2715,13 @@ void request_setBreakpoints(const llvm::json::Object &request) { // Delete any breakpoints in this source file that aren't in the // request_bps set. There is no call to remove breakpoints other than // calling this function with a smaller or empty "breakpoints" list. - auto old_src_bp_pos = g_dap.source_breakpoints.find(path); - if (old_src_bp_pos != g_dap.source_breakpoints.end()) { + auto old_src_bp_pos = dap.source_breakpoints.find(path); + if (old_src_bp_pos != dap.source_breakpoints.end()) { for (auto &old_bp : old_src_bp_pos->second) { auto request_pos = request_bps.find(old_bp.first); if (request_pos == request_bps.end()) { // This breakpoint no longer exists in this source file, delete it - g_dap.target.BreakpointDelete(old_bp.second.bp.GetID()); + dap.target.BreakpointDelete(old_bp.second.bp.GetID()); old_src_bp_pos->second.erase(old_bp.first); } } @@ -2736,7 +2730,7 @@ void request_setBreakpoints(const llvm::json::Object &request) { llvm::json::Object body; body.try_emplace("breakpoints", std::move(response_breakpoints)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SetExceptionBreakpointsRequest": { @@ -2786,7 +2780,8 @@ void request_setBreakpoints(const llvm::json::Object &request) { // just an acknowledgement, so no body field is required." // }] // } -void request_setExceptionBreakpoints(const llvm::json::Object &request) { +void request_setExceptionBreakpoints(DAP &dap, + const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); @@ -2795,23 +2790,23 @@ void request_setExceptionBreakpoints(const llvm::json::Object &request) { // Keep a list of any exception breakpoint filter names that weren't set // so we can clear any exception breakpoints if needed. std::set unset_filters; - for (const auto &bp : *g_dap.exception_breakpoints) + for (const auto &bp : *dap.exception_breakpoints) unset_filters.insert(bp.filter); for (const auto &value : *filters) { const auto filter = GetAsString(value); - auto *exc_bp = g_dap.GetExceptionBreakpoint(std::string(filter)); + auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter)); if (exc_bp) { exc_bp->SetBreakpoint(); unset_filters.erase(std::string(filter)); } } for (const auto &filter : unset_filters) { - auto *exc_bp = g_dap.GetExceptionBreakpoint(filter); + auto *exc_bp = dap.GetExceptionBreakpoint(filter); if (exc_bp) exc_bp->ClearBreakpoint(); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SetFunctionBreakpointsRequest": { @@ -2892,7 +2887,8 @@ void request_setExceptionBreakpoints(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_setFunctionBreakpoints(const llvm::json::Object &request) { +void request_setFunctionBreakpoints(DAP &dap, + const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); @@ -2903,15 +2899,15 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) { // Disable any function breakpoints that aren't in this request. // There is no call to remove function breakpoints other than calling this // function with a smaller or empty "breakpoints" list. - const auto name_iter = g_dap.function_breakpoints.keys(); + const auto name_iter = dap.function_breakpoints.keys(); llvm::DenseSet seen(name_iter.begin(), name_iter.end()); for (const auto &value : *breakpoints) { const auto *bp_obj = value.getAsObject(); if (!bp_obj) continue; - FunctionBreakpoint fn_bp(g_dap, *bp_obj); - const auto [it, inserted] = g_dap.function_breakpoints.try_emplace( - fn_bp.functionName, g_dap, *bp_obj); + FunctionBreakpoint fn_bp(dap, *bp_obj); + const auto [it, inserted] = + dap.function_breakpoints.try_emplace(fn_bp.functionName, dap, *bp_obj); if (inserted) it->second.SetBreakpoint(); else @@ -2923,17 +2919,17 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) { // Remove any breakpoints that are no longer in our list for (const auto &name : seen) { - auto fn_bp = g_dap.function_breakpoints.find(name); - if (fn_bp == g_dap.function_breakpoints.end()) + auto fn_bp = dap.function_breakpoints.find(name); + if (fn_bp == dap.function_breakpoints.end()) continue; - g_dap.target.BreakpointDelete(fn_bp->second.bp.GetID()); - g_dap.function_breakpoints.erase(name); + dap.target.BreakpointDelete(fn_bp->second.bp.GetID()); + dap.function_breakpoints.erase(name); } llvm::json::Object body; body.try_emplace("breakpoints", std::move(response_breakpoints)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "DataBreakpointInfoRequest": { @@ -3028,7 +3024,7 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_dataBreakpointInfo(const llvm::json::Object &request) { +void request_dataBreakpointInfo(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body; @@ -3038,8 +3034,8 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) { const auto variablesReference = GetUnsigned(arguments, "variablesReference", 0); llvm::StringRef name = GetString(arguments, "name"); - lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); - lldb::SBValue variable = FindVariable(variablesReference, name); + lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); + lldb::SBValue variable = FindVariable(dap, variablesReference, name); std::string addr, size; if (variable.IsValid()) { @@ -3074,7 +3070,7 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) { addr = llvm::utohexstr(load_addr); lldb::SBMemoryRegionInfo region; lldb::SBError err = - g_dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region); + dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region); // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this // request if SBProcess::GetMemoryRegionInfo returns error. if (err.Success()) { @@ -3104,7 +3100,7 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) { size + " bytes at " + addr + " " + name.str()); } response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SetDataBreakpointsRequest": { @@ -3167,20 +3163,20 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_setDataBreakpoints(const llvm::json::Object &request) { +void request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); const auto *arguments = request.getObject("arguments"); const auto *breakpoints = arguments->getArray("breakpoints"); llvm::json::Array response_breakpoints; - g_dap.target.DeleteAllWatchpoints(); + dap.target.DeleteAllWatchpoints(); std::vector watchpoints; if (breakpoints) { for (const auto &bp : *breakpoints) { const auto *bp_obj = bp.getAsObject(); if (bp_obj) - watchpoints.emplace_back(g_dap, *bp_obj); + watchpoints.emplace_back(dap, *bp_obj); } } // If two watchpoints start at the same address, the latter overwrite the @@ -3199,7 +3195,7 @@ void request_setDataBreakpoints(const llvm::json::Object &request) { llvm::json::Object body; body.try_emplace("breakpoints", std::move(response_breakpoints)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SourceRequest": { @@ -3260,12 +3256,12 @@ void request_setDataBreakpoints(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_source(const llvm::json::Object &request) { +void request_source(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Object body{{"content", ""}}; response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "StackTraceRequest": { @@ -3346,12 +3342,12 @@ void request_source(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_stackTrace(const llvm::json::Object &request) { +void request_stackTrace(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); lldb::SBError error; - auto arguments = request.getObject("arguments"); - lldb::SBThread thread = g_dap.GetLLDBThread(*arguments); + const auto *arguments = request.getObject("arguments"); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); llvm::json::Array stack_frames; llvm::json::Object body; @@ -3360,7 +3356,7 @@ void request_stackTrace(const llvm::json::Object &request) { const auto levels = GetUnsigned(arguments, "levels", 0); int64_t offset = 0; bool reached_end_of_stack = - FillStackFrames(thread, stack_frames, offset, start_frame, + FillStackFrames(dap, thread, stack_frames, offset, start_frame, levels == 0 ? INT64_MAX : levels); body.try_emplace("totalFrames", start_frame + stack_frames.size() + @@ -3369,7 +3365,7 @@ void request_stackTrace(const llvm::json::Object &request) { body.try_emplace("stackFrames", std::move(stack_frames)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "StepInRequest": { @@ -3422,25 +3418,25 @@ void request_stackTrace(const llvm::json::Object &request) { // acknowledgement, so no body field is required." // }] // } -void request_stepIn(const llvm::json::Object &request) { +void request_stepIn(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); std::string step_in_target; uint64_t target_id = GetUnsigned(arguments, "targetId", 0); - auto it = g_dap.step_in_targets.find(target_id); - if (it != g_dap.step_in_targets.end()) + auto it = dap.step_in_targets.find(target_id); + if (it != dap.step_in_targets.end()) step_in_target = it->second; const bool single_thread = GetBoolean(arguments, "singleThread", false); lldb::RunMode run_mode = single_thread ? lldb::eOnlyThisThread : lldb::eOnlyDuringStepping; - lldb::SBThread thread = g_dap.GetLLDBThread(*arguments); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); if (thread.IsValid()) { // Remember the thread ID that caused the resume so we can set the // "threadCausedFocus" boolean value in the "stopped" events. - g_dap.focus_tid = thread.GetThreadID(); + dap.focus_tid = thread.GetThreadID(); if (hasInstructionGranularity(*arguments)) { thread.StepInstruction(/*step_over=*/false); } else { @@ -3449,7 +3445,7 @@ void request_stepIn(const llvm::json::Object &request) { } else { response["success"] = llvm::json::Value(false); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "StepInTargetsRequest": { @@ -3505,24 +3501,24 @@ void request_stepIn(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_stepInTargets(const llvm::json::Object &request) { +void request_stepInTargets(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); - g_dap.step_in_targets.clear(); - lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); + dap.step_in_targets.clear(); + lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); if (frame.IsValid()) { lldb::SBAddress pc_addr = frame.GetPCAddress(); lldb::SBAddress line_end_addr = pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true); - lldb::SBInstructionList insts = g_dap.target.ReadInstructions( + lldb::SBInstructionList insts = dap.target.ReadInstructions( pc_addr, line_end_addr, /*flavor_string=*/nullptr); if (!insts.IsValid()) { response["success"] = false; response["message"] = "Failed to get instructions for frame."; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } @@ -3533,29 +3529,29 @@ void request_stepInTargets(const llvm::json::Object &request) { if (!inst.IsValid()) break; - lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(g_dap.target); + lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target); // Note: currently only x86/x64 supports flow kind. lldb::InstructionControlFlowKind flow_kind = - inst.GetControlFlowKind(g_dap.target); + inst.GetControlFlowKind(dap.target); if (flow_kind == lldb::eInstructionControlFlowKindCall) { // Use call site instruction address as id which is easy to debug. llvm::json::Object step_in_target; step_in_target["id"] = inst_addr; - llvm::StringRef call_operand_name = inst.GetOperands(g_dap.target); + llvm::StringRef call_operand_name = inst.GetOperands(dap.target); lldb::addr_t call_target_addr; if (call_operand_name.getAsInteger(0, call_target_addr)) continue; lldb::SBAddress call_target_load_addr = - g_dap.target.ResolveLoadAddress(call_target_addr); + dap.target.ResolveLoadAddress(call_target_addr); if (!call_target_load_addr.IsValid()) continue; // The existing ThreadPlanStepInRange only accept step in target // function with debug info. - lldb::SBSymbolContext sc = g_dap.target.ResolveSymbolContextForAddress( + lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress( call_target_load_addr, lldb::eSymbolContextFunction); // The existing ThreadPlanStepInRange only accept step in target @@ -3568,7 +3564,7 @@ void request_stepInTargets(const llvm::json::Object &request) { if (step_in_target_name.empty()) continue; - g_dap.step_in_targets.try_emplace(inst_addr, step_in_target_name); + dap.step_in_targets.try_emplace(inst_addr, step_in_target_name); step_in_target.try_emplace("label", step_in_target_name); step_in_targets.emplace_back(std::move(step_in_target)); } @@ -3580,7 +3576,7 @@ void request_stepInTargets(const llvm::json::Object &request) { response["success"] = llvm::json::Value(false); response["message"] = "Failed to get frame for input frameId."; } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "StepOutRequest": { @@ -3619,20 +3615,20 @@ void request_stepInTargets(const llvm::json::Object &request) { // acknowledgement, so no body field is required." // }] // } -void request_stepOut(const llvm::json::Object &request) { +void request_stepOut(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - lldb::SBThread thread = g_dap.GetLLDBThread(*arguments); + const auto *arguments = request.getObject("arguments"); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); if (thread.IsValid()) { // Remember the thread ID that caused the resume so we can set the // "threadCausedFocus" boolean value in the "stopped" events. - g_dap.focus_tid = thread.GetThreadID(); + dap.focus_tid = thread.GetThreadID(); thread.StepOut(); } else { response["success"] = llvm::json::Value(false); } - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "ThreadsRequest": { @@ -3670,8 +3666,8 @@ void request_stepOut(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_threads(const llvm::json::Object &request) { - lldb::SBProcess process = g_dap.target.GetProcess(); +void request_threads(DAP &dap, const llvm::json::Object &request) { + lldb::SBProcess process = dap.target.GetProcess(); llvm::json::Object response; FillResponse(request, response); @@ -3679,7 +3675,7 @@ void request_threads(const llvm::json::Object &request) { llvm::json::Array threads; for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { lldb::SBThread thread = process.GetThreadAtIndex(thread_idx); - threads.emplace_back(CreateThread(thread, g_dap.thread_format)); + threads.emplace_back(CreateThread(thread, dap.thread_format)); } if (threads.size() == 0) { response["success"] = llvm::json::Value(false); @@ -3687,7 +3683,7 @@ void request_threads(const llvm::json::Object &request) { llvm::json::Object body; body.try_emplace("threads", std::move(threads)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SetVariableRequest": { @@ -3783,12 +3779,12 @@ void request_threads(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_setVariable(const llvm::json::Object &request) { +void request_setVariable(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Array variables; llvm::json::Object body; - auto arguments = request.getObject("arguments"); + const auto *arguments = request.getObject("arguments"); // This is a reference to the containing variable/scope const auto variablesReference = GetUnsigned(arguments, "variablesReference", 0); @@ -3808,28 +3804,28 @@ void request_setVariable(const llvm::json::Object &request) { // only specifies the variable reference of the enclosing scope/variable, and // the name of the variable. We could have two shadowed variables with the // same name in "Locals" or "Globals". In our case the "id" absolute index - // of the variable within the g_dap.variables list. + // of the variable within the dap.variables list. const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX); if (id_value != UINT64_MAX) { - variable = g_dap.variables.GetVariable(id_value); + variable = dap.variables.GetVariable(id_value); } else { - variable = FindVariable(variablesReference, name); + variable = FindVariable(dap, variablesReference, name); } if (variable.IsValid()) { lldb::SBError error; bool success = variable.SetValueFromCString(value.data(), error); if (success) { - VariableDescription desc(variable, g_dap.enable_auto_variable_summaries); + VariableDescription desc(variable, dap.enable_auto_variable_summaries); EmplaceSafeString(body, "result", desc.display_value); EmplaceSafeString(body, "type", desc.display_type_name); - // We don't know the index of the variable in our g_dap.variables + // We don't know the index of the variable in our dap.variables // so always insert a new one to get its variablesReference. // is_permanent is false because debug console does not support // setVariable request. int64_t new_var_ref = - g_dap.variables.InsertVariable(variable, /*is_permanent=*/false); + dap.variables.InsertVariable(variable, /*is_permanent=*/false); if (variable.MightHaveChildren()) body.try_emplace("variablesReference", new_var_ref); else @@ -3848,7 +3844,7 @@ void request_setVariable(const llvm::json::Object &request) { } response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "VariablesRequest": { @@ -3924,7 +3920,7 @@ void request_setVariable(const llvm::json::Object &request) { // "required": [ "body" ] // }] // } -void request_variables(const llvm::json::Object &request) { +void request_variables(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Array variables; @@ -3938,7 +3934,8 @@ void request_variables(const llvm::json::Object &request) { if (format) hex = GetBoolean(format, "hex", false); - if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) { + if (lldb::SBValueList *top_scope = + GetTopLevelScope(dap, variablesReference)) { // variablesReference is one of our scopes, not an actual variable it is // asking for the list of args, locals or globals. int64_t start_idx = 0; @@ -3950,8 +3947,8 @@ void request_variables(const llvm::json::Object &request) { // and resolve what the pointer resolves to. Only change the format if the // format was set to the default format or if it was hex as some registers // have formats set for them. - const uint32_t addr_size = g_dap.target.GetProcess().GetAddressByteSize(); - lldb::SBValue reg_set = g_dap.variables.registers.GetValueAtIndex(0); + const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize(); + lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(0); const uint32_t num_regs = reg_set.GetNumChildren(); for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) { lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx); @@ -4008,27 +4005,27 @@ void request_variables(const llvm::json::Object &request) { break; int64_t var_ref = - g_dap.variables.InsertVariable(variable, /*is_permanent=*/false); + dap.variables.InsertVariable(variable, /*is_permanent=*/false); variables.emplace_back(CreateVariable( - variable, var_ref, hex, g_dap.enable_auto_variable_summaries, - g_dap.enable_synthetic_child_debugging, + variable, var_ref, hex, dap.enable_auto_variable_summaries, + dap.enable_synthetic_child_debugging, variable_name_counts[GetNonNullVariableName(variable)] > 1)); } } else { // We are expanding a variable that has children, so we will return its // children. - lldb::SBValue variable = g_dap.variables.GetVariable(variablesReference); + lldb::SBValue variable = dap.variables.GetVariable(variablesReference); if (variable.IsValid()) { auto addChild = [&](lldb::SBValue child, std::optional custom_name = {}) { if (!child.IsValid()) return; bool is_permanent = - g_dap.variables.IsPermanentVariableReference(variablesReference); - int64_t var_ref = g_dap.variables.InsertVariable(child, is_permanent); + dap.variables.IsPermanentVariableReference(variablesReference); + int64_t var_ref = dap.variables.InsertVariable(child, is_permanent); variables.emplace_back(CreateVariable( - child, var_ref, hex, g_dap.enable_auto_variable_summaries, - g_dap.enable_synthetic_child_debugging, + child, var_ref, hex, dap.enable_auto_variable_summaries, + dap.enable_synthetic_child_debugging, /*is_name_duplicated=*/false, custom_name)); }; const int64_t num_children = variable.GetNumChildren(); @@ -4041,7 +4038,7 @@ void request_variables(const llvm::json::Object &request) { // "[raw]" child that can be used to inspect the raw version of a // synthetic member. That eliminates the need for the user to go to the // debug console and type `frame var to get these values. - if (g_dap.enable_synthetic_child_debugging && variable.IsSynthetic() && + if (dap.enable_synthetic_child_debugging && variable.IsSynthetic() && i == num_children) addChild(variable.GetNonSyntheticValue(), "[raw]"); } @@ -4049,7 +4046,7 @@ void request_variables(const llvm::json::Object &request) { llvm::json::Object body; body.try_emplace("variables", std::move(variables)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "LocationsRequest": { @@ -4129,7 +4126,7 @@ void request_variables(const llvm::json::Object &request) { // } // }] // }, -void request_locations(const llvm::json::Object &request) { +void request_locations(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); auto *arguments = request.getObject("arguments"); @@ -4138,11 +4135,11 @@ void request_locations(const llvm::json::Object &request) { // We use the lowest bit to distinguish between value location and declaration // location auto [var_ref, is_value_location] = UnpackLocation(location_id); - lldb::SBValue variable = g_dap.variables.GetVariable(var_ref); + lldb::SBValue variable = dap.variables.GetVariable(var_ref); if (!variable.IsValid()) { response["success"] = false; response["message"] = "Invalid variable reference"; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } @@ -4154,18 +4151,18 @@ void request_locations(const llvm::json::Object &request) { response["success"] = false; response["message"] = "Value locations are only available for pointers and references"; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } lldb::addr_t addr = variable.GetValueAsAddress(); lldb::SBLineEntry line_entry = - g_dap.target.ResolveLoadAddress(addr).GetLineEntry(); + dap.target.ResolveLoadAddress(addr).GetLineEntry(); if (!line_entry.IsValid()) { response["success"] = false; response["message"] = "Failed to resolve line entry for location"; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } @@ -4180,7 +4177,7 @@ void request_locations(const llvm::json::Object &request) { if (!decl.IsValid()) { response["success"] = false; response["message"] = "No declaration location available"; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } @@ -4192,7 +4189,7 @@ void request_locations(const llvm::json::Object &request) { } response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "DisassembleRequest": { @@ -4267,7 +4264,7 @@ void request_locations(const llvm::json::Object &request) { // } // }] // } -void request_disassemble(const llvm::json::Object &request) { +void request_disassemble(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); auto *arguments = request.getObject("arguments"); @@ -4278,28 +4275,27 @@ void request_disassemble(const llvm::json::Object &request) { response["success"] = false; response["message"] = "Malformed memory reference: " + memoryReference.str(); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } lldb::addr_t addr_ptr = *addr_opt; addr_ptr += GetSigned(arguments, "instructionOffset", 0); - lldb::SBAddress addr(addr_ptr, g_dap.target); + lldb::SBAddress addr(addr_ptr, dap.target); if (!addr.IsValid()) { response["success"] = false; response["message"] = "Memory reference not found in the current binary."; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } const auto inst_count = GetUnsigned(arguments, "instructionCount", 0); - lldb::SBInstructionList insts = - g_dap.target.ReadInstructions(addr, inst_count); + lldb::SBInstructionList insts = dap.target.ReadInstructions(addr, inst_count); if (!insts.IsValid()) { response["success"] = false; response["message"] = "Failed to find instructions for memory address."; - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } @@ -4309,11 +4305,11 @@ void request_disassemble(const llvm::json::Object &request) { for (size_t i = 0; i < num_insts; ++i) { lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); auto addr = inst.GetAddress(); - const auto inst_addr = addr.GetLoadAddress(g_dap.target); - const char *m = inst.GetMnemonic(g_dap.target); - const char *o = inst.GetOperands(g_dap.target); - const char *c = inst.GetComment(g_dap.target); - auto d = inst.GetData(g_dap.target); + const auto inst_addr = addr.GetLoadAddress(dap.target); + const char *m = inst.GetMnemonic(dap.target); + const char *o = inst.GetOperands(dap.target); + const char *c = inst.GetComment(dap.target); + auto d = inst.GetData(dap.target); std::string bytes; llvm::raw_string_ostream sb(bytes); @@ -4397,7 +4393,7 @@ void request_disassemble(const llvm::json::Object &request) { llvm::json::Object body; body.try_emplace("instructions", std::move(instructions)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "ReadMemoryRequest": { @@ -4477,7 +4473,7 @@ void request_disassemble(const llvm::json::Object &request) { // } // }] // }, -void request_readMemory(const llvm::json::Object &request) { +void request_readMemory(DAP &dap, const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); auto *arguments = request.getObject("arguments"); @@ -4488,7 +4484,7 @@ void request_readMemory(const llvm::json::Object &request) { response["success"] = false; response["message"] = "Malformed memory reference: " + memoryReference.str(); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } lldb::addr_t addr_int = *addr_opt; @@ -4502,13 +4498,13 @@ void request_readMemory(const llvm::json::Object &request) { std::vector buf; buf.resize(count_read); lldb::SBError error; - lldb::SBAddress addr{addr_int, g_dap.target}; + lldb::SBAddress addr{addr_int, dap.target}; size_t count_result = - g_dap.target.ReadMemory(addr, buf.data(), count_read, error); + dap.target.ReadMemory(addr, buf.data(), count_read, error); if (count_result == 0) { response["success"] = false; EmplaceSafeString(response, "message", error.GetCString()); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); return; } buf.resize(std::min(count_result, count_requested)); @@ -4518,25 +4514,26 @@ void request_readMemory(const llvm::json::Object &request) { body.try_emplace("address", formatted_addr); body.try_emplace("data", llvm::encodeBase64(buf)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // A request used in testing to get the details on all breakpoints that are // currently set in the target. This helps us to test "setBreakpoints" and // "setFunctionBreakpoints" requests to verify we have the correct set of // breakpoints currently set in LLDB. -void request__testGetTargetBreakpoints(const llvm::json::Object &request) { +void request__testGetTargetBreakpoints(DAP &dap, + const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); llvm::json::Array response_breakpoints; - for (uint32_t i = 0; g_dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) { - auto bp = Breakpoint(g_dap, g_dap.target.GetBreakpointAtIndex(i)); + for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) { + auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i)); AppendBreakpoint(&bp, response_breakpoints); } llvm::json::Object body; body.try_emplace("breakpoints", std::move(response_breakpoints)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } // "SetInstructionBreakpointsRequest": { @@ -4726,7 +4723,8 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) { // }, // "required": ["verified"] // }, -void request_setInstructionBreakpoints(const llvm::json::Object &request) { +void request_setInstructionBreakpoints(DAP &dap, + const llvm::json::Object &request) { llvm::json::Object response; llvm::json::Array response_breakpoints; llvm::json::Object body; @@ -4739,7 +4737,7 @@ void request_setInstructionBreakpoints(const llvm::json::Object &request) { // There is no call to remove instruction breakpoints other than calling this // function with a smaller or empty "breakpoints" list. llvm::DenseSet seen; - for (const auto &addr : g_dap.instruction_breakpoints) + for (const auto &addr : dap.instruction_breakpoints) seen.insert(addr.first); for (const auto &bp : *breakpoints) { @@ -4747,9 +4745,9 @@ void request_setInstructionBreakpoints(const llvm::json::Object &request) { if (!bp_obj) continue; // Read instruction breakpoint request. - InstructionBreakpoint inst_bp(g_dap, *bp_obj); - const auto [iv, inserted] = g_dap.instruction_breakpoints.try_emplace( - inst_bp.instructionAddressReference, g_dap, *bp_obj); + InstructionBreakpoint inst_bp(dap, *bp_obj); + const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace( + inst_bp.instructionAddressReference, dap, *bp_obj); if (inserted) iv->second.SetBreakpoint(); else @@ -4759,60 +4757,58 @@ void request_setInstructionBreakpoints(const llvm::json::Object &request) { } for (const auto &addr : seen) { - auto inst_bp = g_dap.instruction_breakpoints.find(addr); - if (inst_bp == g_dap.instruction_breakpoints.end()) + auto inst_bp = dap.instruction_breakpoints.find(addr); + if (inst_bp == dap.instruction_breakpoints.end()) continue; - g_dap.target.BreakpointDelete(inst_bp->second.bp.GetID()); - g_dap.instruction_breakpoints.erase(addr); + dap.target.BreakpointDelete(inst_bp->second.bp.GetID()); + dap.instruction_breakpoints.erase(addr); } body.try_emplace("breakpoints", std::move(response_breakpoints)); response.try_emplace("body", std::move(body)); - g_dap.SendJSON(llvm::json::Value(std::move(response))); + dap.SendJSON(llvm::json::Value(std::move(response))); } -void RegisterRequestCallbacks() { - g_dap.RegisterRequestCallback("attach", request_attach); - g_dap.RegisterRequestCallback("completions", request_completions); - g_dap.RegisterRequestCallback("continue", request_continue); - g_dap.RegisterRequestCallback("configurationDone", request_configurationDone); - g_dap.RegisterRequestCallback("disconnect", request_disconnect); - g_dap.RegisterRequestCallback("evaluate", request_evaluate); - g_dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo); - g_dap.RegisterRequestCallback("initialize", request_initialize); - g_dap.RegisterRequestCallback("launch", request_launch); - g_dap.RegisterRequestCallback("next", request_next); - g_dap.RegisterRequestCallback("pause", request_pause); - g_dap.RegisterRequestCallback("restart", request_restart); - g_dap.RegisterRequestCallback("scopes", request_scopes); - g_dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints); - g_dap.RegisterRequestCallback("setExceptionBreakpoints", - request_setExceptionBreakpoints); - g_dap.RegisterRequestCallback("setFunctionBreakpoints", - request_setFunctionBreakpoints); - g_dap.RegisterRequestCallback("dataBreakpointInfo", - request_dataBreakpointInfo); - g_dap.RegisterRequestCallback("setDataBreakpoints", - request_setDataBreakpoints); - g_dap.RegisterRequestCallback("setVariable", request_setVariable); - g_dap.RegisterRequestCallback("source", request_source); - g_dap.RegisterRequestCallback("stackTrace", request_stackTrace); - g_dap.RegisterRequestCallback("stepIn", request_stepIn); - g_dap.RegisterRequestCallback("stepInTargets", request_stepInTargets); - g_dap.RegisterRequestCallback("stepOut", request_stepOut); - g_dap.RegisterRequestCallback("threads", request_threads); - g_dap.RegisterRequestCallback("variables", request_variables); - g_dap.RegisterRequestCallback("locations", request_locations); - g_dap.RegisterRequestCallback("disassemble", request_disassemble); - g_dap.RegisterRequestCallback("readMemory", request_readMemory); - g_dap.RegisterRequestCallback("setInstructionBreakpoints", - request_setInstructionBreakpoints); +void RegisterRequestCallbacks(DAP &dap) { + dap.RegisterRequestCallback("attach", request_attach); + dap.RegisterRequestCallback("completions", request_completions); + dap.RegisterRequestCallback("continue", request_continue); + dap.RegisterRequestCallback("configurationDone", request_configurationDone); + dap.RegisterRequestCallback("disconnect", request_disconnect); + dap.RegisterRequestCallback("evaluate", request_evaluate); + dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo); + dap.RegisterRequestCallback("initialize", request_initialize); + dap.RegisterRequestCallback("launch", request_launch); + dap.RegisterRequestCallback("next", request_next); + dap.RegisterRequestCallback("pause", request_pause); + dap.RegisterRequestCallback("restart", request_restart); + dap.RegisterRequestCallback("scopes", request_scopes); + dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints); + dap.RegisterRequestCallback("setExceptionBreakpoints", + request_setExceptionBreakpoints); + dap.RegisterRequestCallback("setFunctionBreakpoints", + request_setFunctionBreakpoints); + dap.RegisterRequestCallback("dataBreakpointInfo", request_dataBreakpointInfo); + dap.RegisterRequestCallback("setDataBreakpoints", request_setDataBreakpoints); + dap.RegisterRequestCallback("setVariable", request_setVariable); + dap.RegisterRequestCallback("source", request_source); + dap.RegisterRequestCallback("stackTrace", request_stackTrace); + dap.RegisterRequestCallback("stepIn", request_stepIn); + dap.RegisterRequestCallback("stepInTargets", request_stepInTargets); + dap.RegisterRequestCallback("stepOut", request_stepOut); + dap.RegisterRequestCallback("threads", request_threads); + dap.RegisterRequestCallback("variables", request_variables); + dap.RegisterRequestCallback("locations", request_locations); + dap.RegisterRequestCallback("disassemble", request_disassemble); + dap.RegisterRequestCallback("readMemory", request_readMemory); + dap.RegisterRequestCallback("setInstructionBreakpoints", + request_setInstructionBreakpoints); // Custom requests - g_dap.RegisterRequestCallback("compileUnits", request_compileUnits); - g_dap.RegisterRequestCallback("modules", request_modules); + dap.RegisterRequestCallback("compileUnits", request_compileUnits); + dap.RegisterRequestCallback("modules", request_modules); // Testing requests - g_dap.RegisterRequestCallback("_testGetTargetBreakpoints", - request__testGetTargetBreakpoints); + dap.RegisterRequestCallback("_testGetTargetBreakpoints", + request__testGetTargetBreakpoints); } } // anonymous namespace @@ -4858,9 +4854,9 @@ static void printHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) { // // In case of errors launching the target, a suitable error message will be // emitted to the debug adaptor. -void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, - llvm::StringRef comm_file, - lldb::pid_t debugger_pid, char *argv[]) { +static void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, + llvm::StringRef comm_file, + lldb::pid_t debugger_pid, char *argv[]) { #if defined(_WIN32) llvm::errs() << "runInTerminal is only supported on POSIX systems\n"; exit(EXIT_FAILURE); @@ -4904,7 +4900,7 @@ void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, } /// used only by TestVSCode_redirection_to_console.py -void redirection_test() { +static void redirection_test() { printf("stdout message\n"); fprintf(stderr, "stderr message\n"); fflush(stdout); @@ -4918,31 +4914,28 @@ void redirection_test() { /// /// \return /// A fd pointing to the original stdout. -int SetupStdoutStderrRedirection() { +static int SetupStdoutStderrRedirection(DAP &dap) { int stdoutfd = fileno(stdout); int new_stdout_fd = dup(stdoutfd); - auto output_callback_stderr = [](llvm::StringRef data) { - g_dap.SendOutput(OutputType::Stderr, data); + auto output_callback_stderr = [&dap](llvm::StringRef data) { + dap.SendOutput(OutputType::Stderr, data); }; - auto output_callback_stdout = [](llvm::StringRef data) { - g_dap.SendOutput(OutputType::Stdout, data); + auto output_callback_stdout = [&dap](llvm::StringRef data) { + dap.SendOutput(OutputType::Stdout, data); }; if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) { std::string error_message = llvm::toString(std::move(err)); - if (g_dap.log) - *g_dap.log << error_message << std::endl; + if (dap.log) + *dap.log << error_message << std::endl; output_callback_stderr(error_message); } if (llvm::Error err = RedirectFd(fileno(stderr), output_callback_stderr)) { std::string error_message = llvm::toString(std::move(err)); - if (g_dap.log) - *g_dap.log << error_message << std::endl; + if (dap.log) + *dap.log << error_message << std::endl; output_callback_stderr(error_message); } - /// used only by TestVSCode_redirection_to_console.py - if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) - redirection_test(); return new_stdout_fd; } @@ -4959,7 +4952,6 @@ int main(int argc, char *argv[]) { llvm::SmallString<256> program_path(argv[0]); llvm::sys::fs::make_absolute(program_path); - g_dap.debug_adaptor_path = program_path.str().str(); LLDBDAPOptTable T; unsigned MAI, MAC; @@ -4971,15 +4963,16 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + ReplMode default_repl_mode = ReplMode::Auto; if (input_args.hasArg(OPT_repl_mode)) { llvm::opt::Arg *repl_mode = input_args.getLastArg(OPT_repl_mode); llvm::StringRef repl_mode_value = repl_mode->getValue(); if (repl_mode_value == "auto") { - g_dap.repl_mode = ReplMode::Auto; + default_repl_mode = ReplMode::Auto; } else if (repl_mode_value == "variable") { - g_dap.repl_mode = ReplMode::Variable; + default_repl_mode = ReplMode::Variable; } else if (repl_mode_value == "command") { - g_dap.repl_mode = ReplMode::Command; + default_repl_mode = ReplMode::Command; } else { llvm::errs() << "'" << repl_mode_value @@ -5016,22 +5009,9 @@ int main(int argc, char *argv[]) { } } - // stdout/stderr redirection to the IDE's console - int new_stdout_fd = SetupStdoutStderrRedirection(); - - // Initialize LLDB first before we do anything. - lldb::SBDebugger::Initialize(); - - // Terminate the debugger before the C++ destructor chain kicks in. - auto terminate_debugger = - llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); }); - - RegisterRequestCallbacks(); - int portno = -1; - if (auto *arg = input_args.getLastArg(OPT_port)) { - auto optarg = arg->getValue(); + const auto *optarg = arg->getValue(); char *remainder; portno = strtol(optarg, &remainder, 0); if (remainder == optarg || *remainder != '\0') { @@ -5046,30 +5026,48 @@ int main(int argc, char *argv[]) { pause(); } #endif + + // Initialize LLDB first before we do anything. + lldb::SBDebugger::Initialize(); + + // Terminate the debugger before the C++ destructor chain kicks in. + auto terminate_debugger = + llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); }); + + DAP dap = DAP(program_path.str(), default_repl_mode); + + RegisterRequestCallbacks(dap); + + // stdout/stderr redirection to the IDE's console + int new_stdout_fd = SetupStdoutStderrRedirection(dap); + if (portno != -1) { printf("Listening on port %i...\n", portno); - SOCKET socket_fd = AcceptConnection(portno); + SOCKET socket_fd = AcceptConnection(dap, portno); if (socket_fd >= 0) { - g_dap.input.descriptor = StreamDescriptor::from_socket(socket_fd, true); - g_dap.output.descriptor = StreamDescriptor::from_socket(socket_fd, false); + dap.input.descriptor = StreamDescriptor::from_socket(socket_fd, true); + dap.output.descriptor = StreamDescriptor::from_socket(socket_fd, false); } else { return EXIT_FAILURE; } } else { - g_dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false); - g_dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false); + dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false); + dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false); + + /// used only by TestVSCode_redirection_to_console.py + if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) + redirection_test(); } for (const std::string &arg : input_args.getAllArgValues(OPT_pre_init_command)) { - g_dap.pre_init_commands.push_back(arg); + dap.pre_init_commands.push_back(arg); } bool CleanExit = true; - if (auto Err = g_dap.Loop()) { - if (g_dap.log) - *g_dap.log << "Transport Error: " << llvm::toString(std::move(Err)) - << "\n"; + if (auto Err = dap.Loop()) { + if (dap.log) + *dap.log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n"; CleanExit = false; }