11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#ifndef V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
61cb0ef41Sopenharmony_ci#define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci#include <map>
91cb0ef41Sopenharmony_ci#include <memory>
101cb0ef41Sopenharmony_ci#include "src/debug/wasm/gdb-server/gdb-server-thread.h"
111cb0ef41Sopenharmony_ci#include "src/debug/wasm/gdb-server/wasm-module-debug.h"
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_cinamespace v8 {
141cb0ef41Sopenharmony_cinamespace internal {
151cb0ef41Sopenharmony_cinamespace wasm {
161cb0ef41Sopenharmony_cinamespace gdb_server {
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciclass TaskRunner;
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci// class GdbServer acts as a manager for the GDB-remote stub. It is instantiated
211cb0ef41Sopenharmony_ci// as soon as the first Wasm module is loaded in the Wasm engine and spawns a
221cb0ef41Sopenharmony_ci// separate thread to accept connections and exchange messages with a debugger.
231cb0ef41Sopenharmony_ci// It will contain the logic to serve debugger queries and access the state of
241cb0ef41Sopenharmony_ci// the Wasm engine.
251cb0ef41Sopenharmony_ciclass GdbServer {
261cb0ef41Sopenharmony_ci public:
271cb0ef41Sopenharmony_ci  GdbServer(const GdbServer&) = delete;
281cb0ef41Sopenharmony_ci  GdbServer& operator=(const GdbServer&) = delete;
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci  // Factory method: creates and returns a GdbServer. Spawns a "GDB-remote"
311cb0ef41Sopenharmony_ci  // thread that will be used to communicate with the debugger.
321cb0ef41Sopenharmony_ci  // May return null on failure.
331cb0ef41Sopenharmony_ci  // This should be called once, the first time a Wasm module is loaded in the
341cb0ef41Sopenharmony_ci  // Wasm engine.
351cb0ef41Sopenharmony_ci  static std::unique_ptr<GdbServer> Create();
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci  // Stops the "GDB-remote" thread and waits for it to complete. This should be
381cb0ef41Sopenharmony_ci  // called once, when the Wasm engine shuts down.
391cb0ef41Sopenharmony_ci  ~GdbServer();
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci  // Queries the set of the Wasm modules currently loaded. Each module is
421cb0ef41Sopenharmony_ci  // identified by a unique integer module id.
431cb0ef41Sopenharmony_ci  struct WasmModuleInfo {
441cb0ef41Sopenharmony_ci    uint32_t module_id;
451cb0ef41Sopenharmony_ci    std::string module_name;
461cb0ef41Sopenharmony_ci  };
471cb0ef41Sopenharmony_ci  std::vector<WasmModuleInfo> GetLoadedModules(
481cb0ef41Sopenharmony_ci      bool clear_module_list_changed_flag = false);
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci  bool HasModuleListChanged() const { return has_module_list_changed_; }
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  // Queries the value of the {index} global value in the Wasm module identified
531cb0ef41Sopenharmony_ci  // by {frame_index}.
541cb0ef41Sopenharmony_ci  //
551cb0ef41Sopenharmony_ci  bool GetWasmGlobal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
561cb0ef41Sopenharmony_ci                     uint32_t buffer_size, uint32_t* size);
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci  // Queries the value of the {index} local value in the {frame_index}th stack
591cb0ef41Sopenharmony_ci  // frame in the Wasm module identified by {frame_index}.
601cb0ef41Sopenharmony_ci  //
611cb0ef41Sopenharmony_ci  bool GetWasmLocal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
621cb0ef41Sopenharmony_ci                    uint32_t buffer_size, uint32_t* size);
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci  // Queries the value of the {index} value in the operand stack.
651cb0ef41Sopenharmony_ci  //
661cb0ef41Sopenharmony_ci  bool GetWasmStackValue(uint32_t frame_index, uint32_t index, uint8_t* buffer,
671cb0ef41Sopenharmony_ci                         uint32_t buffer_size, uint32_t* size);
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci  // Reads {size} bytes, starting from {offset}, from the Memory instance
701cb0ef41Sopenharmony_ci  // associated to the Wasm module identified by {module_id}.
711cb0ef41Sopenharmony_ci  // Returns the number of bytes copied to {buffer}, or 0 is case of error.
721cb0ef41Sopenharmony_ci  // Note: only one Memory for Module is currently supported.
731cb0ef41Sopenharmony_ci  //
741cb0ef41Sopenharmony_ci  uint32_t GetWasmMemory(uint32_t module_id, uint32_t offset, uint8_t* buffer,
751cb0ef41Sopenharmony_ci                         uint32_t size);
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  // Reads {size} bytes, starting from {offset}, from the first Data segment
781cb0ef41Sopenharmony_ci  // in the Wasm module identified by {module_id}.
791cb0ef41Sopenharmony_ci  // Returns the number of bytes copied to {buffer}, or 0 is case of error.
801cb0ef41Sopenharmony_ci  // Note: only one Memory for Module is currently supported.
811cb0ef41Sopenharmony_ci  //
821cb0ef41Sopenharmony_ci  uint32_t GetWasmData(uint32_t module_id, uint32_t offset, uint8_t* buffer,
831cb0ef41Sopenharmony_ci                       uint32_t size);
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci  // Reads {size} bytes, starting from the low dword of {address}, from the Code
861cb0ef41Sopenharmony_ci  // space of th Wasm module identified by high dword of {address}.
871cb0ef41Sopenharmony_ci  // Returns the number of bytes copied to {buffer}, or 0 is case of error.
881cb0ef41Sopenharmony_ci  uint32_t GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer,
891cb0ef41Sopenharmony_ci                              uint32_t size);
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ci  // Inserts a breakpoint at the offset {offset} of the Wasm module identified
921cb0ef41Sopenharmony_ci  // by {wasm_module_id}.
931cb0ef41Sopenharmony_ci  // Returns true if the breakpoint was successfully added.
941cb0ef41Sopenharmony_ci  bool AddBreakpoint(uint32_t wasm_module_id, uint32_t offset);
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  // Removes a breakpoint at the offset {offset} of the Wasm module identified
971cb0ef41Sopenharmony_ci  // by {wasm_module_id}.
981cb0ef41Sopenharmony_ci  // Returns true if the breakpoint was successfully removed.
991cb0ef41Sopenharmony_ci  bool RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset);
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci  // Returns the current call stack as a vector of program counters.
1021cb0ef41Sopenharmony_ci  std::vector<wasm_addr_t> GetWasmCallStack() const;
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci  // Manage the set of Isolates for this GdbServer.
1051cb0ef41Sopenharmony_ci  void AddIsolate(Isolate* isolate);
1061cb0ef41Sopenharmony_ci  void RemoveIsolate(Isolate* isolate);
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci  // Requests that the thread suspend execution at the next Wasm instruction.
1091cb0ef41Sopenharmony_ci  void Suspend();
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci  // Handle stepping in wasm functions via the wasm interpreter.
1121cb0ef41Sopenharmony_ci  void PrepareStep();
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci  // Called when the target debuggee can resume execution (for example after
1151cb0ef41Sopenharmony_ci  // having been suspended on a breakpoint). Terminates the task runner leaving
1161cb0ef41Sopenharmony_ci  // all pending tasks in the queue.
1171cb0ef41Sopenharmony_ci  void QuitMessageLoopOnPause();
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci private:
1201cb0ef41Sopenharmony_ci  GdbServer();
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci  // When the target debuggee is suspended for a breakpoint or exception, blocks
1231cb0ef41Sopenharmony_ci  // the main (isolate) thread and enters in a message loop. Here it waits on a
1241cb0ef41Sopenharmony_ci  // queue of Task objects that are posted by the GDB-stub thread and that
1251cb0ef41Sopenharmony_ci  // represent queries received from the debugger via the GDB-remote protocol.
1261cb0ef41Sopenharmony_ci  void RunMessageLoopOnPause();
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  // Post a task to run a callback in the isolate thread.
1291cb0ef41Sopenharmony_ci  template <typename Callback>
1301cb0ef41Sopenharmony_ci  auto RunSyncTask(Callback&& callback) const;
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ci  void AddWasmModule(uint32_t module_id, Local<debug::WasmScript> wasm_script);
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci  // Given a Wasm module id, retrieves the corresponding debugging WasmScript
1351cb0ef41Sopenharmony_ci  // object.
1361cb0ef41Sopenharmony_ci  bool GetModuleDebugHandler(uint32_t module_id,
1371cb0ef41Sopenharmony_ci                             WasmModuleDebug** wasm_module_debug);
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  // Returns the debugging target.
1401cb0ef41Sopenharmony_ci  Target& GetTarget() const;
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci  // Class DebugDelegate implements the debug::DebugDelegate interface to
1431cb0ef41Sopenharmony_ci  // receive notifications when debug events happen in a given isolate, like a
1441cb0ef41Sopenharmony_ci  // script being loaded, a breakpoint being hit, an exception being thrown.
1451cb0ef41Sopenharmony_ci  class DebugDelegate : public debug::DebugDelegate {
1461cb0ef41Sopenharmony_ci   public:
1471cb0ef41Sopenharmony_ci    DebugDelegate(Isolate* isolate, GdbServer* gdb_server);
1481cb0ef41Sopenharmony_ci    ~DebugDelegate();
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci    // debug::DebugDelegate
1511cb0ef41Sopenharmony_ci    void ScriptCompiled(Local<debug::Script> script, bool is_live_edited,
1521cb0ef41Sopenharmony_ci                        bool has_compile_error) override;
1531cb0ef41Sopenharmony_ci    void BreakProgramRequested(
1541cb0ef41Sopenharmony_ci        Local<v8::Context> paused_context,
1551cb0ef41Sopenharmony_ci        const std::vector<debug::BreakpointId>& inspector_break_points_hit,
1561cb0ef41Sopenharmony_ci        v8::debug::BreakReasons break_reasons) override;
1571cb0ef41Sopenharmony_ci    void ExceptionThrown(Local<v8::Context> paused_context,
1581cb0ef41Sopenharmony_ci                         Local<Value> exception, Local<Value> promise,
1591cb0ef41Sopenharmony_ci                         bool is_uncaught,
1601cb0ef41Sopenharmony_ci                         debug::ExceptionType exception_type) override;
1611cb0ef41Sopenharmony_ci    bool IsFunctionBlackboxed(Local<debug::Script> script,
1621cb0ef41Sopenharmony_ci                              const debug::Location& start,
1631cb0ef41Sopenharmony_ci                              const debug::Location& end) override;
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci   private:
1661cb0ef41Sopenharmony_ci    // Calculates module_id as:
1671cb0ef41Sopenharmony_ci    // +--------------------+------------------- +
1681cb0ef41Sopenharmony_ci    // | DebugDelegate::id_ |    Script::Id()    |
1691cb0ef41Sopenharmony_ci    // +--------------------+------------------- +
1701cb0ef41Sopenharmony_ci    //  <----- 16 bit -----> <----- 16 bit ----->
1711cb0ef41Sopenharmony_ci    uint32_t GetModuleId(uint32_t script_id) const {
1721cb0ef41Sopenharmony_ci      DCHECK_LT(script_id, 0x10000);
1731cb0ef41Sopenharmony_ci      DCHECK_LT(id_, 0x10000);
1741cb0ef41Sopenharmony_ci      return id_ << 16 | script_id;
1751cb0ef41Sopenharmony_ci    }
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci    Isolate* isolate_;
1781cb0ef41Sopenharmony_ci    uint32_t id_;
1791cb0ef41Sopenharmony_ci    GdbServer* gdb_server_;
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci    static std::atomic<uint32_t> id_s;
1821cb0ef41Sopenharmony_ci  };
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci  // The GDB-stub thread where all the communication with the debugger happens.
1851cb0ef41Sopenharmony_ci  std::unique_ptr<GdbServerThread> thread_;
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci  // Used to transform the queries that arrive in the GDB-stub thread into
1881cb0ef41Sopenharmony_ci  // tasks executed in the main (isolate) thread.
1891cb0ef41Sopenharmony_ci  std::unique_ptr<TaskRunner> task_runner_;
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci  std::atomic<bool> has_module_list_changed_;
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci  //////////////////////////////////////////////////////////////////////////////
1941cb0ef41Sopenharmony_ci  // Always accessed in the isolate thread.
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci  // Set of breakpoints currently defines in Wasm code.
1971cb0ef41Sopenharmony_ci  typedef std::map<uint64_t, int> BreakpointsMap;
1981cb0ef41Sopenharmony_ci  BreakpointsMap breakpoints_;
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_ci  typedef std::map<uint32_t, WasmModuleDebug> ScriptsMap;
2011cb0ef41Sopenharmony_ci  ScriptsMap scripts_;
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  typedef std::map<Isolate*, std::unique_ptr<DebugDelegate>>
2041cb0ef41Sopenharmony_ci      IsolateDebugDelegateMap;
2051cb0ef41Sopenharmony_ci  IsolateDebugDelegateMap isolate_delegates_;
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci  // End of fields always accessed in the isolate thread.
2081cb0ef41Sopenharmony_ci  //////////////////////////////////////////////////////////////////////////////
2091cb0ef41Sopenharmony_ci};
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci}  // namespace gdb_server
2121cb0ef41Sopenharmony_ci}  // namespace wasm
2131cb0ef41Sopenharmony_ci}  // namespace internal
2141cb0ef41Sopenharmony_ci}  // namespace v8
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci#endif  // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
217