Commit c7848612 authored by Ingvar Stepanyan's avatar Ingvar Stepanyan Committed by Commit Bot

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: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63969}
parent 01e52d80
...@@ -169,6 +169,8 @@ domain Debugger ...@@ -169,6 +169,8 @@ 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
...@@ -227,6 +229,15 @@ domain Debugger ...@@ -227,6 +229,15 @@ domain Debugger
# Script source. # Script source.
string scriptSource string scriptSource
# Returns bytecode for the WebAssembly script with given id.
command getWasmBytecode
parameters
# Id of the Wasm script to get source for.
Runtime.ScriptId scriptId
returns
# Script source.
binary bytecode
# Returns stack trace with given `stackTraceId`. # Returns stack trace with given `stackTraceId`.
experimental command getStackTrace experimental command getStackTrace
parameters parameters
......
...@@ -9439,6 +9439,21 @@ int debug::WasmScript::NumImportedFunctions() const { ...@@ -9439,6 +9439,21 @@ 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 {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Vector<const uint8_t> wire_bytes =
script->wasm_native_module()->wire_bytes();
return {wire_bytes.begin(), wire_bytes.size()};
}
std::pair<int, int> debug::WasmScript::GetFunctionRange( std::pair<int, int> debug::WasmScript::GetFunctionRange(
int function_index) const { int function_index) const {
i::DisallowHeapAllocation no_gc; i::DisallowHeapAllocation no_gc;
......
...@@ -159,6 +159,8 @@ class WasmScript : public Script { ...@@ -159,6 +159,8 @@ 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;
std::pair<int, int> GetFunctionRange(int function_index) const; std::pair<int, int> GetFunctionRange(int function_index) const;
......
...@@ -101,13 +101,23 @@ class StringUtil { ...@@ -101,13 +101,23 @@ class StringUtil {
// therefore it's unnecessary to provide an implementation here. // therefore it's unnecessary to provide an implementation here.
class Binary { class Binary {
public: public:
const uint8_t* data() const { UNIMPLEMENTED(); } Binary() = default;
size_t size() const { UNIMPLEMENTED(); }
const uint8_t* data() const { return bytes_->data(); }
size_t size() const { return bytes_->size(); }
String toBase64() const { UNIMPLEMENTED(); } String toBase64() const { UNIMPLEMENTED(); }
static Binary fromBase64(const String& base64, bool* success) { static Binary fromBase64(const String& base64, bool* success) {
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
static Binary fromSpan(const uint8_t* data, size_t size) { UNIMPLEMENTED(); } static Binary fromSpan(const uint8_t* data, size_t size) {
return Binary(std::make_shared<std::vector<uint8_t>>(data, data + size));
}
private:
std::shared_ptr<std::vector<uint8_t>> bytes_;
explicit Binary(std::shared_ptr<std::vector<uint8_t>> bytes)
: bytes_(bytes) {}
}; };
} // namespace protocol } // namespace protocol
......
...@@ -350,11 +350,13 @@ void V8DebuggerAgentImpl::enableImpl() { ...@@ -350,11 +350,13 @@ 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()))
...@@ -962,6 +964,20 @@ Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId, ...@@ -962,6 +964,20 @@ Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
return Response::OK(); return Response::OK();
} }
Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId,
protocol::Binary* bytecode) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
ScriptsMap::iterator it = m_scripts.find(scriptId);
if (it == m_scripts.end())
return Response::Error("No script for id: " + scriptId);
v8::MemorySpan<const uint8_t> span;
if (!it->second->wasmBytecode().To(&span))
return Response::Error("Script with id " + scriptId +
" is not WebAssembly");
*bytecode = protocol::Binary::fromSpan(span.data(), span.size());
return Response::OK();
}
void V8DebuggerAgentImpl::pushBreakDetails( void V8DebuggerAgentImpl::pushBreakDetails(
const String16& breakReason, const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> breakAuxData) { std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
......
...@@ -43,6 +43,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -43,6 +43,7 @@ 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;
...@@ -95,6 +96,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -95,6 +96,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) override; Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) override;
Response getScriptSource(const String16& scriptId, Response getScriptSource(const String16& scriptId,
String16* scriptSource) override; String16* scriptSource) override;
Response getWasmBytecode(const String16& scriptId,
protocol::Binary* bytecode) override;
Response pause() override; Response pause() override;
Response resume() override; Response resume() override;
Response stepOver() override; Response stepOver() override;
...@@ -126,6 +129,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -126,6 +129,7 @@ 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,
...@@ -229,6 +233,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -229,6 +233,8 @@ 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);
}; };
......
...@@ -141,6 +141,12 @@ class ActualScript : public V8DebuggerScript { ...@@ -141,6 +141,12 @@ class ActualScript : public V8DebuggerScript {
static_cast<int>(pos), static_cast<int>(substringLength)); static_cast<int>(pos), static_cast<int>(substringLength));
return String16(buffer.get(), substringLength); return String16(buffer.get(), substringLength);
} }
v8::Maybe<v8::MemorySpan<const uint8_t>> wasmBytecode() const override {
v8::HandleScope scope(m_isolate);
auto script = this->script();
if (!script->IsWasm()) return v8::Nothing<v8::MemorySpan<const uint8_t>>();
return v8::Just(v8::debug::WasmScript::Cast(*script)->Bytecode());
}
int startLine() const override { return m_startLine; } int startLine() const override { return m_startLine; }
int startColumn() const override { return m_startColumn; } int startColumn() const override { return m_startColumn; }
int endLine() const override { return m_endLine; } int endLine() const override { return m_endLine; }
...@@ -281,9 +287,8 @@ class ActualScript : public V8DebuggerScript { ...@@ -281,9 +287,8 @@ class ActualScript : public V8DebuggerScript {
m_startLine = script->LineOffset(); m_startLine = script->LineOffset();
m_startColumn = script->ColumnOffset(); m_startColumn = script->ColumnOffset();
std::vector<int> lineEnds = script->LineEnds(); std::vector<int> lineEnds = script->LineEnds();
CHECK(lineEnds.size());
int source_length = lineEnds[lineEnds.size() - 1];
if (lineEnds.size()) { if (lineEnds.size()) {
int source_length = lineEnds[lineEnds.size() - 1];
m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1; m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
if (lineEnds.size() > 1) { if (lineEnds.size() > 1) {
m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1; m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
...@@ -356,6 +361,9 @@ class WasmVirtualScript : public V8DebuggerScript { ...@@ -356,6 +361,9 @@ class WasmVirtualScript : public V8DebuggerScript {
return m_wasmTranslation->GetSource(m_id, m_functionIndex) return m_wasmTranslation->GetSource(m_id, m_functionIndex)
.substring(pos, len); .substring(pos, len);
} }
v8::Maybe<v8::MemorySpan<const uint8_t>> wasmBytecode() const override {
return v8::Nothing<v8::MemorySpan<const uint8_t>>();
}
int startLine() const override { int startLine() const override {
return m_wasmTranslation->GetStartLine(m_id, m_functionIndex); return m_wasmTranslation->GetStartLine(m_id, m_functionIndex);
} }
......
...@@ -63,6 +63,7 @@ class V8DebuggerScript { ...@@ -63,6 +63,7 @@ class V8DebuggerScript {
virtual const String16& sourceMappingURL() const = 0; virtual const String16& sourceMappingURL() const = 0;
virtual String16 source(size_t pos, size_t len = UINT_MAX) const = 0; virtual String16 source(size_t pos, size_t len = UINT_MAX) const = 0;
virtual v8::Maybe<v8::MemorySpan<const uint8_t>> wasmBytecode() const = 0;
virtual const String16& hash() const = 0; virtual const String16& hash() const = 0;
virtual int startLine() const = 0; virtual int startLine() const = 0;
virtual int startColumn() const = 0; virtual int startColumn() const = 0;
......
...@@ -507,31 +507,36 @@ size_t V8Debugger::nearHeapLimitCallback(void* data, size_t current_heap_limit, ...@@ -507,31 +507,36 @@ size_t V8Debugger::nearHeapLimitCallback(void* data, size_t current_heap_limit,
void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script, void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
bool is_live_edited, bool has_compile_error) { bool is_live_edited, bool has_compile_error) {
if (m_ignoreScriptParsedEventsCounter != 0) return;
int contextId; int contextId;
if (!script->ContextId().To(&contextId)) return; if (!script->ContextId().To(&contextId)) return;
if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
WasmTranslation* wasmTranslation = &m_wasmTranslation; v8::Isolate* isolate = m_isolate;
m_inspector->forEachSession( V8InspectorClient* client = m_inspector->client();
m_inspector->contextGroupId(contextId), WasmTranslation& wasmTranslation = m_wasmTranslation;
[&script, &wasmTranslation](V8InspectorSessionImpl* session) {
if (!session->debuggerAgent()->enabled()) return; m_inspector->forEachSession(
wasmTranslation->AddScript(script.As<v8::debug::WasmScript>(), m_inspector->contextGroupId(contextId),
session->debuggerAgent()); [isolate, &script, has_compile_error, is_live_edited, client,
}); &wasmTranslation](V8InspectorSessionImpl* session) {
} else if (m_ignoreScriptParsedEventsCounter == 0) { auto agent = session->debuggerAgent();
v8::Isolate* isolate = m_isolate; if (!agent->enabled()) return;
V8InspectorClient* client = m_inspector->client(); if (script->IsWasm() && script->SourceMappingURL().IsEmpty() &&
m_inspector->forEachSession( !(agent->supportsWasmDwarf() &&
m_inspector->contextGroupId(contextId), script.As<v8::debug::WasmScript>()->HasDwarf())) {
[&isolate, &script, &has_compile_error, &is_live_edited, wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
&client](V8InspectorSessionImpl* session) { } else {
if (!session->debuggerAgent()->enabled()) return; auto debuggerScript = V8DebuggerScript::Create(
session->debuggerAgent()->didParseSource( isolate, script, is_live_edited, agent, client);
V8DebuggerScript::Create(isolate, script, is_live_edited, if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
session->debuggerAgent(), 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);
}
});
} }
void V8Debugger::BreakProgramRequested( void V8Debugger::BreakProgramRequested(
......
...@@ -30,6 +30,7 @@ namespace { ...@@ -30,6 +30,7 @@ namespace {
constexpr char kNameString[] = "name"; constexpr char kNameString[] = "name";
constexpr char kSourceMappingURLString[] = "sourceMappingURL"; constexpr char kSourceMappingURLString[] = "sourceMappingURL";
constexpr char kCompilationHintsString[] = "compilationHints"; constexpr char kCompilationHintsString[] = "compilationHints";
constexpr char kDebugInfoString[] = ".debug_info";
template <size_t N> template <size_t N>
constexpr size_t num_chars(const char (&)[N]) { constexpr size_t num_chars(const char (&)[N]) {
...@@ -88,6 +89,8 @@ const char* SectionName(SectionCode code) { ...@@ -88,6 +89,8 @@ const char* SectionName(SectionCode code) {
return kNameString; return kNameString;
case kSourceMappingURLSectionCode: case kSourceMappingURLSectionCode:
return kSourceMappingURLString; return kSourceMappingURLString;
case kDebugInfoSectionCode:
return kDebugInfoString;
case kCompilationHintsSectionCode: case kCompilationHintsSectionCode:
return kCompilationHintsString; return kCompilationHintsString;
default: default:
...@@ -398,6 +401,10 @@ class ModuleDecoderImpl : public Decoder { ...@@ -398,6 +401,10 @@ class ModuleDecoderImpl : public Decoder {
// sourceMappingURL is a custom section and currently can occur anywhere // sourceMappingURL is a custom section and currently can occur anywhere
// in the module. In case of multiple sourceMappingURL sections, all // in the module. In case of multiple sourceMappingURL sections, all
// except the first occurrence are ignored. // except the first occurrence are ignored.
case kDebugInfoSectionCode:
// .debug_info is a custom section containing core DWARF information
// if produced by compiler. Its presence likely means that Wasm was
// built in a debug mode.
case kCompilationHintsSectionCode: case kCompilationHintsSectionCode:
// TODO(frgossen): report out of place compilation hints section as a // TODO(frgossen): report out of place compilation hints section as a
// warning. // warning.
...@@ -452,6 +459,10 @@ class ModuleDecoderImpl : public Decoder { ...@@ -452,6 +459,10 @@ class ModuleDecoderImpl : public Decoder {
case kSourceMappingURLSectionCode: case kSourceMappingURLSectionCode:
DecodeSourceMappingURLSection(); DecodeSourceMappingURLSection();
break; break;
case kDebugInfoSectionCode:
module_->has_dwarf = true;
consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info");
break;
case kCompilationHintsSectionCode: case kCompilationHintsSectionCode:
if (enabled_features_.compilation_hints) { if (enabled_features_.compilation_hints) {
DecodeCompilationHintsSection(); DecodeCompilationHintsSection();
...@@ -1950,6 +1961,10 @@ SectionCode ModuleDecoder::IdentifyUnknownSection(Decoder* decoder, ...@@ -1950,6 +1961,10 @@ SectionCode ModuleDecoder::IdentifyUnknownSection(Decoder* decoder,
kCompilationHintsString, kCompilationHintsString,
num_chars(kCompilationHintsString)) == 0) { num_chars(kCompilationHintsString)) == 0) {
return kCompilationHintsSectionCode; return kCompilationHintsSectionCode;
} else if (string.length() == num_chars(kDebugInfoString) &&
strncmp(reinterpret_cast<const char*>(section_name_start),
kDebugInfoString, num_chars(kDebugInfoString)) == 0) {
return kDebugInfoSectionCode;
} }
return kUnknownSectionCode; return kUnknownSectionCode;
} }
......
...@@ -81,6 +81,7 @@ enum SectionCode : int8_t { ...@@ -81,6 +81,7 @@ enum SectionCode : int8_t {
// to be consistent. // to be consistent.
kNameSectionCode, // Name section (encoded as a string) kNameSectionCode, // Name section (encoded as a string)
kSourceMappingURLSectionCode, // Source Map URL section kSourceMappingURLSectionCode, // Source Map URL section
kDebugInfoSectionCode, // DWARF section .debug_info
kCompilationHintsSectionCode, // Compilation hints section kCompilationHintsSectionCode, // Compilation hints section
// Helper values // Helper values
......
...@@ -193,6 +193,7 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -193,6 +193,7 @@ 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-7b04570e/wasm-7b04570e-0 Session #1: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-0
Session #1: Script #1 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1 Session #1: Script #1 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-1
Session #2: Script #0 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-0 Session #2: Script #0 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-0
Session #2: Script #1 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1 Session #2: Script #1 parsed. URL: wasm://wasm/wasm-77a937ae/wasm-77a937ae-1
Session #1: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0: Session #3: Script #0 parsed. URL: wasm-77a937ae
Session #1: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-0:
func $nopFunction func $nopFunction
nop nop
end end
Session #1: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1: Session #1: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-1:
func $main func $main
block block
i32.const 2 i32.const 2
...@@ -17,15 +18,20 @@ func $main ...@@ -17,15 +18,20 @@ func $main
end end
end end
Session #2: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0: Session #2: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-0:
func $nopFunction func $nopFunction
nop nop
end end
Session #2: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1: Session #2: Source for wasm://wasm/wasm-77a937ae/wasm-77a937ae-1:
func $main func $main
block block
i32.const 2 i32.const 2
drop drop
end end
end end
Session #3: Source for 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]
...@@ -13,6 +13,8 @@ let sessions = [ ...@@ -13,6 +13,8 @@ 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');
...@@ -24,6 +26,7 @@ builder.addFunction('nopFunction', kSig_v_v).addBody([kExprNop]); ...@@ -24,6 +26,7 @@ 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();
function testFunction(bytes) { function testFunction(bytes) {
...@@ -48,30 +51,68 @@ sessions[0].Protocol.Runtime ...@@ -48,30 +51,68 @@ sessions[0].Protocol.Runtime
Promise.all(sessions.map(session => session.getScripts())) Promise.all(sessions.map(session => session.getScripts()))
)) ))
.catch(err => { .catch(err => {
InspectorTest.log("FAIL: " + err.message); InspectorTest.log(err.stack);
}) })
.then(() => InspectorTest.completeTest()); .then(() => InspectorTest.completeTest());
function trackScripts() { 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 {id: sessionId, Protocol} = contextGroup.connect();
let scripts = []; let scripts = [];
Protocol.Debugger.enable(); Protocol.Debugger.enable(debuggerParams);
Protocol.Debugger.onScriptParsed(handleScriptParsed); Protocol.Debugger.onScriptParsed(handleScriptParsed);
async function getScript({url, scriptId}) { async function loadScript({url, scriptId}, raw) {
let {result: {scriptSource}} = await Protocol.Debugger.getScriptSource({scriptId}); 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(`Session #${sessionId}: Source for ${url}:`);
InspectorTest.log(scriptSource); InspectorTest.log(scriptSource);
return {url, scriptSource};
} }
function handleScriptParsed({params}) { function handleScriptParsed({params}) {
let {url} = params; if (params.sourceMapURL === "wasm://dwarf") {
if (!url.startsWith("wasm://")) return; scripts.push(loadScript(params, true));
} else if (params.url.startsWith("wasm://")) {
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}`); scripts.push(loadScript(params));
scripts.push(getScript(params)); }
} }
return { return {
......
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