xref: /third_party/node/deps/v8/src/wasm/wasm-debug.h (revision 1cb0ef41)
11cb0ef41Sopenharmony_ci// Copyright 2019 the V8 project authors. All rights reserved.  Use of
21cb0ef41Sopenharmony_ci// this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#if !V8_ENABLE_WEBASSEMBLY
61cb0ef41Sopenharmony_ci#error This header should only be included if WebAssembly is enabled.
71cb0ef41Sopenharmony_ci#endif  // !V8_ENABLE_WEBASSEMBLY
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci#ifndef V8_WASM_WASM_DEBUG_H_
101cb0ef41Sopenharmony_ci#define V8_WASM_WASM_DEBUG_H_
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci#include <algorithm>
131cb0ef41Sopenharmony_ci#include <memory>
141cb0ef41Sopenharmony_ci#include <vector>
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci#include "include/v8-internal.h"
171cb0ef41Sopenharmony_ci#include "src/base/iterator.h"
181cb0ef41Sopenharmony_ci#include "src/base/logging.h"
191cb0ef41Sopenharmony_ci#include "src/base/macros.h"
201cb0ef41Sopenharmony_ci#include "src/base/vector.h"
211cb0ef41Sopenharmony_ci#include "src/wasm/value-type.h"
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_cinamespace v8 {
241cb0ef41Sopenharmony_cinamespace internal {
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_citemplate <typename T>
271cb0ef41Sopenharmony_ciclass Handle;
281cb0ef41Sopenharmony_ciclass WasmFrame;
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_cinamespace wasm {
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ciclass DebugInfoImpl;
331cb0ef41Sopenharmony_ciclass IndirectNameMap;
341cb0ef41Sopenharmony_ciclass NativeModule;
351cb0ef41Sopenharmony_ciclass WasmCode;
361cb0ef41Sopenharmony_ciclass WireBytesRef;
371cb0ef41Sopenharmony_ciclass WasmValue;
381cb0ef41Sopenharmony_cistruct WasmFunction;
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci// Side table storing information used to inspect Liftoff frames at runtime.
411cb0ef41Sopenharmony_ci// This table is only created on demand for debugging, so it is not optimized
421cb0ef41Sopenharmony_ci// for memory size.
431cb0ef41Sopenharmony_ciclass DebugSideTable {
441cb0ef41Sopenharmony_ci public:
451cb0ef41Sopenharmony_ci  class Entry {
461cb0ef41Sopenharmony_ci   public:
471cb0ef41Sopenharmony_ci    enum Storage : int8_t { kConstant, kRegister, kStack };
481cb0ef41Sopenharmony_ci    struct Value {
491cb0ef41Sopenharmony_ci      int index;
501cb0ef41Sopenharmony_ci      ValueType type;
511cb0ef41Sopenharmony_ci      Storage storage;
521cb0ef41Sopenharmony_ci      union {
531cb0ef41Sopenharmony_ci        int32_t i32_const;  // if kind == kConstant
541cb0ef41Sopenharmony_ci        int reg_code;       // if kind == kRegister
551cb0ef41Sopenharmony_ci        int stack_offset;   // if kind == kStack
561cb0ef41Sopenharmony_ci      };
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci      bool operator==(const Value& other) const {
591cb0ef41Sopenharmony_ci        if (index != other.index) return false;
601cb0ef41Sopenharmony_ci        if (type != other.type) return false;
611cb0ef41Sopenharmony_ci        if (storage != other.storage) return false;
621cb0ef41Sopenharmony_ci        switch (storage) {
631cb0ef41Sopenharmony_ci          case kConstant:
641cb0ef41Sopenharmony_ci            return i32_const == other.i32_const;
651cb0ef41Sopenharmony_ci          case kRegister:
661cb0ef41Sopenharmony_ci            return reg_code == other.reg_code;
671cb0ef41Sopenharmony_ci          case kStack:
681cb0ef41Sopenharmony_ci            return stack_offset == other.stack_offset;
691cb0ef41Sopenharmony_ci        }
701cb0ef41Sopenharmony_ci      }
711cb0ef41Sopenharmony_ci      bool operator!=(const Value& other) const { return !(*this == other); }
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci      bool is_constant() const { return storage == kConstant; }
741cb0ef41Sopenharmony_ci      bool is_register() const { return storage == kRegister; }
751cb0ef41Sopenharmony_ci    };
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci    Entry(int pc_offset, int stack_height, std::vector<Value> changed_values)
781cb0ef41Sopenharmony_ci        : pc_offset_(pc_offset),
791cb0ef41Sopenharmony_ci          stack_height_(stack_height),
801cb0ef41Sopenharmony_ci          changed_values_(std::move(changed_values)) {}
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci    // Constructor for map lookups (only initializes the {pc_offset_}).
831cb0ef41Sopenharmony_ci    explicit Entry(int pc_offset) : pc_offset_(pc_offset) {}
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci    int pc_offset() const { return pc_offset_; }
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    // Stack height, including locals.
881cb0ef41Sopenharmony_ci    int stack_height() const { return stack_height_; }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci    base::Vector<const Value> changed_values() const {
911cb0ef41Sopenharmony_ci      return base::VectorOf(changed_values_);
921cb0ef41Sopenharmony_ci    }
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci    const Value* FindChangedValue(int stack_index) const {
951cb0ef41Sopenharmony_ci      DCHECK_GT(stack_height_, stack_index);
961cb0ef41Sopenharmony_ci      auto it = std::lower_bound(
971cb0ef41Sopenharmony_ci          changed_values_.begin(), changed_values_.end(), stack_index,
981cb0ef41Sopenharmony_ci          [](const Value& changed_value, int stack_index) {
991cb0ef41Sopenharmony_ci            return changed_value.index < stack_index;
1001cb0ef41Sopenharmony_ci          });
1011cb0ef41Sopenharmony_ci      return it != changed_values_.end() && it->index == stack_index ? &*it
1021cb0ef41Sopenharmony_ci                                                                     : nullptr;
1031cb0ef41Sopenharmony_ci    }
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci    void Print(std::ostream&) const;
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci   private:
1081cb0ef41Sopenharmony_ci    int pc_offset_;
1091cb0ef41Sopenharmony_ci    int stack_height_;
1101cb0ef41Sopenharmony_ci    // Only store differences from the last entry, to keep the table small.
1111cb0ef41Sopenharmony_ci    std::vector<Value> changed_values_;
1121cb0ef41Sopenharmony_ci  };
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci  // Technically it would be fine to copy this class, but there should not be a
1151cb0ef41Sopenharmony_ci  // reason to do so, hence mark it move only.
1161cb0ef41Sopenharmony_ci  MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(DebugSideTable);
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci  explicit DebugSideTable(int num_locals, std::vector<Entry> entries)
1191cb0ef41Sopenharmony_ci      : num_locals_(num_locals), entries_(std::move(entries)) {
1201cb0ef41Sopenharmony_ci    DCHECK(
1211cb0ef41Sopenharmony_ci        std::is_sorted(entries_.begin(), entries_.end(), EntryPositionLess{}));
1221cb0ef41Sopenharmony_ci  }
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ci  const Entry* GetEntry(int pc_offset) const {
1251cb0ef41Sopenharmony_ci    auto it = std::lower_bound(entries_.begin(), entries_.end(),
1261cb0ef41Sopenharmony_ci                               Entry{pc_offset}, EntryPositionLess{});
1271cb0ef41Sopenharmony_ci    if (it == entries_.end() || it->pc_offset() != pc_offset) return nullptr;
1281cb0ef41Sopenharmony_ci    DCHECK_LE(num_locals_, it->stack_height());
1291cb0ef41Sopenharmony_ci    return &*it;
1301cb0ef41Sopenharmony_ci  }
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ci  const Entry::Value* FindValue(const Entry* entry, int stack_index) const {
1331cb0ef41Sopenharmony_ci    while (true) {
1341cb0ef41Sopenharmony_ci      if (auto* value = entry->FindChangedValue(stack_index)) {
1351cb0ef41Sopenharmony_ci        // Check that the table was correctly minimized: If the previous stack
1361cb0ef41Sopenharmony_ci        // also had an entry for {stack_index}, it must be different.
1371cb0ef41Sopenharmony_ci        DCHECK(entry == &entries_.front() ||
1381cb0ef41Sopenharmony_ci               (entry - 1)->stack_height() <= stack_index ||
1391cb0ef41Sopenharmony_ci               *FindValue(entry - 1, stack_index) != *value);
1401cb0ef41Sopenharmony_ci        return value;
1411cb0ef41Sopenharmony_ci      }
1421cb0ef41Sopenharmony_ci      DCHECK_NE(&entries_.front(), entry);
1431cb0ef41Sopenharmony_ci      --entry;
1441cb0ef41Sopenharmony_ci    }
1451cb0ef41Sopenharmony_ci  }
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci  auto entries() const {
1481cb0ef41Sopenharmony_ci    return base::make_iterator_range(entries_.begin(), entries_.end());
1491cb0ef41Sopenharmony_ci  }
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci  int num_locals() const { return num_locals_; }
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  void Print(std::ostream&) const;
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci private:
1561cb0ef41Sopenharmony_ci  struct EntryPositionLess {
1571cb0ef41Sopenharmony_ci    bool operator()(const Entry& a, const Entry& b) const {
1581cb0ef41Sopenharmony_ci      return a.pc_offset() < b.pc_offset();
1591cb0ef41Sopenharmony_ci    }
1601cb0ef41Sopenharmony_ci  };
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci  int num_locals_;
1631cb0ef41Sopenharmony_ci  std::vector<Entry> entries_;
1641cb0ef41Sopenharmony_ci};
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci// Debug info per NativeModule, created lazily on demand.
1671cb0ef41Sopenharmony_ci// Implementation in {wasm-debug.cc} using PIMPL.
1681cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE DebugInfo {
1691cb0ef41Sopenharmony_ci public:
1701cb0ef41Sopenharmony_ci  explicit DebugInfo(NativeModule*);
1711cb0ef41Sopenharmony_ci  ~DebugInfo();
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci  // For the frame inspection methods below:
1741cb0ef41Sopenharmony_ci  // {fp} is the frame pointer of the Liftoff frame, {debug_break_fp} that of
1751cb0ef41Sopenharmony_ci  // the {WasmDebugBreak} frame (if any).
1761cb0ef41Sopenharmony_ci  int GetNumLocals(Address pc);
1771cb0ef41Sopenharmony_ci  WasmValue GetLocalValue(int local, Address pc, Address fp,
1781cb0ef41Sopenharmony_ci                          Address debug_break_fp, Isolate* isolate);
1791cb0ef41Sopenharmony_ci  int GetStackDepth(Address pc);
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci  const wasm::WasmFunction& GetFunctionAtAddress(Address pc);
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci  WasmValue GetStackValue(int index, Address pc, Address fp,
1841cb0ef41Sopenharmony_ci                          Address debug_break_fp, Isolate* isolate);
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci  // Returns the name of the entity (with the given |index| and |kind|) derived
1871cb0ef41Sopenharmony_ci  // from the exports table. If the entity is not exported, an empty reference
1881cb0ef41Sopenharmony_ci  // will be returned instead.
1891cb0ef41Sopenharmony_ci  WireBytesRef GetExportName(ImportExportKindCode kind, uint32_t index);
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci  // Returns the module and field name of the entity (with the given |index|
1921cb0ef41Sopenharmony_ci  // and |kind|) derived from the imports table. If the entity is not imported,
1931cb0ef41Sopenharmony_ci  // a pair of empty references will be returned instead.
1941cb0ef41Sopenharmony_ci  std::pair<WireBytesRef, WireBytesRef> GetImportName(ImportExportKindCode kind,
1951cb0ef41Sopenharmony_ci                                                      uint32_t index);
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci  WireBytesRef GetTypeName(int type_index);
1981cb0ef41Sopenharmony_ci  WireBytesRef GetLocalName(int func_index, int local_index);
1991cb0ef41Sopenharmony_ci  WireBytesRef GetFieldName(int struct_index, int field_index);
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci  void SetBreakpoint(int func_index, int offset, Isolate* current_isolate);
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  // Returns true if we stay inside the passed frame (or a called frame) after
2041cb0ef41Sopenharmony_ci  // the step. False if the frame will return after the step.
2051cb0ef41Sopenharmony_ci  bool PrepareStep(WasmFrame*);
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci  void PrepareStepOutTo(WasmFrame*);
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci  void ClearStepping(Isolate*);
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci  // Remove stepping code from a single frame; this is a performance
2121cb0ef41Sopenharmony_ci  // optimization only, hitting debug breaks while not stepping and not at a set
2131cb0ef41Sopenharmony_ci  // breakpoint would be unobservable otherwise.
2141cb0ef41Sopenharmony_ci  void ClearStepping(WasmFrame*);
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci  bool IsStepping(WasmFrame*);
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_ci  void RemoveBreakpoint(int func_index, int offset, Isolate* current_isolate);
2191cb0ef41Sopenharmony_ci
2201cb0ef41Sopenharmony_ci  void RemoveDebugSideTables(base::Vector<WasmCode* const>);
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  // Return the debug side table for the given code object, but only if it has
2231cb0ef41Sopenharmony_ci  // already been created. This will never trigger generation of the table.
2241cb0ef41Sopenharmony_ci  DebugSideTable* GetDebugSideTableIfExists(const WasmCode*) const;
2251cb0ef41Sopenharmony_ci
2261cb0ef41Sopenharmony_ci  void RemoveIsolate(Isolate*);
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci private:
2291cb0ef41Sopenharmony_ci  std::unique_ptr<DebugInfoImpl> impl_;
2301cb0ef41Sopenharmony_ci};
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci}  // namespace wasm
2331cb0ef41Sopenharmony_ci}  // namespace internal
2341cb0ef41Sopenharmony_ci}  // namespace v8
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci#endif  // V8_WASM_WASM_DEBUG_H_
237