Commit 6ec6ed9c authored by Eric Leese's avatar Eric Leese Committed by Commit Bot

Report real module in addition to fake scripts

Currently the inspector reports Wasm in one of two ways:
 - If there is a source map, report one script per Wasm script, with
   bytecode but no source.
 - If there is no source map, report one script per Wasm function, with
   source (Wasm disassembly) but no bytecode.

With this change, behavior with source map is same, but without source
map it will report both ways. This will allow us to change the frontend
to do its own disassembly, allowing us to remove the per-function scripts
in a future change.

Bug: chromium:1013527
Change-Id: I0c559ad08896e8d0da419e3c6ad8d1edff3976fc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1899782Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Eric Leese <leese@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64980}
parent dce12b63
......@@ -224,11 +224,13 @@ domain Debugger
# Id of the script to get source for.
Runtime.ScriptId scriptId
returns
# Script source.
# Script source (empty in case of Wasm bytecode).
string scriptSource
# Wasm bytecode.
optional binary bytecode
# Returns bytecode for the WebAssembly script with given id.
command getWasmBytecode
# This command is deprecated. Use getScriptSource instead.
deprecated command getWasmBytecode
parameters
# Id of the Wasm script to get source for.
Runtime.ScriptId scriptId
......
......@@ -952,13 +952,18 @@ Response V8DebuggerAgentImpl::restartFrame(
return Response::OK();
}
Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
String16* scriptSource) {
Response V8DebuggerAgentImpl::getScriptSource(
const String16& scriptId, String16* scriptSource,
Maybe<protocol::Binary>* bytecode) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
ScriptsMap::iterator it = m_scripts.find(scriptId);
if (it == m_scripts.end())
return Response::Error("No script for id: " + scriptId);
*scriptSource = it->second->source(0);
v8::MemorySpan<const uint8_t> span;
if (it->second->wasmBytecode().To(&span)) {
*bytecode = protocol::Binary::fromSpan(span.data(), span.size());
}
return Response::OK();
}
......
......@@ -93,8 +93,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
newCallFrames,
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) override;
Response getScriptSource(const String16& scriptId,
String16* scriptSource) override;
Response getScriptSource(const String16& scriptId, String16* scriptSource,
Maybe<protocol::Binary>* bytecode) override;
Response getWasmBytecode(const String16& scriptId,
protocol::Binary* bytecode) override;
Response pause() override;
......
......@@ -522,13 +522,15 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
&wasmTranslation](V8InspectorSessionImpl* session) {
auto agent = session->debuggerAgent();
if (!agent->enabled()) return;
agent->didParseSource(
V8DebuggerScript::Create(isolate, script, is_live_edited, agent,
client),
!has_compile_error);
if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
// In this case we also create fake scripts corresponding to each
// function.
// TODO(leese): Get rid of this once frontend no longer uses these.
wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
} else {
agent->didParseSource(
V8DebuggerScript::Create(isolate, script, is_live_edited, agent,
client),
!has_compile_error);
}
});
}
......
......@@ -9,7 +9,10 @@ Debug = debug.Debug
function listener(event, exec_state, event_data, data) {
if (event == Debug.DebugEvent.AfterCompile) {
// The actual source doesn't matter, just don't crash.
assertEquals("func $main\nend\n", event_data.script().source());
var source = event_data.script().source();
// Source will be empty for the script representing the entire module,
// disassembly for the script representing just the function.
assertTrue(source == "func $main\nend\n" || source == "");
}
};
......
......@@ -39,7 +39,8 @@ let {session, contextGroup, Protocol} =
let scriptId;
while (true) {
let msg = await Protocol.Debugger.onceScriptParsed();
if (msg.params.url.startsWith('wasm://')) {
if (msg.params.url.startsWith('wasm://') &&
msg.params.url.split('/').length == 5) {
scriptId = msg.params.scriptId;
break;
}
......
Tests that cloning a module notifies the debugger
Got URL: wasm://wasm/95d1e44e
Got URL: wasm://wasm/95d1e44e/95d1e44e-0
Got URL: wasm://wasm/95d1e44e
Got URL: wasm://wasm/95d1e44e/95d1e44e-0
Got URL: wasm://wasm/95d1e44e
Got URL: wasm://wasm/95d1e44e/95d1e44e-0
Done!
......@@ -29,7 +29,7 @@ Protocol.Debugger.onScriptParsed(msg => {
let url = msg.params.url;
if (url.startsWith('wasm://')) {
InspectorTest.log(`Got URL: ${url}`);
if (++scriptsSeen == 3) {
if (++scriptsSeen == 6) {
InspectorTest.log('Done!');
InspectorTest.completeTest();
}
......
Tests breakable locations in wasm
Running testFunction...
Script nr 0 parsed. URL: v8://test/setup
Script nr 1 parsed. URL: v8://test/runTestFunction
Script nr 2 parsed. URL: wasm://wasm/6a95b41e
This is a wasm script (nr 0).
Script nr 3 parsed. URL: wasm://wasm/6a95b41e/6a95b41e-0
Script nr 4 parsed. URL: wasm://wasm/6a95b41e/6a95b41e-1
Querying breakable locations for all wasm scripts now...
Requesting all breakable locations in wasm script 0
Bytecode matches!
11 breakable location(s):
[0] 0:40 || byte=1
[1] 0:41 || byte=65
[2] 0:43 || byte=33
[3] 0:45 || byte=11
[4] 0:48 || byte=32
[5] 0:50 || byte=4
[6] 0:52 || byte=2
[7] 0:54 || byte=16
[8] 0:56 || byte=11
[9] 0:57 || byte=11
[10] 0:58 || byte=11
Requesting breakable locations in offsets [0,45)
3 breakable location(s):
[0] 0:40 || byte=1
[1] 0:41 || byte=65
[2] 0:43 || byte=33
Requesting breakable locations in offsets [50,60)
6 breakable location(s):
[0] 0:50 || byte=4
[1] 0:52 || byte=2
[2] 0:54 || byte=16
[3] 0:56 || byte=11
[4] 0:57 || byte=11
[5] 0:58 || byte=11
Setting a breakpoint on each breakable location...
Setting at wasm://wasm/6a95b41e:0:40
Success!
Setting at wasm://wasm/6a95b41e:0:41
Success!
Setting at wasm://wasm/6a95b41e:0:43
Success!
Setting at wasm://wasm/6a95b41e:0:45
Success!
Setting at wasm://wasm/6a95b41e:0:48
Success!
Setting at wasm://wasm/6a95b41e:0:50
Success!
Setting at wasm://wasm/6a95b41e:0:52
Success!
Setting at wasm://wasm/6a95b41e:0:54
Success!
Setting at wasm://wasm/6a95b41e:0:56
Success!
Setting at wasm://wasm/6a95b41e:0:57
Success!
Setting at wasm://wasm/6a95b41e:0:58
Success!
Running wasm code...
Missing breakpoints: 11
Script nr 5 parsed. URL: v8://test/runWasm
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:1:2
Missing breakpoints: 10
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:2:2
Missing breakpoints: 9
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:3:4
Missing breakpoints: 8
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:4:6
Missing breakpoints: 7
Stopped at wasm://wasm/6a95b41e/6a95b41e-0:2:2
Missing breakpoints: 6
Stopped at wasm://wasm/6a95b41e/6a95b41e-0:3:2
Missing breakpoints: 5
Stopped at wasm://wasm/6a95b41e/6a95b41e-0:4:2
Missing breakpoints: 4
Stopped at wasm://wasm/6a95b41e/6a95b41e-0:5:0
Missing breakpoints: 3
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:5:4
Missing breakpoints: 2
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:6:2
Missing breakpoints: 1
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:7:0
Missing breakpoints: 0
Finished!
// 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
let {session, contextGroup, Protocol} = InspectorTest.start('Tests breakable locations in wasm');
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
var builder = new WasmModuleBuilder();
// clang-format off
var func_idx = builder.addFunction('helper', kSig_v_v)
.addLocals({ i32_count: 1 })
.addBody([
kExprNop,
kExprI32Const, 12,
kExprLocalSet, 0,
]).index;
builder.addFunction('main', kSig_v_i)
.addBody([
kExprLocalGet, 0,
kExprIf, kWasmStmt,
kExprBlock, kWasmStmt,
kExprCallFunction, func_idx,
kExprEnd,
kExprEnd
]).exportAs('main');
// clang-format on
var module_bytes = builder.toArray();
function testFunction(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});
var setupCode = testFunction.toString() + ';\nvar module_bytes = ' +
JSON.stringify(module_bytes) + ';\nvar instance;';
Protocol.Debugger.enable();
Protocol.Debugger.onScriptParsed(handleScriptParsed);
async function runTest() {
InspectorTest.log('Running testFunction...');
await evalWithUrl(setupCode, 'setup');
await evalWithUrl('testFunction(module_bytes)', 'runTestFunction');
await getBreakableLocationsForAllWasmScripts();
await setAllBreakableLocations();
InspectorTest.log('Running wasm code...');
// Begin executing code:
var promise = evalWithUrl('instance.exports.main(1)', 'runWasm');
await waitForAllPauses();
// Code should now complete
await promise;
InspectorTest.log('Finished!');
}
runTest()
.catch(reason => InspectorTest.log(`Failed: ${reason}`))
.then(InspectorTest.completeTest);
var allBreakableLocations = [];
var urls = {};
var numScripts = 0;
var wasmScripts = [];
function handleScriptParsed(messageObject) {
var scriptId = messageObject.params.scriptId;
var url = messageObject.params.url;
urls[scriptId] = url;
InspectorTest.log('Script nr ' + numScripts + ' parsed. URL: ' + url);
++numScripts;
if (url.startsWith('wasm://')) {
// Only want the full module script, not the function specific ones.
if (url.split('/').length == 4) {
InspectorTest.log('This is a wasm script (nr ' + wasmScripts.length + ').');
wasmScripts.push(scriptId);
}
}
}
function printFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
}
function printBreakableLocations(message, expectedScriptId, bytecode) {
var locations = message.result.locations;
InspectorTest.log(locations.length + ' breakable location(s):');
for (var i = 0; i < locations.length; ++i) {
if (locations[i].scriptId != expectedScriptId) {
InspectorTest.log(
'SCRIPT ID MISMATCH!! ' + locations[i].scriptId + ' != ' +
expectedScriptId);
}
if (locations[i].lineNumber != 0) {
InspectorTest.log(`Unexpected line number: ${bytecode[locations[i].lineNumber]}`);
}
var line = `byte=${bytecode[locations[i].columnNumber]}`;
InspectorTest.log(
'[' + i + '] ' + locations[i].lineNumber + ':' +
locations[i].columnNumber + ' || ' + line);
}
}
function checkModuleBytes(encoded, bytecode) {
// Encode bytecode as base64.
var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var PAD = '=';
var ret = '';
var leftchar = 0;
var leftbits = 0;
for (var i = 0; i < bytecode.length; i++) {
leftchar = (leftchar << 8) | bytecode[i];
leftbits += 8;
while (leftbits >= 6) {
var curr = (leftchar >> (leftbits-6)) & 0x3f;
leftbits -= 6;
ret += BASE[curr];
}
}
if (leftbits == 2) {
ret += BASE[(leftchar&3) << 4];
ret += PAD + PAD;
} else if (leftbits == 4) {
ret += BASE[(leftchar&0xf) << 2];
ret += PAD;
}
// Check the base64 strings match.
if(ret == encoded) {
InspectorTest.log('Bytecode matches!');
} else {
InspectorTest.log('Original: ' + ret);
InspectorTest.log('Returned by API: ' + encoded);
}
}
async function checkGetBreakableLocations(wasmScriptNr) {
InspectorTest.log(
'Requesting all breakable locations in wasm script ' + wasmScriptNr);
var scriptId = wasmScripts[wasmScriptNr];
var msg = await Protocol.Debugger.getScriptSource({scriptId: scriptId});
checkModuleBytes(msg.result.bytecode, module_bytes);
msg = await Protocol.Debugger.getPossibleBreakpoints(
{start: {lineNumber: 0, columnNumber: 0, scriptId: scriptId}});
printFailure(msg);
allBreakableLocations.push(...msg.result.locations);
printBreakableLocations(msg, scriptId, module_bytes);
InspectorTest.log('Requesting breakable locations in offsets [0,45)');
msg = await Protocol.Debugger.getPossibleBreakpoints({
start: {lineNumber: 0, columnNumber: 0, scriptId: scriptId},
end: {lineNumber: 0, columnNumber: 45, scriptId: scriptId}});
printFailure(msg);
printBreakableLocations(msg, scriptId, module_bytes);
InspectorTest.log('Requesting breakable locations in offsets [50,60)');
msg = await Protocol.Debugger.getPossibleBreakpoints({
start: {lineNumber: 0, columnNumber: 50, scriptId: scriptId},
end: {lineNumber: 0, columnNumber: 60, scriptId: scriptId}});
printFailure(msg);
printBreakableLocations(msg, scriptId, module_bytes);
}
async function getBreakableLocationsForAllWasmScripts() {
InspectorTest.log('Querying breakable locations for all wasm scripts now...');
for (var wasmScriptNr = 0; wasmScriptNr < wasmScripts.length;
++wasmScriptNr) {
await checkGetBreakableLocations(wasmScriptNr);
}
}
function locationMatches(loc1, loc2) {
return loc1.scriptId == loc2.scriptId && loc1.lineNumber == loc2.lineNumber &&
loc1.columnNumber == loc2.columnNumber;
}
function locationStr(loc) {
return urls[loc.scriptId] + ':' + loc.lineNumber + ':' + loc.columnNumber;
}
async function setBreakpoint(loc) {
InspectorTest.log('Setting at ' + locationStr(loc));
var msg = await Protocol.Debugger.setBreakpoint({ 'location': loc });
printFailure(msg);
if (locationMatches(loc, msg.result.actualLocation)) {
InspectorTest.log("Success!");
} else {
InspectorTest.log("Mismatch!");
InspectorTest.logMessage(msg);
}
}
async function setAllBreakableLocations() {
InspectorTest.log('Setting a breakpoint on each breakable location...');
for (var loc of allBreakableLocations) {
await setBreakpoint(loc);
}
}
function recordPausedLocation(msg) {
var topLocation = msg.params.callFrames[0].location;
InspectorTest.log('Stopped at ' + locationStr(topLocation));
}
async function waitForAllPauses() {
var remaining = allBreakableLocations.length;
InspectorTest.log('Missing breakpoints: ' + remaining);
while (remaining > 0) {
recordPausedLocation(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
remaining--;
InspectorTest.log('Missing breakpoints: ' + remaining);
}
}
......@@ -2,9 +2,10 @@ Tests breakable locations in wasm
Running testFunction...
Script nr 0 parsed. URL: v8://test/setup
Script nr 1 parsed. URL: v8://test/runTestFunction
Script nr 2 parsed. URL: wasm://wasm/6a95b41e/6a95b41e-0
Script nr 2 parsed. URL: wasm://wasm/6a95b41e
Script nr 3 parsed. URL: wasm://wasm/6a95b41e/6a95b41e-0
This is a wasm script (nr 0).
Script nr 3 parsed. URL: wasm://wasm/6a95b41e/6a95b41e-1
Script nr 4 parsed. URL: wasm://wasm/6a95b41e/6a95b41e-1
This is a wasm script (nr 1).
Querying breakable locations for all wasm scripts now...
Requesting all breakable locations in wasm script 0
......@@ -62,7 +63,7 @@ Setting at wasm://wasm/6a95b41e/6a95b41e-1:7:0
Success!
Running wasm code...
Missing breakpoints: 11
Script nr 4 parsed. URL: v8://test/runWasm
Script nr 5 parsed. URL: v8://test/runWasm
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:1:2
Missing breakpoints: 10
Stopped at wasm://wasm/6a95b41e/6a95b41e-1:2:2
......
......@@ -76,8 +76,12 @@ function handleScriptParsed(messageObject) {
++numScripts;
if (url.startsWith('wasm://')) {
InspectorTest.log('This is a wasm script (nr ' + wasmScripts.length + ').');
wasmScripts.push(scriptId);
// Only want the fake (function specific) scripts, not the one for the full
// module.
if (url.split('/').length == 5) {
InspectorTest.log('This is a wasm script (nr ' + wasmScripts.length + ').');
wasmScripts.push(scriptId);
}
}
}
......
......@@ -101,7 +101,8 @@ function printFailure(message) {
}
function waitForWasmScript(msg) {
if (!msg || !msg.params.url.startsWith('wasm://')) {
if (!msg || !msg.params.url.startsWith('wasm://') ||
msg.params.url.split('/').length != 5) {
return Protocol.Debugger.onceScriptParsed().then(waitForWasmScript);
}
InspectorTest.log('Got wasm script!');
......
......@@ -111,7 +111,7 @@ async function waitForWasmScripts() {
while (wasm_script_ids.length < 2) {
let script_msg = await Protocol.Debugger.onceScriptParsed();
let url = script_msg.params.url;
if (url.startsWith('wasm://')) {
if (url.startsWith('wasm://') && url.split('/').length == 5) {
InspectorTest.log('Got wasm script!');
wasm_script_ids.push(script_msg.params.scriptId);
}
......
Tests how wasm scripts are reported
Check that each inspector gets two wasm scripts at module creation time.
Session #1: Script #0 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-0. Source map URL:
Session #1: Script #1 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-1. Source map URL:
Session #2: Script #0 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-0. Source map URL:
Session #2: Script #1 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-1. Source map URL:
Session #1: Script #2 parsed. URL: wasm://wasm/74f86b7e. Source map URL: wasm://dwarf
Session #2: Script #2 parsed. URL: wasm://wasm/74f86b7e. Source map URL: wasm://dwarf
Session #1: Script #3 parsed. URL: wasm://wasm/3754e3fe. Source map URL: abc
Session #2: Script #3 parsed. URL: wasm://wasm/3754e3fe. Source map URL: abc
Session #1: Script #4 parsed. URL: wasm://wasm/2bd2e40e. Source map URL: abc
Session #2: Script #4 parsed. URL: wasm://wasm/2bd2e40e. Source map URL: abc
Session #1: Script #5 parsed. URL: wasm://wasm/f568e726. Source map URL: abc
Session #2: Script #5 parsed. URL: wasm://wasm/f568e726. Source map URL: abc
Session #1: Script #0 parsed. URL: wasm://wasm/f608ae1e. Source map URL:
Session #1: Script #1 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-0. Source map URL:
Session #1: Script #2 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-1. Source map URL:
Session #2: Script #0 parsed. URL: wasm://wasm/f608ae1e. Source map URL:
Session #2: Script #1 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-0. Source map URL:
Session #2: Script #2 parsed. URL: wasm://wasm/f608ae1e/f608ae1e-1. Source map URL:
Session #1: Script #3 parsed. URL: wasm://wasm/74f86b7e. Source map URL: wasm://dwarf
Session #2: Script #3 parsed. URL: wasm://wasm/74f86b7e. Source map URL: wasm://dwarf
Session #1: Script #4 parsed. URL: wasm://wasm/3754e3fe. Source map URL: abc
Session #2: Script #4 parsed. URL: wasm://wasm/3754e3fe. Source map URL: abc
Session #1: Script #5 parsed. URL: wasm://wasm/2bd2e40e. Source map URL: abc
Session #2: Script #5 parsed. URL: wasm://wasm/2bd2e40e. Source map URL: abc
Session #1: Script #6 parsed. URL: wasm://wasm/f568e726. Source map URL: abc
Session #2: Script #6 parsed. URL: wasm://wasm/f568e726. Source map URL: abc
Session #1: Source for wasm://wasm/f608ae1e:
Session #1: Source for wasm://wasm/f608ae1e/f608ae1e-0:
func $nopFunction
nop
......@@ -25,6 +29,8 @@ func $main
end
end
Session #2: Source for wasm://wasm/f608ae1e:
Session #2: Source for wasm://wasm/f608ae1e/f608ae1e-0:
func $nopFunction
nop
......
Tests how wasm scripts are reported with name
Check that each inspector gets two wasm scripts at module creation time.
Session #1: Script #0 parsed. URL: wasm://wasm/49a8663e/49a8663e-0.
Session #1: Script #1 parsed. URL: wasm://wasm/moduleName-aea4a206/moduleName-aea4a206-0.
Check that the inspector gets four wasm scripts at module creation time.
Session #1: Script #0 parsed. URL: wasm://wasm/49a8663e.
Session #1: Script #1 parsed. URL: wasm://wasm/49a8663e/49a8663e-0.
Session #1: Script #2 parsed. URL: wasm://wasm/moduleName-aea4a206.
Session #1: Script #3 parsed. URL: wasm://wasm/moduleName-aea4a206/moduleName-aea4a206-0.
Session #1: Source for wasm://wasm/49a8663e:
Session #1: Source for wasm://wasm/49a8663e/49a8663e-0:
func $nopFunction
nop
end
Session #1: Source for wasm://wasm/moduleName-aea4a206:
Session #1: Source for wasm://wasm/moduleName-aea4a206/moduleName-aea4a206-0:
func $nopFunction
nop
......
......@@ -30,7 +30,7 @@ contextGroup.addScript('var module_bytes = ' + JSON.stringify(module_bytes));
contextGroup.addScript('var module_bytes_with_name = ' + JSON.stringify(module_bytes_with_name));
InspectorTest.log(
'Check that each inspector gets two wasm scripts at module creation time.');
'Check that the inspector gets four wasm scripts at module creation time.');
sessions[0].Protocol.Runtime
.evaluate({
......
......@@ -75,8 +75,9 @@ Protocol.Debugger.onPaused(pause_msg => {
evalWithUrl(instantiate_code, 'instantiate');
InspectorTest.log(
'Waiting for two wasm scripts (ignoring first non-wasm script).');
const [, {params: wasm_script_a}, {params: wasm_script_b}] =
await Protocol.Debugger.onceScriptParsed(3);
// Ignore javascript and full module wasm script, get scripts for functions.
const [, , {params: wasm_script_a}, {params: wasm_script_b}] =
await Protocol.Debugger.onceScriptParsed(4);
for (script of [wasm_script_a, wasm_script_b]) {
InspectorTest.log('Source of script ' + script.url + ':');
let src_msg =
......
Tests stepping through wasm scripts with source maps
Installing code an global variable and instantiate.
Got wasm script: wasm://wasm/18214bfe
Requesting source for wasm://wasm/18214bfe...
Source retrieved without error: true
Setting breakpoint on offset 54 (on the setlocal before the call), url wasm://wasm/18214bfe
Result: {"id":4,"result":{"breakpointId":"4:0:54:4","actualLocation":{"scriptId":"4","lineNumber":0,"columnNumber":54}}}
{
columnNumber : 54
lineNumber : 0
scriptId : <scriptId>
}
Paused at wasm://wasm/18214bfe/18214bfe-1:7:6
at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":4}
stack: {"0":3}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:8:6
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":3}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-0:1:2
at wasm_A (1:2):
- scope (global):
-- skipped
- scope (local):
stack: {}
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":3}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepOver called
Paused at wasm://wasm/18214bfe/18214bfe-0:2:2
at wasm_A (2:2):
- scope (global):
-- skipped
- scope (local):
stack: {}
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":3}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepOut called
Paused at wasm://wasm/18214bfe/18214bfe-1:9:6
at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":3}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepOut called
Paused at wasm://wasm/18214bfe/18214bfe-1:7:6
at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":3}
stack: {"0":2}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepOver called
Paused at wasm://wasm/18214bfe/18214bfe-1:8:6
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":2}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepOver called
Paused at wasm://wasm/18214bfe/18214bfe-1:9:6
at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":2}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.resume called
Paused at wasm://wasm/18214bfe/18214bfe-1:7:6
at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":2}
stack: {"0":1}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:8:6
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-0:1:2
at wasm_A (1:2):
- scope (global):
-- skipped
- scope (local):
stack: {}
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepOut called
Paused at wasm://wasm/18214bfe/18214bfe-1:9:6
at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:1:2
at wasm_B (1:2):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:2:4
at wasm_B (2:4):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:3:4
at wasm_B (3:4):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {"0":1}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:4:6
at wasm_B (4:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:5:6
at wasm_B (5:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {"0":1}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:6:6
at wasm_B (6:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {"0":1,"1":1}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:7:6
at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":1}
stack: {"0":0}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:8:6
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":0}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-0:1:2
at wasm_A (1:2):
- scope (global):
-- skipped
- scope (local):
stack: {}
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":0}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-0:2:2
at wasm_A (2:2):
- scope (global):
-- skipped
- scope (local):
stack: {}
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":0}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-0:3:0
at wasm_A (3:0):
- scope (global):
-- skipped
- scope (local):
stack: {}
at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":0}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.stepInto called
Paused at wasm://wasm/18214bfe/18214bfe-1:9:6
at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
locals: {"arg#0":0}
stack: {}
at (anonymous) (0:17):
- scope (global):
-- skipped
Debugger.resume called
exports.main returned!
Finished!
// Copyright 2018 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.
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests stepping through wasm scripts with source maps');
utils.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
kExprLocalGet, 0, // -
kExprIf, kWasmStmt, // if <param0> != 0
kExprLocalGet, 0, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprLocalSet, 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);
}
(async function test() {
for (const action of ['stepInto', 'stepOver', 'stepOut', 'resume'])
InspectorTest.logProtocolCommandCalls('Debugger.' + action);
await Protocol.Debugger.enable();
InspectorTest.log('Installing code an global variable and instantiate.');
Protocol.Runtime.evaluate({
expression: `var instance;(${instantiate.toString()})(${JSON.stringify(module_bytes)})`
});
const [, {params: wasmScript}, ,] = await Protocol.Debugger.onceScriptParsed(4);
InspectorTest.log('Got wasm script: ' + wasmScript.url);
InspectorTest.log('Requesting source for ' + wasmScript.url + '...');
const msg =
await Protocol.Debugger.getScriptSource({scriptId: wasmScript.scriptId});
InspectorTest.log(`Source retrieved without error: ${!msg.error}`);
// TODO: Add check that source text is empty but bytecode is present.
InspectorTest.log(
`Setting breakpoint on offset 54 (on the setlocal before the call), url ${wasmScript.url}`);
const bpmsg = await Protocol.Debugger.setBreakpoint({
location: {scriptId: wasmScript.scriptId, lineNumber: 0, columnNumber: 54}
});
InspectorTest.log(`Result: ${JSON.stringify(bpmsg)}`);
const actualLocation = bpmsg.result.actualLocation;
InspectorTest.logMessage(actualLocation);
Protocol.Runtime.evaluate({ expression: 'instance.exports.main(4)' });
await waitForPauseAndStep('stepInto'); // == stepOver, to call instruction
await waitForPauseAndStep('stepInto'); // into call to wasm_A
await waitForPauseAndStep('stepOver'); // over first nop
await waitForPauseAndStep('stepOut'); // out of wasm_A
await waitForPauseAndStep('stepOut'); // out of wasm_B, stop on breakpoint again
await waitForPauseAndStep('stepOver'); // to call
await waitForPauseAndStep('stepOver'); // over call
await waitForPauseAndStep('resume'); // to next breakpoint (third iteration)
await waitForPauseAndStep('stepInto'); // to call
await waitForPauseAndStep('stepInto'); // into wasm_A
await waitForPauseAndStep('stepOut'); // out to wasm_B
// now step 9 times, until we are in wasm_A again.
for (let i = 0; i < 9; ++i) await waitForPauseAndStep('stepInto');
// 3 more times, back to wasm_B.
for (let i = 0; i < 3; ++i) await waitForPauseAndStep('stepInto');
// then just resume.
await waitForPauseAndStep('resume');
InspectorTest.log('exports.main returned!');
InspectorTest.log('Finished!');
//InspectorTest.completeTest();
})().catch(reason => InspectorTest.log(`Failed: ${reason}`)).finally(InspectorTest.completeTest);
async function waitForPauseAndStep(stepAction) {
const {params: {callFrames}} = await Protocol.Debugger.oncePaused();
const topFrame = callFrames[0];
InspectorTest.log(
`Paused at ${topFrame.url}:${topFrame.location.lineNumber}:${topFrame.location.columnNumber}`);
for (var frame of callFrames) {
const functionName = frame.functionName || '(anonymous)';
const lineNumber = frame.location.lineNumber;
const columnNumber = frame.location.columnNumber;
InspectorTest.log(`at ${functionName} (${lineNumber}:${columnNumber}):`);
for (var scope of frame.scopeChain) {
InspectorTest.logObject(' - scope (' + scope.type + '):');
if (scope.type === 'global') {
InspectorTest.logObject(' -- skipped');
} else {
const {result: {result: {value}}} =
await Protocol.Runtime.callFunctionOn({
objectId: scope.object.objectId,
functionDeclaration: 'function() { return this; }',
returnByValue: true
});
if (value.locals)
InspectorTest.log(` locals: ${JSON.stringify(value.locals)}`);
InspectorTest.log(` stack: ${JSON.stringify(value.stack)}`);
}
}
}
Protocol.Debugger[stepAction]();
}
......@@ -3,6 +3,7 @@ Installing code an global variable.
Calling instantiate function.
Waiting for two wasm scripts to be parsed.
Ignoring script with url v8://test/callInstantiate
Ignoring script with url wasm://wasm/18214bfe
Got wasm script: wasm://wasm/18214bfe/18214bfe-0
Requesting source for wasm://wasm/18214bfe/18214bfe-0...
Got wasm script: wasm://wasm/18214bfe/18214bfe-1
......
......@@ -117,7 +117,7 @@ async function waitForTwoWasmScripts() {
while (num < 2) {
let msg = await Protocol.Debugger.onceScriptParsed();
let url = msg.params.url;
if (!url.startsWith('wasm://')) {
if (!url.startsWith('wasm://') || url.split('/').length != 5) {
InspectorTest.log('Ignoring script with url ' + url);
continue;
}
......
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