v8/test/inspector/debugger/wasm-scripts.js
Ingvar Stepanyan 1b5f3be087 [wasm] Pretend that DWARF section is a fake source map
Unfortunately, codebase contains lots of places that use one of the two
formats as an internal representation for Wasm locations:
1) {line: 0, column: byte offset within entire module}
2) {line: function index, column: byte offset within function}

These places choose these formats interchangeably and convert from one
to another depending on the presence of source map URL in Wasm.

This is not very convenient and makes it hard to add support for DWARF
which should behave just like Wasm with source maps - that is, report a
raw Wasm script instead of fake scripts per each disassembled function,
and use representation (1) instead of (2) internally.

I tried to refactor these locations and avoid checking for source map
URLs in the previous CL - https://crrev.com/c/v8/v8/+/1833688. However,
it quickly got out of hand, and updating code in one place just kept
revealing yet another that gets broken by the changes, so I made a
decision to abandon it and leave to someone who knows the codebase
better.

Instead, this CL is based on https://crrev.com/c/v8/v8/+/1809375, but,
rather than trying to integrate DWARF separately and only for supported
agents, it pretends that encountering DWARF section is the same as
encountering a `sourceMappingURL` section with fake URL "wasm://dwarf".

This ensures that Wasm with DWARF behaves exactly in the same way as
Wasm with source maps, just like we want, with minimal changes to the
codebase. The only downside is that frontends without DWARF support
won't get even a disassembled version of Wasm that contains DWARF info.
This is unfortunate, but, as per previous discussions, should be fine
given current state of Wasm debugging.

Change-Id: Ia7256075e4bfd2f407d001d02b96883d7267436e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1834341
Commit-Queue: Ingvar Stepanyan <rreverser@google.com>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64157}
2019-10-08 10:54:09 +00:00

121 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(),
];
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');
var module_bytes = builder.toArray();
builder.addCustomSection('.debug_info', []);
var module_bytes_with_dwarf = 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));
contextGroup.addScript('var module_bytes_with_dwarf = ' + JSON.stringify(module_bytes_with_dwarf));
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); testFunction(module_bytes_with_dwarf);'
})
.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, sourceMapURL}) {
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}. Source map URL: ${sourceMapURL}`);
let scriptSource;
if (sourceMapURL === "wasm://dwarf") {
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.url.startsWith("wasm://")) {
scripts.push(loadScript(params));
}
}
return {
Protocol,
getScripts: () => Promise.all(scripts),
};
}