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