7f5383e8ad
The adding of base:: was mostly prepared using git grep and sed: git grep -l <pattern> | grep -v base/vector.h | \ xargs sed -i 's/\b<pattern>\b/base::<pattern>/ with lots of manual clean-ups due to the resulting v8::internal::base::Vectors. #includes were fixed using: git grep -l "src/utils/vector.h" | \ axargs sed -i 's!src/utils/vector.h!src/base/vector.h!' Bug: v8:11879 Change-Id: I3e6d622987fee4478089c40539724c19735bd625 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2968412 Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Hannes Payer <hpayer@chromium.org> Commit-Queue: Dan Elphick <delphick@chromium.org> Cr-Commit-Position: refs/heads/master@{#75243}
237 lines
7.6 KiB
C++
237 lines
7.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.
|
|
|
|
#if !V8_ENABLE_WEBASSEMBLY
|
|
#error This header should only be included if WebAssembly is enabled.
|
|
#endif // !V8_ENABLE_WEBASSEMBLY
|
|
|
|
#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/base/vector.h"
|
|
#include "src/wasm/value-type.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
template <typename T>
|
|
class Handle;
|
|
class WasmFrame;
|
|
|
|
namespace wasm {
|
|
|
|
class DebugInfoImpl;
|
|
class IndirectNameMap;
|
|
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;
|
|
ValueType type;
|
|
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 (type != other.type) 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_; }
|
|
|
|
base::Vector<const Value> changed_values() const {
|
|
return base::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, Isolate* isolate);
|
|
int GetStackDepth(Address pc);
|
|
|
|
const wasm::WasmFunction& GetFunctionAtAddress(Address pc);
|
|
|
|
WasmValue GetStackValue(int index, Address pc, Address fp,
|
|
Address debug_break_fp, Isolate* isolate);
|
|
|
|
// 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 GetTypeName(int type_index);
|
|
WireBytesRef GetLocalName(int func_index, int local_index);
|
|
WireBytesRef GetFieldName(int struct_index, int field_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*);
|
|
|
|
// Remove stepping code from a single frame; this is a performance
|
|
// optimization only, hitting debug breaks while not stepping and not at a set
|
|
// breakpoint would be unobservable otherwise.
|
|
void ClearStepping(WasmFrame*);
|
|
|
|
bool IsStepping(WasmFrame*);
|
|
|
|
void RemoveBreakpoint(int func_index, int offset, Isolate* current_isolate);
|
|
|
|
void RemoveDebugSideTables(base::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_
|