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