v8/test/inspector/debugger/wasm-stepping-with-source-map.js
Benedikt Meurer f10edd0900 [inspector][wasm] Re-add WasmValue as JSObject subclass.
BREAKING CHANGE: The values of Wasm locals, stack, and globals are now
represented as objects instead of holding the (primitive) values
directly, and SIMD128 values are no longer represented as Uint8Arrays.
The DWARF extension has been prepared for this breaking change.

The new `WasmValue` comes with `type` and `value` properties that hold
its contents. The motivation here is that this is a more extensible
approach. In case of SIMD128, the `value` property holds the canonical
string representation, which has the additional advantage that these
values can be compared with `===` (and `==`).

This partially reverts https://crrev.com/c/2614428, the main difference
here being that WasmValue is now a proper JSObject that can be exposed
on the DebugEvaluate proxy API.

Screenshot: https://imgur.com/rcahNKM.png
Bug: chromium:1170282, chromium:1071432, chromium:1159402
Change-Id: Iea304e3680775123c41deb4c3d172ac949da1b98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2643384
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Reviewed-by: Zhi An Ng <zhin@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72570}
2021-02-09 05:50:16 +00:00

105 lines
4.5 KiB
JavaScript

// 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.
utils.load('test/inspector/wasm-inspector-test.js');
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests stepping through wasm scripts with source maps');
session.setupScriptMap();
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');
builder.addCustomSection('sourceMappingURL', [3, 97, 98, 99]);
var module_bytes = builder.toArray();
InspectorTest.runAsyncTestSuite([
async function test() {
for (const action of ['stepInto', 'stepOver', 'stepOut', 'resume'])
InspectorTest.logProtocolCommandCalls('Debugger.' + action);
await Protocol.Debugger.enable();
WasmInspectorTest.instantiate(module_bytes);
const [, {params: wasmScript}] = await Protocol.Debugger.onceScriptParsed(2);
InspectorTest.log('Got wasm script: ' + wasmScript.url);
InspectorTest.log('Script sourceMapURL: ' + wasmScript.sourceMapURL);
InspectorTest.log('Requesting source for ' + wasmScript.url + '...');
const msg =
await Protocol.Debugger.getScriptSource({scriptId: wasmScript.scriptId});
InspectorTest.log(`Source retrieved without error: ${!msg.error}`);
InspectorTest.log(
`Setting breakpoint on offset 54 (on the setlocal before the call), url ${wasmScript.url}`);
const {result: {actualLocation}} = await Protocol.Debugger.setBreakpoint({
location:{scriptId: wasmScript.scriptId, lineNumber: 0, columnNumber: 54}});
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 8 times, until we are in wasm_A again.
for (let i = 0; i < 8; ++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!');
}
]);
async function waitForPauseAndStep(stepAction) {
const {params: {callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
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' || scope.type === 'module') {
InspectorTest.logObject(' -- skipped');
} else {
let properties = await Protocol.Runtime.getProperties(
{objectId: scope.object.objectId});
for (let {name, value} of properties.result.result) {
value = await WasmInspectorTest.getWasmValue(value);
InspectorTest.log(` ${name}: ${value}`);
}
}
}
}
Protocol.Debugger[stepAction]();
}