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
13namespace v8 {
14namespace internal {
15namespace wasm {
16namespace gdb_server {
17
18class 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.
25class 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
50  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 ----->
171    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