1061da546Spatrick //===-- VSCode.h ------------------------------------------------*- C++ -*-===// 2061da546Spatrick // 3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick // 7061da546Spatrick //===----------------------------------------------------------------------===// 8061da546Spatrick 9dda28197Spatrick #ifndef LLDB_TOOLS_LLDB_VSCODE_VSCODE_H 10dda28197Spatrick #define LLDB_TOOLS_LLDB_VSCODE_VSCODE_H 11061da546Spatrick 12be691f3bSpatrick #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX 13be691f3bSpatrick 14be691f3bSpatrick #include <condition_variable> 15be691f3bSpatrick #include <cstdio> 16061da546Spatrick #include <iosfwd> 17061da546Spatrick #include <map> 18061da546Spatrick #include <set> 19061da546Spatrick #include <thread> 20061da546Spatrick 21061da546Spatrick #include "llvm/ADT/DenseMap.h" 22061da546Spatrick #include "llvm/ADT/DenseSet.h" 23061da546Spatrick #include "llvm/ADT/StringMap.h" 24061da546Spatrick #include "llvm/ADT/StringRef.h" 25be691f3bSpatrick #include "llvm/Support/JSON.h" 26061da546Spatrick #include "llvm/Support/raw_ostream.h" 27061da546Spatrick 28061da546Spatrick #include "lldb/API/SBAttachInfo.h" 29061da546Spatrick #include "lldb/API/SBBreakpoint.h" 30061da546Spatrick #include "lldb/API/SBBreakpointLocation.h" 31061da546Spatrick #include "lldb/API/SBCommandInterpreter.h" 32061da546Spatrick #include "lldb/API/SBCommandReturnObject.h" 33061da546Spatrick #include "lldb/API/SBCommunication.h" 34061da546Spatrick #include "lldb/API/SBDebugger.h" 35061da546Spatrick #include "lldb/API/SBEvent.h" 36061da546Spatrick #include "lldb/API/SBHostOS.h" 37061da546Spatrick #include "lldb/API/SBInstruction.h" 38061da546Spatrick #include "lldb/API/SBInstructionList.h" 39061da546Spatrick #include "lldb/API/SBLanguageRuntime.h" 40061da546Spatrick #include "lldb/API/SBLaunchInfo.h" 41061da546Spatrick #include "lldb/API/SBLineEntry.h" 42061da546Spatrick #include "lldb/API/SBListener.h" 43061da546Spatrick #include "lldb/API/SBProcess.h" 44061da546Spatrick #include "lldb/API/SBStream.h" 45061da546Spatrick #include "lldb/API/SBStringList.h" 46061da546Spatrick #include "lldb/API/SBTarget.h" 47061da546Spatrick #include "lldb/API/SBThread.h" 48061da546Spatrick 49061da546Spatrick #include "ExceptionBreakpoint.h" 50061da546Spatrick #include "FunctionBreakpoint.h" 51061da546Spatrick #include "IOStream.h" 52be691f3bSpatrick #include "ProgressEvent.h" 53be691f3bSpatrick #include "RunInTerminal.h" 54061da546Spatrick #include "SourceBreakpoint.h" 55061da546Spatrick #include "SourceReference.h" 56061da546Spatrick 57061da546Spatrick #define VARREF_LOCALS (int64_t)1 58061da546Spatrick #define VARREF_GLOBALS (int64_t)2 59061da546Spatrick #define VARREF_REGS (int64_t)3 60061da546Spatrick #define VARREF_FIRST_VAR_IDX (int64_t)4 61061da546Spatrick #define NO_TYPENAME "<no-type>" 62061da546Spatrick 63061da546Spatrick namespace lldb_vscode { 64061da546Spatrick 65061da546Spatrick typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap; 66061da546Spatrick typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; 67061da546Spatrick enum class OutputType { Console, Stdout, Stderr, Telemetry }; 68061da546Spatrick 69be691f3bSpatrick enum VSCodeBroadcasterBits { 70be691f3bSpatrick eBroadcastBitStopEventThread = 1u << 0, 71be691f3bSpatrick eBroadcastBitStopProgressThread = 1u << 1 72be691f3bSpatrick }; 73be691f3bSpatrick 74be691f3bSpatrick typedef void (*RequestCallback)(const llvm::json::Object &command); 75be691f3bSpatrick 76be691f3bSpatrick enum class PacketStatus { 77be691f3bSpatrick Success = 0, 78be691f3bSpatrick EndOfFile, 79be691f3bSpatrick JSONMalformed, 80be691f3bSpatrick JSONNotObject 81be691f3bSpatrick }; 82dda28197Spatrick 83*f6aab3d8Srobert struct Variables { 84*f6aab3d8Srobert /// Variable_reference start index of permanent expandable variable. 85*f6aab3d8Srobert static constexpr int64_t PermanentVariableStartIndex = (1ll << 32); 86*f6aab3d8Srobert 87*f6aab3d8Srobert lldb::SBValueList locals; 88*f6aab3d8Srobert lldb::SBValueList globals; 89*f6aab3d8Srobert lldb::SBValueList registers; 90*f6aab3d8Srobert 91*f6aab3d8Srobert int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX}; 92*f6aab3d8Srobert int64_t next_permanent_var_ref{PermanentVariableStartIndex}; 93*f6aab3d8Srobert 94*f6aab3d8Srobert /// Expandable variables that are alive in this stop state. 95*f6aab3d8Srobert /// Will be cleared when debuggee resumes. 96*f6aab3d8Srobert llvm::DenseMap<int64_t, lldb::SBValue> expandable_variables; 97*f6aab3d8Srobert /// Expandable variables that persist across entire debug session. 98*f6aab3d8Srobert /// These are the variables evaluated from debug console REPL. 99*f6aab3d8Srobert llvm::DenseMap<int64_t, lldb::SBValue> expandable_permanent_variables; 100*f6aab3d8Srobert 101*f6aab3d8Srobert /// Check if \p var_ref points to a variable that should persist for the 102*f6aab3d8Srobert /// entire duration of the debug session, e.g. repl expandable variables 103*f6aab3d8Srobert static bool IsPermanentVariableReference(int64_t var_ref); 104*f6aab3d8Srobert 105*f6aab3d8Srobert /// \return a new variableReference. 106*f6aab3d8Srobert /// Specify is_permanent as true for variable that should persist entire 107*f6aab3d8Srobert /// debug session. 108*f6aab3d8Srobert int64_t GetNewVariableRefence(bool is_permanent); 109*f6aab3d8Srobert 110*f6aab3d8Srobert /// \return the expandable variable corresponding with variableReference 111*f6aab3d8Srobert /// value of \p value. 112*f6aab3d8Srobert /// If \p var_ref is invalid an empty SBValue is returned. 113*f6aab3d8Srobert lldb::SBValue GetVariable(int64_t var_ref) const; 114*f6aab3d8Srobert 115*f6aab3d8Srobert /// Insert a new \p variable. 116*f6aab3d8Srobert /// \return variableReference assigned to this expandable variable. 117*f6aab3d8Srobert int64_t InsertExpandableVariable(lldb::SBValue variable, bool is_permanent); 118*f6aab3d8Srobert 119*f6aab3d8Srobert /// Clear all scope variables and non-permanent expandable variables. 120*f6aab3d8Srobert void Clear(); 121*f6aab3d8Srobert }; 122*f6aab3d8Srobert 123061da546Spatrick struct VSCode { 124be691f3bSpatrick std::string debug_adaptor_path; 125061da546Spatrick InputStream input; 126061da546Spatrick OutputStream output; 127061da546Spatrick lldb::SBDebugger debugger; 128061da546Spatrick lldb::SBTarget target; 129*f6aab3d8Srobert Variables variables; 130061da546Spatrick lldb::SBBroadcaster broadcaster; 131061da546Spatrick std::thread event_thread; 132be691f3bSpatrick std::thread progress_event_thread; 133061da546Spatrick std::unique_ptr<std::ofstream> log; 134061da546Spatrick llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref; 135061da546Spatrick llvm::DenseMap<int64_t, SourceReference> source_map; 136061da546Spatrick llvm::StringMap<SourceBreakpointMap> source_breakpoints; 137061da546Spatrick FunctionBreakpointMap function_breakpoints; 138061da546Spatrick std::vector<ExceptionBreakpoint> exception_breakpoints; 139061da546Spatrick std::vector<std::string> init_commands; 140061da546Spatrick std::vector<std::string> pre_run_commands; 141061da546Spatrick std::vector<std::string> exit_commands; 142061da546Spatrick std::vector<std::string> stop_commands; 143dda28197Spatrick std::vector<std::string> terminate_commands; 144061da546Spatrick lldb::tid_t focus_tid; 145061da546Spatrick bool sent_terminated_event; 146061da546Spatrick bool stop_at_entry; 147dda28197Spatrick bool is_attach; 148*f6aab3d8Srobert bool configuration_done_sent; 149be691f3bSpatrick uint32_t reverse_request_seq; 150be691f3bSpatrick std::map<std::string, RequestCallback> request_handlers; 151be691f3bSpatrick bool waiting_for_run_in_terminal; 152be691f3bSpatrick ProgressEventReporter progress_event_reporter; 153061da546Spatrick // Keep track of the last stop thread index IDs as threads won't go away 154061da546Spatrick // unless we send a "thread" event to indicate the thread exited. 155061da546Spatrick llvm::DenseSet<lldb::tid_t> thread_ids; 156061da546Spatrick VSCode(); 157061da546Spatrick ~VSCode(); 158061da546Spatrick VSCode(const VSCode &rhs) = delete; 159061da546Spatrick void operator=(const VSCode &rhs) = delete; 160061da546Spatrick int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const; 161061da546Spatrick ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); 162061da546Spatrick ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); 163061da546Spatrick 164061da546Spatrick // Serialize the JSON value into a string and send the JSON packet to 165061da546Spatrick // the "out" stream. 166061da546Spatrick void SendJSON(const llvm::json::Value &json); 167061da546Spatrick 168061da546Spatrick std::string ReadJSON(); 169061da546Spatrick 170061da546Spatrick void SendOutput(OutputType o, const llvm::StringRef output); 171061da546Spatrick 172be691f3bSpatrick void SendProgressEvent(uint64_t progress_id, const char *message, 173be691f3bSpatrick uint64_t completed, uint64_t total); 174be691f3bSpatrick 175061da546Spatrick void __attribute__((format(printf, 3, 4))) 176061da546Spatrick SendFormattedOutput(OutputType o, const char *format, ...); 177061da546Spatrick 178061da546Spatrick static int64_t GetNextSourceReference(); 179061da546Spatrick 180061da546Spatrick ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread); 181061da546Spatrick 182061da546Spatrick lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); 183061da546Spatrick 184061da546Spatrick lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); 185061da546Spatrick 186061da546Spatrick llvm::json::Value CreateTopLevelScopes(); 187061da546Spatrick 188061da546Spatrick void RunLLDBCommands(llvm::StringRef prefix, 189061da546Spatrick const std::vector<std::string> &commands); 190061da546Spatrick 191061da546Spatrick void RunInitCommands(); 192061da546Spatrick void RunPreRunCommands(); 193061da546Spatrick void RunStopCommands(); 194061da546Spatrick void RunExitCommands(); 195dda28197Spatrick void RunTerminateCommands(); 196dda28197Spatrick 197dda28197Spatrick /// Create a new SBTarget object from the given request arguments. 198dda28197Spatrick /// \param[in] arguments 199dda28197Spatrick /// Launch configuration arguments. 200dda28197Spatrick /// 201dda28197Spatrick /// \param[out] error 202dda28197Spatrick /// An SBError object that will contain an error description if 203dda28197Spatrick /// function failed to create the target. 204dda28197Spatrick /// 205dda28197Spatrick /// \return 206dda28197Spatrick /// An SBTarget object. 207be691f3bSpatrick lldb::SBTarget CreateTargetFromArguments(const llvm::json::Object &arguments, 208dda28197Spatrick lldb::SBError &error); 209dda28197Spatrick 210dda28197Spatrick /// Set given target object as a current target for lldb-vscode and start 211dda28197Spatrick /// listeing for its breakpoint events. 212dda28197Spatrick void SetTarget(const lldb::SBTarget target); 213be691f3bSpatrick 214be691f3bSpatrick const std::map<std::string, RequestCallback> &GetRequestHandlers(); 215be691f3bSpatrick 216be691f3bSpatrick PacketStatus GetNextObject(llvm::json::Object &object); 217be691f3bSpatrick bool HandleObject(const llvm::json::Object &object); 218be691f3bSpatrick 219be691f3bSpatrick /// Send a Debug Adapter Protocol reverse request to the IDE 220be691f3bSpatrick /// 221be691f3bSpatrick /// \param[in] request 222be691f3bSpatrick /// The payload of the request to send. 223be691f3bSpatrick /// 224be691f3bSpatrick /// \param[out] response 225be691f3bSpatrick /// The response of the IDE. It might be undefined if there was an error. 226be691f3bSpatrick /// 227be691f3bSpatrick /// \return 228be691f3bSpatrick /// A \a PacketStatus object indicating the sucess or failure of the 229be691f3bSpatrick /// request. 230be691f3bSpatrick PacketStatus SendReverseRequest(llvm::json::Object request, 231be691f3bSpatrick llvm::json::Object &response); 232be691f3bSpatrick 233be691f3bSpatrick /// Registers a callback handler for a Debug Adapter Protocol request 234be691f3bSpatrick /// 235be691f3bSpatrick /// \param[in] request 236be691f3bSpatrick /// The name of the request following the Debug Adapter Protocol 237be691f3bSpatrick /// specification. 238be691f3bSpatrick /// 239be691f3bSpatrick /// \param[in] callback 240be691f3bSpatrick /// The callback to execute when the given request is triggered by the 241be691f3bSpatrick /// IDE. 242be691f3bSpatrick void RegisterRequestCallback(std::string request, RequestCallback callback); 243be691f3bSpatrick 244*f6aab3d8Srobert /// Debuggee will continue from stopped state. WillContinueVSCode245*f6aab3d8Srobert void WillContinue() { variables.Clear(); } 246*f6aab3d8Srobert 247*f6aab3d8Srobert /// Poll the process to wait for it to reach the eStateStopped state. 248*f6aab3d8Srobert /// 249*f6aab3d8Srobert /// Wait for the process hit a stopped state. When running a launch with 250*f6aab3d8Srobert /// "launchCommands", or attach with "attachCommands", the calls might take 251*f6aab3d8Srobert /// some time to stop at the entry point since the command is asynchronous. We 252*f6aab3d8Srobert /// need to sync up with the process and make sure it is stopped before we 253*f6aab3d8Srobert /// proceed to do anything else as we will soon be asked to set breakpoints 254*f6aab3d8Srobert /// and other things that require the process to be stopped. We must use 255*f6aab3d8Srobert /// polling because "attachCommands" or "launchCommands" may or may not send 256*f6aab3d8Srobert /// process state change events depending on if the user modifies the async 257*f6aab3d8Srobert /// setting in the debugger. Since both "attachCommands" and "launchCommands" 258*f6aab3d8Srobert /// could end up using any combination of LLDB commands, we must ensure we can 259*f6aab3d8Srobert /// also catch when the process stops, so we must poll the process to make 260*f6aab3d8Srobert /// sure we handle all cases. 261*f6aab3d8Srobert /// 262*f6aab3d8Srobert /// \param[in] seconds 263*f6aab3d8Srobert /// The number of seconds to poll the process to wait until it is stopped. 264*f6aab3d8Srobert /// 265*f6aab3d8Srobert /// \return Error if waiting for the process fails, no error if succeeds. 266*f6aab3d8Srobert lldb::SBError WaitForProcessToStop(uint32_t seconds); 267*f6aab3d8Srobert 268be691f3bSpatrick private: 269be691f3bSpatrick // Send the JSON in "json_str" to the "out" stream. Correctly send the 270be691f3bSpatrick // "Content-Length:" field followed by the length, followed by the raw 271be691f3bSpatrick // JSON bytes. 272be691f3bSpatrick void SendJSON(const std::string &json_str); 273061da546Spatrick }; 274061da546Spatrick 275061da546Spatrick extern VSCode g_vsc; 276061da546Spatrick 277061da546Spatrick } // namespace lldb_vscode 278061da546Spatrick 279061da546Spatrick #endif 280