1 //===-- VSCode.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_VSCODE_VSCODE_H 10 #define LLDB_TOOLS_LLDB_VSCODE_VSCODE_H 11 12 #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX 13 14 #include <condition_variable> 15 #include <cstdio> 16 #include <iosfwd> 17 #include <map> 18 #include <set> 19 #include <thread> 20 21 #include "llvm/ADT/DenseMap.h" 22 #include "llvm/ADT/DenseSet.h" 23 #include "llvm/ADT/StringMap.h" 24 #include "llvm/ADT/StringRef.h" 25 #include "llvm/Support/JSON.h" 26 #include "llvm/Support/raw_ostream.h" 27 28 #include "lldb/API/SBAttachInfo.h" 29 #include "lldb/API/SBBreakpoint.h" 30 #include "lldb/API/SBBreakpointLocation.h" 31 #include "lldb/API/SBCommandInterpreter.h" 32 #include "lldb/API/SBCommandReturnObject.h" 33 #include "lldb/API/SBCommunication.h" 34 #include "lldb/API/SBDebugger.h" 35 #include "lldb/API/SBEvent.h" 36 #include "lldb/API/SBHostOS.h" 37 #include "lldb/API/SBInstruction.h" 38 #include "lldb/API/SBInstructionList.h" 39 #include "lldb/API/SBLanguageRuntime.h" 40 #include "lldb/API/SBLaunchInfo.h" 41 #include "lldb/API/SBLineEntry.h" 42 #include "lldb/API/SBListener.h" 43 #include "lldb/API/SBProcess.h" 44 #include "lldb/API/SBStream.h" 45 #include "lldb/API/SBStringList.h" 46 #include "lldb/API/SBTarget.h" 47 #include "lldb/API/SBThread.h" 48 49 #include "ExceptionBreakpoint.h" 50 #include "FunctionBreakpoint.h" 51 #include "IOStream.h" 52 #include "ProgressEvent.h" 53 #include "RunInTerminal.h" 54 #include "SourceBreakpoint.h" 55 #include "SourceReference.h" 56 57 #define VARREF_LOCALS (int64_t)1 58 #define VARREF_GLOBALS (int64_t)2 59 #define VARREF_REGS (int64_t)3 60 #define VARREF_FIRST_VAR_IDX (int64_t)4 61 #define NO_TYPENAME "<no-type>" 62 63 namespace lldb_vscode { 64 65 typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap; 66 typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; 67 enum class OutputType { Console, Stdout, Stderr, Telemetry }; 68 69 enum VSCodeBroadcasterBits { 70 eBroadcastBitStopEventThread = 1u << 0, 71 eBroadcastBitStopProgressThread = 1u << 1 72 }; 73 74 typedef void (*RequestCallback)(const llvm::json::Object &command); 75 76 enum class PacketStatus { 77 Success = 0, 78 EndOfFile, 79 JSONMalformed, 80 JSONNotObject 81 }; 82 83 struct Variables { 84 /// Variable_reference start index of permanent expandable variable. 85 static constexpr int64_t PermanentVariableStartIndex = (1ll << 32); 86 87 lldb::SBValueList locals; 88 lldb::SBValueList globals; 89 lldb::SBValueList registers; 90 91 int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX}; 92 int64_t next_permanent_var_ref{PermanentVariableStartIndex}; 93 94 /// Expandable variables that are alive in this stop state. 95 /// Will be cleared when debuggee resumes. 96 llvm::DenseMap<int64_t, lldb::SBValue> expandable_variables; 97 /// Expandable variables that persist across entire debug session. 98 /// These are the variables evaluated from debug console REPL. 99 llvm::DenseMap<int64_t, lldb::SBValue> expandable_permanent_variables; 100 101 /// Check if \p var_ref points to a variable that should persist for the 102 /// entire duration of the debug session, e.g. repl expandable variables 103 static bool IsPermanentVariableReference(int64_t var_ref); 104 105 /// \return a new variableReference. 106 /// Specify is_permanent as true for variable that should persist entire 107 /// debug session. 108 int64_t GetNewVariableRefence(bool is_permanent); 109 110 /// \return the expandable variable corresponding with variableReference 111 /// value of \p value. 112 /// If \p var_ref is invalid an empty SBValue is returned. 113 lldb::SBValue GetVariable(int64_t var_ref) const; 114 115 /// Insert a new \p variable. 116 /// \return variableReference assigned to this expandable variable. 117 int64_t InsertExpandableVariable(lldb::SBValue variable, bool is_permanent); 118 119 /// Clear all scope variables and non-permanent expandable variables. 120 void Clear(); 121 }; 122 123 struct VSCode { 124 std::string debug_adaptor_path; 125 InputStream input; 126 OutputStream output; 127 lldb::SBDebugger debugger; 128 lldb::SBTarget target; 129 Variables variables; 130 lldb::SBBroadcaster broadcaster; 131 std::thread event_thread; 132 std::thread progress_event_thread; 133 std::unique_ptr<std::ofstream> log; 134 llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref; 135 llvm::DenseMap<int64_t, SourceReference> source_map; 136 llvm::StringMap<SourceBreakpointMap> source_breakpoints; 137 FunctionBreakpointMap function_breakpoints; 138 std::vector<ExceptionBreakpoint> exception_breakpoints; 139 std::vector<std::string> init_commands; 140 std::vector<std::string> pre_run_commands; 141 std::vector<std::string> exit_commands; 142 std::vector<std::string> stop_commands; 143 std::vector<std::string> terminate_commands; 144 lldb::tid_t focus_tid; 145 bool sent_terminated_event; 146 bool stop_at_entry; 147 bool is_attach; 148 bool configuration_done_sent; 149 uint32_t reverse_request_seq; 150 std::map<std::string, RequestCallback> request_handlers; 151 bool waiting_for_run_in_terminal; 152 ProgressEventReporter progress_event_reporter; 153 // Keep track of the last stop thread index IDs as threads won't go away 154 // unless we send a "thread" event to indicate the thread exited. 155 llvm::DenseSet<lldb::tid_t> thread_ids; 156 VSCode(); 157 ~VSCode(); 158 VSCode(const VSCode &rhs) = delete; 159 void operator=(const VSCode &rhs) = delete; 160 int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const; 161 ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); 162 ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); 163 164 // Serialize the JSON value into a string and send the JSON packet to 165 // the "out" stream. 166 void SendJSON(const llvm::json::Value &json); 167 168 std::string ReadJSON(); 169 170 void SendOutput(OutputType o, const llvm::StringRef output); 171 172 void SendProgressEvent(uint64_t progress_id, const char *message, 173 uint64_t completed, uint64_t total); 174 175 void __attribute__((format(printf, 3, 4))) 176 SendFormattedOutput(OutputType o, const char *format, ...); 177 178 static int64_t GetNextSourceReference(); 179 180 ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread); 181 182 lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); 183 184 lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); 185 186 llvm::json::Value CreateTopLevelScopes(); 187 188 void RunLLDBCommands(llvm::StringRef prefix, 189 const std::vector<std::string> &commands); 190 191 void RunInitCommands(); 192 void RunPreRunCommands(); 193 void RunStopCommands(); 194 void RunExitCommands(); 195 void RunTerminateCommands(); 196 197 /// Create a new SBTarget object from the given request arguments. 198 /// \param[in] arguments 199 /// Launch configuration arguments. 200 /// 201 /// \param[out] error 202 /// An SBError object that will contain an error description if 203 /// function failed to create the target. 204 /// 205 /// \return 206 /// An SBTarget object. 207 lldb::SBTarget CreateTargetFromArguments(const llvm::json::Object &arguments, 208 lldb::SBError &error); 209 210 /// Set given target object as a current target for lldb-vscode and start 211 /// listeing for its breakpoint events. 212 void SetTarget(const lldb::SBTarget target); 213 214 const std::map<std::string, RequestCallback> &GetRequestHandlers(); 215 216 PacketStatus GetNextObject(llvm::json::Object &object); 217 bool HandleObject(const llvm::json::Object &object); 218 219 /// Send a Debug Adapter Protocol reverse request to the IDE 220 /// 221 /// \param[in] request 222 /// The payload of the request to send. 223 /// 224 /// \param[out] response 225 /// The response of the IDE. It might be undefined if there was an error. 226 /// 227 /// \return 228 /// A \a PacketStatus object indicating the sucess or failure of the 229 /// request. 230 PacketStatus SendReverseRequest(llvm::json::Object request, 231 llvm::json::Object &response); 232 233 /// Registers a callback handler for a Debug Adapter Protocol request 234 /// 235 /// \param[in] request 236 /// The name of the request following the Debug Adapter Protocol 237 /// specification. 238 /// 239 /// \param[in] callback 240 /// The callback to execute when the given request is triggered by the 241 /// IDE. 242 void RegisterRequestCallback(std::string request, RequestCallback callback); 243 244 /// Debuggee will continue from stopped state. WillContinueVSCode245 void WillContinue() { variables.Clear(); } 246 247 /// Poll the process to wait for it to reach the eStateStopped state. 248 /// 249 /// Wait for the process hit a stopped state. When running a launch with 250 /// "launchCommands", or attach with "attachCommands", the calls might take 251 /// some time to stop at the entry point since the command is asynchronous. We 252 /// need to sync up with the process and make sure it is stopped before we 253 /// proceed to do anything else as we will soon be asked to set breakpoints 254 /// and other things that require the process to be stopped. We must use 255 /// polling because "attachCommands" or "launchCommands" may or may not send 256 /// process state change events depending on if the user modifies the async 257 /// setting in the debugger. Since both "attachCommands" and "launchCommands" 258 /// could end up using any combination of LLDB commands, we must ensure we can 259 /// also catch when the process stops, so we must poll the process to make 260 /// sure we handle all cases. 261 /// 262 /// \param[in] seconds 263 /// The number of seconds to poll the process to wait until it is stopped. 264 /// 265 /// \return Error if waiting for the process fails, no error if succeeds. 266 lldb::SBError WaitForProcessToStop(uint32_t seconds); 267 268 private: 269 // Send the JSON in "json_str" to the "out" stream. Correctly send the 270 // "Content-Length:" field followed by the length, followed by the raw 271 // JSON bytes. 272 void SendJSON(const std::string &json_str); 273 }; 274 275 extern VSCode g_vsc; 276 277 } // namespace lldb_vscode 278 279 #endif 280