[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}
This commit is contained in:
clemensh 2016-05-06 02:07:30 -07:00 committed by Commit bot
parent 3b7ff999f3
commit a4cd1eef0a
16 changed files with 353 additions and 257 deletions

View File

@ -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);

View File

@ -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,32 +1329,38 @@ 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::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);
}
void WasmFrame::Iterate(ObjectVisitor* v) const { IterateCompiledFrame(v); }
Address WasmFrame::GetCallerStackPointer() const {
return fp() + ExitFrameConstants::kCallerSPOffset;
}
Object* WasmFrame::wasm_obj() {
FixedArray* deopt_data = LookupCode()->deoptimization_data();
DCHECK(deopt_data->length() == 2);
return deopt_data->get(0);
}
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;
}
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 {

View File

@ -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);

View File

@ -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) \

View File

@ -433,14 +433,13 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
Code* code = wasm_frame->unchecked_code();
Handle<AbstractCode> abstract_code =
Handle<AbstractCode>(AbstractCode::cast(code));
Handle<JSFunction> fun = factory()->NewFunction(
factory()->NewStringFromAsciiChecked("<WASM>"));
int offset =
static_cast<int>(wasm_frame->pc() - code->instruction_start());
elements = MaybeGrow(this, elements, cursor, cursor + 4);
// TODO(jfb) Pass module object.
elements->set(cursor++, *factory()->undefined_value());
elements->set(cursor++, *fun);
elements->set(cursor++, wasm_frame->wasm_obj());
elements->set(cursor++, Smi::FromInt(wasm_frame->function_index()));
elements->set(cursor++, *abstract_code);
elements->set(cursor++, Internals::IntToSmi(0));
elements->set(cursor++, Smi::FromInt(offset));
frames_seen++;
} break;
@ -542,65 +541,64 @@ class CaptureStackTraceHelper {
}
}
Handle<JSObject> NewStackFrameObject(FrameSummary& summ) {
int position = summ.abstract_code()->SourcePosition(summ.code_offset());
return NewStackFrameObject(summ.function(), position,
summ.is_constructor());
}
Handle<JSObject> NewStackFrameObject(Handle<JSFunction> fun, int position,
bool is_constructor) {
Handle<JSObject> stack_frame =
factory()->NewJSObject(isolate_->object_function());
Handle<Script> script(Script::cast(fun->shared()->script()));
// TODO(clemensh): this can be changed to a DCHECK once also WASM frames
// define a script
if (!fun->shared()->script()->IsUndefined()) {
Handle<Script> script(Script::cast(fun->shared()->script()));
if (!line_key_.is_null()) {
int script_line_offset = script->line_offset();
int line_number = Script::GetLineNumber(script, position);
// line_number is already shifted by the script_line_offset.
int relative_line_number = line_number - script_line_offset;
if (!column_key_.is_null() && relative_line_number >= 0) {
Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
int start = (relative_line_number == 0)
? 0
: Smi::cast(line_ends->get(relative_line_number - 1))
->value() +
1;
int column_offset = position - start;
if (relative_line_number == 0) {
// For the case where the code is on the same line as the script
// tag.
column_offset += script->column_offset();
}
JSObject::AddProperty(
stack_frame, column_key_,
handle(Smi::FromInt(column_offset + 1), isolate_), NONE);
if (!line_key_.is_null()) {
int script_line_offset = script->line_offset();
int line_number = Script::GetLineNumber(script, position);
// line_number is already shifted by the script_line_offset.
int relative_line_number = line_number - script_line_offset;
if (!column_key_.is_null() && relative_line_number >= 0) {
Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
int start =
(relative_line_number == 0)
? 0
: Smi::cast(line_ends->get(relative_line_number - 1))->value() +
1;
int column_offset = position - start;
if (relative_line_number == 0) {
// For the case where the code is on the same line as the script tag.
column_offset += script->column_offset();
}
JSObject::AddProperty(stack_frame, line_key_,
handle(Smi::FromInt(line_number + 1), isolate_),
JSObject::AddProperty(stack_frame, column_key_,
handle(Smi::FromInt(column_offset + 1), isolate_),
NONE);
}
JSObject::AddProperty(stack_frame, line_key_,
handle(Smi::FromInt(line_number + 1), isolate_),
NONE);
}
if (!script_id_key_.is_null()) {
JSObject::AddProperty(stack_frame, script_id_key_,
handle(Smi::FromInt(script->id()), isolate_),
NONE);
}
if (!script_id_key_.is_null()) {
JSObject::AddProperty(stack_frame, script_id_key_,
handle(Smi::FromInt(script->id()), isolate_), NONE);
}
if (!script_name_key_.is_null()) {
JSObject::AddProperty(stack_frame, script_name_key_,
handle(script->name(), isolate_), NONE);
}
if (!script_name_key_.is_null()) {
JSObject::AddProperty(stack_frame, script_name_key_,
handle(script->name(), isolate_), NONE);
}
if (!script_name_or_source_url_key_.is_null()) {
Handle<Object> result = Script::GetNameOrSourceURL(script);
JSObject::AddProperty(stack_frame, script_name_or_source_url_key_,
result, NONE);
}
if (!script_name_or_source_url_key_.is_null()) {
Handle<Object> result = Script::GetNameOrSourceURL(script);
JSObject::AddProperty(stack_frame, script_name_or_source_url_key_, result,
NONE);
}
if (!eval_key_.is_null()) {
Handle<Object> is_eval = factory()->ToBoolean(
script->compilation_type() == Script::COMPILATION_TYPE_EVAL);
JSObject::AddProperty(stack_frame, eval_key_, is_eval, NONE);
}
if (!eval_key_.is_null()) {
Handle<Object> is_eval = factory()->ToBoolean(
script->compilation_type() == Script::COMPILATION_TYPE_EVAL);
JSObject::AddProperty(stack_frame, eval_key_, is_eval, NONE);
}
if (!function_key_.is_null()) {
@ -613,6 +611,35 @@ class CaptureStackTraceHelper {
JSObject::AddProperty(stack_frame, constructor_key_, is_constructor_obj,
NONE);
}
return stack_frame;
}
Handle<JSObject> NewStackFrameObject(WasmFrame* frame) {
Handle<JSObject> stack_frame =
factory()->NewJSObject(isolate_->object_function());
if (!function_key_.is_null()) {
Handle<Object> fun_name = handle(frame->function_name(), isolate_);
if (fun_name->IsUndefined())
fun_name = isolate_->factory()->InternalizeUtf8String(
Vector<const char>("<WASM>"));
JSObject::AddProperty(stack_frame, function_key_, fun_name, NONE);
}
// Encode the function index as line number.
if (!line_key_.is_null()) {
JSObject::AddProperty(
stack_frame, line_key_,
isolate_->factory()->NewNumberFromInt(frame->function_index()), NONE);
}
// Encode the byte offset as column.
if (!column_key_.is_null()) {
Code* code = frame->LookupCode();
int offset = static_cast<int>(frame->pc() - code->instruction_start());
int position = code->SourcePosition(offset);
JSObject::AddProperty(stack_frame, column_key_,
isolate_->factory()->NewNumberFromInt(position),
NONE);
}
return stack_frame;
}
@ -691,29 +718,34 @@ Handle<JSArray> Isolate::CaptureCurrentStackTrace(
// Ensure no negative values.
int limit = Max(frame_limit, 0);
Handle<JSArray> stack_trace = factory()->NewJSArray(frame_limit);
Handle<FixedArray> stack_trace_elems(
FixedArray::cast(stack_trace->elements()), this);
StackTraceFrameIterator it(this);
int frames_seen = 0;
while (!it.done() && (frames_seen < limit)) {
for (StackTraceFrameIterator it(this); !it.done() && (frames_seen < limit);
it.Advance()) {
StandardFrame* frame = it.frame();
// Set initial size to the maximum inlining level + 1 for the outermost
// function.
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
frame->Summarize(&frames);
for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
Handle<JSFunction> fun = frames[i].function();
// Filter frames from other security contexts.
if (!(options & StackTrace::kExposeFramesAcrossSecurityOrigins) &&
!this->context()->HasSameSecurityTokenAs(fun->context())) continue;
int position =
frames[i].abstract_code()->SourcePosition(frames[i].code_offset());
Handle<JSObject> stack_frame =
helper.NewStackFrameObject(fun, position, frames[i].is_constructor());
FixedArray::cast(stack_trace->elements())->set(frames_seen, *stack_frame);
if (frame->is_java_script()) {
// Set initial size to the maximum inlining level + 1 for the outermost
// function.
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
JavaScriptFrame::cast(frame)->Summarize(&frames);
for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
Handle<JSFunction> fun = frames[i].function();
// Filter frames from other security contexts.
if (!(options & StackTrace::kExposeFramesAcrossSecurityOrigins) &&
!this->context()->HasSameSecurityTokenAs(fun->context()))
continue;
Handle<JSObject> new_frame_obj = helper.NewStackFrameObject(frames[i]);
stack_trace_elems->set(frames_seen, *new_frame_obj);
frames_seen++;
}
} else {
WasmFrame* wasm_frame = WasmFrame::cast(frame);
Handle<JSObject> new_frame_obj = helper.NewStackFrameObject(wasm_frame);
stack_trace_elems->set(frames_seen, *new_frame_obj);
frames_seen++;
}
it.Advance();
}
stack_trace->set_length(Smi::FromInt(frames_seen));
@ -1332,15 +1364,20 @@ void Isolate::PrintCurrentStackTrace(FILE* out) {
InterpretedFrame* iframe = reinterpret_cast<InterpretedFrame*>(frame);
pos = iframe->GetBytecodeArray()->SourcePosition(
iframe->GetBytecodeOffset());
} else {
} else if (frame->is_java_script()) {
Code* code = frame->LookupCode();
int offset = static_cast<int>(frame->pc() - code->instruction_start());
pos = frame->LookupCode()->SourcePosition(offset);
} else {
DCHECK(frame->is_wasm());
// TODO(clemensh): include wasm frames here
continue;
}
JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
Handle<Object> pos_obj(Smi::FromInt(pos), this);
// Fetch function and receiver.
Handle<JSFunction> fun(frame->function());
Handle<Object> recv(frame->receiver(), this);
Handle<JSFunction> fun(js_frame->function());
Handle<Object> recv(js_frame->receiver(), this);
// Advance to the next JavaScript frame and determine if the
// current frame is the top-level frame.
it.Advance();
@ -1355,31 +1392,29 @@ void Isolate::PrintCurrentStackTrace(FILE* out) {
}
}
bool Isolate::ComputeLocation(MessageLocation* target) {
StackTraceFrameIterator it(this);
if (!it.done()) {
StandardFrame* frame = it.frame();
JSFunction* fun = frame->function();
Object* script = fun->shared()->script();
if (script->IsScript() &&
!(Script::cast(script)->source()->IsUndefined())) {
Handle<Script> casted_script(Script::cast(script));
// Compute the location from the function and the relocation info of the
// baseline code. For optimized code this will use the deoptimization
// information to get canonical location information.
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
frame->Summarize(&frames);
FrameSummary& summary = frames.last();
int pos = summary.abstract_code()->SourcePosition(summary.code_offset());
*target = MessageLocation(casted_script, pos, pos + 1, handle(fun));
return true;
}
if (it.done()) return false;
StandardFrame* frame = it.frame();
// TODO(clemensh): handle wasm frames
if (!frame->is_java_script()) return false;
JSFunction* fun = JavaScriptFrame::cast(frame)->function();
Object* script = fun->shared()->script();
if (!script->IsScript() || (Script::cast(script)->source()->IsUndefined())) {
return false;
}
return false;
Handle<Script> casted_script(Script::cast(script));
// Compute the location from the function and the relocation info of the
// baseline code. For optimized code this will use the deoptimization
// information to get canonical location information.
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
JavaScriptFrame::cast(frame)->Summarize(&frames);
FrameSummary& summary = frames.last();
int pos = summary.abstract_code()->SourcePosition(summary.code_offset());
*target = MessageLocation(casted_script, pos, pos + 1, handle(fun));
return true;
}
bool Isolate::ComputeLocationFromException(MessageLocation* target,
Handle<Object> exception) {
if (!exception->IsJSObject()) return false;
@ -1420,8 +1455,12 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
int elements_limit = Smi::cast(simple_stack_trace->length())->value();
for (int i = 1; i < elements_limit; i += 4) {
Handle<JSFunction> fun =
handle(JSFunction::cast(elements->get(i + 1)), this);
Handle<Object> fun_obj = handle(elements->get(i + 1), this);
if (fun_obj->IsSmi()) {
// TODO(clemensh): handle wasm frames
return false;
}
Handle<JSFunction> fun = Handle<JSFunction>::cast(fun_obj);
if (!fun->shared()->IsSubjectToDebugging()) continue;
Object* script = fun->shared()->script();

View File

@ -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)));
}

View File

@ -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());

View File

@ -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") \

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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) \

View File

@ -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);
}

View File

@ -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);

View File

@ -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"]
]);
}
})();