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