Commit 1b5f3be0 authored by Ingvar Stepanyan's avatar Ingvar Stepanyan Committed by Commit Bot

[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: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64157}
parent f5e73635
...@@ -169,8 +169,6 @@ domain Debugger ...@@ -169,8 +169,6 @@ domain Debugger
# The maximum size in bytes of collected scripts (not referenced by other heap objects) # The maximum size in bytes of collected scripts (not referenced by other heap objects)
# the debugger can hold. Puts no limit if paramter is omitted. # the debugger can hold. Puts no limit if paramter is omitted.
experimental optional number maxScriptsCacheSize experimental optional number maxScriptsCacheSize
# Whether to report Wasm modules as raw binaries instead of disassembled functions.
experimental optional boolean supportsWasmDwarf
returns returns
# Unique identifier of the debugger. # Unique identifier of the debugger.
experimental Runtime.UniqueDebuggerId debuggerId experimental Runtime.UniqueDebuggerId debuggerId
......
...@@ -9444,14 +9444,6 @@ int debug::WasmScript::NumImportedFunctions() const { ...@@ -9444,14 +9444,6 @@ int debug::WasmScript::NumImportedFunctions() const {
return static_cast<int>(module->num_imported_functions); return static_cast<int>(module->num_imported_functions);
} }
bool debug::WasmScript::HasDwarf() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
return module->has_dwarf;
}
MemorySpan<const uint8_t> debug::WasmScript::Bytecode() const { MemorySpan<const uint8_t> debug::WasmScript::Bytecode() const {
i::Handle<i::Script> script = Utils::OpenHandle(this); i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Vector<const uint8_t> wire_bytes = i::Vector<const uint8_t> wire_bytes =
......
...@@ -159,7 +159,6 @@ class WasmScript : public Script { ...@@ -159,7 +159,6 @@ class WasmScript : public Script {
int NumFunctions() const; int NumFunctions() const;
int NumImportedFunctions() const; int NumImportedFunctions() const;
bool HasDwarf() const;
MemorySpan<const uint8_t> Bytecode() const; MemorySpan<const uint8_t> Bytecode() const;
std::pair<int, int> GetFunctionRange(int function_index) const; std::pair<int, int> GetFunctionRange(int function_index) const;
......
...@@ -350,13 +350,11 @@ void V8DebuggerAgentImpl::enableImpl() { ...@@ -350,13 +350,11 @@ void V8DebuggerAgentImpl::enableImpl() {
} }
Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize, Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
Maybe<bool> supportsWasmDwarf,
String16* outDebuggerId) { String16* outDebuggerId) {
m_maxScriptCacheSize = v8::base::saturated_cast<size_t>( m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max())); maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
*outDebuggerId = *outDebuggerId =
m_debugger->debuggerIdFor(m_session->contextGroupId()).toString(); m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
m_supportsWasmDwarf = supportsWasmDwarf.fromMaybe(false);
if (enabled()) return Response::OK(); if (enabled()) return Response::OK();
if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId())) if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
......
...@@ -43,7 +43,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -43,7 +43,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
// Part of the protocol. // Part of the protocol.
Response enable(Maybe<double> maxScriptsCacheSize, Response enable(Maybe<double> maxScriptsCacheSize,
Maybe<bool> supportsWasmDwarf,
String16* outDebuggerId) override; String16* outDebuggerId) override;
Response disable() override; Response disable() override;
Response setBreakpointsActive(bool active) override; Response setBreakpointsActive(bool active) override;
...@@ -129,7 +128,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -129,7 +128,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
positions) override; positions) override;
bool enabled() const { return m_enabled; } bool enabled() const { return m_enabled; }
bool supportsWasmDwarf() const { return m_supportsWasmDwarf; }
void setBreakpointFor(v8::Local<v8::Function> function, void setBreakpointFor(v8::Local<v8::Function> function,
v8::Local<v8::String> condition, v8::Local<v8::String> condition,
...@@ -233,8 +231,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -233,8 +231,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
std::unordered_map<String16, std::vector<std::pair<int, int>>> std::unordered_map<String16, std::vector<std::pair<int, int>>>
m_blackboxedPositions; m_blackboxedPositions;
bool m_supportsWasmDwarf = false;
DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl); DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl);
}; };
......
...@@ -522,19 +522,13 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script, ...@@ -522,19 +522,13 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
&wasmTranslation](V8InspectorSessionImpl* session) { &wasmTranslation](V8InspectorSessionImpl* session) {
auto agent = session->debuggerAgent(); auto agent = session->debuggerAgent();
if (!agent->enabled()) return; if (!agent->enabled()) return;
if (script->IsWasm() && script->SourceMappingURL().IsEmpty() && if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
!(agent->supportsWasmDwarf() &&
script.As<v8::debug::WasmScript>()->HasDwarf())) {
wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent); wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
} else { } else {
auto debuggerScript = V8DebuggerScript::Create( agent->didParseSource(
isolate, script, is_live_edited, agent, client); V8DebuggerScript::Create(isolate, script, is_live_edited, agent,
if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) { client),
DCHECK(agent->supportsWasmDwarf()); !has_compile_error);
DCHECK(script.As<v8::debug::WasmScript>()->HasDwarf());
debuggerScript->setSourceMappingURL("wasm://dwarf");
}
agent->didParseSource(std::move(debuggerScript), !has_compile_error);
} }
}); });
} }
......
...@@ -460,7 +460,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -460,7 +460,7 @@ class ModuleDecoderImpl : public Decoder {
DecodeSourceMappingURLSection(); DecodeSourceMappingURLSection();
break; break;
case kDebugInfoSectionCode: case kDebugInfoSectionCode:
module_->has_dwarf = true; module_->source_map_url.assign("wasm://dwarf");
consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info"); consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info");
break; break;
case kCompilationHintsSectionCode: case kCompilationHintsSectionCode:
......
...@@ -193,7 +193,6 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -193,7 +193,6 @@ struct V8_EXPORT_PRIVATE WasmModule {
bool has_maximum_pages = false; // true if there is a maximum memory size bool has_maximum_pages = false; // true if there is a maximum memory size
bool has_memory = false; // true if the memory was defined or imported bool has_memory = false; // true if the memory was defined or imported
bool mem_export = false; // true if the memory is exported bool mem_export = false; // true if the memory is exported
bool has_dwarf = false; // true if .debug_info section is present
int start_function_index = -1; // start function, >= 0 if any int start_function_index = -1; // start function, >= 0 if any
std::vector<WasmGlobal> globals; std::vector<WasmGlobal> globals;
......
Tests how wasm scripts are reported Tests how wasm scripts are reported
Check that each inspector gets two wasm scripts at module creation time. Check that each inspector gets two wasm scripts at module creation time.
Session #1: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-0 Session #1: Script #0 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-0. Source map URL:
Session #1: Script #1 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-1 Session #1: Script #1 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1. Source map URL:
Session #2: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-0 Session #2: Script #0 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-0. Source map URL:
Session #2: Script #1 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-1 Session #2: Script #1 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1. Source map URL:
Session #3: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae Session #1: Script #2 parsed. URL: wasm://wasm/wasm-77a937ae. Source map URL: wasm://dwarf
Session #1: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-0: Session #2: Script #2 parsed. URL: wasm://wasm/wasm-77a937ae. Source map URL: wasm://dwarf
Session #1: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0:
func $nopFunction func $nopFunction
nop nop
end end
Session #1: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-1: Session #1: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1:
func $main func $main
block block
i32.const 2 i32.const 2
...@@ -18,12 +19,12 @@ func $main ...@@ -18,12 +19,12 @@ func $main
end end
end end
Session #2: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-0: Session #2: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0:
func $nopFunction func $nopFunction
nop nop
end end
Session #2: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-1: Session #2: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1:
func $main func $main
block block
i32.const 2 i32.const 2
...@@ -31,7 +32,11 @@ func $main ...@@ -31,7 +32,11 @@ func $main
end end
end end
Session #3: Source for wasm://wasm/wasm-77a937ae: Session #1: Source for wasm://wasm/wasm-77a937ae:
Raw: 00 61 73 6d 01 00 00 00 01 07 02 60 00 00 60 00 00 03 03 02 00 01 07 08 01 04 6d 61 69 6e 00 01 0a 0e 02 03 00 01 0b 08 00 02 40 41 02 1a 0b 0b 00 0c 0b 2e 64 65 62 75 67 5f 69 6e 66 6f 00 1b 04 6e 61 6d 65 01 14 02 00 0b 6e 6f 70 46 75 6e 63 74 69 6f 6e 01 04 6d 61 69 6e
Imports: []
Exports: [main: function]
Session #2: Source for wasm://wasm/wasm-77a937ae:
Raw: 00 61 73 6d 01 00 00 00 01 07 02 60 00 00 60 00 00 03 03 02 00 01 07 08 01 04 6d 61 69 6e 00 01 0a 0e 02 03 00 01 0b 08 00 02 40 41 02 1a 0b 0b 00 0c 0b 2e 64 65 62 75 67 5f 69 6e 66 6f 00 1b 04 6e 61 6d 65 01 14 02 00 0b 6e 6f 70 46 75 6e 63 74 69 6f 6e 01 04 6d 61 69 6e Raw: 00 61 73 6d 01 00 00 00 01 07 02 60 00 00 60 00 00 03 03 02 00 01 07 08 01 04 6d 61 69 6e 00 01 0a 0e 02 03 00 01 0b 08 00 02 40 41 02 1a 0b 0b 00 0c 0b 2e 64 65 62 75 67 5f 69 6e 66 6f 00 1b 04 6e 61 6d 65 01 14 02 00 0b 6e 6f 70 46 75 6e 63 74 69 6f 6e 01 04 6d 61 69 6e
Imports: [] Imports: []
Exports: [main: function] Exports: [main: function]
...@@ -13,8 +13,6 @@ let sessions = [ ...@@ -13,8 +13,6 @@ let sessions = [
// Extra session to verify that all inspectors get same messages. // Extra session to verify that all inspectors get same messages.
// See https://bugs.chromium.org/p/v8/issues/detail?id=9725. // See https://bugs.chromium.org/p/v8/issues/detail?id=9725.
trackScripts(), trackScripts(),
// Another session to check how raw Wasm is reported.
trackScripts({ supportsWasmDwarf: true }),
]; ];
utils.load('test/mjsunit/wasm/wasm-module-builder.js'); utils.load('test/mjsunit/wasm/wasm-module-builder.js');
...@@ -26,8 +24,9 @@ builder.addFunction('nopFunction', kSig_v_v).addBody([kExprNop]); ...@@ -26,8 +24,9 @@ builder.addFunction('nopFunction', kSig_v_v).addBody([kExprNop]);
builder.addFunction('main', kSig_v_v) builder.addFunction('main', kSig_v_v)
.addBody([kExprBlock, kWasmStmt, kExprI32Const, 2, kExprDrop, kExprEnd]) .addBody([kExprBlock, kWasmStmt, kExprI32Const, 2, kExprDrop, kExprEnd])
.exportAs('main'); .exportAs('main');
builder.addCustomSection('.debug_info', []);
var module_bytes = builder.toArray(); var module_bytes = builder.toArray();
builder.addCustomSection('.debug_info', []);
var module_bytes_with_dwarf = builder.toArray();
function testFunction(bytes) { function testFunction(bytes) {
// Compilation triggers registration of wasm scripts. // Compilation triggers registration of wasm scripts.
...@@ -36,6 +35,7 @@ function testFunction(bytes) { ...@@ -36,6 +35,7 @@ function testFunction(bytes) {
contextGroup.addScript(testFunction.toString(), 0, 0, 'v8://test/testFunction'); contextGroup.addScript(testFunction.toString(), 0, 0, 'v8://test/testFunction');
contextGroup.addScript('var module_bytes = ' + JSON.stringify(module_bytes)); contextGroup.addScript('var module_bytes = ' + JSON.stringify(module_bytes));
contextGroup.addScript('var module_bytes_with_dwarf = ' + JSON.stringify(module_bytes_with_dwarf));
InspectorTest.log( InspectorTest.log(
'Check that each inspector gets two wasm scripts at module creation time.'); 'Check that each inspector gets two wasm scripts at module creation time.');
...@@ -43,7 +43,7 @@ InspectorTest.log( ...@@ -43,7 +43,7 @@ InspectorTest.log(
sessions[0].Protocol.Runtime sessions[0].Protocol.Runtime
.evaluate({ .evaluate({
'expression': '//# sourceURL=v8://test/runTestRunction\n' + 'expression': '//# sourceURL=v8://test/runTestRunction\n' +
'testFunction(module_bytes)' 'testFunction(module_bytes); testFunction(module_bytes_with_dwarf);'
}) })
.then(() => ( .then(() => (
// At this point all scripts were parsed. // At this point all scripts were parsed.
...@@ -86,10 +86,10 @@ function trackScripts(debuggerParams) { ...@@ -86,10 +86,10 @@ function trackScripts(debuggerParams) {
Protocol.Debugger.enable(debuggerParams); Protocol.Debugger.enable(debuggerParams);
Protocol.Debugger.onScriptParsed(handleScriptParsed); Protocol.Debugger.onScriptParsed(handleScriptParsed);
async function loadScript({url, scriptId}, raw) { async function loadScript({url, scriptId, sourceMapURL}) {
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}`); InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}. Source map URL: ${sourceMapURL}`);
let scriptSource; let scriptSource;
if (raw) { if (sourceMapURL === "wasm://dwarf") {
let {result: {bytecode}} = await Protocol.Debugger.getWasmBytecode({scriptId}); let {result: {bytecode}} = await Protocol.Debugger.getWasmBytecode({scriptId});
// Binary value is represented as base64 in JSON, decode it. // Binary value is represented as base64 in JSON, decode it.
bytecode = decodeBase64(bytecode); bytecode = decodeBase64(bytecode);
...@@ -108,9 +108,7 @@ Exports: [${WebAssembly.Module.exports(module).map(e => `${e.name}: ${e.kind}`). ...@@ -108,9 +108,7 @@ Exports: [${WebAssembly.Module.exports(module).map(e => `${e.name}: ${e.kind}`).
} }
function handleScriptParsed({params}) { function handleScriptParsed({params}) {
if (params.sourceMapURL === "wasm://dwarf") { if (params.url.startsWith("wasm://")) {
scripts.push(loadScript(params, true));
} else if (params.url.startsWith("wasm://")) {
scripts.push(loadScript(params)); scripts.push(loadScript(params));
} }
} }
......
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