[wasm] Add inspector test for stepping

This also fixes bugs found by the new test. It only tests stepping
inside of wasm code. Wasm to JS and vice versa will follow in another
CL.

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

Review-Url: https://codereview.chromium.org/2651043004
Cr-Commit-Position: refs/heads/master@{#42729}
This commit is contained in:
clemensh 2017-01-27 00:50:50 -08:00 committed by Commit bot
parent 21340ded47
commit b7947f8cd7
5 changed files with 250 additions and 5 deletions

View File

@ -165,7 +165,7 @@ class InterpreterHandle {
return thread->Step();
case StepOut:
thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
return thread->Step();
return thread->Run();
case StepNext: {
int stack_depth = thread->GetFrameCount();
if (stack_depth == last_step_stack_depth_) return thread->Step();

View File

@ -1203,13 +1203,11 @@ class ThreadImpl {
// skip breakpoint by switching on original code.
skip = "[skip] ";
} else {
state_ = WasmInterpreter::PAUSED;
TRACE("@%-3zu: [break] %-24s:", pc,
WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(orig)));
TraceValueStack();
TRACE("\n");
break_pc_ = pc;
return CommitPc(pc);
break;
}
}
@ -1638,6 +1636,9 @@ class ThreadImpl {
pc += len;
}
// Set break_pc_, even though we might have stopped because max was reached.
// We don't want to stop after executing zero instructions next time.
break_pc_ = pc;
state_ = WasmInterpreter::PAUSED;
CommitPc(pc);
}

View File

@ -0,0 +1,83 @@
Installing code an global variable.
Calling instantiate function.
Waiting for two wasm scripts to be parsed.
Ignoring script with url v8://test/callInstantiate
Got wasm script: wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0
Requesting source for wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0...
Got wasm script: wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1
Requesting source for wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1...
func $wasm_A
nop
nop
end
func $wasm_B (param i32)
loop
get_local 0
if
get_local 0
i32.const 1
i32.sub
set_local 0
call 0
br 1
end
end
end
Setting breakpoint on line 7 (on the setlocal before the call), url wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1
{
columnNumber : 6
lineNumber : 7
scriptId : <scriptId>
}
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:7:6: >set_local 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:8:6: >call 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:1:2: >nop
Step action: stepOver
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:2:2: >nop
Step action: stepOut
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:9:6: >br 1
Step action: stepOut
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:7:6: >set_local 0
Step action: stepOver
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:8:6: >call 0
Step action: stepOver
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:9:6: >br 1
Step action: resume
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:7:6: >set_local 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:8:6: >call 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:1:2: >nop
Step action: stepOut
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:9:6: >br 1
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:1:2: >loop
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:2:4: >get_local 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:3:4: >if
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:4:6: >get_local 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:5:6: >i32.const 1
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:6:6: >i32.sub
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:7:6: >set_local 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-1:8:6: >call 0
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:1:2: >nop
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:2:2: >nop
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:3:0: >end
Step action: stepInto
Paused at wasm://wasm/wasm-ccfaf3fa/wasm-ccfaf3fa-0:0:0: >func $wasm_A
Step action: resume
exports.main returned!
Finished!

View File

