[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:
parent
18db69c38c
commit
e902b69d88
@ -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()
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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());
|
||||||
|
@ -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());
|
||||||
|
89
test/mjsunit/wasm/asm-debug.js
Normal file
89
test/mjsunit/wasm/asm-debug.js
Normal 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);
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user