[wasm] Stack inspection support for asm.js frames

This CL fixes the debugger interface to provide correct (high-level)
information for asm.js frames.
It moves the computation of the source position from the FrameInspector
to the individual StackFrame implementations, such that we can easily
specualize it for certain frame types, and can potentially reuse this
in other locations.

Also, we are finalizing the setup of the wasm module earlier, before
executing the start function. This is required for correct stack traces
during the execution of the start function.

R=titzer@chromium.org, yangguo@chromium.org
BUG=v8:4203

Review-Url: https://codereview.chromium.org/2413693003
Cr-Commit-Position: refs/heads/master@{#40268}
This commit is contained in:
clemensh 2016-10-13 06:54:46 -07:00 committed by Commit bot
parent 18db69c38c
commit e902b69d88
6 changed files with 165 additions and 46 deletions

View File

@ -77,23 +77,11 @@ Handle<Object> FrameInspector::GetExpression(int index) {
: handle(frame_->GetExpression(index), isolate_); : handle(frame_->GetExpression(index), isolate_);
} }
int FrameInspector::GetSourcePosition() { int FrameInspector::GetSourcePosition() {
if (is_optimized_) return deoptimized_frame_->GetSourcePosition(); return is_optimized_ ? deoptimized_frame_->GetSourcePosition()
AbstractCode* code; : frame_->position();
int code_offset;
if (is_interpreted_) {
InterpretedFrame* frame = reinterpret_cast<InterpretedFrame*>(frame_);
code = AbstractCode::cast(frame->GetBytecodeArray());
code_offset = frame->GetBytecodeOffset();
} else {
code = AbstractCode::cast(frame_->LookupCode());
code_offset = static_cast<int>(frame_->pc() - code->instruction_start());
}
return code->SourcePosition(code_offset);
} }
bool FrameInspector::IsConstructor() { bool FrameInspector::IsConstructor() {
return is_optimized_ && !is_bottommost_ return is_optimized_ && !is_bottommost_
? deoptimized_frame_->HasConstructStub() ? deoptimized_frame_->HasConstructStub()

View File

@ -1878,6 +1878,15 @@ FrameMirror.prototype.func = function() {
}; };
FrameMirror.prototype.script = function() {
if (!this.script_) {
this.script_ = MakeMirror(this.details_.script());
}
return this.script_;
}
FrameMirror.prototype.receiver = function() { FrameMirror.prototype.receiver = function() {
return MakeMirror(this.details_.receiver()); return MakeMirror(this.details_.receiver());
}; };
@ -1954,12 +1963,9 @@ FrameMirror.prototype.sourcePosition = function() {
FrameMirror.prototype.sourceLocation = function() { FrameMirror.prototype.sourceLocation = function() {
var func = this.func(); var script = this.script();
if (func.resolved()) { if (script) {
var script = func.script(); return script.locationFromPosition(this.sourcePosition(), true);
if (script) {
return script.locationFromPosition(this.sourcePosition(), true);
}
} }
}; };

View File

@ -720,6 +720,12 @@ Object* StandardFrame::context() const {
return isolate()->heap()->undefined_value(); return isolate()->heap()->undefined_value();
} }
int StandardFrame::position() const {
AbstractCode* code = AbstractCode::cast(LookupCode());
int code_offset = static_cast<int>(pc() - code->instruction_start());
return code->SourcePosition(code_offset);
}
int StandardFrame::ComputeExpressionsCount() const { int StandardFrame::ComputeExpressionsCount() const {
Address base = GetExpressionAddress(0); Address base = GetExpressionAddress(0);
Address limit = sp() - kPointerSize; Address limit = sp() - kPointerSize;
@ -1335,6 +1341,12 @@ Object* OptimizedFrame::StackSlotAt(int index) const {
return Memory::Object_at(fp() + StackSlotOffsetRelativeToFp(index)); return Memory::Object_at(fp() + StackSlotOffsetRelativeToFp(index));
} }
int InterpretedFrame::position() const {
AbstractCode* code = AbstractCode::cast(GetBytecodeArray());
int code_offset = GetBytecodeOffset();
return code->SourcePosition(code_offset);
}
int InterpretedFrame::LookupExceptionHandlerInTable( int InterpretedFrame::LookupExceptionHandlerInTable(
int* context_register, HandlerTable::CatchPrediction* prediction) { int* context_register, HandlerTable::CatchPrediction* prediction) {
BytecodeArray* bytecode = function()->shared()->bytecode_array(); BytecodeArray* bytecode = function()->shared()->bytecode_array();
@ -1474,10 +1486,22 @@ uint32_t WasmFrame::function_index() const {
Script* WasmFrame::script() const { Script* WasmFrame::script() const {
Handle<JSObject> wasm(JSObject::cast(wasm_obj()), isolate()); Handle<JSObject> wasm(JSObject::cast(wasm_obj()), isolate());
if (wasm::WasmIsAsmJs(*wasm, isolate())) {
return *wasm::GetAsmWasmScript(wasm);
}
Handle<wasm::WasmDebugInfo> debug_info = wasm::GetDebugInfo(wasm); Handle<wasm::WasmDebugInfo> debug_info = wasm::GetDebugInfo(wasm);
return wasm::WasmDebugInfo::GetFunctionScript(debug_info, function_index()); return wasm::WasmDebugInfo::GetFunctionScript(debug_info, function_index());
} }
int WasmFrame::position() const {
int position = StandardFrame::position();
if (wasm::WasmIsAsmJs(wasm_obj(), isolate())) {
Handle<JSObject> wasm(JSObject::cast(wasm_obj()), isolate());
position = wasm::GetAsmWasmSourcePosition(wasm, function_index(), position);
}
return position;
}
int WasmFrame::LookupExceptionHandlerInTable(int* stack_slots) { int WasmFrame::LookupExceptionHandlerInTable(int* stack_slots) {
DCHECK_NOT_NULL(stack_slots); DCHECK_NOT_NULL(stack_slots);
Code* code = LookupCode(); Code* code = LookupCode();

View File

@ -734,6 +734,7 @@ class StandardFrame : public StackFrame {
virtual Object* receiver() const; virtual Object* receiver() const;
virtual Script* script() const; virtual Script* script() const;
virtual Object* context() const; virtual Object* context() const;
virtual int position() const;
// Access the expressions in the stack frame including locals. // Access the expressions in the stack frame including locals.
inline Object* GetExpression(int index) const; inline Object* GetExpression(int index) const;
@ -957,6 +958,9 @@ class InterpretedFrame : public JavaScriptFrame {
public: public:
Type type() const override { return INTERPRETED; } Type type() const override { return INTERPRETED; }
// Accessors.
int position() const override;
// Lookup exception handler for current {pc}, returns -1 if none found. // Lookup exception handler for current {pc}, returns -1 if none found.
int LookupExceptionHandlerInTable( int LookupExceptionHandlerInTable(
int* data, HandlerTable::CatchPrediction* prediction) override; int* data, HandlerTable::CatchPrediction* prediction) override;
@ -1067,6 +1071,7 @@ class WasmFrame : public StandardFrame {
Object* wasm_obj() const; Object* wasm_obj() const;
uint32_t function_index() const; uint32_t function_index() const;
Script* script() const override; Script* script() const override;
int position() const override;
static WasmFrame* cast(StackFrame* frame) { static WasmFrame* cast(StackFrame* frame) {
DCHECK(frame->is_wasm()); DCHECK(frame->is_wasm());

View File

@ -1388,33 +1388,8 @@ class WasmInstanceBuilder {
FlushICache(isolate_, code_table); FlushICache(isolate_, code_table);
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Run the start function if one was specified. // Set up and link the new instance.
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (compiled_module_->has_startup_function()) {
Handle<FixedArray> startup_data = compiled_module_->startup_function();
HandleScope scope(isolate_);
int32_t start_index =
startup_data->GetValueChecked<Smi>(isolate_, kExportIndex)->value();
Handle<Code> startup_code =
code_table->GetValueChecked<Code>(isolate_, start_index);
int arity = Smi::cast(startup_data->get(kExportArity))->value();
MaybeHandle<ByteArray> startup_signature =
startup_data->GetValue<ByteArray>(isolate_, kExportedSignature);
Handle<JSFunction> startup_fct = WrapExportCodeAsJSFunction(
isolate_, startup_code, factory->InternalizeUtf8String("start"),
arity, startup_signature, instance);
RecordStats(isolate_, *startup_code);
// Call the JS function.
Handle<Object> undefined = factory->undefined_value();
MaybeHandle<Object> retval =
Execution::Call(isolate_, startup_fct, undefined, 0, nullptr);
if (retval.is_null()) {
thrower_->Error("WASM.instantiateModule(): start function failed");
return nothing;
}
}
{ {
Handle<Object> global_handle = Handle<Object> global_handle =
isolate_->global_handles()->Create(*instance); isolate_->global_handles()->Create(*instance);
@ -1450,6 +1425,38 @@ class WasmInstanceBuilder {
} }
} }
//--------------------------------------------------------------------------
// Run the start function if one was specified.
//--------------------------------------------------------------------------
if (compiled_module_->has_startup_function()) {
Handle<FixedArray> startup_data = compiled_module_->startup_function();
HandleScope scope(isolate_);
int32_t start_index =
startup_data->GetValueChecked<Smi>(isolate_, kExportIndex)->value();
Handle<Code> startup_code =
code_table->GetValueChecked<Code>(isolate_, start_index);
int arity = Smi::cast(startup_data->get(kExportArity))->value();
MaybeHandle<ByteArray> startup_signature =
startup_data->GetValue<ByteArray>(isolate_, kExportedSignature);
Handle<JSFunction> startup_fct = WrapExportCodeAsJSFunction(
isolate_, startup_code, factory->InternalizeUtf8String("start"),
arity, startup_signature, instance);
RecordStats(isolate_, *startup_code);
// Call the JS function.
Handle<Object> undefined = factory->undefined_value();
MaybeHandle<Object> retval =
Execution::Call(isolate_, startup_fct, undefined, 0, nullptr);
if (retval.is_null()) {
thrower_->Error("WASM.instantiateModule(): start function failed");
// It's unfortunate that the new instance is already linked in the
// chain. However, we need to set up everything before executing the
// start function, such that stack trace information can be generated
// correctly already in the start function.
return nothing;
}
}
DCHECK(wasm::IsWasmObject(*instance)); DCHECK(wasm::IsWasmObject(*instance));
TRACE("Finishing instance %d\n", compiled_module_->instance_id()); TRACE("Finishing instance %d\n", compiled_module_->instance_id());

View File

@ -0,0 +1,89 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --expose-debug-as debug --validate-asm
Debug = debug.Debug
// Initialized in setup().
var exception;
var break_count;
var num_wasm_scripts;
var module;
var filename = '(?:[^ ]+/)?test/mjsunit/wasm/asm-debug.js';
filename = filename.replace(/\//g, '[/\\\\]');
var expected_stack_entries = [];
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
++break_count;
// Request frame details.
var num_frames = exec_state.frameCount();
assertEquals(
expected_stack_entries.length, num_frames, 'number of frames');
print('Stack Trace (length ' + num_frames + '):');
for (var i = 0; i < num_frames; ++i) {
var frame = exec_state.frame(i);
var script = frame.script();
assertNotNull(script);
var line = frame.sourceLine() + 1;
var column = frame.sourceColumn() + 1;
var funcName = frame.func().name();
var name = script.name();
print(
' [' + i + '] ' + funcName + ' (' + name + ':' + line + ':' +
column + ')');
assertMatches(filename, name, 'name');
assertEquals(
expected_stack_entries[i][0], funcName, 'function name at ' + i);
assertEquals(expected_stack_entries[i][1], line, 'line at ' + i);
assertEquals(expected_stack_entries[i][2], column, 'column at ' + i);
}
}
} catch (e) {
print('exception: ' + e);
exception = e;
}
};
function generateWasmFromAsmJs(stdlib, foreign, heap) {
'use asm';
var debugger_fun = foreign.call_debugger;
function callDebugger() {
debugger_fun();
}
function redirectFun() {
callDebugger();
}
return redirectFun;
}
function call_debugger() {
debugger;
}
function setup() {
exception = null;
break_count = 0;
}
(function FrameInspection() {
setup();
var fun =
generateWasmFromAsmJs(this, {'call_debugger': call_debugger}, undefined);
expected_stack_entries = [
['call_debugger', 66, 3], // --
['callDebugger', 57, 5], // --
['redirectFun', 60, 5], // --
['FrameInspection', 86, 3], // --
['', 89, 3]
];
Debug.setListener(listener);
fun();
Debug.setListener(null);
assertEquals(1, break_count);
})();