brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.1 KiB · b5f2a57 Raw
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