46be2f2696
This is a minor refactoring before fixing actual issues. 1) The update of the {per_isolate_data_} is moved into {FloodWithBreakpoints}, which is already taking the mutex. 2) The {PrepareStep} method takes a {WasmFrame*} directly instead of its ID. In most cases, this prevents the creation of an additional stack frame iterator. R=thibaudm@chromium.org Bug: chromium:1145176 Change-Id: I1a6cd15550bbb4ef78ba522427bed1c23185569e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2558318 Reviewed-by: Simon Zünd <szuend@chromium.org> Reviewed-by: Thibaud Michaud <thibaudm@chromium.org> Commit-Queue: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#71399}
198 lines
5.6 KiB
C++
198 lines
5.6 KiB
C++
// 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/wasm/value-type.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
template <typename T>
|
|
class Handle;
|
|
class JSObject;
|
|
template <typename T>
|
|
class Vector;
|
|
class WasmFrame;
|
|
class WasmInstanceObject;
|
|
|
|
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 ValueKind : int8_t { kConstant, kRegister, kStack };
|
|
struct Value {
|
|
ValueType type;
|
|
ValueKind kind;
|
|
union {
|
|
int32_t i32_const; // if kind == kConstant
|
|
int reg_code; // if kind == kRegister
|
|
int stack_offset; // if kind == kStack
|
|
};
|
|
};
|
|
|
|
Entry(int pc_offset, std::vector<Value> values)
|
|
: pc_offset_(pc_offset), values_(std::move(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_; }
|
|
|
|
int num_values() const { return static_cast<int>(values_.size()); }
|
|
ValueType value_type(int index) const { return values_[index].type; }
|
|
|
|
auto values() const {
|
|
return base::make_iterator_range(values_.begin(), values_.end());
|
|
}
|
|
|
|
int stack_offset(int index) const {
|
|
DCHECK_EQ(kStack, values_[index].kind);
|
|
return values_[index].stack_offset;
|
|
}
|
|
|
|
bool is_constant(int index) const {
|
|
return values_[index].kind == kConstant;
|
|
}
|
|
|
|
bool is_register(int index) const {
|
|
return values_[index].kind == kRegister;
|
|
}
|
|
|
|
int32_t i32_constant(int index) const {
|
|
DCHECK_EQ(kConstant, values_[index].kind);
|
|
return values_[index].i32_const;
|
|
}
|
|
|
|
int32_t register_code(int index) const {
|
|
DCHECK_EQ(kRegister, values_[index].kind);
|
|
return values_[index].reg_code;
|
|
}
|
|
|
|
void Print(std::ostream&) const;
|
|
|
|
private:
|
|
int pc_offset_;
|
|
std::vector<Value> 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->num_values());
|
|
return &*it;
|
|
}
|
|
|
|
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_;
|
|
};
|
|
|
|
// Get the module scope for a given instance. This will contain the wasm memory
|
|
// (if the instance has a memory) and the values of all globals.
|
|
Handle<JSObject> GetModuleScopeObject(Handle<WasmInstanceObject>);
|
|
|
|
// 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);
|
|
|
|
Handle<JSObject> GetLocalScopeObject(Isolate*, Address pc, Address fp,
|
|
Address debug_break_fp);
|
|
|
|
Handle<JSObject> GetStackScopeObject(Isolate*, Address pc, Address fp,
|
|
Address debug_break_fp);
|
|
|
|
WireBytesRef GetLocalName(int func_index, int local_index);
|
|
|
|
void SetBreakpoint(int func_index, int offset, Isolate* current_isolate);
|
|
|
|
void PrepareStep(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_
|