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