summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/wasm-debug.h
blob: 837692644c1e2900ee14633c3cb856751274ba26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Copyright 2019 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_WASM_WASM_DEBUG_H_
#define V8_WASM_WASM_DEBUG_H_

#include <algorithm>
#include <memory>
#include <vector>

#include "include/v8-internal.h"
#include "src/base/iterator.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/utils/vector.h"
#include "src/wasm/value-type.h"

namespace v8 {
namespace internal {

template <typename T>
class Handle;
class WasmFrame;

namespace wasm {

class DebugInfoImpl;
class LocalNames;
class NativeModule;
class WasmCode;
class WireBytesRef;
class WasmValue;
struct WasmFunction;

// Side table storing information used to inspect Liftoff frames at runtime.
// This table is only created on demand for debugging, so it is not optimized
// for memory size.
class DebugSideTable {
 public:
  class Entry {
   public:
    enum Storage : int8_t { kConstant, kRegister, kStack };
    struct Value {
      int index;
      ValueKind kind;
      Storage storage;
      union {
        int32_t i32_const;  // if kind == kConstant
        int reg_code;       // if kind == kRegister
        int stack_offset;   // if kind == kStack
      };

      bool operator==(const Value& other) const {
        if (index != other.index) return false;
        if (kind != other.kind) return false;
        if (storage != other.storage) return false;
        switch (storage) {
          case kConstant:
            return i32_const == other.i32_const;
          case kRegister:
            return reg_code == other.reg_code;
          case kStack:
            return stack_offset == other.stack_offset;
        }
      }
      bool operator!=(const Value& other) const { return !(*this == other); }

      bool is_constant() const { return storage == kConstant; }
      bool is_register() const { return storage == kRegister; }
    };

    Entry(int pc_offset, int stack_height, std::vector<Value> changed_values)
        : pc_offset_(pc_offset),
          stack_height_(stack_height),
          changed_values_(std::move(changed_values)) {}

    // Constructor for map lookups (only initializes the {pc_offset_}).
    explicit Entry(int pc_offset) : pc_offset_(pc_offset) {}

    int pc_offset() const { return pc_offset_; }

    // Stack height, including locals.
    int stack_height() const { return stack_height_; }

    Vector<const Value> changed_values() const {
      return VectorOf(changed_values_);
    }

    const Value* FindChangedValue(int stack_index) const {
      DCHECK_GT(stack_height_, stack_index);
      auto it = std::lower_bound(
          changed_values_.begin(), changed_values_.end(), stack_index,
          [](const Value& changed_value, int stack_index) {
            return changed_value.index < stack_index;
          });
      return it != changed_values_.end() && it->index == stack_index ? &*it
                                                                     : nullptr;
    }

    void Print(std::ostream&) const;

   private:
    int pc_offset_;
    int stack_height_;
    // Only store differences from the last entry, to keep the table small.
    std::vector<Value> changed_values_;
  };

  // Technically it would be fine to copy this class, but there should not be a
  // reason to do so, hence mark it move only.
  MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(DebugSideTable);

  explicit DebugSideTable(int num_locals, std::vector<Entry> entries)
      : num_locals_(num_locals), entries_(std::move(entries)) {
    DCHECK(
        std::is_sorted(entries_.begin(), entries_.end(), EntryPositionLess{}));
  }

  const Entry* GetEntry(int pc_offset) const {
    auto it = std::lower_bound(entries_.begin(), entries_.end(),
                               Entry{pc_offset}, EntryPositionLess{});
    if (it == entries_.end() || it->pc_offset() != pc_offset) return nullptr;
    DCHECK_LE(num_locals_, it->stack_height());
    return &*it;
  }

  const Entry::Value* FindValue(const Entry* entry, int stack_index) const {
    while (true) {
      if (auto* value = entry->FindChangedValue(stack_index)) {
        // Check that the table was correctly minimized: If the previous stack
        // also had an entry for {stack_index}, it must be different.
        DCHECK(entry == &entries_.front() ||
               (entry - 1)->stack_height() <= stack_index ||
               *FindValue(entry - 1, stack_index) != *value);
        return value;
      }
      DCHECK_NE(&entries_.front(), entry);
      --entry;
    }
  }

  auto entries() const {
    return base::make_iterator_range(entries_.begin(), entries_.end());
  }

  int num_locals() const { return num_locals_; }

  void Print(std::ostream&) const;

 private:
  struct EntryPositionLess {
    bool operator()(const Entry& a, const Entry& b) const {
      return a.pc_offset() < b.pc_offset();
    }
  };

  int num_locals_;
  std::vector<Entry> entries_;
};

// Debug info per NativeModule, created lazily on demand.
// Implementation in {wasm-debug.cc} using PIMPL.
class V8_EXPORT_PRIVATE DebugInfo {
 public:
  explicit DebugInfo(NativeModule*);
  ~DebugInfo();

  // For the frame inspection methods below:
  // {fp} is the frame pointer of the Liftoff frame, {debug_break_fp} that of
  // the {WasmDebugBreak} frame (if any).
  int GetNumLocals(Address pc);
  WasmValue GetLocalValue(int local, Address pc, Address fp,
                          Address debug_break_fp);
  int GetStackDepth(Address pc);

  const wasm::WasmFunction& GetFunctionAtAddress(Address pc);

  WasmValue GetStackValue(int index, Address pc, Address fp,
                          Address debug_break_fp);

  // Returns the name of the entity (with the given |index| and |kind|) derived
  // from the exports table. If the entity is not exported, an empty reference
  // will be returned instead.
  WireBytesRef GetExportName(ImportExportKindCode kind, uint32_t index);

  // Returns the module and field name of the entity (with the given |index|
  // and |kind|) derived from the imports table. If the entity is not imported,
  // a pair of empty references will be returned instead.
  std::pair<WireBytesRef, WireBytesRef> GetImportName(ImportExportKindCode kind,
                                                      uint32_t index);

  WireBytesRef GetLocalName(int func_index, int local_index);

  void SetBreakpoint(int func_index, int offset, Isolate* current_isolate);

  // Returns true if we stay inside the passed frame (or a called frame) after
  // the step. False if the frame will return after the step.
  bool PrepareStep(WasmFrame*);

  void PrepareStepOutTo(WasmFrame*);

  void ClearStepping(Isolate*);

  bool IsStepping(WasmFrame*);

  void RemoveBreakpoint(int func_index, int offset, Isolate* current_isolate);

  void RemoveDebugSideTables(Vector<WasmCode* const>);

  // Return the debug side table for the given code object, but only if it has
  // already been created. This will never trigger generation of the table.
  DebugSideTable* GetDebugSideTableIfExists(const WasmCode*) const;

  void RemoveIsolate(Isolate*);

 private:
  std::unique_ptr<DebugInfoImpl> impl_;
};

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

#endif  // V8_WASM_WASM_DEBUG_H_