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