v8/src/wasm/wasm-debug.h
Clemens Backes 0c7595b9fb [wasm][debug] Implement stepping out from JS to wasm
This specific case was not implemented or tested before. Implementing it
actually simplifies some of the existing logic, since StepOut can now
reuse the generic logic in debug.cc for all cases (Wasm->Wasm, Wasm->JS,
JS->Wasm).

Drive-by:
1) Fix typo ("skip" -> "step").
2) Move the check for Liftoff code from debug.cc to wasm-debug.cc, where
   it fits better.
3) Remove a TODO which is done already.

R=thibaudm@chromium.org, szuend@chromium.org

Bug: chromium:1145176
Change-Id: I415ca1d8bacef5b21bf1dafd9e16417ec2d12c7c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2560719
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71428}
2020-11-26 14:22:25 +00:00

202 lines
5.7 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);
// 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_