gdb-server.h 7.7 KB
Newer Older
1 2 3 4 5 6 7
// 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_

8
#include <map>
9 10
#include <memory>
#include "src/debug/wasm/gdb-server/gdb-server-thread.h"
11
#include "src/debug/wasm/gdb-server/wasm-module-debug.h"
12 13 14 15 16 17

namespace v8 {
namespace internal {
namespace wasm {
namespace gdb_server {

18 19
class TaskRunner;

20 21 22 23 24 25 26
// 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:
27 28 29 30 31 32
  // 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();
33 34 35 36 37

  // Stops the "GDB-remote" thread and waits for it to complete. This should be
  // called once, when the Wasm engine shuts down.
  ~GdbServer();

38 39 40 41 42 43
  // 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;
  };
44
  std::vector<WasmModuleInfo> GetLoadedModules();
45 46

  // Queries the value of the {index} global value in the Wasm module identified
47 48 49 50
  // by {frame_index}.
  //
  bool GetWasmGlobal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
                     uint32_t buffer_size, uint32_t* size);
51 52

  // Queries the value of the {index} local value in the {frame_index}th stack
53 54 55 56 57 58 59 60 61
  // 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);
62 63

  // Reads {size} bytes, starting from {offset}, from the Memory instance
64
  // associated to the Wasm module identified by {frame_index}.
65 66
  // Returns the number of bytes copied to {buffer}, or 0 is case of error.
  // Note: only one Memory for Module is currently supported.
67 68 69
  //
  uint32_t GetWasmMemory(uint32_t frame_index, uint32_t offset, uint8_t* buffer,
                         uint32_t size);
70

71 72
  // Reads {size} bytes, starting from the low dword of {address}, from the Code
  // space of th Wasm module identified by high dword of {address}.
73 74 75 76 77 78 79
  // 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.
80
  bool AddBreakpoint(uint32_t wasm_module_id, uint32_t offset);
81 82 83 84

  // Removes a breakpoint at the offset {offset} of the Wasm module identified
  // by {wasm_module_id}.
  // Returns true if the breakpoint was successfully removed.
85
  bool RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset);
86 87 88 89

  // Returns the current call stack as a vector of program counters.
  std::vector<wasm_addr_t> GetWasmCallStack() const;

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  // 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();

105
 private:
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  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;
  };
168

169
  // The GDB-stub thread where all the communication with the debugger happens.
170 171
  std::unique_ptr<GdbServerThread> thread_;

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
  // 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.
  //////////////////////////////////////////////////////////////////////////////

193 194 195 196 197 198 199 200 201
  DISALLOW_COPY_AND_ASSIGN(GdbServer);
};

}  // namespace gdb_server
}  // namespace wasm
}  // namespace internal
}  // namespace v8

#endif  // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_