1 // Copyright 2019 the V8 project authors. All rights reserved. Use of 2 // this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_WASM_WASM_DEBUG_H_ 6 #define V8_WASM_WASM_DEBUG_H_ 7 8 #include <algorithm> 9 #include <memory> 10 #include <vector> 11 12 #include "include/v8-internal.h" 13 #include "src/base/iterator.h" 14 #include "src/base/logging.h" 15 #include "src/base/macros.h" 16 #include "src/wasm/value-type.h" 17 18 namespace v8 { 19 namespace internal { 20 21 template <typename T> 22 class Handle; 23 class JSObject; 24 template <typename T> 25 class Vector; 26 class WasmFrame; 27 class WasmInstanceObject; 28 29 namespace wasm { 30 31 class DebugInfoImpl; 32 class LocalNames; 33 class NativeModule; 34 class WasmCode; 35 class WireBytesRef; 36 class WasmValue; 37 struct WasmFunction; 38 39 // Side table storing information used to inspect Liftoff frames at runtime. 40 // This table is only created on demand for debugging, so it is not optimized 41 // for memory size. 42 class DebugSideTable { 43 public: 44 class Entry { 45 public: 46 enum ValueKind : int8_t { kConstant, kRegister, kStack }; 47 struct Value { 48 ValueType type; 49 ValueKind kind; 50 union { 51 int32_t i32_const; // if kind == kConstant 52 int reg_code; // if kind == kRegister 53 int stack_offset; // if kind == kStack 54 }; 55 }; 56 Entry(int pc_offset,std::vector<Value> values)57 Entry(int pc_offset, std::vector<Value> values) 58 : pc_offset_(pc_offset), values_(std::move(values)) {} 59 60 // Constructor for map lookups (only initializes the {pc_offset_}). Entry(int pc_offset)61 explicit Entry(int pc_offset) : pc_offset_(pc_offset) {} 62 pc_offset()63 int pc_offset() const { return pc_offset_; } 64 num_values()65 int num_values() const { return static_cast<int>(values_.size()); } value_type(int index)66 ValueType value_type(int index) const { return values_[index].type; } 67 values()68 auto values() const { 69 return base::make_iterator_range(values_.begin(), values_.end()); 70 } 71 stack_offset(int index)72 int stack_offset(int index) const { 73 DCHECK_EQ(kStack, values_[index].kind); 74 return values_[index].stack_offset; 75 } 76 is_constant(int index)77 bool is_constant(int index) const { 78 return values_[index].kind == kConstant; 79 } 80 is_register(int index)81 bool is_register(int index) const { 82 return values_[index].kind == kRegister; 83 } 84 i32_constant(int index)85 int32_t i32_constant(int index) const { 86 DCHECK_EQ(kConstant, values_[index].kind); 87 return values_[index].i32_const; 88 } 89 register_code(int index)90 int32_t register_code(int index) const { 91 DCHECK_EQ(kRegister, values_[index].kind); 92 return values_[index].reg_code; 93 } 94 95 void Print(std::ostream&) const; 96 97 private: 98 int pc_offset_; 99 std::vector<Value> values_; 100 }; 101 102 // Technically it would be fine to copy this class, but there should not be a 103 // reason to do so, hence mark it move only. 104 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(DebugSideTable); 105 DebugSideTable(int num_locals,std::vector<Entry> entries)106 explicit DebugSideTable(int num_locals, std::vector<Entry> entries) 107 : num_locals_(num_locals), entries_(std::move(entries)) { 108 DCHECK( 109 std::is_sorted(entries_.begin(), entries_.end(), EntryPositionLess{})); 110 } 111 GetEntry(int pc_offset)112 const Entry* GetEntry(int pc_offset) const { 113 auto it = std::lower_bound(entries_.begin(), entries_.end(), 114 Entry{pc_offset}, EntryPositionLess{}); 115 if (it == entries_.end() || it->pc_offset() != pc_offset) return nullptr; 116 DCHECK_LE(num_locals_, it->num_values()); 117 return &*it; 118 } 119 entries()120 auto entries() const { 121 return base::make_iterator_range(entries_.begin(), entries_.end()); 122 } 123 num_locals()124 int num_locals() const { return num_locals_; } 125 126 void Print(std::ostream&) const; 127 128 private: 129 struct EntryPositionLess { operatorEntryPositionLess130 bool operator()(const Entry& a, const Entry& b) const { 131 return a.pc_offset() < b.pc_offset(); 132 } 133 }; 134 135 int num_locals_; 136 std::vector<Entry> entries_; 137 }; 138 139 // Get the module scope for a given instance. This will contain the wasm memory 140 // (if the instance has a memory) and the values of all globals. 141 Handle<JSObject> GetModuleScopeObject(Handle<WasmInstanceObject>); 142 143 // Debug info per NativeModule, created lazily on demand. 144 // Implementation in {wasm-debug.cc} using PIMPL. 145 class V8_EXPORT_PRIVATE DebugInfo { 146 public: 147 explicit DebugInfo(NativeModule*); 148 ~DebugInfo(); 149 150 // For the frame inspection methods below: 151 // {fp} is the frame pointer of the Liftoff frame, {debug_break_fp} that of 152 // the {WasmDebugBreak} frame (if any). 153 int GetNumLocals(Address pc); 154 WasmValue GetLocalValue(int local, Address pc, Address fp, 155 Address debug_break_fp); 156 int GetStackDepth(Address pc); 157 158 const wasm::WasmFunction& GetFunctionAtAddress(Address pc); 159 160 WasmValue GetStackValue(int index, Address pc, Address fp, 161 Address debug_break_fp); 162 163 Handle<JSObject> GetLocalScopeObject(Isolate*, Address pc, Address fp, 164 Address debug_break_fp); 165 166 Handle<JSObject> GetStackScopeObject(Isolate*, Address pc, Address fp, 167 Address debug_break_fp); 168 169 WireBytesRef GetLocalName(int func_index, int local_index); 170 171 void SetBreakpoint(int func_index, int offset, Isolate* current_isolate); 172 173 void PrepareStep(Isolate*, StackFrameId); 174 175 void ClearStepping(Isolate*); 176 177 bool IsStepping(WasmFrame*); 178 179 void RemoveBreakpoint(int func_index, int offset, Isolate* current_isolate); 180 181 void RemoveDebugSideTables(Vector<WasmCode* const>); 182 183 // Return the debug side table for the given code object, but only if it has 184 // already been created. This will never trigger generation of the table. 185 DebugSideTable* GetDebugSideTableIfExists(const WasmCode*) const; 186 187 void RemoveIsolate(Isolate*); 188 189 private: 190 std::unique_ptr<DebugInfoImpl> impl_; 191 }; 192 193 } // namespace wasm 194 } // namespace internal 195 } // namespace v8 196 197 #endif // V8_WASM_WASM_DEBUG_H_ 198