[wasm-c-api] Roll dc8cc29: Implement stack trace API
Change-Id: Ic5145b7ba15ae58d15e2cc4511afc2f8c6d42ea0 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1741654 Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/master@{#63132}
This commit is contained in:
parent
c9f9d1b0b4
commit
5e46b285df
@ -925,6 +925,7 @@ extern class StackFrameInfo extends Struct {
|
||||
type_name: String | Null | Undefined;
|
||||
eval_origin: String | Null | Undefined;
|
||||
wasm_module_name: String | Null | Undefined;
|
||||
wasm_instance: WasmInstanceObject | Null | Undefined;
|
||||
flag: Smi;
|
||||
}
|
||||
|
||||
|
@ -314,6 +314,10 @@ Handle<Object> StackFrameBase::GetWasmModuleName() {
|
||||
return isolate_->factory()->undefined_value();
|
||||
}
|
||||
|
||||
Handle<Object> StackFrameBase::GetWasmInstance() {
|
||||
return isolate_->factory()->undefined_value();
|
||||
}
|
||||
|
||||
int StackFrameBase::GetScriptId() const {
|
||||
if (!HasScript()) return kNone;
|
||||
return GetScript()->id();
|
||||
@ -580,6 +584,8 @@ Handle<Object> WasmStackFrame::GetWasmModuleName() {
|
||||
return module_name;
|
||||
}
|
||||
|
||||
Handle<Object> WasmStackFrame::GetWasmInstance() { return wasm_instance_; }
|
||||
|
||||
int WasmStackFrame::GetPosition() const {
|
||||
return IsInterpreted()
|
||||
? offset_
|
||||
|
@ -73,6 +73,7 @@ class StackFrameBase {
|
||||
virtual Handle<Object> GetTypeName() = 0;
|
||||
virtual Handle<Object> GetEvalOrigin();
|
||||
virtual Handle<Object> GetWasmModuleName();
|
||||
virtual Handle<Object> GetWasmInstance();
|
||||
|
||||
// Returns the script ID if one is attached, -1 otherwise.
|
||||
int GetScriptId() const;
|
||||
@ -170,12 +171,13 @@ class WasmStackFrame : public StackFrameBase {
|
||||
Handle<Object> GetMethodName() override { return Null(); }
|
||||
Handle<Object> GetTypeName() override { return Null(); }
|
||||
Handle<Object> GetWasmModuleName() override;
|
||||
Handle<Object> GetWasmInstance() override;
|
||||
|
||||
int GetPosition() const override;
|
||||
int GetLineNumber() override { return wasm_func_index_; }
|
||||
int GetColumnNumber() override;
|
||||
|
||||
int GetPromiseIndex() const override { return kNone; }
|
||||
int GetPromiseIndex() const override { return GetPosition(); }
|
||||
|
||||
bool IsNative() override { return false; }
|
||||
bool IsToplevel() override { return false; }
|
||||
|
@ -3708,6 +3708,7 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
|
||||
Handle<Object> type_name = undefined_value();
|
||||
Handle<Object> eval_origin = frame->GetEvalOrigin();
|
||||
Handle<Object> wasm_module_name = frame->GetWasmModuleName();
|
||||
Handle<Object> wasm_instance = frame->GetWasmInstance();
|
||||
|
||||
// MethodName and TypeName are expensive to look up, so they are only
|
||||
// included when they are strictly needed by the stack trace
|
||||
@ -3743,6 +3744,7 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
|
||||
info->set_type_name(*type_name);
|
||||
info->set_eval_origin(*eval_origin);
|
||||
info->set_wasm_module_name(*wasm_module_name);
|
||||
info->set_wasm_instance(*wasm_instance);
|
||||
|
||||
info->set_is_eval(frame->IsEval());
|
||||
info->set_is_constructor(is_constructor);
|
||||
|
@ -28,6 +28,8 @@ SMI_ACCESSORS(StackFrameInfo, line_number, kLineNumberOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, column_number, kColumnNumberOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, script_id, kScriptIdOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, promise_all_index, kPromiseAllIndexOffset)
|
||||
SMI_ACCESSORS_CHECKED(StackFrameInfo, function_offset, kPromiseAllIndexOffset,
|
||||
is_wasm())
|
||||
ACCESSORS(StackFrameInfo, script_name, Object, kScriptNameOffset)
|
||||
ACCESSORS(StackFrameInfo, script_name_or_source_url, Object,
|
||||
kScriptNameOrSourceUrlOffset)
|
||||
@ -36,6 +38,7 @@ ACCESSORS(StackFrameInfo, method_name, Object, kMethodNameOffset)
|
||||
ACCESSORS(StackFrameInfo, type_name, Object, kTypeNameOffset)
|
||||
ACCESSORS(StackFrameInfo, eval_origin, Object, kEvalOriginOffset)
|
||||
ACCESSORS(StackFrameInfo, wasm_module_name, Object, kWasmModuleNameOffset)
|
||||
ACCESSORS(StackFrameInfo, wasm_instance, Object, kWasmInstanceOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, flag, kFlagOffset)
|
||||
BOOL_ACCESSORS(StackFrameInfo, flag, is_eval, kIsEvalBit)
|
||||
BOOL_ACCESSORS(StackFrameInfo, flag, is_constructor, kIsConstructorBit)
|
||||
|
@ -51,6 +51,12 @@ int StackTraceFrame::GetPromiseAllIndex(Handle<StackTraceFrame> frame) {
|
||||
return GetFrameInfo(frame)->promise_all_index();
|
||||
}
|
||||
|
||||
// static
|
||||
int StackTraceFrame::GetFunctionOffset(Handle<StackTraceFrame> frame) {
|
||||
DCHECK(IsWasm(frame));
|
||||
return GetFrameInfo(frame)->function_offset();
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> StackTraceFrame::GetFileName(Handle<StackTraceFrame> frame) {
|
||||
auto name = GetFrameInfo(frame)->script_name();
|
||||
@ -95,6 +101,13 @@ Handle<Object> StackTraceFrame::GetWasmModuleName(
|
||||
return handle(module, frame->GetIsolate());
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<WasmInstanceObject> StackTraceFrame::GetWasmInstance(
|
||||
Handle<StackTraceFrame> frame) {
|
||||
Object instance = GetFrameInfo(frame)->wasm_instance();
|
||||
return handle(WasmInstanceObject::cast(instance), frame->GetIsolate());
|
||||
}
|
||||
|
||||
// static
|
||||
bool StackTraceFrame::IsEval(Handle<StackTraceFrame> frame) {
|
||||
return GetFrameInfo(frame)->is_eval();
|
||||
|
@ -14,6 +14,7 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class FrameArray;
|
||||
class WasmInstanceObject;
|
||||
|
||||
class StackFrameInfo : public Struct {
|
||||
public:
|
||||
@ -22,6 +23,8 @@ class StackFrameInfo : public Struct {
|
||||
DECL_INT_ACCESSORS(column_number)
|
||||
DECL_INT_ACCESSORS(script_id)
|
||||
DECL_INT_ACCESSORS(promise_all_index)
|
||||
// Wasm frames only: function_offset instead of promise_all_index.
|
||||
DECL_INT_ACCESSORS(function_offset)
|
||||
DECL_ACCESSORS(script_name, Object)
|
||||
DECL_ACCESSORS(script_name_or_source_url, Object)
|
||||
DECL_ACCESSORS(function_name, Object)
|
||||
@ -29,6 +32,7 @@ class StackFrameInfo : public Struct {
|
||||
DECL_ACCESSORS(type_name, Object)
|
||||
DECL_ACCESSORS(eval_origin, Object)
|
||||
DECL_ACCESSORS(wasm_module_name, Object)
|
||||
DECL_ACCESSORS(wasm_instance, Object)
|
||||
DECL_BOOLEAN_ACCESSORS(is_eval)
|
||||
DECL_BOOLEAN_ACCESSORS(is_constructor)
|
||||
DECL_BOOLEAN_ACCESSORS(is_wasm)
|
||||
@ -90,6 +94,7 @@ class StackTraceFrame : public Struct {
|
||||
static int GetOneBasedColumnNumber(Handle<StackTraceFrame> frame);
|
||||
static int GetScriptId(Handle<StackTraceFrame> frame);
|
||||
static int GetPromiseAllIndex(Handle<StackTraceFrame> frame);
|
||||
static int GetFunctionOffset(Handle<StackTraceFrame> frame);
|
||||
|
||||
static Handle<Object> GetFileName(Handle<StackTraceFrame> frame);
|
||||
static Handle<Object> GetScriptNameOrSourceUrl(Handle<StackTraceFrame> frame);
|
||||
@ -98,6 +103,8 @@ class StackTraceFrame : public Struct {
|
||||
static Handle<Object> GetTypeName(Handle<StackTraceFrame> frame);
|
||||
static Handle<Object> GetEvalOrigin(Handle<StackTraceFrame> frame);
|
||||
static Handle<Object> GetWasmModuleName(Handle<StackTraceFrame> frame);
|
||||
static Handle<WasmInstanceObject> GetWasmInstance(
|
||||
Handle<StackTraceFrame> frame);
|
||||
|
||||
static bool IsEval(Handle<StackTraceFrame> frame);
|
||||
static bool IsConstructor(Handle<StackTraceFrame> frame);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "include/libplatform/libplatform.h"
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/objects/stack-frame-info-inl.h"
|
||||
#include "src/wasm/leb-helper.h"
|
||||
#include "src/wasm/module-instantiate.h"
|
||||
#include "src/wasm/wasm-arguments.h"
|
||||
@ -309,6 +310,10 @@ auto Store::make(Engine*) -> own<Store*> {
|
||||
// and hence must not be called by anything reachable via this file.
|
||||
store->context()->Enter();
|
||||
isolate->SetData(0, store.get());
|
||||
// We want stack traces for traps.
|
||||
constexpr int kStackLimit = 10;
|
||||
isolate->SetCaptureStackTraceForUncaughtExceptions(true, kStackLimit,
|
||||
v8::StackTrace::kOverview);
|
||||
|
||||
return make_own(seal<Store>(store.release()));
|
||||
}
|
||||
@ -771,6 +776,52 @@ void Ref::set_host_info(void* info, void (*finalizer)(void*)) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Runtime Objects
|
||||
|
||||
// Frames
|
||||
|
||||
namespace {
|
||||
|
||||
struct FrameImpl {
|
||||
FrameImpl(own<Instance*>&& instance, uint32_t func_index, size_t func_offset,
|
||||
size_t module_offset)
|
||||
: instance(std::move(instance)),
|
||||
func_index(func_index),
|
||||
func_offset(func_offset),
|
||||
module_offset(module_offset) {}
|
||||
|
||||
~FrameImpl() {}
|
||||
|
||||
own<Instance*> instance;
|
||||
uint32_t func_index;
|
||||
size_t func_offset;
|
||||
size_t module_offset;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template <>
|
||||
struct implement<Frame> {
|
||||
using type = FrameImpl;
|
||||
};
|
||||
|
||||
Frame::~Frame() { impl(this)->~FrameImpl(); }
|
||||
|
||||
void Frame::operator delete(void* p) { ::operator delete(p); }
|
||||
|
||||
own<Frame*> Frame::copy() const {
|
||||
auto self = impl(this);
|
||||
return own<Frame*>(seal<Frame>(
|
||||
new (std::nothrow) FrameImpl(self->instance->copy(), self->func_index,
|
||||
self->func_offset, self->module_offset)));
|
||||
}
|
||||
|
||||
Instance* Frame::instance() const { return impl(this)->instance.get(); }
|
||||
|
||||
uint32_t Frame::func_index() const { return impl(this)->func_index; }
|
||||
|
||||
size_t Frame::func_offset() const { return impl(this)->func_offset; }
|
||||
|
||||
size_t Frame::module_offset() const { return impl(this)->module_offset; }
|
||||
|
||||
// Traps
|
||||
|
||||
template <>
|
||||
@ -806,6 +857,56 @@ auto Trap::message() const -> Message {
|
||||
return vec<byte_t>::adopt(length, utf8.release());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
own<Instance*> GetInstance(StoreImpl* store,
|
||||
i::Handle<i::WasmInstanceObject> instance);
|
||||
|
||||
own<Frame*> CreateFrameFromInternal(i::Handle<i::FixedArray> frames, int index,
|
||||
i::Isolate* isolate, StoreImpl* store) {
|
||||
i::Handle<i::StackTraceFrame> frame(i::StackTraceFrame::cast(frames->get(0)),
|
||||
isolate);
|
||||
i::Handle<i::WasmInstanceObject> instance =
|
||||
i::StackTraceFrame::GetWasmInstance(frame);
|
||||
uint32_t func_index = i::StackTraceFrame::GetLineNumber(frame);
|
||||
size_t func_offset = i::StackTraceFrame::GetFunctionOffset(frame);
|
||||
size_t module_offset = i::StackTraceFrame::GetColumnNumber(frame);
|
||||
return own<Frame*>(seal<Frame>(new (std::nothrow) FrameImpl(
|
||||
GetInstance(store, instance), func_index, func_offset, module_offset)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
own<Frame*> Trap::origin() const {
|
||||
i::Isolate* isolate = impl(this)->isolate();
|
||||
i::HandleScope handle_scope(isolate);
|
||||
|
||||
i::Handle<i::JSMessageObject> message =
|
||||
isolate->CreateMessage(impl(this)->v8_object(), nullptr);
|
||||
i::Handle<i::FixedArray> frames(i::FixedArray::cast(message->stack_frames()),
|
||||
isolate);
|
||||
DCHECK_GT(frames->length(), 0);
|
||||
return CreateFrameFromInternal(frames, 0, isolate, impl(this)->store());
|
||||
}
|
||||
|
||||
vec<Frame*> Trap::trace() const {
|
||||
i::Isolate* isolate = impl(this)->isolate();
|
||||
i::HandleScope handle_scope(isolate);
|
||||
|
||||
i::Handle<i::JSMessageObject> message =
|
||||
isolate->CreateMessage(impl(this)->v8_object(), nullptr);
|
||||
i::Handle<i::FixedArray> frames(i::FixedArray::cast(message->stack_frames()),
|
||||
isolate);
|
||||
int num_frames = frames->length();
|
||||
DCHECK_GT(num_frames, 0);
|
||||
vec<Frame*> result = vec<Frame*>::make_uninitialized(num_frames);
|
||||
for (int i = 0; i < num_frames; i++) {
|
||||
result[i] =
|
||||
CreateFrameFromInternal(frames, i, isolate, impl(this)->store());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Foreign Objects
|
||||
|
||||
template <>
|
||||
@ -1832,6 +1933,15 @@ auto Instance::make(Store* store_abs, const Module* module_abs,
|
||||
return implement<Instance>::type::make(store, instance_obj);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
own<Instance*> GetInstance(StoreImpl* store,
|
||||
i::Handle<i::WasmInstanceObject> instance) {
|
||||
return implement<Instance>::type::make(store, instance);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto Instance::exports() const -> vec<Extern*> {
|
||||
const implement<Instance>::type* instance = impl(this);
|
||||
StoreImpl* store = instance->store();
|
||||
@ -2536,6 +2646,30 @@ void wasm_val_copy(wasm_val_t* out, const wasm_val_t* v) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Runtime Objects
|
||||
|
||||
// Frames
|
||||
|
||||
WASM_DEFINE_OWN(frame, wasm::Frame)
|
||||
WASM_DEFINE_VEC(frame, wasm::Frame, *)
|
||||
|
||||
wasm_frame_t* wasm_frame_copy(const wasm_frame_t* frame) {
|
||||
return release(frame->copy());
|
||||
}
|
||||
|
||||
wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame);
|
||||
// Defined below along with wasm_instance_t.
|
||||
|
||||
uint32_t wasm_frame_func_index(const wasm_frame_t* frame) {
|
||||
return reveal(frame)->func_index();
|
||||
}
|
||||
|
||||
size_t wasm_frame_func_offset(const wasm_frame_t* frame) {
|
||||
return reveal(frame)->func_offset();
|
||||
}
|
||||
|
||||
size_t wasm_frame_module_offset(const wasm_frame_t* frame) {
|
||||
return reveal(frame)->module_offset();
|
||||
}
|
||||
|
||||
// Traps
|
||||
|
||||
WASM_DEFINE_REF(trap, wasm::Trap)
|
||||
@ -2549,6 +2683,14 @@ void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) {
|
||||
*out = release(reveal(trap)->message());
|
||||
}
|
||||
|
||||
wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) {
|
||||
return release(reveal(trap)->origin());
|
||||
}
|
||||
|
||||
void wasm_trap_trace(const wasm_trap_t* trap, wasm_frame_vec_t* out) {
|
||||
*out = release(reveal(trap)->trace());
|
||||
}
|
||||
|
||||
// Foreign Objects
|
||||
|
||||
WASM_DEFINE_REF(foreign, wasm::Foreign)
|
||||
@ -2829,6 +2971,10 @@ void wasm_instance_exports(const wasm_instance_t* instance,
|
||||
*out = release(instance->exports());
|
||||
}
|
||||
|
||||
wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) {
|
||||
return hide(reveal(frame)->instance());
|
||||
}
|
||||
|
||||
#undef WASM_DEFINE_OWN
|
||||
#undef WASM_DEFINE_VEC_BASE
|
||||
#undef WASM_DEFINE_VEC_PLAIN
|
||||
|
@ -267,7 +267,7 @@ size_t GetFunctionOffset(i::Isolate* isolate, const uint8_t* buffer,
|
||||
kAllWasmFeatures, buffer, buffer + size, false, ModuleOrigin::kWasmOrigin,
|
||||
isolate->counters(), isolate->wasm_engine()->allocator());
|
||||
CHECK(result.ok());
|
||||
const WasmFunction* func = &result.value()->functions[1];
|
||||
const WasmFunction* func = &result.value()->functions[index];
|
||||
return func->code.offset();
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,18 @@
|
||||
|
||||
#include "test/wasm-api-tests/wasm-api-test.h"
|
||||
|
||||
#include "src/execution/isolate.h"
|
||||
#include "src/wasm/c-api.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-engine.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
using ::wasm::Frame;
|
||||
using ::wasm::Message;
|
||||
|
||||
namespace {
|
||||
@ -34,7 +40,9 @@ TEST_F(WasmCapiTest, Traps) {
|
||||
uint32_t callback_index = builder()->AddImport(CStrVector("callback"), &sig);
|
||||
byte code[] = {WASM_CALL_FUNCTION0(callback_index)};
|
||||
AddExportedFunction(CStrVector("callback"), code, sizeof(code), &sig);
|
||||
byte code2[] = {WASM_UNREACHABLE, WASM_I32V_1(1)};
|
||||
// The first constant is a 4-byte dummy so that the {unreachable} trap
|
||||
// has a more interesting offset.
|
||||
byte code2[] = {WASM_I32V_3(0), WASM_UNREACHABLE, WASM_I32V_1(1)};
|
||||
AddExportedFunction(CStrVector("unreachable"), code2, sizeof(code2), &sig);
|
||||
|
||||
own<FuncType*> func_type = FuncType::make(
|
||||
@ -44,15 +52,53 @@ TEST_F(WasmCapiTest, Traps) {
|
||||
Extern* imports[] = {cpp_callback.get()};
|
||||
Instantiate(imports);
|
||||
|
||||
// Use internal machinery to parse the module to find the function offsets.
|
||||
// This makes the test more robust than hardcoding them.
|
||||
i::Isolate* isolate =
|
||||
reinterpret_cast<::wasm::StoreImpl*>(store())->i_isolate();
|
||||
ModuleResult result = DecodeWasmModule(
|
||||
kAllWasmFeatures, wire_bytes()->begin(), wire_bytes()->end(), false,
|
||||
ModuleOrigin::kWasmOrigin, isolate->counters(),
|
||||
isolate->wasm_engine()->allocator());
|
||||
ASSERT_TRUE(result.ok());
|
||||
const WasmFunction* func1 = &result.value()->functions[1];
|
||||
const WasmFunction* func2 = &result.value()->functions[2];
|
||||
const uint32_t func1_offset = func1->code.offset();
|
||||
const uint32_t func2_offset = func2->code.offset();
|
||||
|
||||
Func* cpp_trapping_func = GetExportedFunction(0);
|
||||
own<Trap*> cpp_trap = cpp_trapping_func->call();
|
||||
EXPECT_NE(nullptr, cpp_trap.get());
|
||||
ExpectMessage("Uncaught Error: callback abort", cpp_trap->message());
|
||||
own<Frame*> frame = cpp_trap->origin();
|
||||
EXPECT_TRUE(frame->instance()->same(instance()));
|
||||
EXPECT_EQ(1u, frame->func_index());
|
||||
EXPECT_EQ(1u, frame->func_offset());
|
||||
EXPECT_EQ(func1_offset + frame->func_offset(), frame->module_offset());
|
||||
vec<Frame*> trace = cpp_trap->trace();
|
||||
EXPECT_EQ(1u, trace.size());
|
||||
frame = trace[0].move();
|
||||
EXPECT_TRUE(frame->instance()->same(instance()));
|
||||
EXPECT_EQ(1u, frame->func_index());
|
||||
EXPECT_EQ(1u, frame->func_offset());
|
||||
EXPECT_EQ(func1_offset + frame->func_offset(), frame->module_offset());
|
||||
|
||||
Func* wasm_trapping_func = GetExportedFunction(1);
|
||||
own<Trap*> wasm_trap = wasm_trapping_func->call();
|
||||
EXPECT_NE(nullptr, wasm_trap.get());
|
||||
ExpectMessage("Uncaught RuntimeError: unreachable", wasm_trap->message());
|
||||
frame = wasm_trap->origin();
|
||||
EXPECT_TRUE(frame->instance()->same(instance()));
|
||||
EXPECT_EQ(2u, frame->func_index());
|
||||
EXPECT_EQ(5u, frame->func_offset());
|
||||
EXPECT_EQ(func2_offset + frame->func_offset(), frame->module_offset());
|
||||
trace = wasm_trap->trace();
|
||||
EXPECT_EQ(1u, trace.size());
|
||||
frame = trace[0].move();
|
||||
EXPECT_TRUE(frame->instance()->same(instance()));
|
||||
EXPECT_EQ(2u, frame->func_index());
|
||||
EXPECT_EQ(5u, frame->func_offset());
|
||||
EXPECT_EQ(func2_offset + frame->func_offset(), frame->module_offset());
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -53,6 +53,7 @@ class WasmCapiTest : public ::testing::Test {
|
||||
WasmCapiTest()
|
||||
: Test(),
|
||||
zone_(&allocator_, ZONE_NAME),
|
||||
wire_bytes_(&zone_),
|
||||
builder_(&zone_),
|
||||
exports_(vec<Extern*>::make()),
|
||||
wasm_i_i_sig_(1, 1, wasm_i_i_sig_types_) {
|
||||
@ -64,11 +65,11 @@ class WasmCapiTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
void Compile() {
|
||||
ZoneBuffer buffer(&zone_);
|
||||
builder_.WriteTo(&buffer);
|
||||
size_t size = buffer.end() - buffer.begin();
|
||||
builder_.WriteTo(&wire_bytes_);
|
||||
size_t size = wire_bytes_.end() - wire_bytes_.begin();
|
||||
vec<byte_t> binary = vec<byte_t>::make(
|
||||
size, reinterpret_cast<byte_t*>(const_cast<byte*>(buffer.begin())));
|
||||
size,
|
||||
reinterpret_cast<byte_t*>(const_cast<byte*>(wire_bytes_.begin())));
|
||||
|
||||
module_ = Module::make(store_.get(), binary);
|
||||
DCHECK_NE(module_.get(), nullptr);
|
||||
@ -136,7 +137,9 @@ class WasmCapiTest : public ::testing::Test {
|
||||
Engine* engine() { return engine_.get(); }
|
||||
Store* store() { return store_.get(); }
|
||||
Module* module() { return module_.get(); }
|
||||
Instance* instance() { return instance_.get(); }
|
||||
const vec<Extern*>& exports() { return exports_; }
|
||||
ZoneBuffer* wire_bytes() { return &wire_bytes_; }
|
||||
|
||||
FunctionSig* wasm_i_i_sig() { return &wasm_i_i_sig_; }
|
||||
FuncType* cpp_i_i_sig() { return cpp_i_i_sig_.get(); }
|
||||
@ -144,6 +147,7 @@ class WasmCapiTest : public ::testing::Test {
|
||||
private:
|
||||
AccountingAllocator allocator_;
|
||||
Zone zone_;
|
||||
ZoneBuffer wire_bytes_;
|
||||
WasmModuleBuilder builder_;
|
||||
own<Engine*> engine_;
|
||||
own<Store*> store_;
|
||||
|
31
third_party/wasm-api/example/trap.c
vendored
31
third_party/wasm-api/example/trap.c
vendored
@ -20,6 +20,16 @@ own wasm_trap_t* fail_callback(
|
||||
}
|
||||
|
||||
|
||||
void print_frame(wasm_frame_t* frame) {
|
||||
printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n",
|
||||
wasm_frame_instance(frame),
|
||||
wasm_frame_module_offset(frame),
|
||||
wasm_frame_func_index(frame),
|
||||
wasm_frame_func_offset(frame)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
// Initialize.
|
||||
printf("Initializing...\n");
|
||||
@ -104,6 +114,27 @@ int main(int argc, const char* argv[]) {
|
||||
wasm_trap_message(trap, &message);
|
||||
printf("> %s\n", message.data);
|
||||
|
||||
printf("Printing origin...\n");
|
||||
own wasm_frame_t* frame = wasm_trap_origin(trap);
|
||||
if (frame) {
|
||||
print_frame(frame);
|
||||
wasm_frame_delete(frame);
|
||||
} else {
|
||||
printf("> Empty origin.\n");
|
||||
}
|
||||
|
||||
printf("Printing trace...\n");
|
||||
own wasm_frame_vec_t trace;
|
||||
wasm_trap_trace(trap, &trace);
|
||||
if (trace.size > 0) {
|
||||
for (size_t i = 0; i < trace.size; ++i) {
|
||||
print_frame(trace.data[i]);
|
||||
}
|
||||
} else {
|
||||
printf("> Empty trace.\n");
|
||||
}
|
||||
|
||||
wasm_frame_vec_delete(&trace);
|
||||
wasm_trap_delete(trap);
|
||||
wasm_name_delete(&message);
|
||||
}
|
||||
|
26
third_party/wasm-api/example/trap.cc
vendored
26
third_party/wasm-api/example/trap.cc
vendored
@ -17,6 +17,14 @@ auto fail_callback(
|
||||
}
|
||||
|
||||
|
||||
void print_frame(const wasm::Frame* frame) {
|
||||
std::cout << "> " << frame->instance();
|
||||
std::cout << " @ 0x" << std::hex << frame->module_offset();
|
||||
std::cout << " = " << frame->func_index();
|
||||
std::cout << ".0x" << std::hex << frame->func_offset() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void run() {
|
||||
// Initialize.
|
||||
std::cout << "Initializing..." << std::endl;
|
||||
@ -85,6 +93,24 @@ void run() {
|
||||
|
||||
std::cout << "Printing message..." << std::endl;
|
||||
std::cout << "> " << trap->message().get() << std::endl;
|
||||
|
||||
std::cout << "Printing origin..." << std::endl;
|
||||
auto frame = trap->origin();
|
||||
if (frame) {
|
||||
print_frame(frame.get());
|
||||
} else {
|
||||
std::cout << "> Empty origin." << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Printing trace..." << std::endl;
|
||||
auto trace = trap->trace();
|
||||
if (trace.size() > 0) {
|
||||
for (size_t i = 0; i < trace.size(); ++i) {
|
||||
print_frame(trace[i].get());
|
||||
}
|
||||
} else {
|
||||
std::cout << "> Empty trace." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Shut down.
|
||||
|
14
third_party/wasm-api/wasm.h
vendored
14
third_party/wasm-api/wasm.h
vendored
@ -345,6 +345,18 @@ WASM_DECLARE_VEC(val, )
|
||||
WASM_DECLARE_REF_BASE(ref)
|
||||
|
||||
|
||||
// Frames
|
||||
|
||||
WASM_DECLARE_OWN(frame)
|
||||
WASM_DECLARE_VEC(frame, *)
|
||||
own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*);
|
||||
|
||||
struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*);
|
||||
uint32_t wasm_frame_func_index(const wasm_frame_t*);
|
||||
size_t wasm_frame_func_offset(const wasm_frame_t*);
|
||||
size_t wasm_frame_module_offset(const wasm_frame_t*);
|
||||
|
||||
|
||||
// Traps
|
||||
|
||||
typedef wasm_name_t wasm_message_t; // null terminated
|
||||
@ -354,6 +366,8 @@ WASM_DECLARE_REF(trap)
|
||||
own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*);
|
||||
|
||||
void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out);
|
||||
own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*);
|
||||
void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out);
|
||||
|
||||
|
||||
// Foreign Objects
|
||||
|
18
third_party/wasm-api/wasm.hh
vendored
18
third_party/wasm-api/wasm.hh
vendored
@ -584,6 +584,22 @@ template<> inline auto Val::get<uint64_t>() const -> uint64_t {
|
||||
|
||||
using Message = vec<byte_t>; // null terminated
|
||||
|
||||
class Instance;
|
||||
|
||||
class Frame {
|
||||
public:
|
||||
Frame() = delete;
|
||||
~Frame();
|
||||
void operator delete(void*);
|
||||
|
||||
auto copy() const -> own<Frame*>;
|
||||
|
||||
auto instance() const -> Instance*;
|
||||
auto func_index() const -> uint32_t;
|
||||
auto func_offset() const -> size_t;
|
||||
auto module_offset() const -> size_t;
|
||||
};
|
||||
|
||||
class Trap : public Ref {
|
||||
public:
|
||||
Trap() = delete;
|
||||
@ -593,6 +609,8 @@ public:
|
||||
auto copy() const -> own<Trap*>;
|
||||
|
||||
auto message() const -> Message;
|
||||
auto origin() const -> own<Frame*>; // may be null
|
||||
auto trace() const -> vec<Frame*>; // may be empty, origin first
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user