[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}
This commit is contained in:
Ingvar Stepanyan 2019-10-07 14:50:37 +01:00 committed by Commit Bot
parent f5e7363561
commit 1b5f3be087
10 changed files with 29 additions and 50 deletions

View File

@ -169,8 +169,6 @@ domain Debugger
# 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.
experimental optional number maxScriptsCacheSize
# Whether to report Wasm modules as raw binaries instead of disassembled functions.
experimental optional boolean supportsWasmDwarf
returns
# Unique identifier of the debugger.
experimental Runtime.UniqueDebuggerId debuggerId

View File

@ -9444,14 +9444,6 @@ int debug::WasmScript::NumImportedFunctions() const {
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 {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Vector<const uint8_t> wire_bytes =

View File

@ -159,7 +159,6 @@ class WasmScript : public Script {
int NumFunctions() const;
int NumImportedFunctions() const;
bool HasDwarf() const;
MemorySpan<const uint8_t> Bytecode() const;
std::pair<int, int> GetFunctionRange(int function_index) const;

View File

@ -350,13 +350,11 @@ void V8DebuggerAgentImpl::enableImpl() {
}
Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
Maybe<bool> supportsWasmDwarf,
String16* outDebuggerId) {
m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
*outDebuggerId =
m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
m_supportsWasmDwarf = supportsWasmDwarf.fromMaybe(false);
if (enabled()) return Response::OK();
if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))

View File

@ -43,7 +43,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
// Part of the protocol.
Response enable(Maybe<double> maxScriptsCacheSize,
Maybe<bool> supportsWasmDwarf,
String16* outDebuggerId) override;
Response disable() override;
Response setBreakpointsActive(bool active) override;
@ -129,7 +128,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
positions) override;
bool enabled() const { return m_enabled; }
bool supportsWasmDwarf() const { return m_supportsWasmDwarf; }
void setBreakpointFor(v8::Local<v8::Function> function,
v8::Local<v8::String> condition,
@ -233,8 +231,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
std::unordered_map<String16, std::vector<std::pair<int, int>>>
m_blackboxedPositions;
bool m_supportsWasmDwarf = false;
DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl);
};

View File

@ -522,19 +522,13 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
&wasmTranslation](V8InspectorSessionImpl* session) {
auto agent = session->debuggerAgent();
if (!agent->enabled()) return;
if (script->IsWasm() && script->SourceMappingURL().IsEmpty() &&
!(agent->supportsWasmDwarf() &&
script.As<v8::debug::WasmScript>()->HasDwarf())) {
if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
} else {
auto debuggerScript = V8DebuggerScript::Create(
isolate, script, is_live_edited, agent, client);
if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
DCHECK(agent->supportsWasmDwarf());
DCHECK(script.As<v8::debug::WasmScript>()->HasDwarf());
debuggerScript->setSourceMappingURL("wasm://dwarf");
}
agent->didParseSource(std::move(debuggerScript), !has_compile_error);
agent->didParseSource(
V8DebuggerScript::Create(isolate, script, is_live_edited, agent,
client),
!has_compile_error);
}
});
}

View File

@ -460,7 +460,7 @@ class ModuleDecoderImpl : public Decoder {
DecodeSourceMappingURLSection();
break;
case kDebugInfoSectionCode:
module_->has_dwarf = true;
module_->source_map_url.assign("wasm://dwarf");
consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info");
break;
case kCompilationHintsSectionCode:

View File

@ -193,7 +193,6 @@ struct V8_EXPORT_PRIVATE WasmModule {
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 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
std::vector<WasmGlobal> globals;

View File

@ -1,16 +1,17 @@
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/wasm-77a937ae/wasm-77a937ae-0
Session #1: Script #1 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-1
Session #2: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-0
Session #2: Script #1 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-1
Session #3: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae
Session #1: Source for 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-7b04570e/wasm-7b04570e-1. Source map URL:
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-7b04570e/wasm-7b04570e-1. Source map URL:
Session #1: Script #2 parsed. URL: wasm://wasm/wasm-77a937ae. Source map URL: wasm://dwarf
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
nop
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
block
i32.const 2
@ -18,12 +19,12 @@ func $main
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
nop
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
block
i32.const 2
@ -31,7 +32,11 @@ func $main
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
Imports: []
Exports: [main: function]

View File

@ -13,8 +13,6 @@ let sessions = [
// 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');
@ -26,8 +24,9 @@ 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();
builder.addCustomSection('.debug_info', []);
var module_bytes_with_dwarf = builder.toArray();
function testFunction(bytes) {
// Compilation triggers registration of wasm scripts.
@ -36,6 +35,7 @@ function testFunction(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.');
@ -43,7 +43,7 @@ InspectorTest.log(
sessions[0].Protocol.Runtime
.evaluate({
'expression': '//# sourceURL=v8://test/runTestRunction\n' +
'testFunction(module_bytes)'
'testFunction(module_bytes); testFunction(module_bytes_with_dwarf);'
})
.then(() => (
// At this point all scripts were parsed.
@ -86,10 +86,10 @@ function trackScripts(debuggerParams) {
Protocol.Debugger.enable(debuggerParams);
Protocol.Debugger.onScriptParsed(handleScriptParsed);
async function loadScript({url, scriptId}, raw) {
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}`);
async function loadScript({url, scriptId, sourceMapURL}) {
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}. Source map URL: ${sourceMapURL}`);
let scriptSource;
if (raw) {
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);
@ -108,9 +108,7 @@ Exports: [${WebAssembly.Module.exports(module).map(e => `${e.name}: ${e.kind}`).
}
function handleScriptParsed({params}) {
if (params.sourceMapURL === "wasm://dwarf") {
scripts.push(loadScript(params, true));
} else if (params.url.startsWith("wasm://")) {
if (params.url.startsWith("wasm://")) {
scripts.push(loadScript(params));
}
}