v8/test/inspector/debugger/wasm-scripts.js
Ingvar Stepanyan c7848612d8 Add support for reporting raw Wasm scripts
This addition will allow to experiment with parsing DWARF information from
WebAssembly on the frontend side for improved debugging.

The frontend must explicitly opt-in to this experiment by setting
`supportsWasmDwarf: true` in `Debugger.enable` params.

When this option is present, and Wasm appears to contain DWARF information
(heuristic: `.debug_info` custom section is present), V8 will not try to
disassemble and report each WebAssembly function as a separate fake script, but
instead will report Wasm module as a whole.

Note that V8 already does this when Wasm is associated with a source map.

Additionally, this CL adds a dedicated `Debugger.getWasmBytecode` command that
accepts scriptId and returns raw wire bytes of the chosen WebAssembly module.

Change-Id: I7a6e80daf8d91ffaaba04fa15688f2ba9552870f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1809375
Commit-Queue: Ingvar Stepanyan <rreverser@google.com>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63969}
2019-09-25 14:05:26 +00:00

123 lines
4.1 KiB
JavaScript

// 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
InspectorTest.log("Tests how wasm scripts are reported");
let contextGroup = new InspectorTest.ContextGroup();
let sessions = [
// Main session.
trackScripts(),
// Extra session to verify that all inspectors get same messages.
// See https://bugs.chromium.org/p/v8/issues/detail?id=9725.
trackScripts(),
// Another session to check how raw Wasm is reported.
trackScripts({ supportsWasmDwarf: true }),
];
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
// Add two empty functions. Both should be registered as individual scripts at
// module creation time.
var builder = new WasmModuleBuilder();
builder.addFunction('nopFunction', kSig_v_v).addBody([kExprNop]);
builder.addFunction('main', kSig_v_v)
.addBody([kExprBlock, kWasmStmt, kExprI32Const, 2, kExprDrop, kExprEnd])
.exportAs('main');
builder.addCustomSection('.debug_info', []);
var module_bytes = builder.toArray();
function testFunction(bytes) {
// Compilation triggers registration of wasm scripts.
new WebAssembly.Module(new Uint8Array(bytes));
}
contextGroup.addScript(testFunction.toString(), 0, 0, 'v8://test/testFunction');
contextGroup.addScript('var module_bytes = ' + JSON.stringify(module_bytes));
InspectorTest.log(
'Check that each inspector gets two wasm scripts at module creation time.');
sessions[0].Protocol.Runtime
.evaluate({
'expression': '//# sourceURL=v8://test/runTestRunction\n' +
'testFunction(module_bytes)'
})
.then(() => (
// At this point all scripts were parsed.
// Stop tracking and wait for script sources in each session.
Promise.all(sessions.map(session => session.getScripts()))
))
.catch(err => {
InspectorTest.log(err.stack);
})
.then(() => InspectorTest.completeTest());
function decodeBase64(base64) {
const LOOKUP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const paddingLength = base64.match(/=*$/)[0].length;
const bytesLength = base64.length * 0.75 - paddingLength;
let bytes = new Uint8Array(bytesLength);
for (let i = 0, p = 0; i < base64.length; i += 4, p += 3) {
let bits = 0;
for (let j = 0; j < 4; j++) {
bits <<= 6;
const c = base64[i + j];
if (c !== '=') bits |= LOOKUP.indexOf(c);
}
for (let j = p + 2; j >= p; j--) {
if (j < bytesLength) bytes[j] = bits;
bits >>= 8;
}
}
return bytes;
}
function trackScripts(debuggerParams) {
let {id: sessionId, Protocol} = contextGroup.connect();
let scripts = [];
Protocol.Debugger.enable(debuggerParams);
Protocol.Debugger.onScriptParsed(handleScriptParsed);
async function loadScript({url, scriptId}, raw) {
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}`);
let scriptSource;
if (raw) {
let {result: {bytecode}} = await Protocol.Debugger.getWasmBytecode({scriptId});
// Binary value is represented as base64 in JSON, decode it.
bytecode = decodeBase64(bytecode);
// Check that it can be parsed back to a WebAssembly module.
let module = new WebAssembly.Module(bytecode);
scriptSource = `
Raw: ${Array.from(bytecode, b => ('0' + b.toString(16)).slice(-2)).join(' ')}
Imports: [${WebAssembly.Module.imports(module).map(i => `${i.name}: ${i.kind} from "${i.module}"`).join(', ')}]
Exports: [${WebAssembly.Module.exports(module).map(e => `${e.name}: ${e.kind}`).join(', ')}]
`.trim();
} else {
({result: {scriptSource}} = await Protocol.Debugger.getScriptSource({scriptId}));
}
InspectorTest.log(`Session #${sessionId}: Source for ${url}:`);
InspectorTest.log(scriptSource);
}
function handleScriptParsed({params}) {
if (params.sourceMapURL === "wasm://dwarf") {
scripts.push(loadScript(params, true));
} else if (params.url.startsWith("wasm://")) {
scripts.push(loadScript(params));
}
}
return {
Protocol,
getScripts: () => Promise.all(scripts),
};
}