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 "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX |
13 | |
14 | #include <atomic> |
15 | #include <condition_variable> |
16 | #include <cstdio> |
17 | #include <future> |
18 | #include <iosfwd> |
19 | #include <map> |
20 | #include <optional> |
21 | #include <set> |
22 | #include <thread> |
23 | |
24 | #include "llvm/ADT/DenseMap.h" |
25 | #include "llvm/ADT/DenseSet.h" |
26 | #include "llvm/ADT/StringMap.h" |
27 | #include "llvm/ADT/StringRef.h" |
28 | #include "llvm/Support/JSON.h" |
29 | #include "llvm/Support/raw_ostream.h" |
30 | |
31 | #include "lldb/API/SBAttachInfo.h" |
32 | #include "lldb/API/SBBreakpoint.h" |
33 | #include "lldb/API/SBBreakpointLocation.h" |
34 | #include "lldb/API/SBCommandInterpreter.h" |
35 | #include "lldb/API/SBCommandReturnObject.h" |
36 | #include "lldb/API/SBCommunication.h" |
37 | #include "lldb/API/SBDebugger.h" |
38 | #include "lldb/API/SBEvent.h" |
39 | #include "lldb/API/SBFormat.h" |
40 | #include "lldb/API/SBHostOS.h" |
41 | #include "lldb/API/SBInstruction.h" |
42 | #include "lldb/API/SBInstructionList.h" |
43 | #include "lldb/API/SBLanguageRuntime.h" |
44 | #include "lldb/API/SBLaunchInfo.h" |
45 | #include "lldb/API/SBLineEntry.h" |
46 | #include "lldb/API/SBListener.h" |
47 | #include "lldb/API/SBProcess.h" |
48 | #include "lldb/API/SBStream.h" |
49 | #include "lldb/API/SBStringList.h" |
50 | #include "lldb/API/SBTarget.h" |
51 | #include "lldb/API/SBThread.h" |
52 | |
53 | #include "ExceptionBreakpoint.h" |
54 | #include "FunctionBreakpoint.h" |
55 | #include "IOStream.h" |
56 | #include "ProgressEvent.h" |
57 | #include "RunInTerminal.h" |
58 | #include "SourceBreakpoint.h" |
59 | |
60 | #define VARREF_LOCALS (int64_t)1 |
61 | #define VARREF_GLOBALS (int64_t)2 |
62 | #define VARREF_REGS (int64_t)3 |
63 | #define VARREF_FIRST_VAR_IDX (int64_t)4 |
64 | #define NO_TYPENAME "<no-type>" |
65 | |
66 | namespace lldb_dap { |
67 | |
68 | typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap; |
69 | typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; |
70 | enum class OutputType { Console, Stdout, Stderr, Telemetry }; |
71 | |
72 | enum DAPBroadcasterBits { |
73 | eBroadcastBitStopEventThread = 1u << 0, |
74 | eBroadcastBitStopProgressThread = 1u << 1 |
75 | }; |
76 | |
77 | typedef void (*RequestCallback)(const llvm::json::Object &command); |
78 | typedef void (*ResponseCallback)(llvm::Expected<llvm::json::Value> value); |
79 | |
80 | enum class PacketStatus { |
81 | Success = 0, |
82 | EndOfFile, |
83 | JSONMalformed, |
84 | JSONNotObject |
85 | }; |
86 | |
87 | enum class ReplMode { Variable = 0, Command, Auto }; |
88 | |
89 | /// The detected context of an expression based off the current repl mode. |
90 | enum class ExpressionContext { |
91 | Variable = 0, |
92 | Command, |
93 | }; |
94 | |
95 | struct Variables { |
96 | /// Variable_reference start index of permanent expandable variable. |
97 | static constexpr int64_t PermanentVariableStartIndex = (1ll << 32); |
98 | |
99 | lldb::SBValueList locals; |
100 | lldb::SBValueList globals; |
101 | lldb::SBValueList registers; |
102 | |
103 | int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX}; |
104 | int64_t next_permanent_var_ref{PermanentVariableStartIndex}; |
105 | |
106 | /// Expandable variables that are alive in this stop state. |
107 | /// Will be cleared when debuggee resumes. |
108 | llvm::DenseMap<int64_t, lldb::SBValue> expandable_variables; |
109 | /// Expandable variables that persist across entire debug session. |
110 | /// These are the variables evaluated from debug console REPL. |
111 | llvm::DenseMap<int64_t, lldb::SBValue> expandable_permanent_variables; |
112 | |
113 | /// Check if \p var_ref points to a variable that should persist for the |
114 | /// entire duration of the debug session, e.g. repl expandable variables |
115 | static bool IsPermanentVariableReference(int64_t var_ref); |
116 | |
117 | /// \return a new variableReference. |
118 | /// Specify is_permanent as true for variable that should persist entire |
119 | /// debug session. |
120 | int64_t GetNewVariableReference(bool is_permanent); |
121 | |
122 | /// \return the expandable variable corresponding with variableReference |
123 | /// value of \p value. |
124 | /// If \p var_ref is invalid an empty SBValue is returned. |
125 | lldb::SBValue GetVariable(int64_t var_ref) const; |
126 | |
127 | /// Insert a new \p variable. |
128 | /// \return variableReference assigned to this expandable variable. |
129 | int64_t InsertExpandableVariable(lldb::SBValue variable, bool is_permanent); |
130 | |
131 | /// Clear all scope variables and non-permanent expandable variables. |
132 | void Clear(); |
133 | }; |
134 | |
135 | struct StartDebuggingRequestHandler : public lldb::SBCommandPluginInterface { |
136 | bool DoExecute(lldb::SBDebugger debugger, char **command, |
137 | lldb::SBCommandReturnObject &result) override; |
138 | }; |
139 | |
140 | struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface { |
141 | bool DoExecute(lldb::SBDebugger debugger, char **command, |
142 | lldb::SBCommandReturnObject &result) override; |
143 | }; |
144 | |
145 | struct DAP { |
146 | std::string debug_adaptor_path; |
147 | InputStream input; |
148 | OutputStream output; |
149 | lldb::SBDebugger debugger; |
150 | lldb::SBTarget target; |
151 | Variables variables; |
152 | lldb::SBBroadcaster broadcaster; |
153 | std::thread event_thread; |
154 | std::thread progress_event_thread; |
155 | std::unique_ptr<std::ofstream> log; |
156 | llvm::StringMap<SourceBreakpointMap> source_breakpoints; |
157 | FunctionBreakpointMap function_breakpoints; |
158 | std::vector<ExceptionBreakpoint> exception_breakpoints; |
159 | std::vector<std::string> init_commands; |
160 | std::vector<std::string> pre_run_commands; |
161 | std::vector<std::string> post_run_commands; |
162 | std::vector<std::string> exit_commands; |
163 | std::vector<std::string> stop_commands; |
164 | std::vector<std::string> terminate_commands; |
165 | // A copy of the last LaunchRequest or AttachRequest so we can reuse its |
166 | // arguments if we get a RestartRequest. |
167 | std::optional<llvm::json::Object> last_launch_or_attach_request; |
168 | lldb::tid_t focus_tid; |
169 | std::atomic<bool> sent_terminated_event; |
170 | bool stop_at_entry; |
171 | bool is_attach; |
172 | bool enable_auto_variable_summaries; |
173 | bool enable_synthetic_child_debugging; |
174 | // The process event thread normally responds to process exited events by |
175 | // shutting down the entire adapter. When we're restarting, we keep the id of |
176 | // the old process here so we can detect this case and keep running. |
177 | lldb::pid_t restarting_process_id; |
178 | bool configuration_done_sent; |
179 | std::map<std::string, RequestCallback> request_handlers; |
180 | bool waiting_for_run_in_terminal; |
181 | ProgressEventReporter progress_event_reporter; |
182 | // Keep track of the last stop thread index IDs as threads won't go away |
183 | // unless we send a "thread" event to indicate the thread exited. |
184 | llvm::DenseSet<lldb::tid_t> thread_ids; |
185 | uint32_t reverse_request_seq; |
186 | std::mutex call_mutex; |
187 | std::map<int /* request_seq */, ResponseCallback /* reply handler */> |
188 | inflight_reverse_requests; |
189 | StartDebuggingRequestHandler start_debugging_request_handler; |
190 | ReplModeRequestHandler repl_mode_request_handler; |
191 | ReplMode repl_mode; |
192 | std::string command_escape_prefix = "`" ; |
193 | lldb::SBFormat frame_format; |
194 | lldb::SBFormat thread_format; |
195 | |
196 | DAP(); |
197 | ~DAP(); |
198 | DAP(const DAP &rhs) = delete; |
199 | void operator=(const DAP &rhs) = delete; |
200 | ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); |
201 | ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); |
202 | |
203 | // Serialize the JSON value into a string and send the JSON packet to |
204 | // the "out" stream. |
205 | void SendJSON(const llvm::json::Value &json); |
206 | |
207 | std::string ReadJSON(); |
208 | |
209 | void SendOutput(OutputType o, const llvm::StringRef output); |
210 | |
211 | void SendProgressEvent(uint64_t progress_id, const char *message, |
212 | uint64_t completed, uint64_t total); |
213 | |
214 | void __attribute__((format(printf, 3, 4))) |
215 | SendFormattedOutput(OutputType o, const char *format, ...); |
216 | |
217 | static int64_t GetNextSourceReference(); |
218 | |
219 | ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread); |
220 | |
221 | lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); |
222 | |
223 | lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); |
224 | |
225 | llvm::json::Value CreateTopLevelScopes(); |
226 | |
227 | /// \return |
228 | /// Attempt to determine if an expression is a variable expression or |
229 | /// lldb command using a hueristic based on the first term of the |
230 | /// expression. |
231 | ExpressionContext DetectExpressionContext(lldb::SBFrame frame, |
232 | std::string &expression); |
233 | |
234 | /// \return |
235 | /// \b false if a fatal error was found while executing these commands, |
236 | /// according to the rules of \a LLDBUtils::RunLLDBCommands. |
237 | bool RunLLDBCommands(llvm::StringRef prefix, |
238 | llvm::ArrayRef<std::string> commands); |
239 | |
240 | llvm::Error RunAttachCommands(llvm::ArrayRef<std::string> attach_commands); |
241 | llvm::Error RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands); |
242 | llvm::Error RunInitCommands(); |
243 | llvm::Error RunPreRunCommands(); |
244 | void RunPostRunCommands(); |
245 | void RunStopCommands(); |
246 | void RunExitCommands(); |
247 | void RunTerminateCommands(); |
248 | |
249 | /// Create a new SBTarget object from the given request arguments. |
250 | /// \param[in] arguments |
251 | /// Launch configuration arguments. |
252 | /// |
253 | /// \param[out] error |
254 | /// An SBError object that will contain an error description if |
255 | /// function failed to create the target. |
256 | /// |
257 | /// \return |
258 | /// An SBTarget object. |
259 | lldb::SBTarget CreateTargetFromArguments(const llvm::json::Object &arguments, |
260 | lldb::SBError &error); |
261 | |
262 | /// Set given target object as a current target for lldb-dap and start |
263 | /// listeing for its breakpoint events. |
264 | void SetTarget(const lldb::SBTarget target); |
265 | |
266 | const std::map<std::string, RequestCallback> &GetRequestHandlers(); |
267 | |
268 | PacketStatus GetNextObject(llvm::json::Object &object); |
269 | bool HandleObject(const llvm::json::Object &object); |
270 | |
271 | llvm::Error Loop(); |
272 | |
273 | /// Send a Debug Adapter Protocol reverse request to the IDE. |
274 | /// |
275 | /// \param[in] command |
276 | /// The reverse request command. |
277 | /// |
278 | /// \param[in] arguments |
279 | /// The reverse request arguements. |
280 | /// |
281 | /// \param[in] callback |
282 | /// A callback to execute when the response arrives. |
283 | void SendReverseRequest(llvm::StringRef command, llvm::json::Value arguments, |
284 | ResponseCallback callback); |
285 | |
286 | /// Registers a callback handler for a Debug Adapter Protocol request |
287 | /// |
288 | /// \param[in] request |
289 | /// The name of the request following the Debug Adapter Protocol |
290 | /// specification. |
291 | /// |
292 | /// \param[in] callback |
293 | /// The callback to execute when the given request is triggered by the |
294 | /// IDE. |
295 | void RegisterRequestCallback(std::string request, RequestCallback callback); |
296 | |
297 | /// Debuggee will continue from stopped state. |
298 | void WillContinue() { variables.Clear(); } |
299 | |
300 | /// Poll the process to wait for it to reach the eStateStopped state. |
301 | /// |
302 | /// Wait for the process hit a stopped state. When running a launch with |
303 | /// "launchCommands", or attach with "attachCommands", the calls might take |
304 | /// some time to stop at the entry point since the command is asynchronous. We |
305 | /// need to sync up with the process and make sure it is stopped before we |
306 | /// proceed to do anything else as we will soon be asked to set breakpoints |
307 | /// and other things that require the process to be stopped. We must use |
308 | /// polling because "attachCommands" or "launchCommands" may or may not send |
309 | /// process state change events depending on if the user modifies the async |
310 | /// setting in the debugger. Since both "attachCommands" and "launchCommands" |
311 | /// could end up using any combination of LLDB commands, we must ensure we can |
312 | /// also catch when the process stops, so we must poll the process to make |
313 | /// sure we handle all cases. |
314 | /// |
315 | /// \param[in] seconds |
316 | /// The number of seconds to poll the process to wait until it is stopped. |
317 | /// |
318 | /// \return Error if waiting for the process fails, no error if succeeds. |
319 | lldb::SBError WaitForProcessToStop(uint32_t seconds); |
320 | |
321 | void SetFrameFormat(llvm::StringRef format); |
322 | |
323 | void SetThreadFormat(llvm::StringRef format); |
324 | |
325 | private: |
326 | // Send the JSON in "json_str" to the "out" stream. Correctly send the |
327 | // "Content-Length:" field followed by the length, followed by the raw |
328 | // JSON bytes. |
329 | void SendJSON(const std::string &json_str); |
330 | }; |
331 | |
332 | extern DAP g_dap; |
333 | |
334 | } // namespace lldb_dap |
335 | |
336 | #endif |
337 | |