@ -0,0 +1,160 @@
// Copyright 2017 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.
load('test/mjsunit/wasm/wasm-constants.js');
load('test/mjsunit/wasm/wasm-module-builder.js');
var builder = new WasmModuleBuilder();
var func_a_idx =
builder.addFunction('wasm_A', kSig_v_v).addBody([kExprNop, kExprNop]).index;
// wasm_B calls wasm_A <param0> times.
builder.addFunction('wasm_B', kSig_v_i)
.addBody([
// clang-format off
kExprLoop, kWasmStmt, // while
kExprGetLocal, 0, // -
kExprIf, kWasmStmt, // if <param0> != 0
kExprGetLocal, 0, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprSetLocal, 0, // decrease <param0>
kExprCallFunction, func_a_idx, // -
kExprBr, 1, // continue
kExprEnd, // -
kExprEnd, // break
// clang-format on
])
.exportAs('main');
var module_bytes = builder.toArray();
function instantiate(bytes) {
var buffer = new ArrayBuffer(bytes.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < bytes.length; ++i) {
view[i] = bytes[i] | 0;
}
var module = new WebAssembly.Module(buffer);
// Set global variable.
instance = new WebAssembly.Instance(module);
}
var evalWithUrl = (code, url) => Protocol.Runtime.evaluate(
{'expression': code + '\n//# sourceURL=v8://test/' + url});
Protocol.Debugger.onPaused(handlePaused);
var wasm_B_scriptId;
var step_actions = [
'stepInto', // == stepOver, to call instruction
'stepInto', // into call to wasm_A
'stepOver', // over first nop
'stepOut', // out of wasm_A
'stepOut', // out of wasm_B, stop on breakpoint again
'stepOver', // to call
'stepOver', // over call
'resume', // to next breakpoint (third iteration)
'stepInto', // to call
'stepInto', // into wasm_A
'stepOut', // out to wasm_B
// now step 9 times, until we are in wasm_A again.
'stepInto', 'stepInto', 'stepInto', 'stepInto', 'stepInto', 'stepInto',
'stepInto', 'stepInto', 'stepInto',
// 3 more times, back to wasm_B.
'stepInto', 'stepInto', 'stepInto',
// then just resume.
'resume'
];
var sources = {};
var urls = {};
var afterTwoSourcesCallback;
Protocol.Debugger.enable()
.then(() => InspectorTest.log('Installing code an global variable.'))
.then(
() => evalWithUrl('var instance;\n' + instantiate.toString(), 'setup'))
.then(() => InspectorTest.log('Calling instantiate function.'))
.then(
() =>
(evalWithUrl(
'instantiate(' + JSON.stringify(module_bytes) + ')',
'callInstantiate'),
0))
.then(waitForTwoWasmScripts)
.then(
() => InspectorTest.log(
'Setting breakpoint on line 7 (on the setlocal before the call), url ' +
urls[wasm_B_scriptId]))
.then(
() => Protocol.Debugger.setBreakpoint(
{'location': {'scriptId': wasm_B_scriptId, 'lineNumber': 7}}))
.then(printFailure)
.then(msg => InspectorTest.logMessage(msg.result.actualLocation))
.then(() => evalWithUrl('instance.exports.main(4)', 'runWasm'))
.then(() => InspectorTest.log('exports.main returned!'))
.then(() => InspectorTest.log('Finished!'))
.then(InspectorTest.completeTest);
function printFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
function waitForTwoWasmScripts() {
var num = 0;
InspectorTest.log('Waiting for two wasm scripts to be parsed.');
var promise = new Promise(fulfill => gotBothSources = fulfill);
function waitForMore() {
if (num == 2) return promise;
Protocol.Debugger.onceScriptParsed()
.then(handleNewScript)
.then(waitForMore);
}
function handleNewScript(msg) {
var url = msg.params.url;
if (!url.startsWith('wasm://')) {
InspectorTest.log('Ignoring script with url ' + url);
return;
}
num += 1;
var scriptId = msg.params.scriptId;
urls[scriptId] = url;
InspectorTest.log('Got wasm script: ' + url);
if (url.substr(-2) == '-1') wasm_B_scriptId = scriptId;
InspectorTest.log('Requesting source for ' + url + '...');
Protocol.Debugger.getScriptSource({scriptId: scriptId})
.then(printFailure)
.then(msg => sources[scriptId] = msg.result.scriptSource)
.then(InspectorTest.log)
.then(() => Object.keys(sources).length == 2 ? gotBothSources() : 0);
}
waitForMore();
return promise;
}
function printPauseLocation(scriptId, lineNr, columnNr) {
var lines = sources[scriptId].split('\n');
var line = '<illegal line number>';
if (lineNr < lines.length) {
line = lines[lineNr];
if (columnNr < line.length) {
line = line.substr(0, columnNr) + '>' + line.substr(columnNr);
}
}
InspectorTest.log(
'Paused at ' + urls[scriptId] + ':' + lineNr + ':' + columnNr + ': ' +
line);
}
function handlePaused(msg) {
var loc = msg.params.callFrames[0].location;
printPauseLocation(loc.scriptId, loc.lineNumber, loc.columnNumber);
var action = step_actions.shift();
InspectorTest.log('Step action: ' + action);
Protocol.Debugger[action]();
}

View File

@ -20,7 +20,8 @@ Protocol = new Proxy({}, {
var eventName = match[2];
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
if (match[1])
return (args) => InspectorTest._waitForEventPromise(`${agentName}.${eventName}`, args || {});
return () => InspectorTest._waitForEventPromise(
`${agentName}.${eventName}`);
else
return (listener) => { InspectorTest._eventHandler[`${agentName}.${eventName}`] = listener };
}