xref: /openbsd/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h (revision f6aab3d8)
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