526 lines · c
1//===-- DAP.h ---------------------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLDB_TOOLS_LLDB_DAP_DAP_H10#define LLDB_TOOLS_LLDB_DAP_DAP_H11 12#include "DAPForward.h"13#include "DAPSessionManager.h"14#include "ExceptionBreakpoint.h"15#include "FunctionBreakpoint.h"16#include "InstructionBreakpoint.h"17#include "OutputRedirector.h"18#include "ProgressEvent.h"19#include "Protocol/ProtocolBase.h"20#include "Protocol/ProtocolRequests.h"21#include "Protocol/ProtocolTypes.h"22#include "SourceBreakpoint.h"23#include "Transport.h"24#include "Variables.h"25#include "lldb/API/SBBroadcaster.h"26#include "lldb/API/SBCommandInterpreter.h"27#include "lldb/API/SBDebugger.h"28#include "lldb/API/SBError.h"29#include "lldb/API/SBFile.h"30#include "lldb/API/SBFormat.h"31#include "lldb/API/SBFrame.h"32#include "lldb/API/SBMutex.h"33#include "lldb/API/SBTarget.h"34#include "lldb/API/SBThread.h"35#include "lldb/Host/MainLoop.h"36#include "lldb/Utility/Status.h"37#include "lldb/lldb-types.h"38#include "llvm/ADT/DenseMap.h"39#include "llvm/ADT/DenseSet.h"40#include "llvm/ADT/FunctionExtras.h"41#include "llvm/ADT/SmallSet.h"42#include "llvm/ADT/StringMap.h"43#include "llvm/ADT/StringRef.h"44#include "llvm/ADT/StringSet.h"45#include "llvm/Support/Error.h"46#include "llvm/Support/JSON.h"47#include "llvm/Support/Threading.h"48#include <condition_variable>49#include <cstdint>50#include <deque>51#include <map>52#include <memory>53#include <mutex>54#include <optional>55#include <thread>56#include <vector>57 58#define NO_TYPENAME "<no-type>"59 60namespace lldb_dap {61 62typedef std::map<std::pair<uint32_t, uint32_t>, SourceBreakpoint>63 SourceBreakpointMap;64typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;65typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>66 InstructionBreakpointMap;67 68using AdapterFeature = protocol::AdapterFeature;69using ClientFeature = protocol::ClientFeature;70 71enum class OutputType { Console, Important, Stdout, Stderr, Telemetry };72 73/// Buffer size for handling output events.74constexpr uint64_t OutputBufferSize = (1u << 12);75 76enum DAPBroadcasterBits {77 eBroadcastBitStopEventThread = 1u << 0,78 eBroadcastBitStopProgressThread = 1u << 179};80 81enum class ReplMode { Variable = 0, Command, Auto };82 83using DAPTransport = lldb_private::transport::JSONTransport<ProtocolDescriptor>;84 85struct DAP final : public DAPTransport::MessageHandler {86 friend class DAPSessionManager;87 88 /// Path to the lldb-dap binary itself.89 static llvm::StringRef debug_adapter_path;90 91 Log *log;92 DAPTransport &transport;93 lldb::SBFile in;94 OutputRedirector out;95 OutputRedirector err;96 97 /// Configuration specified by the launch or attach commands.98 protocol::Configuration configuration;99 100 /// The debugger instance for this DAP session.101 lldb::SBDebugger debugger;102 103 /// The target instance for this DAP session.104 lldb::SBTarget target;105 106 Variables variables;107 lldb::SBBroadcaster broadcaster;108 FunctionBreakpointMap function_breakpoints;109 InstructionBreakpointMap instruction_breakpoints;110 std::vector<ExceptionBreakpoint> exception_breakpoints;111 llvm::once_flag init_exception_breakpoints_flag;112 113 /// Map step in target id to list of function targets that user can choose.114 llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;115 116 /// A copy of the last LaunchRequest so we can reuse its arguments if we get a117 /// RestartRequest. Restarting an AttachRequest is not supported.118 std::optional<protocol::LaunchRequestArguments> last_launch_request;119 120 /// The focused thread for this DAP session.121 lldb::tid_t focus_tid = LLDB_INVALID_THREAD_ID;122 123 llvm::once_flag terminated_event_flag;124 bool stop_at_entry = false;125 bool is_attach = false;126 127 /// The process event thread normally responds to process exited events by128 /// shutting down the entire adapter. When we're restarting, we keep the id of129 /// the old process here so we can detect this case and keep running.130 lldb::pid_t restarting_process_id = LLDB_INVALID_PROCESS_ID;131 132 /// Whether we have received the ConfigurationDone request, indicating that133 /// the client has finished initialization of the debug adapter.134 bool configuration_done;135 136 bool waiting_for_run_in_terminal = false;137 ProgressEventReporter progress_event_reporter;138 139 /// Keep track of the last stop thread index IDs as threads won't go away140 /// unless we send a "thread" event to indicate the thread exited.141 llvm::DenseSet<lldb::tid_t> thread_ids;142 143 protocol::Id seq = 0;144 std::mutex call_mutex;145 llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>>146 inflight_reverse_requests;147 ReplMode repl_mode;148 lldb::SBFormat frame_format;149 lldb::SBFormat thread_format;150 151 /// This is used to allow request_evaluate to handle empty expressions152 /// (ie the user pressed 'return' and expects the previous expression to153 /// repeat). If the previous expression was a command, this string will be154 /// empty; if the previous expression was a variable expression, this string155 /// will contain that expression.156 std::string last_nonempty_var_expression;157 158 /// The set of features supported by the connected client.159 llvm::DenseSet<ClientFeature> clientFeatures;160 161 /// Whether to disable sourcing .lldbinit files.162 bool no_lldbinit;163 164 /// Stores whether the initialize request specified a value for165 /// lldbExtSourceInitFile. Used by the test suite to prevent sourcing166 /// `.lldbinit` and changing its behavior.167 bool sourceInitFile = true;168 169 /// The initial thread list upon attaching.170 std::vector<protocol::Thread> initial_thread_list;171 172 /// Keep track of all the modules our client knows about: either through the173 /// modules request or the module events.174 /// @{175 std::mutex modules_mutex;176 llvm::StringSet<> modules;177 /// @}178 179 /// Number of lines of assembly code to show when no debug info is available.180 static constexpr uint32_t k_number_of_assembly_lines_for_nodebug = 32;181 182 /// Creates a new DAP sessions.183 ///184 /// \param[in] log185 /// Log stream, if configured.186 /// \param[in] default_repl_mode187 /// Default repl mode behavior, as configured by the binary.188 /// \param[in] pre_init_commands189 /// LLDB commands to execute as soon as the debugger instance is190 /// allocated.191 /// \param[in] no_lldbinit192 /// Whether to disable sourcing .lldbinit files.193 /// \param[in] transport194 /// Transport for this debug session.195 /// \param[in] loop196 /// Main loop associated with this instance.197 DAP(Log *log, const ReplMode default_repl_mode,198 std::vector<std::string> pre_init_commands, bool no_lldbinit,199 llvm::StringRef client_name, DAPTransport &transport,200 lldb_private::MainLoop &loop);201 202 ~DAP();203 204 /// DAP is not copyable.205 /// @{206 DAP(const DAP &rhs) = delete;207 void operator=(const DAP &rhs) = delete;208 /// @}209 210 ExceptionBreakpoint *GetExceptionBreakpoint(llvm::StringRef filter);211 ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);212 213 /// Redirect stdout and stderr fo the IDE's console output.214 ///215 /// Errors in this operation will be printed to the log file and the IDE's216 /// console output as well.217 llvm::Error ConfigureIO(std::FILE *overrideOut = nullptr,218 std::FILE *overrideErr = nullptr);219 220 /// Stop event handler threads.221 void StopEventHandlers();222 223 /// Configures the debug adapter for launching/attaching.224 void SetConfiguration(const protocol::Configuration &confing, bool is_attach);225 226 /// Configure source maps based on the current `DAPConfiguration`.227 void ConfigureSourceMaps();228 229 /// Serialize the JSON value into a string and send the JSON packet to the230 /// "out" stream.231 void SendJSON(const llvm::json::Value &json);232 /// Send the given message to the client.233 protocol::Id Send(const protocol::Message &message);234 235 void SendOutput(OutputType o, const llvm::StringRef output);236 237 void SendProgressEvent(uint64_t progress_id, const char *message,238 uint64_t completed, uint64_t total);239 240 void __attribute__((format(printf, 3, 4)))241 SendFormattedOutput(OutputType o, const char *format, ...);242 243 int32_t CreateSourceReference(lldb::addr_t address);244 245 std::optional<lldb::addr_t> GetSourceReferenceAddress(int32_t reference);246 247 ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread);248 249 lldb::SBThread GetLLDBThread(lldb::tid_t id);250 lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);251 252 lldb::SBFrame GetLLDBFrame(uint64_t frame_id);253 /// TODO: remove this function when we finish migrating to the254 /// new protocol types.255 lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);256 257 void PopulateExceptionBreakpoints();258 259 /// Attempt to determine if an expression is a variable expression or260 /// lldb command using a heuristic based on the first term of the261 /// expression.262 ///263 /// \param[in] frame264 /// The frame, used as context to detect local variable names265 /// \param[inout] expression266 /// The expression string. Might be modified by this function to267 /// remove the leading escape character.268 /// \param[in] partial_expression269 /// Whether the provided `expression` is only a prefix of the270 /// final expression. If `true`, this function might return271 /// `ReplMode::Auto` to indicate that the expression could be272 /// either an expression or a statement, depending on the rest of273 /// the expression.274 /// \return the expression mode275 ReplMode DetectReplMode(lldb::SBFrame frame, std::string &expression,276 bool partial_expression);277 278 /// Create a `protocol::Source` object as described in the debug adapter279 /// definition.280 ///281 /// \param[in] frame282 /// The frame to use when populating the "Source" object.283 ///284 /// \return285 /// A `protocol::Source` object that follows the formal JSON286 /// definition outlined by Microsoft.287 std::optional<protocol::Source> ResolveSource(const lldb::SBFrame &frame);288 289 /// Create a "Source" JSON object as described in the debug adapter290 /// definition.291 ///292 /// \param[in] address293 /// The address to use when populating out the "Source" object.294 ///295 /// \return296 /// An optional "Source" JSON object that follows the formal JSON297 /// definition outlined by Microsoft.298 std::optional<protocol::Source> ResolveSource(lldb::SBAddress address);299 300 /// Create a "Source" JSON object as described in the debug adapter301 /// definition.302 ///303 /// \param[in] address304 /// The address to use when populating out the "Source" object.305 ///306 /// \return307 /// An optional "Source" JSON object that follows the formal JSON308 /// definition outlined by Microsoft.309 std::optional<protocol::Source>310 ResolveAssemblySource(lldb::SBAddress address);311 312 /// \return313 /// \b false if a fatal error was found while executing these commands,314 /// according to the rules of \a LLDBUtils::RunLLDBCommands.315 bool RunLLDBCommands(llvm::StringRef prefix,316 llvm::ArrayRef<std::string> commands);317 318 llvm::Error RunAttachCommands(llvm::ArrayRef<std::string> attach_commands);319 llvm::Error RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands);320 llvm::Error RunPreInitCommands();321 llvm::Error RunInitCommands();322 llvm::Error RunPreRunCommands();323 void RunPostRunCommands();324 void RunStopCommands();325 void RunExitCommands();326 void RunTerminateCommands();327 328 /// Create a new SBTarget object from the given request arguments.329 ///330 /// \param[out] error331 /// An SBError object that will contain an error description if332 /// function failed to create the target.333 ///334 /// \return335 /// An SBTarget object.336 lldb::SBTarget CreateTarget(lldb::SBError &error);337 338 /// Set given target object as a current target for lldb-dap and start339 /// listening for its breakpoint events.340 void SetTarget(const lldb::SBTarget target);341 342 bool HandleObject(const protocol::Message &M);343 344 /// Disconnect the DAP session.345 llvm::Error Disconnect();346 347 /// Disconnect the DAP session and optionally terminate the debuggee.348 llvm::Error Disconnect(bool terminateDebuggee);349 350 /// Send a "terminated" event to indicate the process is done being debugged.351 void SendTerminatedEvent();352 353 llvm::Error Loop();354 355 /// Send a Debug Adapter Protocol reverse request to the IDE.356 ///357 /// \param[in] command358 /// The reverse request command.359 ///360 /// \param[in] arguments361 /// The reverse request arguments.362 template <typename Handler>363 void SendReverseRequest(llvm::StringRef command,364 llvm::json::Value arguments) {365 protocol::Id id = Send(protocol::Request{366 command.str(),367 std::move(arguments),368 });369 370 std::lock_guard<std::mutex> locker(call_mutex);371 inflight_reverse_requests[id] = std::make_unique<Handler>(command, id);372 }373 374 /// The set of capabilities supported by this adapter.375 protocol::Capabilities GetCapabilities();376 377 /// The set of custom capabilities supported by this adapter.378 protocol::Capabilities GetCustomCapabilities();379 380 /// Debuggee will continue from stopped state.381 void WillContinue() { variables.Clear(); }382 383 /// Poll the process to wait for it to reach the eStateStopped state.384 ///385 /// Wait for the process hit a stopped state. When running a launch with386 /// "launchCommands", or attach with "attachCommands", the calls might take387 /// some time to stop at the entry point since the command is asynchronous. We388 /// need to sync up with the process and make sure it is stopped before we389 /// proceed to do anything else as we will soon be asked to set breakpoints390 /// and other things that require the process to be stopped. We must use391 /// polling because "attachCommands" or "launchCommands" may or may not send392 /// process state change events depending on if the user modifies the async393 /// setting in the debugger. Since both "attachCommands" and "launchCommands"394 /// could end up using any combination of LLDB commands, we must ensure we can395 /// also catch when the process stops, so we must poll the process to make396 /// sure we handle all cases.397 ///398 /// \param[in] seconds399 /// The number of seconds to poll the process to wait until it is stopped.400 ///401 /// \return Error if waiting for the process fails, no error if succeeds.402 lldb::SBError WaitForProcessToStop(std::chrono::seconds seconds);403 404 void SetFrameFormat(llvm::StringRef format);405 406 void SetThreadFormat(llvm::StringRef format);407 408 InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);409 410 InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);411 412 /// Checks if the request is cancelled.413 bool IsCancelled(const protocol::Request &);414 415 /// Clears the cancel request from the set of tracked cancel requests.416 void ClearCancelRequest(const protocol::CancelArguments &);417 418 lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }419 420 /// Get the client name for this DAP session.421 llvm::StringRef GetClientName() const { return m_client_name; }422 423 void StartEventThread();424 void StartProgressEventThread();425 426 /// DAP debugger initialization functions.427 /// @{428 429 /// Perform complete DAP initialization for a new debugger.430 llvm::Error InitializeDebugger();431 432 /// Perform complete DAP initialization by reusing an existing debugger and433 /// target.434 ///435 /// \param[in] debugger_id436 /// The ID of the existing debugger to reuse.437 ///438 /// \param[in] target_id439 /// The globally unique ID of the existing target to reuse.440 llvm::Error InitializeDebugger(int debugger_id, lldb::user_id_t target_id);441 442 /// Start event handling threads based on client capabilities.443 void StartEventThreads();444 445 /// @}446 447 /// Sets the given protocol `breakpoints` in the given `source`, while448 /// removing any existing breakpoints in the given source if they are not in449 /// `breakpoint`.450 ///451 /// \param[in] source452 /// The relevant source of the breakpoints.453 ///454 /// \param[in] breakpoints455 /// The breakpoints to set.456 ///457 /// \return a vector of the breakpoints that were set.458 std::vector<protocol::Breakpoint> SetSourceBreakpoints(459 const protocol::Source &source,460 const std::optional<std::vector<protocol::SourceBreakpoint>>461 &breakpoints);462 463 void Received(const protocol::Event &) override;464 void Received(const protocol::Request &) override;465 void Received(const protocol::Response &) override;466 void OnError(llvm::Error) override;467 void OnClosed() override;468 469private:470 std::vector<protocol::Breakpoint> SetSourceBreakpoints(471 const protocol::Source &source,472 const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints,473 SourceBreakpointMap &existing_breakpoints);474 475 void TransportHandler();476 void TerminateLoop(bool failed = false);477 478 /// Registration of request handler.479 /// @{480 void RegisterRequests();481 template <typename Handler> void RegisterRequest() {482 request_handlers[Handler::GetCommand()] = std::make_unique<Handler>(*this);483 }484 llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;485 /// @}486 487 /// Event threads.488 /// @{489 void ProgressEventThread();490 491 /// Event thread is a shared pointer in case we have a multiple492 /// DAP instances sharing the same event thread.493 std::shared_ptr<ManagedEventThread> event_thread_sp;494 std::thread progress_event_thread;495 /// @}496 497 const llvm::StringRef m_client_name;498 499 /// List of addresses mapped by sourceReference.500 std::vector<lldb::addr_t> m_source_references;501 std::mutex m_source_references_mutex;502 503 /// Queue for all incoming messages.504 std::deque<protocol::Message> m_queue;505 std::mutex m_queue_mutex;506 std::condition_variable m_queue_cv;507 bool m_disconnecting = false;508 bool m_error_occurred = false;509 510 // Loop for managing reading from the client.511 lldb_private::MainLoop &m_loop;512 513 std::mutex m_cancelled_requests_mutex;514 llvm::SmallSet<int64_t, 4> m_cancelled_requests;515 516 std::mutex m_active_request_mutex;517 const protocol::Request *m_active_request;518 519 llvm::StringMap<SourceBreakpointMap> m_source_breakpoints;520 llvm::DenseMap<int64_t, SourceBreakpointMap> m_source_assembly_breakpoints;521};522 523} // namespace lldb_dap524 525#endif526