| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLDB_TOOLS_LLDB_DAP_DAP_H |
| 10 | #define LLDB_TOOLS_LLDB_DAP_DAP_H |
| 11 | |
| 12 | #include "DAPForward.h" |
| 13 | #include "ExceptionBreakpoint.h" |
| 14 | #include "FunctionBreakpoint.h" |
| 15 | #include "InstructionBreakpoint.h" |
| 16 | #include "OutputRedirector.h" |
| 17 | #include "ProgressEvent.h" |
| 18 | #include "Protocol/ProtocolBase.h" |
| 19 | #include "Protocol/ProtocolRequests.h" |
| 20 | #include "Protocol/ProtocolTypes.h" |
| 21 | #include "SourceBreakpoint.h" |
| 22 | #include "Transport.h" |
| 23 | #include "Variables.h" |
| 24 | #include "lldb/API/SBBroadcaster.h" |
| 25 | #include "lldb/API/SBCommandInterpreter.h" |
| 26 | #include "lldb/API/SBDebugger.h" |
| 27 | #include "lldb/API/SBError.h" |
| 28 | #include "lldb/API/SBFile.h" |
| 29 | #include "lldb/API/SBFormat.h" |
| 30 | #include "lldb/API/SBFrame.h" |
| 31 | #include "lldb/API/SBMutex.h" |
| 32 | #include "lldb/API/SBTarget.h" |
| 33 | #include "lldb/API/SBThread.h" |
| 34 | #include "lldb/lldb-types.h" |
| 35 | #include "llvm/ADT/DenseMap.h" |
| 36 | #include "llvm/ADT/DenseSet.h" |
| 37 | #include "llvm/ADT/FunctionExtras.h" |
| 38 | #include "llvm/ADT/SmallSet.h" |
| 39 | #include "llvm/ADT/StringMap.h" |
| 40 | #include "llvm/ADT/StringRef.h" |
| 41 | #include "llvm/ADT/StringSet.h" |
| 42 | #include "llvm/Support/Error.h" |
| 43 | #include "llvm/Support/JSON.h" |
| 44 | #include "llvm/Support/Threading.h" |
| 45 | #include <condition_variable> |
| 46 | #include <cstdint> |
| 47 | #include <deque> |
| 48 | #include <memory> |
| 49 | #include <mutex> |
| 50 | #include <optional> |
| 51 | #include <thread> |
| 52 | #include <vector> |
| 53 | |
| 54 | #define NO_TYPENAME "<no-type>" |
| 55 | |
| 56 | namespace lldb_dap { |
| 57 | |
| 58 | typedef std::map<std::pair<uint32_t, uint32_t>, SourceBreakpoint> |
| 59 | SourceBreakpointMap; |
| 60 | typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; |
| 61 | typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint> |
| 62 | InstructionBreakpointMap; |
| 63 | |
| 64 | using AdapterFeature = protocol::AdapterFeature; |
| 65 | using ClientFeature = protocol::ClientFeature; |
| 66 | |
| 67 | enum class OutputType { Console, Important, Stdout, Stderr, Telemetry }; |
| 68 | |
| 69 | /// Buffer size for handling output events. |
| 70 | constexpr uint64_t OutputBufferSize = (1u << 12); |
| 71 | |
| 72 | enum DAPBroadcasterBits { |
| 73 | eBroadcastBitStopEventThread = 1u << 0, |
| 74 | eBroadcastBitStopProgressThread = 1u << 1 |
| 75 | }; |
| 76 | |
| 77 | enum class ReplMode { Variable = 0, Command, Auto }; |
| 78 | |
| 79 | struct DAP { |
| 80 | /// Path to the lldb-dap binary itself. |
| 81 | static llvm::StringRef debug_adapter_path; |
| 82 | |
| 83 | Log *log; |
| 84 | Transport &transport; |
| 85 | lldb::SBFile in; |
| 86 | OutputRedirector out; |
| 87 | OutputRedirector err; |
| 88 | |
| 89 | /// Configuration specified by the launch or attach commands. |
| 90 | protocol::Configuration configuration; |
| 91 | |
| 92 | /// The debugger instance for this DAP session. |
| 93 | lldb::SBDebugger debugger; |
| 94 | |
| 95 | /// The target instance for this DAP session. |
| 96 | lldb::SBTarget target; |
| 97 | |
| 98 | Variables variables; |
| 99 | lldb::SBBroadcaster broadcaster; |
| 100 | FunctionBreakpointMap function_breakpoints; |
| 101 | InstructionBreakpointMap instruction_breakpoints; |
| 102 | std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints; |
| 103 | llvm::once_flag init_exception_breakpoints_flag; |
| 104 | |
| 105 | /// Map step in target id to list of function targets that user can choose. |
| 106 | llvm::DenseMap<lldb::addr_t, std::string> step_in_targets; |
| 107 | |
| 108 | /// A copy of the last LaunchRequest so we can reuse its arguments if we get a |
| 109 | /// RestartRequest. Restarting an AttachRequest is not supported. |
| 110 | std::optional<protocol::LaunchRequestArguments> last_launch_request; |
| 111 | |
| 112 | /// The focused thread for this DAP session. |
| 113 | lldb::tid_t focus_tid = LLDB_INVALID_THREAD_ID; |
| 114 | |
| 115 | bool disconnecting = false; |
| 116 | llvm::once_flag terminated_event_flag; |
| 117 | bool stop_at_entry = false; |
| 118 | bool is_attach = false; |
| 119 | |
| 120 | /// The process event thread normally responds to process exited events by |
| 121 | /// shutting down the entire adapter. When we're restarting, we keep the id of |
| 122 | /// the old process here so we can detect this case and keep running. |
| 123 | lldb::pid_t restarting_process_id = LLDB_INVALID_PROCESS_ID; |
| 124 | |
| 125 | /// Whether we have received the ConfigurationDone request, indicating that |
| 126 | /// the client has finished initialization of the debug adapter. |
| 127 | bool configuration_done; |
| 128 | |
| 129 | bool waiting_for_run_in_terminal = false; |
| 130 | ProgressEventReporter progress_event_reporter; |
| 131 | |
| 132 | /// Keep track of the last stop thread index IDs as threads won't go away |
| 133 | /// unless we send a "thread" event to indicate the thread exited. |
| 134 | llvm::DenseSet<lldb::tid_t> thread_ids; |
| 135 | |
| 136 | uint32_t reverse_request_seq = 0; |
| 137 | std::mutex call_mutex; |
| 138 | llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>> |
| 139 | inflight_reverse_requests; |
| 140 | ReplMode repl_mode; |
| 141 | lldb::SBFormat frame_format; |
| 142 | lldb::SBFormat thread_format; |
| 143 | |
| 144 | /// This is used to allow request_evaluate to handle empty expressions |
| 145 | /// (ie the user pressed 'return' and expects the previous expression to |
| 146 | /// repeat). If the previous expression was a command, this string will be |
| 147 | /// empty; if the previous expression was a variable expression, this string |
| 148 | /// will contain that expression. |
| 149 | std::string last_nonempty_var_expression; |
| 150 | |
| 151 | /// The set of features supported by the connected client. |
| 152 | llvm::DenseSet<ClientFeature> clientFeatures; |
| 153 | |
| 154 | /// The initial thread list upon attaching. |
| 155 | std::vector<protocol::Thread> initial_thread_list; |
| 156 | |
| 157 | /// Keep track of all the modules our client knows about: either through the |
| 158 | /// modules request or the module events. |
| 159 | /// @{ |
| 160 | std::mutex modules_mutex; |
| 161 | llvm::StringSet<> modules; |
| 162 | /// @} |
| 163 | |
| 164 | /// Number of lines of assembly code to show when no debug info is available. |
| 165 | static constexpr uint32_t k_number_of_assembly_lines_for_nodebug = 32; |
| 166 | |
| 167 | /// Creates a new DAP sessions. |
| 168 | /// |
| 169 | /// \param[in] log |
| 170 | /// Log stream, if configured. |
| 171 | /// \param[in] default_repl_mode |
| 172 | /// Default repl mode behavior, as configured by the binary. |
| 173 | /// \param[in] pre_init_commands |
| 174 | /// LLDB commands to execute as soon as the debugger instance is |
| 175 | /// allocated. |
| 176 | /// \param[in] transport |
| 177 | /// Transport for this debug session. |
| 178 | DAP(Log *log, const ReplMode default_repl_mode, |
| 179 | std::vector<std::string> pre_init_commands, Transport &transport); |
| 180 | |
| 181 | ~DAP(); |
| 182 | |
| 183 | /// DAP is not copyable. |
| 184 | /// @{ |
| 185 | DAP(const DAP &rhs) = delete; |
| 186 | void operator=(const DAP &rhs) = delete; |
| 187 | /// @} |
| 188 | |
| 189 | ExceptionBreakpoint *GetExceptionBreakpoint(llvm::StringRef filter); |
| 190 | ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); |
| 191 | |
| 192 | /// Redirect stdout and stderr fo the IDE's console output. |
| 193 | /// |
| 194 | /// Errors in this operation will be printed to the log file and the IDE's |
| 195 | /// console output as well. |
| 196 | llvm::Error ConfigureIO(std::FILE *overrideOut = nullptr, |
| 197 | std::FILE *overrideErr = nullptr); |
| 198 | |
| 199 | /// Stop event handler threads. |
| 200 | void StopEventHandlers(); |
| 201 | |
| 202 | /// Configures the debug adapter for launching/attaching. |
| 203 | void SetConfiguration(const protocol::Configuration &confing, bool is_attach); |
| 204 | |
| 205 | /// Configure source maps based on the current `DAPConfiguration`. |
| 206 | void ConfigureSourceMaps(); |
| 207 | |
| 208 | /// Serialize the JSON value into a string and send the JSON packet to the |
| 209 | /// "out" stream. |
| 210 | void SendJSON(const llvm::json::Value &json); |
| 211 | /// Send the given message to the client |
| 212 | void Send(const protocol::Message &message); |
| 213 | |
| 214 | void SendOutput(OutputType o, const llvm::StringRef output); |
| 215 | |
| 216 | void SendProgressEvent(uint64_t progress_id, const char *message, |
| 217 | uint64_t completed, uint64_t total); |
| 218 | |
| 219 | void __attribute__((format(printf, 3, 4))) |
| 220 | SendFormattedOutput(OutputType o, const char *format, ...); |
| 221 | |
| 222 | static int64_t GetNextSourceReference(); |
| 223 | |
| 224 | ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread); |
| 225 | |
| 226 | lldb::SBThread GetLLDBThread(lldb::tid_t id); |
| 227 | lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); |
| 228 | |
| 229 | lldb::SBFrame GetLLDBFrame(uint64_t frame_id); |
| 230 | /// TODO: remove this function when we finish migrating to the |
| 231 | /// new protocol types. |
| 232 | lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); |
| 233 | |
| 234 | void PopulateExceptionBreakpoints(); |
| 235 | |
| 236 | /// Attempt to determine if an expression is a variable expression or |
| 237 | /// lldb command using a heuristic based on the first term of the |
| 238 | /// expression. |
| 239 | /// |
| 240 | /// \param[in] frame |
| 241 | /// The frame, used as context to detect local variable names |
| 242 | /// \param[inout] expression |
| 243 | /// The expression string. Might be modified by this function to |
| 244 | /// remove the leading escape character. |
| 245 | /// \param[in] partial_expression |
| 246 | /// Whether the provided `expression` is only a prefix of the |
| 247 | /// final expression. If `true`, this function might return |
| 248 | /// `ReplMode::Auto` to indicate that the expression could be |
| 249 | /// either an expression or a statement, depending on the rest of |
| 250 | /// the expression. |
| 251 | /// \return the expression mode |
| 252 | ReplMode DetectReplMode(lldb::SBFrame frame, std::string &expression, |
| 253 | bool partial_expression); |
| 254 | |
| 255 | /// \return |
| 256 | /// \b false if a fatal error was found while executing these commands, |
| 257 | /// according to the rules of \a LLDBUtils::RunLLDBCommands. |
| 258 | bool RunLLDBCommands(llvm::StringRef prefix, |
| 259 | llvm::ArrayRef<std::string> commands); |
| 260 | |
| 261 | llvm::Error RunAttachCommands(llvm::ArrayRef<std::string> attach_commands); |
| 262 | llvm::Error RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands); |
| 263 | llvm::Error RunPreInitCommands(); |
| 264 | llvm::Error RunInitCommands(); |
| 265 | llvm::Error RunPreRunCommands(); |
| 266 | void RunPostRunCommands(); |
| 267 | void RunStopCommands(); |
| 268 | void RunExitCommands(); |
| 269 | void RunTerminateCommands(); |
| 270 | |
| 271 | /// Create a new SBTarget object from the given request arguments. |
| 272 | /// |
| 273 | /// \param[out] error |
| 274 | /// An SBError object that will contain an error description if |
| 275 | /// function failed to create the target. |
| 276 | /// |
| 277 | /// \return |
| 278 | /// An SBTarget object. |
| 279 | lldb::SBTarget CreateTarget(lldb::SBError &error); |
| 280 | |
| 281 | /// Set given target object as a current target for lldb-dap and start |
| 282 | /// listeing for its breakpoint events. |
| 283 | void SetTarget(const lldb::SBTarget target); |
| 284 | |
| 285 | bool HandleObject(const protocol::Message &M); |
| 286 | |
| 287 | /// Disconnect the DAP session. |
| 288 | llvm::Error Disconnect(); |
| 289 | |
| 290 | /// Disconnect the DAP session and optionally terminate the debuggee. |
| 291 | llvm::Error Disconnect(bool terminateDebuggee); |
| 292 | |
| 293 | /// Send a "terminated" event to indicate the process is done being debugged. |
| 294 | void SendTerminatedEvent(); |
| 295 | |
| 296 | llvm::Error Loop(); |
| 297 | |
| 298 | /// Send a Debug Adapter Protocol reverse request to the IDE. |
| 299 | /// |
| 300 | /// \param[in] command |
| 301 | /// The reverse request command. |
| 302 | /// |
| 303 | /// \param[in] arguments |
| 304 | /// The reverse request arguments. |
| 305 | template <typename Handler> |
| 306 | void SendReverseRequest(llvm::StringRef command, |
| 307 | llvm::json::Value arguments) { |
| 308 | int64_t id; |
| 309 | { |
| 310 | std::lock_guard<std::mutex> locker(call_mutex); |
| 311 | id = ++reverse_request_seq; |
| 312 | inflight_reverse_requests[id] = std::make_unique<Handler>(command, id); |
| 313 | } |
| 314 | |
| 315 | SendJSON(json: llvm::json::Object{ |
| 316 | {.K: "type" , .V: "request" }, |
| 317 | {.K: "seq" , .V: id}, |
| 318 | {.K: "command" , .V: command}, |
| 319 | {.K: "arguments" , .V: std::move(arguments)}, |
| 320 | }); |
| 321 | } |
| 322 | |
| 323 | /// The set of capablities supported by this adapter. |
| 324 | protocol::Capabilities GetCapabilities(); |
| 325 | |
| 326 | /// Debuggee will continue from stopped state. |
| 327 | void WillContinue() { variables.Clear(); } |
| 328 | |
| 329 | /// Poll the process to wait for it to reach the eStateStopped state. |
| 330 | /// |
| 331 | /// Wait for the process hit a stopped state. When running a launch with |
| 332 | /// "launchCommands", or attach with "attachCommands", the calls might take |
| 333 | /// some time to stop at the entry point since the command is asynchronous. We |
| 334 | /// need to sync up with the process and make sure it is stopped before we |
| 335 | /// proceed to do anything else as we will soon be asked to set breakpoints |
| 336 | /// and other things that require the process to be stopped. We must use |
| 337 | /// polling because "attachCommands" or "launchCommands" may or may not send |
| 338 | /// process state change events depending on if the user modifies the async |
| 339 | /// setting in the debugger. Since both "attachCommands" and "launchCommands" |
| 340 | /// could end up using any combination of LLDB commands, we must ensure we can |
| 341 | /// also catch when the process stops, so we must poll the process to make |
| 342 | /// sure we handle all cases. |
| 343 | /// |
| 344 | /// \param[in] seconds |
| 345 | /// The number of seconds to poll the process to wait until it is stopped. |
| 346 | /// |
| 347 | /// \return Error if waiting for the process fails, no error if succeeds. |
| 348 | lldb::SBError WaitForProcessToStop(std::chrono::seconds seconds); |
| 349 | |
| 350 | void SetFrameFormat(llvm::StringRef format); |
| 351 | |
| 352 | void SetThreadFormat(llvm::StringRef format); |
| 353 | |
| 354 | InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id); |
| 355 | |
| 356 | InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread); |
| 357 | |
| 358 | /// Checks if the request is cancelled. |
| 359 | bool IsCancelled(const protocol::Request &); |
| 360 | |
| 361 | /// Clears the cancel request from the set of tracked cancel requests. |
| 362 | void ClearCancelRequest(const protocol::CancelArguments &); |
| 363 | |
| 364 | lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } |
| 365 | |
| 366 | void StartEventThread(); |
| 367 | void StartProgressEventThread(); |
| 368 | |
| 369 | /// Sets the given protocol `breakpoints` in the given `source`, while |
| 370 | /// removing any existing breakpoints in the given source if they are not in |
| 371 | /// `breakpoint`. |
| 372 | /// |
| 373 | /// \param[in] source |
| 374 | /// The relevant source of the breakpoints. |
| 375 | /// |
| 376 | /// \param[in] breakpoints |
| 377 | /// The breakpoints to set. |
| 378 | /// |
| 379 | /// \return a vector of the breakpoints that were set. |
| 380 | std::vector<protocol::Breakpoint> SetSourceBreakpoints( |
| 381 | const protocol::Source &source, |
| 382 | const std::optional<std::vector<protocol::SourceBreakpoint>> |
| 383 | &breakpoints); |
| 384 | |
| 385 | private: |
| 386 | std::vector<protocol::Breakpoint> SetSourceBreakpoints( |
| 387 | const protocol::Source &source, |
| 388 | const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints, |
| 389 | SourceBreakpointMap &existing_breakpoints); |
| 390 | |
| 391 | /// Registration of request handler. |
| 392 | /// @{ |
| 393 | void RegisterRequests(); |
| 394 | template <typename Handler> void RegisterRequest() { |
| 395 | request_handlers[Handler::GetCommand()] = std::make_unique<Handler>(*this); |
| 396 | } |
| 397 | llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers; |
| 398 | /// @} |
| 399 | |
| 400 | /// Event threads. |
| 401 | /// @{ |
| 402 | void EventThread(); |
| 403 | void ProgressEventThread(); |
| 404 | |
| 405 | std::thread event_thread; |
| 406 | std::thread progress_event_thread; |
| 407 | /// @} |
| 408 | |
| 409 | /// Queue for all incoming messages. |
| 410 | std::deque<protocol::Message> m_queue; |
| 411 | std::mutex m_queue_mutex; |
| 412 | std::condition_variable m_queue_cv; |
| 413 | |
| 414 | std::mutex m_cancelled_requests_mutex; |
| 415 | llvm::SmallSet<int64_t, 4> m_cancelled_requests; |
| 416 | |
| 417 | std::mutex m_active_request_mutex; |
| 418 | const protocol::Request *m_active_request; |
| 419 | |
| 420 | llvm::StringMap<SourceBreakpointMap> m_source_breakpoints; |
| 421 | llvm::DenseMap<int64_t, SourceBreakpointMap> m_source_assembly_breakpoints; |
| 422 | }; |
| 423 | |
| 424 | } // namespace lldb_dap |
| 425 | |
| 426 | #endif |
| 427 | |