| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_ |
| #define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_ |
| |
| #include <map> |
| #include <memory> |
| #include "src/debug/wasm/gdb-server/gdb-server-thread.h" |
| #include "src/debug/wasm/gdb-server/wasm-module-debug.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| namespace gdb_server { |
| |
| class TaskRunner; |
| |
| // class GdbServer acts as a manager for the GDB-remote stub. It is instantiated |
| // as soon as the first Wasm module is loaded in the Wasm engine and spawns a |
| // separate thread to accept connections and exchange messages with a debugger. |
| // It will contain the logic to serve debugger queries and access the state of |
| // the Wasm engine. |
| class GdbServer { |
| public: |
| // Factory method: creates and returns a GdbServer. Spawns a "GDB-remote" |
| // thread that will be used to communicate with the debugger. |
| // May return null on failure. |
| // This should be called once, the first time a Wasm module is loaded in the |
| // Wasm engine. |
| static std::unique_ptr<GdbServer> Create(); |
| |
| // Stops the "GDB-remote" thread and waits for it to complete. This should be |
| // called once, when the Wasm engine shuts down. |
| ~GdbServer(); |
| |
| // Queries the set of the Wasm modules currently loaded. Each module is |
| // identified by a unique integer module id. |
| struct WasmModuleInfo { |
| uint32_t module_id; |
| std::string module_name; |
| }; |
| std::vector<WasmModuleInfo> GetLoadedModules(); |
| |
| // Queries the value of the {index} global value in the Wasm module identified |
| // by {frame_index}. |
| // |
| bool GetWasmGlobal(uint32_t frame_index, uint32_t index, uint8_t* buffer, |
| uint32_t buffer_size, uint32_t* size); |
| |
| // Queries the value of the {index} local value in the {frame_index}th stack |
| // frame in the Wasm module identified by {frame_index}. |
| // |
| bool GetWasmLocal(uint32_t frame_index, uint32_t index, uint8_t* buffer, |
| uint32_t buffer_size, uint32_t* size); |
| |
| // Queries the value of the {index} value in the operand stack. |
| // |
| bool GetWasmStackValue(uint32_t frame_index, uint32_t index, uint8_t* buffer, |
| uint32_t buffer_size, uint32_t* size); |
| |
| // Reads {size} bytes, starting from {offset}, from the Memory instance |
| // associated to the Wasm module identified by {frame_index}. |
| // Returns the number of bytes copied to {buffer}, or 0 is case of error. |
| // Note: only one Memory for Module is currently supported. |
| // |
| uint32_t GetWasmMemory(uint32_t frame_index, uint32_t offset, uint8_t* buffer, |
| uint32_t size); |
| |
| // Reads {size} bytes, starting from the low dword of {address}, from the Code |
| // space of th Wasm module identified by high dword of {address}. |
| // Returns the number of bytes copied to {buffer}, or 0 is case of error. |
| uint32_t GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer, |
| uint32_t size); |
| |
| // Inserts a breakpoint at the offset {offset} of the Wasm module identified |
| // by {wasm_module_id}. |
| // Returns true if the breakpoint was successfully added. |
| bool AddBreakpoint(uint32_t wasm_module_id, uint32_t offset); |
| |
| // Removes a breakpoint at the offset {offset} of the Wasm module identified |
| // by {wasm_module_id}. |
| // Returns true if the breakpoint was successfully removed. |
| bool RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset); |
| |
| // Returns the current call stack as a vector of program counters. |
| std::vector<wasm_addr_t> GetWasmCallStack() const; |
| |
| // Manage the set of Isolates for this GdbServer. |
| void AddIsolate(Isolate* isolate); |
| void RemoveIsolate(Isolate* isolate); |
| |
| // Requests that the thread suspend execution at the next Wasm instruction. |
| void Suspend(); |
| |
| // Handle stepping in wasm functions via the wasm interpreter. |
| void PrepareStep(); |
| |
| // Called when the target debuggee can resume execution (for example after |
| // having been suspended on a breakpoint). Terminates the task runner leaving |
| // all pending tasks in the queue. |
| void QuitMessageLoopOnPause(); |
| |
| private: |
| GdbServer(); |
| |
| // When the target debuggee is suspended for a breakpoint or exception, blocks |
| // the main (isolate) thread and enters in a message loop. Here it waits on a |
| // queue of Task objects that are posted by the GDB-stub thread and that |
| // represent queries received from the debugger via the GDB-remote protocol. |
| void RunMessageLoopOnPause(); |
| |
| // Post a task to run a callback in the isolate thread. |
| template <typename Callback> |
| auto RunSyncTask(Callback&& callback) const; |
| |
| void AddWasmModule(uint32_t module_id, Local<debug::WasmScript> wasm_script); |
| |
| // Given a Wasm module id, retrieves the corresponding debugging WasmScript |
| // object. |
| bool GetModuleDebugHandler(uint32_t module_id, |
| WasmModuleDebug** wasm_module_debug); |
| |
| // Returns the debugging target. |
| Target& GetTarget() const; |
| |
| // Class DebugDelegate implements the debug::DebugDelegate interface to |
| // receive notifications when debug events happen in a given isolate, like a |
| // script being loaded, a breakpoint being hit, an exception being thrown. |
| class DebugDelegate : public debug::DebugDelegate { |
| public: |
| DebugDelegate(Isolate* isolate, GdbServer* gdb_server); |
| ~DebugDelegate(); |
| |
| // debug::DebugDelegate |
| void ScriptCompiled(Local<debug::Script> script, bool is_live_edited, |
| bool has_compile_error) override; |
| void BreakProgramRequested(Local<v8::Context> paused_context, |
| const std::vector<debug::BreakpointId>& |
| inspector_break_points_hit) override; |
| void ExceptionThrown(Local<v8::Context> paused_context, |
| Local<Value> exception, Local<Value> promise, |
| bool is_uncaught, |
| debug::ExceptionType exception_type) override; |
| bool IsFunctionBlackboxed(Local<debug::Script> script, |
| const debug::Location& start, |
| const debug::Location& end) override; |
| |
| private: |
| // Calculates module_id as: |
| // +--------------------+------------------- + |
| // | DebugDelegate::id_ | Script::Id() | |
| // +--------------------+------------------- + |
| // <----- 16 bit -----> <----- 16 bit -----> |
| uint32_t GetModuleId(uint32_t script_id) const { |
| DCHECK_LT(script_id, 0x10000); |
| DCHECK_LT(id_, 0x10000); |
| return id_ << 16 | script_id; |
| } |
| |
| Isolate* isolate_; |
| uint32_t id_; |
| GdbServer* gdb_server_; |
| |
| static std::atomic<uint32_t> id_s; |
| }; |
| |
| // The GDB-stub thread where all the communication with the debugger happens. |
| std::unique_ptr<GdbServerThread> thread_; |
| |
| // Used to transform the queries that arrive in the GDB-stub thread into |
| // tasks executed in the main (isolate) thread. |
| std::unique_ptr<TaskRunner> task_runner_; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Always accessed in the isolate thread. |
| |
| // Set of breakpoints currently defines in Wasm code. |
| typedef std::map<uint64_t, int> BreakpointsMap; |
| BreakpointsMap breakpoints_; |
| |
| typedef std::map<uint32_t, WasmModuleDebug> ScriptsMap; |
| ScriptsMap scripts_; |
| |
| typedef std::map<Isolate*, std::unique_ptr<DebugDelegate>> |
| IsolateDebugDelegateMap; |
| IsolateDebugDelegateMap isolate_delegates_; |
| |
| // End of fields always accessed in the isolate thread. |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| DISALLOW_COPY_AND_ASSIGN(GdbServer); |
| }; |
| |
| } // namespace gdb_server |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_ |