1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
6 #define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
7 
8 #include <map>
9 #include <memory>
10 #include "src/debug/wasm/gdb-server/gdb-server-thread.h"
11 #include "src/debug/wasm/gdb-server/wasm-module-debug.h"
12 
13 namespace v8 {
14 namespace internal {
15 namespace wasm {
16 namespace gdb_server {
17 
18 class TaskRunner;
19 
20 // class GdbServer acts as a manager for the GDB-remote stub. It is instantiated
21 // as soon as the first Wasm module is loaded in the Wasm engine and spawns a
22 // separate thread to accept connections and exchange messages with a debugger.
23 // It will contain the logic to serve debugger queries and access the state of
24 // the Wasm engine.
25 class GdbServer {
26  public:
27   GdbServer(const GdbServer&) = delete;
28   GdbServer& operator=(const GdbServer&) = delete;
29 
30   // Factory method: creates and returns a GdbServer. Spawns a "GDB-remote"
31   // thread that will be used to communicate with the debugger.
32   // May return null on failure.
33   // This should be called once, the first time a Wasm module is loaded in the
34   // Wasm engine.
35   static std::unique_ptr<GdbServer> Create();
36 
37   // Stops the "GDB-remote" thread and waits for it to complete. This should be
38   // called once, when the Wasm engine shuts down.
39   ~GdbServer();
40 
41   // Queries the set of the Wasm modules currently loaded. Each module is
42   // identified by a unique integer module id.
43   struct WasmModuleInfo {
44     uint32_t module_id;
45     std::string module_name;
46   };
47   std::vector<WasmModuleInfo> GetLoadedModules(
48       bool clear_module_list_changed_flag = false);
49 
HasModuleListChanged() const50   bool HasModuleListChanged() const { return has_module_list_changed_; }
51 
52   // Queries the value of the {index} global value in the Wasm module identified
53   // by {frame_index}.
54   //
55   bool GetWasmGlobal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
56                      uint32_t buffer_size, uint32_t* size);
57 
58   // Queries the value of the {index} local value in the {frame_index}th stack
59   // frame in the Wasm module identified by {frame_index}.
60   //
61   bool GetWasmLocal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
62                     uint32_t buffer_size, uint32_t* size);
63 
64   // Queries the value of the {index} value in the operand stack.
65   //
66   bool GetWasmStackValue(uint32_t frame_index, uint32_t index, uint8_t* buffer,
67                          uint32_t buffer_size, uint32_t* size);
68 
69   // Reads {size} bytes, starting from {offset}, from the Memory instance
70   // associated to the Wasm module identified by {module_id}.
71   // Returns the number of bytes copied to {buffer}, or 0 is case of error.
72   // Note: only one Memory for Module is currently supported.
73   //
74   uint32_t GetWasmMemory(uint32_t module_id, uint32_t offset, uint8_t* buffer,
75                          uint32_t size);
76 
77   // Reads {size} bytes, starting from {offset}, from the first Data segment
78   // in the Wasm module identified by {module_id}.
79   // Returns the number of bytes copied to {buffer}, or 0 is case of error.
80   // Note: only one Memory for Module is currently supported.
81   //
82   uint32_t GetWasmData(uint32_t module_id, uint32_t offset, uint8_t* buffer,
83                        uint32_t size);
84 
85   // Reads {size} bytes, starting from the low dword of {address}, from the Code
86   // space of th Wasm module identified by high dword of {address}.
87   // Returns the number of bytes copied to {buffer}, or 0 is case of error.
88   uint32_t GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer,
89                               uint32_t size);
90 
91   // Inserts a breakpoint at the offset {offset} of the Wasm module identified
92   // by {wasm_module_id}.
93   // Returns true if the breakpoint was successfully added.
94   bool AddBreakpoint(uint32_t wasm_module_id, uint32_t offset);
95 
96   // Removes a breakpoint at the offset {offset} of the Wasm module identified
97   // by {wasm_module_id}.
98   // Returns true if the breakpoint was successfully removed.
99   bool RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset);
100 
101   // Returns the current call stack as a vector of program counters.
102   std::vector<wasm_addr_t> GetWasmCallStack() const;
103 
104   // Manage the set of Isolates for this GdbServer.
105   void AddIsolate(Isolate* isolate);
106   void RemoveIsolate(Isolate* isolate);
107 
108   // Requests that the thread suspend execution at the next Wasm instruction.
109   void Suspend();
110 
111   // Handle stepping in wasm functions via the wasm interpreter.
112   void PrepareStep();
113 
114   // Called when the target debuggee can resume execution (for example after
115   // having been suspended on a breakpoint). Terminates the task runner leaving
116   // all pending tasks in the queue.
117   void QuitMessageLoopOnPause();
118 
119  private:
120   GdbServer();
121 
122   // When the target debuggee is suspended for a breakpoint or exception, blocks
123   // the main (isolate) thread and enters in a message loop. Here it waits on a
124   // queue of Task objects that are posted by the GDB-stub thread and that
125   // represent queries received from the debugger via the GDB-remote protocol.
126   void RunMessageLoopOnPause();
127 
128   // Post a task to run a callback in the isolate thread.
129   template <typename Callback>
130   auto RunSyncTask(Callback&& callback) const;
131 
132   void AddWasmModule(uint32_t module_id, Local<debug::WasmScript> wasm_script);
133 
134   // Given a Wasm module id, retrieves the corresponding debugging WasmScript
135   // object.
136   bool GetModuleDebugHandler(uint32_t module_id,
137                              WasmModuleDebug** wasm_module_debug);
138 
139   // Returns the debugging target.
140   Target& GetTarget() const;
141 
142   // Class DebugDelegate implements the debug::DebugDelegate interface to
143   // receive notifications when debug events happen in a given isolate, like a
144   // script being loaded, a breakpoint being hit, an exception being thrown.
145   class DebugDelegate : public debug::DebugDelegate {
146    public:
147     DebugDelegate(Isolate* isolate, GdbServer* gdb_server);
148     ~DebugDelegate();
149 
150     // debug::DebugDelegate
151     void ScriptCompiled(Local<debug::Script> script, bool is_live_edited,
152                         bool has_compile_error) override;
153     void BreakProgramRequested(
154         Local<v8::Context> paused_context,
155         const std::vector<debug::BreakpointId>& inspector_break_points_hit,
156         v8::debug::BreakReasons break_reasons) override;
157     void ExceptionThrown(Local<v8::Context> paused_context,
158                          Local<Value> exception, Local<Value> promise,
159                          bool is_uncaught,
160                          debug::ExceptionType exception_type) override;
161     bool IsFunctionBlackboxed(Local<debug::Script> script,
162                               const debug::Location& start,
163                               const debug::Location& end) override;
164 
165    private:
166     // Calculates module_id as:
167     // +--------------------+------------------- +
168     // | DebugDelegate::id_ |    Script::Id()    |
169     // +--------------------+------------------- +
170     //  <----- 16 bit -----> <----- 16 bit ----->
GetModuleId(uint32_t script_id) const171     uint32_t GetModuleId(uint32_t script_id) const {
172       DCHECK_LT(script_id, 0x10000);
173       DCHECK_LT(id_, 0x10000);
174       return id_ << 16 | script_id;
175     }
176 
177     Isolate* isolate_;
178     uint32_t id_;
179     GdbServer* gdb_server_;
180 
181     static std::atomic<uint32_t> id_s;
182   };
183 
184   // The GDB-stub thread where all the communication with the debugger happens.
185   std::unique_ptr<GdbServerThread> thread_;
186 
187   // Used to transform the queries that arrive in the GDB-stub thread into
188   // tasks executed in the main (isolate) thread.
189   std::unique_ptr<TaskRunner> task_runner_;
190 
191   std::atomic<bool> has_module_list_changed_;
192 
193   //////////////////////////////////////////////////////////////////////////////
194   // Always accessed in the isolate thread.
195 
196   // Set of breakpoints currently defines in Wasm code.
197   typedef std::map<uint64_t, int> BreakpointsMap;
198   BreakpointsMap breakpoints_;
199 
200   typedef std::map<uint32_t, WasmModuleDebug> ScriptsMap;
201   ScriptsMap scripts_;
202 
203   typedef std::map<Isolate*, std::unique_ptr<DebugDelegate>>
204       IsolateDebugDelegateMap;
205   IsolateDebugDelegateMap isolate_delegates_;
206 
207   // End of fields always accessed in the isolate thread.
208   //////////////////////////////////////////////////////////////////////////////
209 };
210 
211 }  // namespace gdb_server
212 }  // namespace wasm
213 }  // namespace internal
214 }  // namespace v8
215 
216 #endif  // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
217