Commit a4cd1eef authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Make wasm info available on the stack trace

This changes different locations to extract the reference to the wasm
object and the function index from the stack trace, and make it
available through all the APIs which process stack traces.
The javascript CallSite object now has the new methods isWasm(),
getWasmObject() and getWasmFunctionIndex(); the byte offset is
available via getPosition().

Function names of wasm frames should be fully functional with this
commit, position information works reliably for calls, but not for
traps like unreachable or out-of-bounds accesses.

R=titzer@chromium.org, yangguo@chromium.org

Review-Url: https://codereview.chromium.org/1909353002
Cr-Commit-Position: refs/heads/master@{#36067}
parent 3b7ff999
......@@ -3014,13 +3014,8 @@ class WasmCompilationUnit {
isolate_->factory()->NewFixedArray(2, TENURED);
if (!module_env_->instance->js_object.is_null()) {
deopt_data->set(0, *module_env_->instance->js_object);
deopt_data->set(1, Smi::FromInt(function_->func_index));
} else if (info_.GetDebugName().get() != nullptr) {
MaybeHandle<String> maybe_name = isolate_->factory()->NewStringFromUtf8(
CStrVector(info_.GetDebugName().get()));
if (!maybe_name.is_null())
deopt_data->set(0, *maybe_name.ToHandleChecked());
}
deopt_data->set(1, Smi::FromInt(function_->func_index));
deopt_data->set_length(2);
code->set_deoptimization_data(*deopt_data);
......
......@@ -16,6 +16,7 @@
#include "src/safepoint-table.h"
#include "src/string-stream.h"
#include "src/vm-state-inl.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
......@@ -617,21 +618,6 @@ void ExitFrame::FillState(Address fp, Address sp, State* state) {
state->constant_pool_address = NULL;
}
void StandardFrame::Summarize(List<FrameSummary>* functions,
FrameSummary::Mode mode) const {
DCHECK(functions->length() == 0);
// default implementation: no summary added
}
JSFunction* StandardFrame::function() const {
// this default implementation is overridden by JS and WASM frames
return nullptr;
}
Object* StandardFrame::receiver() const {
return isolate()->heap()->undefined_value();
}
Address StandardFrame::GetExpressionAddress(int n) const {
const int offset = StandardFrameConstants::kExpressionsOffset;
return fp() + offset - n * kPointerSize;
......@@ -1343,30 +1329,36 @@ Code* WasmFrame::unchecked_code() const {
return static_cast<Code*>(isolate()->FindCodeObject(pc()));
}
JSFunction* WasmFrame::function() const {
// TODO(clemensh): generate the right JSFunctions once per wasm function and
// cache them
Factory* factory = isolate()->factory();
Handle<JSFunction> fun =
factory->NewFunction(factory->NewStringFromAsciiChecked("<WASM>"));
return *fun;
void WasmFrame::Iterate(ObjectVisitor* v) const { IterateCompiledFrame(v); }
Address WasmFrame::GetCallerStackPointer() const {
return fp() + ExitFrameConstants::kCallerSPOffset;
}
void WasmFrame::Summarize(List<FrameSummary>* functions,
FrameSummary::Mode mode) const {
DCHECK(functions->length() == 0);
Code* code = LookupCode();
int offset = static_cast<int>(pc() - code->instruction_start());
AbstractCode* abstract_code = AbstractCode::cast(code);
Handle<JSFunction> fun(function(), isolate());
FrameSummary summary(receiver(), *fun, abstract_code, offset, false);
functions->Add(summary);
Object* WasmFrame::wasm_obj() {
FixedArray* deopt_data = LookupCode()->deoptimization_data();
DCHECK(deopt_data->length() == 2);
return deopt_data->get(0);
}
void WasmFrame::Iterate(ObjectVisitor* v) const { IterateCompiledFrame(v); }
uint32_t WasmFrame::function_index() {
FixedArray* deopt_data = LookupCode()->deoptimization_data();
DCHECK(deopt_data->length() == 2);
Object* func_index_obj = deopt_data->get(1);
if (func_index_obj->IsUndefined()) return static_cast<uint32_t>(-1);
if (func_index_obj->IsSmi()) return Smi::cast(func_index_obj)->value();
DCHECK(func_index_obj->IsHeapNumber());
uint32_t val = static_cast<uint32_t>(-1);
func_index_obj->ToUint32(&val);
DCHECK(val != static_cast<uint32_t>(-1));
return val;
}
Address WasmFrame::GetCallerStackPointer() const {
return fp() + ExitFrameConstants::kCallerSPOffset;
Object* WasmFrame::function_name() {
Object* wasm_object = wasm_obj();
if (wasm_object->IsUndefined()) return wasm_object;
Handle<JSObject> wasm = handle(JSObject::cast(wasm_object));
return *wasm::GetWasmFunctionName(wasm, function_index());
}
namespace {
......
......@@ -635,8 +635,8 @@ class JavaScriptFrame;
class FrameSummary BASE_EMBEDDED {
public:
// Mode for StandardFrame::Summarize. Exact summary is required to produce an
// exact stack trace. It will trigger an assertion failure if that is not
// Mode for JavaScriptFrame::Summarize. Exact summary is required to produce
// an exact stack trace. It will trigger an assertion failure if that is not
// possible, e.g., because of missing deoptimization information. The
// approximate mode should produce a summary even without deoptimization
// information, but it might miss frames.
......@@ -684,15 +684,6 @@ class StandardFrame : public StackFrame {
return static_cast<StandardFrame*>(frame);
}
// Build a list with summaries for this frame including all inlined frames.
virtual void Summarize(
List<FrameSummary>* frames,
FrameSummary::Mode mode = FrameSummary::kExactSummary) const;
// Accessors.
virtual JSFunction* function() const;
virtual Object* receiver() const;
protected:
inline explicit StandardFrame(StackFrameIteratorBase* iterator);
......@@ -737,8 +728,14 @@ class JavaScriptFrame : public StandardFrame {
public:
Type type() const override { return JAVA_SCRIPT; }
JSFunction* function() const override;
Object* receiver() const override;
// Build a list with summaries for this frame including all inlined frames.
virtual void Summarize(
List<FrameSummary>* frames,
FrameSummary::Mode mode = FrameSummary::kExactSummary) const;
// Accessors.
virtual JSFunction* function() const;
virtual Object* receiver() const;
inline void set_receiver(Object* value);
......@@ -786,10 +783,6 @@ class JavaScriptFrame : public StandardFrame {
// Return a list with JSFunctions of this frame.
virtual void GetFunctions(List<JSFunction*>* functions) const;
void Summarize(
List<FrameSummary>* frames,
FrameSummary::Mode mode = FrameSummary::kExactSummary) const override;
// Lookup exception handler for current {pc}, returns -1 if none found. Also
// returns data associated with the handler site specific to the frame type:
// - JavaScriptFrame : Data is the stack depth at entry of the try-block.
......@@ -975,17 +968,16 @@ class WasmFrame : public StandardFrame {
// Determine the code for the frame.
Code* unchecked_code() const override;
Object* wasm_obj();
uint32_t function_index();
Object* function_name();
static WasmFrame* cast(StackFrame* frame) {
DCHECK(frame->is_wasm());
return static_cast<WasmFrame*>(frame);
}
JSFunction* function() const override;
void Summarize(
List<FrameSummary>* frames,
FrameSummary::Mode mode = FrameSummary::kExactSummary) const override;
protected:
inline explicit WasmFrame(StackFrameIteratorBase* iterator);
......
......@@ -139,6 +139,8 @@
V(call_site_position_symbol) \
V(call_site_receiver_symbol) \
V(call_site_strict_symbol) \
V(call_site_wasm_obj_symbol) \
V(call_site_wasm_func_index_symbol) \
V(class_end_position_symbol) \
V(class_start_position_symbol) \
V(detailed_stack_trace_symbol) \
......
This diff is collapsed.
......@@ -23,6 +23,10 @@ var callSitePositionSymbol =
utils.ImportNow("call_site_position_symbol");
var callSiteStrictSymbol =
utils.ImportNow("call_site_strict_symbol");
var callSiteWasmObjectSymbol =
utils.ImportNow("call_site_wasm_obj_symbol");
var callSiteWasmFunctionIndexSymbol =
utils.ImportNow("call_site_wasm_func_index_symbol");
var Float32x4ToString;
var formattedStackTraceSymbol =
utils.ImportNow("formatted_stack_trace_symbol");
......@@ -553,7 +557,9 @@ function GetStackTraceLine(recv, fun, pos, isGlobal) {
// Error implementation
function CallSite(receiver, fun, pos, strict_mode) {
if (!IS_FUNCTION(fun)) {
// For wasm frames, receiver is the wasm object and fun is the function index
// instead of an actual function.
if (!IS_FUNCTION(fun) && !IS_NUMBER(fun)) {
throw MakeTypeError(kCallSiteExpectsFunction, typeof fun);
}
......@@ -561,14 +567,19 @@ function CallSite(receiver, fun, pos, strict_mode) {
return new CallSite(receiver, fun, pos, strict_mode);
}
SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
SET_PRIVATE(this, callSiteFunctionSymbol, fun);
if (IS_FUNCTION(fun)) {
SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
SET_PRIVATE(this, callSiteFunctionSymbol, fun);
} else {
SET_PRIVATE(this, callSiteWasmObjectSymbol, receiver);
SET_PRIVATE(this, callSiteWasmFunctionIndexSymbol, TO_UINT32(fun));
}
SET_PRIVATE(this, callSitePositionSymbol, TO_INT32(pos));
SET_PRIVATE(this, callSiteStrictSymbol, TO_BOOLEAN(strict_mode));
}
function CheckCallSite(obj, name) {
if (!IS_RECEIVER(obj) || !HAS_PRIVATE(obj, callSiteFunctionSymbol)) {
if (!IS_RECEIVER(obj) || !HAS_PRIVATE(obj, callSitePositionSymbol)) {
throw MakeTypeError(kCallSiteMethod, name);
}
}
......@@ -619,6 +630,12 @@ function CallSiteGetScriptNameOrSourceURL() {
function CallSiteGetFunctionName() {
// See if the function knows its own name
CheckCallSite(this, "getFunctionName");
if (HAS_PRIVATE(this, callSiteWasmObjectSymbol)) {
var wasm = GET_PRIVATE(this, callSiteWasmObjectSymbol);
var func_index = GET_PRIVATE(this, callSiteWasmFunctionIndexSymbol);
if (IS_UNDEFINED(wasm)) return "<WASM>";
return %WasmGetFunctionName(wasm, func_index);
}
return %CallSiteGetFunctionNameRT(this);
}
......@@ -635,6 +652,9 @@ function CallSiteGetFileName() {
}
function CallSiteGetLineNumber() {
if (HAS_PRIVATE(this, callSiteWasmObjectSymbol)) {
return GET_PRIVATE(this, callSiteWasmFunctionIndexSymbol);
}
CheckCallSite(this, "getLineNumber");
return %CallSiteGetLineNumberRT(this);
}
......@@ -655,6 +675,13 @@ function CallSiteIsConstructor() {
}
function CallSiteToString() {
if (HAS_PRIVATE(this, callSiteWasmObjectSymbol)) {
var funName = this.getFunctionName();
var funcIndex = GET_PRIVATE(this, callSiteWasmFunctionIndexSymbol);
var pos = this.getPosition();
return funName + " (<WASM>:" + funcIndex + ":" + pos + ")";
}
var fileName;
var fileLocation = "";
if (this.isNative()) {
......@@ -799,7 +826,7 @@ function GetStackFrames(raw_stack) {
var fun = raw_stack[i + 1];
var code = raw_stack[i + 2];
var pc = raw_stack[i + 3];
var pos = %_IsSmi(code) ? code : %FunctionGetPositionForOffset(code, pc);
var pos = %FunctionGetPositionForOffset(code, pc);
sloppy_frames--;
frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
}
......
......@@ -9,6 +9,7 @@
#include "src/isolate-inl.h"
#include "src/keys.h"
#include "src/string-builder.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
......@@ -169,11 +170,25 @@ CallSite::CallSite(Isolate* isolate, Handle<JSObject> call_site_obj)
: isolate_(isolate) {
Handle<Object> maybe_function = JSObject::GetDataProperty(
call_site_obj, isolate->factory()->call_site_function_symbol());
if (!maybe_function->IsJSFunction()) return;
if (maybe_function->IsJSFunction()) {
// javascript
fun_ = Handle<JSFunction>::cast(maybe_function);
receiver_ = JSObject::GetDataProperty(
call_site_obj, isolate->factory()->call_site_receiver_symbol());
} else {
Handle<Object> maybe_wasm_func_index = JSObject::GetDataProperty(
call_site_obj, isolate->factory()->call_site_wasm_func_index_symbol());
if (!maybe_wasm_func_index->IsSmi()) {
// invalid: neither javascript nor wasm
return;
}
// wasm
wasm_obj_ = Handle<JSObject>::cast(JSObject::GetDataProperty(
call_site_obj, isolate->factory()->call_site_wasm_obj_symbol()));
wasm_func_index_ = Smi::cast(*maybe_wasm_func_index)->value();
DCHECK(static_cast<int>(wasm_func_index_) >= 0);
}
fun_ = Handle<JSFunction>::cast(maybe_function);
receiver_ = JSObject::GetDataProperty(
call_site_obj, isolate->factory()->call_site_receiver_symbol());
CHECK(JSObject::GetDataProperty(
call_site_obj, isolate->factory()->call_site_position_symbol())
->ToInt32(&pos_));
......@@ -181,15 +196,22 @@ CallSite::CallSite(Isolate* isolate, Handle<JSObject> call_site_obj)
Handle<Object> CallSite::GetFileName() {
Handle<Object> script(fun_->shared()->script(), isolate_);
if (script->IsScript()) {
return Handle<Object>(Handle<Script>::cast(script)->name(), isolate_);
}
return isolate_->factory()->null_value();
if (!IsJavaScript()) return isolate_->factory()->null_value();
Object* script = fun_->shared()->script();
if (!script->IsScript()) return isolate_->factory()->null_value();
return Handle<Object>(Script::cast(script)->name(), isolate_);
}
Handle<Object> CallSite::GetFunctionName() {
if (IsWasm()) {
if (wasm_obj_->IsUndefined()) return isolate_->factory()->null_value();
// wasm_obj_ can be a String if we generate WASM code directly in a test
// case.
if (wasm_obj_->IsString()) return wasm_obj_;
return wasm::GetWasmFunctionName(Handle<JSObject>::cast(wasm_obj_),
wasm_func_index_);
}
Handle<String> result = JSFunction::GetName(fun_);
if (result->length() != 0) return result;
......@@ -202,19 +224,16 @@ Handle<Object> CallSite::GetFunctionName() {
return isolate_->factory()->null_value();
}
Handle<Object> CallSite::GetScriptNameOrSourceUrl() {
Handle<Object> script_obj(fun_->shared()->script(), isolate_);
if (script_obj->IsScript()) {
Handle<Script> script = Handle<Script>::cast(script_obj);
Object* source_url = script->source_url();
if (source_url->IsString()) return Handle<Object>(source_url, isolate_);
return Handle<Object>(script->name(), isolate_);
}
return isolate_->factory()->null_value();
if (!IsJavaScript()) return isolate_->factory()->null_value();
Object* script_obj = fun_->shared()->script();
if (!script_obj->IsScript()) return isolate_->factory()->null_value();
Handle<Script> script(Script::cast(script_obj), isolate_);
Object* source_url = script->source_url();
if (source_url->IsString()) return Handle<Object>(source_url, isolate_);
return Handle<Object>(script->name(), isolate_);
}
bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
Handle<JSFunction> fun,
LookupIterator::Configuration config) {
......@@ -234,7 +253,7 @@ bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
Handle<Object> CallSite::GetMethodName() {
if (receiver_->IsNull() || receiver_->IsUndefined()) {
if (!IsJavaScript() || receiver_->IsNull() || receiver_->IsUndefined()) {
return isolate_->factory()->null_value();
}
Handle<JSReceiver> receiver =
......@@ -293,7 +312,7 @@ Handle<Object> CallSite::GetMethodName() {
int CallSite::GetLineNumber() {
if (pos_ >= 0) {
if (pos_ >= 0 && IsJavaScript()) {
Handle<Object> script_obj(fun_->shared()->script(), isolate_);
if (script_obj->IsScript()) {
Handle<Script> script = Handle<Script>::cast(script_obj);
......@@ -305,7 +324,7 @@ int CallSite::GetLineNumber() {
int CallSite::GetColumnNumber() {
if (pos_ >= 0) {
if (pos_ >= 0 && IsJavaScript()) {
Handle<Object> script_obj(fun_->shared()->script(), isolate_);
if (script_obj->IsScript()) {
Handle<Script> script = Handle<Script>::cast(script_obj);
......@@ -317,6 +336,7 @@ int CallSite::GetColumnNumber() {
bool CallSite::IsNative() {
if (!IsJavaScript()) return false;
Handle<Object> script(fun_->shared()->script(), isolate_);
return script->IsScript() &&
Handle<Script>::cast(script)->type() == Script::TYPE_NATIVE;
......@@ -324,12 +344,14 @@ bool CallSite::IsNative() {
bool CallSite::IsToplevel() {
if (IsWasm()) return false;
return receiver_->IsJSGlobalProxy() || receiver_->IsNull() ||
receiver_->IsUndefined();
}
bool CallSite::IsEval() {
if (!IsJavaScript()) return false;
Handle<Object> script(fun_->shared()->script(), isolate_);
return script->IsScript() &&
Handle<Script>::cast(script)->compilation_type() ==
......@@ -338,7 +360,7 @@ bool CallSite::IsEval() {
bool CallSite::IsConstructor() {
if (!receiver_->IsJSObject()) return false;
if (!IsJavaScript() || !receiver_->IsJSObject()) return false;
Handle<Object> constructor =
JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
isolate_->factory()->constructor_string());
......
......@@ -59,13 +59,16 @@ class CallSite {
bool IsEval();
bool IsConstructor();
bool IsValid() { return !fun_.is_null(); }
bool IsJavaScript() { return !fun_.is_null(); }
bool IsWasm() { return !wasm_obj_.is_null(); }
private:
Isolate* isolate_;
Handle<Object> receiver_;
Handle<JSFunction> fun_;
int32_t pos_;
int32_t pos_ = -1;
Handle<JSObject> wasm_obj_;
uint32_t wasm_func_index_ = static_cast<uint32_t>(-1);
};
#define MESSAGE_TEMPLATES(T) \
......@@ -96,7 +99,7 @@ class CallSite {
T(CalledOnNonObject, "% called on non-object") \
T(CalledOnNullOrUndefined, "% called on null or undefined") \
T(CallSiteExpectsFunction, \
"CallSite expects function as second argument, got %") \
"CallSite expects function or number as second argument, got %") \
T(CallSiteMethod, "CallSite method % expects CallSite as receiver") \
T(CannotConvertToPrimitive, "Cannot convert object to primitive value") \
T(CannotPreventExt, "Cannot prevent extensions") \
......
......@@ -230,9 +230,9 @@ void AllocationTracker::AllocationEvent(Address addr, int size) {
Isolate* isolate = heap->isolate();
int length = 0;
StackTraceFrameIterator it(isolate);
JavaScriptFrameIterator it(isolate);
while (!it.done() && length < kMaxAllocationTraceLength) {
StandardFrame* frame = it.frame();
JavaScriptFrame* frame = it.frame();
SharedFunctionInfo* shared = frame->function()->shared();
SnapshotObjectId id = ids_->FindOrAddEntry(
shared->address(), shared->Size(), false);
......
......@@ -155,10 +155,10 @@ SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack() {
AllocationNode* node = &profile_root_;
std::vector<SharedFunctionInfo*> stack;
StackTraceFrameIterator it(isolate_);
JavaScriptFrameIterator it(isolate_);
int frames_captured = 0;
while (!it.done() && frames_captured < stack_depth_) {
StandardFrame* frame = it.frame();
JavaScriptFrame* frame = it.frame();
SharedFunctionInfo* shared = frame->function()->shared();
stack.push_back(shared);
......
......@@ -11,6 +11,7 @@
#include "src/isolate-inl.h"
#include "src/messages.h"
#include "src/profiler/cpu-profiler.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
......@@ -302,5 +303,15 @@ RUNTIME_FUNCTION(Runtime_FunctionToString) {
: *JSFunction::ToString(Handle<JSFunction>::cast(function));
}
RUNTIME_FUNCTION(Runtime_WasmGetFunctionName) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, wasm, 0);
CONVERT_SMI_ARG_CHECKED(func_index, 1);
return *wasm::GetWasmFunctionName(wasm, func_index);
}
} // namespace internal
} // namespace v8
......@@ -334,15 +334,15 @@ RUNTIME_FUNCTION(Runtime_FormatMessageString) {
return *result;
}
#define CALLSITE_GET(NAME, RETURN) \
RUNTIME_FUNCTION(Runtime_CallSite##NAME##RT) { \
HandleScope scope(isolate); \
DCHECK(args.length() == 1); \
CONVERT_ARG_HANDLE_CHECKED(JSObject, call_site_obj, 0); \
Handle<String> result; \
CallSite call_site(isolate, call_site_obj); \
RUNTIME_ASSERT(call_site.IsValid()); \
return RETURN(call_site.NAME(), isolate); \
#define CALLSITE_GET(NAME, RETURN) \
RUNTIME_FUNCTION(Runtime_CallSite##NAME##RT) { \
HandleScope scope(isolate); \
DCHECK(args.length() == 1); \
CONVERT_ARG_HANDLE_CHECKED(JSObject, call_site_obj, 0); \
Handle<String> result; \
CallSite call_site(isolate, call_site_obj); \
RUNTIME_ASSERT(call_site.IsJavaScript() || call_site.IsWasm()); \
return RETURN(call_site.NAME(), isolate); \
}
static inline Object* ReturnDereferencedHandle(Handle<Object> obj,
......
......@@ -314,7 +314,8 @@ namespace internal {
F(GetOrdinaryHasInstance, 0, 1) \
F(GetAndResetRuntimeCallStats, 0, 1) \
F(EnqueueMicrotask, 1, 1) \
F(RunMicrotasks, 0, 1)
F(RunMicrotasks, 0, 1) \
F(WasmGetFunctionName, 2, 1)
#define FOR_EACH_INTRINSIC_JSON(F) \
F(QuoteJSONString, 1, 1) \
......
......@@ -23,10 +23,11 @@ namespace {
do { \
const char* exp_ = (exp); \
const char* found_ = (found); \
if (V8_UNLIKELY(strcmp(exp_, found_) != 0)) { \
DCHECK_NOT_NULL(exp); \
if (V8_UNLIKELY(found_ == nullptr || strcmp(exp_, found_) != 0)) { \
V8_Fatal(__FILE__, __LINE__, \
"Check failed: (%s) != (%s) ('%s' vs '%s').", #exp, #found, \
exp_, found_); \
exp_, found_ ? found_ : "<null>"); \
} \
} while (0)
......@@ -44,8 +45,9 @@ void PrintStackTrace(v8::Local<v8::StackTrace> stack) {
}
struct ExceptionInfo {
const char* funcName;
int lineNr;
const char* func_name;
int line_nr;
int column;
};
template <int N>
......@@ -64,8 +66,9 @@ void CheckExceptionInfos(Isolate* isolate, Handle<Object> exc,
for (int frameNr = 0; frameNr < N; ++frameNr) {
v8::Local<v8::StackFrame> frame = stack->GetFrame(frameNr);
v8::String::Utf8Value funName(frame->GetFunctionName());
CHECK_CSTREQ(excInfos[frameNr].funcName, *funName);
CHECK_EQ(excInfos[frameNr].lineNr, frame->GetLineNumber());
CHECK_CSTREQ(excInfos[frameNr].func_name, *funName);
CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
}
}
......@@ -83,7 +86,8 @@ TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
sigs.v_v(),
"(function js() {\n function a() {\n throw new Error(); };\n a(); })");
BUILD(comp1, WASM_CALL_FUNCTION0(js_throwing_index));
// Add a nop such that we don't always get position 1.
BUILD(comp1, WASM_NOP, WASM_CALL_FUNCTION0(js_throwing_index));
uint32_t wasm_index = comp1.CompileAndAdd();
WasmFunctionCompiler comp2(sigs.v_v(), &module);
......@@ -108,11 +112,12 @@ TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
// Line number is 1-based, with 0 == kNoLineNumberInfo.
ExceptionInfo expected_exceptions[] = {
{"a", 3}, // Comment to prevent clang-format complaints
{"js", 4}, // -
{"<WASM>", 0}, // -
{"<WASM>", 0}, // -
{"callFn", 1}};
{"a", 3, 8}, // -
{"js", 4, 2}, // -
{"<WASM>", static_cast<int>(wasm_index), 2}, // -
{"<WASM>", static_cast<int>(wasm_index_2), 1}, // -
{"callFn", 1, 24} // -
};
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
expected_exceptions);
}
......@@ -152,9 +157,11 @@ TEST(CollectDetailedWasmStack_WasmError) {
// Line number is 1-based, with 0 == kNoLineNumberInfo.
ExceptionInfo expected_exceptions[] = {
{"<WASM>", 0}, // Comment to prevent clang-format complaints.
{"<WASM>", 0},
{"callFn", 1}};
// TODO(clemens): position should be 1
{"<WASM>", static_cast<int>(wasm_index), -1}, // -
{"<WASM>", static_cast<int>(wasm_index_2), 1}, // -
{"callFn", 1, 24} //-
};
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
expected_exceptions);
}
......@@ -495,17 +495,12 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
Handle<Code> code = info.code();
// Length is always 2, since usually <wasm_obj, func_index> is stored in the
// deopt data. Here, we store <func_name, undef> instead.
// deopt data. Here, we only store the function index.
DCHECK(code->deoptimization_data() == nullptr ||
code->deoptimization_data()->length() == 0);
Handle<FixedArray> deopt_data =
isolate()->factory()->NewFixedArray(2, TENURED);
if (debug_name_.start() != nullptr) {
MaybeHandle<String> maybe_name =
isolate()->factory()->NewStringFromUtf8(debug_name_, TENURED);
if (!maybe_name.is_null())
deopt_data->set(0, *maybe_name.ToHandleChecked());
}
deopt_data->set(1, Smi::FromInt(function_index_));
deopt_data->set_length(2);
code->set_deoptimization_data(*deopt_data);
......
......@@ -16,14 +16,23 @@ function stripPath(s) {
function verifyStack(frames, expected) {
assertEquals(expected.length, frames.length, "number of frames mismatch");
expected.forEach(function(exp, i) {
assertEquals(exp[0], frames[i].getFunctionName(),
"["+i+"].getFunctionName()");
assertEquals(exp[1], frames[i].getLineNumber(),
"["+i+"].getLineNumber()");
assertContains(exp[2], frames[i].getFileName(),
"["+i+"].getFileName()");
assertContains(exp[3], frames[i].toString(),
"["+i+"].toString()");
if (exp[1] != "?") {
assertEquals(exp[1], frames[i].getFunctionName(),
"["+i+"].getFunctionName()");
}
assertEquals(exp[2], frames[i].getLineNumber(), "["+i+"].getLineNumber()");
if (exp[0])
assertEquals(exp[3], frames[i].getPosition(),
"["+i+"].getPosition()");
assertContains(exp[4], frames[i].getFileName(), "["+i+"].getFileName()");
var toString;
if (exp[0]) {
var funName = exp[1] == "?" ? "" : exp[1];
toString = funName + " (<WASM>:" + exp[2] + ":" + exp[3] + ")";
} else {
toString = exp[4] + ":" + exp[2] + ":";
}
assertContains(toString, frames[i].toString(), "["+i+"].toString()");
});
}
......@@ -46,13 +55,13 @@ builder.addFunction("exec_unreachable", kSig_v_v)
.addBody([kExprUnreachable])
.exportAs("exec_unreachable");
// make this function unnamed, just to test also this case
// Make this function unnamed, just to test also this case.
var mem_oob_func = builder.addFunction(undefined, kSig_v_v)
// access the memory at offset -1
// Access the memory at offset -1, to provoke a trap.
.addBody([kExprI32Const, 0x7f, kExprI32LoadMem8S, 0, 0])
.exportAs("mem_out_of_bounds");
// call the mem_out_of_bounds function, in order to have two WASM stack frames
// Call the mem_out_of_bounds function, in order to have two WASM stack frames.
builder.addFunction("call_mem_out_of_bounds", kSig_v_v)
.addBody([kExprCallFunction, kArity0, mem_oob_func.index])
.exportAs("call_mem_out_of_bounds");
......@@ -62,10 +71,10 @@ var module = builder.instantiate({func: STACK});
(function testSimpleStack() {
var expected_string = "Error\n" +
// The line numbers below will change as this test gains / loses lines..
" at STACK (stack.js:33:11)\n" + // --
" at <WASM> (<anonymous>)\n" + // TODO(jfb): wasm stack here.
" at testSimpleStack (stack.js:70:18)\n" + // --
" at stack.js:72:3"; // --
" at STACK (stack.js:42:11)\n" + // --
" at main (<WASM>:0:1)\n" + // --
" at testSimpleStack (stack.js:79:18)\n" + // --
" at stack.js:81:3"; // --
module.exports.main();
assertEquals(expected_string, stripPath(stack));
......@@ -80,13 +89,12 @@ Error.prepareStackTrace = function(error, frames) {
(function testStackFrames() {
module.exports.main();
// TODO(clemensh): add a isWasm() method or similar, and test it
verifyStack(stack, [
// function line file toString
[ "STACK", 33, "stack.js", "stack.js:33:11"],
[ "<WASM>", null, null, "<WASM>"],
["testStackFrames", 81, "stack.js", "stack.js:81:18"],
[ null, 91, "stack.js", "stack.js:91:3"]
// isWasm function line pos file
[ false, "STACK", 42, 0, "stack.js"],
[ true, "main", 0, 1, null],
[ false, "testStackFrames", 90, 0, "stack.js"],
[ false, null, 99, 0, "stack.js"]
]);
})();
......@@ -97,10 +105,11 @@ Error.prepareStackTrace = function(error, frames) {
} catch (e) {
assertContains("unreachable", e.message);
verifyStack(e.stack, [
// function line file toString
[ "<WASM>", null, null, "<WASM>"],
["testWasmUnreachable", 95, "stack.js", "stack.js:95:20"],
[ null, 106, "stack.js", "stack.js:106:3"]
// isWasm function line pos file
// TODO(clemensh): pos should be 1
[ true, "exec_unreachable", 1, -1, null],
[ false, "testWasmUnreachable", 103, 0, "stack.js"],
[ false, null, 115, 0, "stack.js"]
]);
}
})();
......@@ -112,11 +121,12 @@ Error.prepareStackTrace = function(error, frames) {
} catch (e) {
assertContains("out of bounds", e.message);
verifyStack(e.stack, [
// function line file toString
[ "<WASM>", null, null, "<WASM>"],
[ "<WASM>", null, null, "<WASM>"],
["testWasmMemOutOfBounds", 110, "stack.js", "stack.js:110:20"],
[ null, 122, "stack.js", "stack.js:122:3"]
// isWasm function line pos file
// TODO(clemensh): pos should be 3
[ true, "?", 2, -1, null],
[ true, "call_mem_out_of_bounds", 3, 1, null],
[ false, "testWasmMemOutOfBounds", 119, 0, "stack.js"],
[ false, null, 132, 0, "stack.js"]
]);
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment