Commit 37cdcdf4 authored by Philip Pfaffe's avatar Philip Pfaffe Committed by Commit Bot

Support .external_debug_info symbol references

Wasm modules generated by emscripten today have two ways to point to
debug symbol files, the source mapping url and external debug info
custom sections. To support both, this CL extends CDP to appropriately
report the symbol type and location.

Bug: chromium:1064248
Change-Id: I9076034f6d73901d8a9c5cfd7c2988fb30bb14c1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2116208Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67571}
parent 0c9a0072
...@@ -525,6 +525,18 @@ domain Debugger ...@@ -525,6 +525,18 @@ domain Debugger
JavaScript JavaScript
WebAssembly WebAssembly
# Debug symbols available for a wasm script.
type DebugSymbols extends object
properties
# Type of the debug symbols.
enum type
None
SourceMap
EmbeddedDWARF
ExternalDWARF
# URL of the external symbol source.
optional string externalURL
# Fired when virtual machine fails to parse the script. # Fired when virtual machine fails to parse the script.
event scriptFailedToParse event scriptFailedToParse
parameters parameters
...@@ -599,6 +611,8 @@ domain Debugger ...@@ -599,6 +611,8 @@ domain Debugger
experimental optional integer codeOffset experimental optional integer codeOffset
# The language of the script. # The language of the script.
experimental optional Debugger.ScriptLanguage scriptLanguage experimental optional Debugger.ScriptLanguage scriptLanguage
# If the scriptLanguage is WebASsembly, the source of debug symbols for the module.
experimental optional Debugger.DebugSymbols debugSymbols
experimental domain HeapProfiler experimental domain HeapProfiler
depends on Runtime depends on Runtime
......
...@@ -9729,6 +9729,37 @@ debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) { ...@@ -9729,6 +9729,37 @@ debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) {
return static_cast<WasmScript*>(script); return static_cast<WasmScript*>(script);
} }
debug::WasmScript::DebugSymbolsType debug::WasmScript::GetDebugSymbolType()
const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
switch (script->wasm_native_module()->module()->debug_symbols.type) {
case i::wasm::WasmDebugSymbols::Type::None:
return debug::WasmScript::DebugSymbolsType::None;
case i::wasm::WasmDebugSymbols::Type::EmbeddedDWARF:
return debug::WasmScript::DebugSymbolsType::EmbeddedDWARF;
case i::wasm::WasmDebugSymbols::Type::ExternalDWARF:
return debug::WasmScript::DebugSymbolsType::ExternalDWARF;
case i::wasm::WasmDebugSymbols::Type::SourceMap:
return debug::WasmScript::DebugSymbolsType::SourceMap;
}
}
MemorySpan<const char> debug::WasmScript::ExternalSymbolsURL() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
const i::wasm::WasmDebugSymbols& symbols =
script->wasm_native_module()->module()->debug_symbols;
if (symbols.external_url.is_empty()) return {};
internal::wasm::ModuleWireBytes wire_bytes(
script->wasm_native_module()->wire_bytes());
i::wasm::WasmName external_url =
wire_bytes.GetNameOrNull(symbols.external_url);
return {external_url.data(), external_url.size()};
}
int debug::WasmScript::NumFunctions() const { int debug::WasmScript::NumFunctions() const {
i::DisallowHeapAllocation no_gc; i::DisallowHeapAllocation no_gc;
i::Handle<i::Script> script = Utils::OpenHandle(this); i::Handle<i::Script> script = Utils::OpenHandle(this);
......
...@@ -173,6 +173,9 @@ class WasmScript : public Script { ...@@ -173,6 +173,9 @@ class WasmScript : public Script {
public: public:
static WasmScript* Cast(Script* script); static WasmScript* Cast(Script* script);
enum class DebugSymbolsType { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
DebugSymbolsType GetDebugSymbolType() const;
MemorySpan<const char> ExternalSymbolsURL() const;
int NumFunctions() const; int NumFunctions() const;
int NumImportedFunctions() const; int NumImportedFunctions() const;
MemorySpan<const uint8_t> Bytecode() const; MemorySpan<const uint8_t> Bytecode() const;
......
...@@ -1471,6 +1471,39 @@ static String16 getScriptLanguage(const V8DebuggerScript& script) { ...@@ -1471,6 +1471,39 @@ static String16 getScriptLanguage(const V8DebuggerScript& script) {
} }
} }
static const char* getDebugSymbolTypeName(
v8::debug::WasmScript::DebugSymbolsType type) {
switch (type) {
case v8::debug::WasmScript::DebugSymbolsType::None:
return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::None;
case v8::debug::WasmScript::DebugSymbolsType::SourceMap:
return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
SourceMap;
case v8::debug::WasmScript::DebugSymbolsType::EmbeddedDWARF:
return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
EmbeddedDWARF;
case v8::debug::WasmScript::DebugSymbolsType::ExternalDWARF:
return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
ExternalDWARF;
}
}
static std::unique_ptr<protocol::Debugger::DebugSymbols> getDebugSymbols(
const V8DebuggerScript& script) {
v8::debug::WasmScript::DebugSymbolsType type;
if (!script.getDebugSymbolsType().To(&type)) return {};
std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
v8_inspector::protocol::Debugger::DebugSymbols::create()
.setType(getDebugSymbolTypeName(type))
.build();
String16 externalUrl;
if (script.getExternalDebugSymbolsURL().To(&externalUrl)) {
debugSymbols->setExternalURL(externalUrl);
}
return debugSymbols;
}
void V8DebuggerAgentImpl::didParseSource( void V8DebuggerAgentImpl::didParseSource(
std::unique_ptr<V8DebuggerScript> script, bool success) { std::unique_ptr<V8DebuggerScript> script, bool success) {
v8::HandleScope handles(m_isolate); v8::HandleScope handles(m_isolate);
...@@ -1506,6 +1539,8 @@ void V8DebuggerAgentImpl::didParseSource( ...@@ -1506,6 +1539,8 @@ void V8DebuggerAgentImpl::didParseSource(
script->getLanguage() == V8DebuggerScript::Language::JavaScript script->getLanguage() == V8DebuggerScript::Language::JavaScript
? Maybe<int>() ? Maybe<int>()
: script->codeOffset(); : script->codeOffset();
std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
getDebugSymbols(*script);
m_scripts[scriptId] = std::move(script); m_scripts[scriptId] = std::move(script);
// Release the strong reference to get notified when debugger is the only // Release the strong reference to get notified when debugger is the only
...@@ -1553,8 +1588,8 @@ void V8DebuggerAgentImpl::didParseSource( ...@@ -1553,8 +1588,8 @@ void V8DebuggerAgentImpl::didParseSource(
scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(), scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
std::move(executionContextAuxDataParam), isLiveEditParam, std::move(executionContextAuxDataParam), isLiveEditParam,
std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0, std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
std::move(stackTrace), std::move(codeOffset), std::move(stackTrace), std::move(codeOffset), std::move(scriptLanguage),
std::move(scriptLanguage)); std::move(debugSymbols));
} else { } else {
m_frontend.scriptParsed( m_frontend.scriptParsed(
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(), scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
...@@ -1562,7 +1597,8 @@ void V8DebuggerAgentImpl::didParseSource( ...@@ -1562,7 +1597,8 @@ void V8DebuggerAgentImpl::didParseSource(
scriptRef->hash(), std::move(executionContextAuxDataParam), scriptRef->hash(), std::move(executionContextAuxDataParam),
isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam, isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
isModuleParam, scriptRef->length(), std::move(stackTrace), isModuleParam, scriptRef->length(), std::move(stackTrace),
std::move(codeOffset), std::move(scriptLanguage)); std::move(codeOffset), std::move(scriptLanguage),
std::move(debugSymbols));
} }
std::vector<protocol::DictionaryValue*> potentialBreakpoints; std::vector<protocol::DictionaryValue*> potentialBreakpoints;
......
...@@ -122,6 +122,21 @@ class ActualScript : public V8DebuggerScript { ...@@ -122,6 +122,21 @@ class ActualScript : public V8DebuggerScript {
return v8::Just(v8::debug::WasmScript::Cast(*script)->Bytecode()); return v8::Just(v8::debug::WasmScript::Cast(*script)->Bytecode());
} }
Language getLanguage() const override { return m_language; } Language getLanguage() const override { return m_language; }
v8::Maybe<v8::debug::WasmScript::DebugSymbolsType> getDebugSymbolsType()
const override {
auto script = this->script();
if (!script->IsWasm())
return v8::Nothing<v8::debug::WasmScript::DebugSymbolsType>();
return v8::Just(v8::debug::WasmScript::Cast(*script)->GetDebugSymbolType());
}
v8::Maybe<String16> getExternalDebugSymbolsURL() const override {
auto script = this->script();
if (!script->IsWasm()) return v8::Nothing<String16>();
v8::MemorySpan<const char> external_url =
v8::debug::WasmScript::Cast(*script)->ExternalSymbolsURL();
if (external_url.size() == 0) return v8::Nothing<String16>();
return v8::Just(String16(external_url.data(), external_url.size()));
}
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; }
......
...@@ -61,6 +61,9 @@ class V8DebuggerScript { ...@@ -61,6 +61,9 @@ class V8DebuggerScript {
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 v8::Maybe<v8::MemorySpan<const uint8_t>> wasmBytecode() const = 0;
virtual Language getLanguage() const = 0; virtual Language getLanguage() const = 0;
virtual v8::Maybe<String16> getExternalDebugSymbolsURL() const = 0;
virtual v8::Maybe<v8::debug::WasmScript::DebugSymbolsType>
getDebugSymbolsType() 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;
......
...@@ -1684,10 +1684,13 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) { ...@@ -1684,10 +1684,13 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
DCHECK(!isolate_->context().is_null()); DCHECK(!isolate_->context().is_null());
// Finish the wasm script now and make it public to the debugger. // Finish the wasm script now and make it public to the debugger.
Handle<Script> script(module_object_->script(), isolate_); Handle<Script> script(module_object_->script(), isolate_);
const WasmModule* module = module_object_->module();
if (script->type() == Script::TYPE_WASM && if (script->type() == Script::TYPE_WASM &&
module_object_->module()->source_map_url.size() != 0) { module->debug_symbols.type == WasmDebugSymbols::Type::SourceMap &&
!module->debug_symbols.external_url.is_empty()) {
ModuleWireBytes wire_bytes(module_object_->native_module()->wire_bytes());
MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8( MaybeHandle<String> src_map_str = isolate_->factory()->NewStringFromUtf8(
CStrVector(module_object_->module()->source_map_url.c_str()), wire_bytes.GetNameOrNull(module->debug_symbols.external_url),
AllocationType::kOld); AllocationType::kOld);
script->set_source_mapping_url(*src_map_str.ToHandleChecked()); script->set_source_mapping_url(*src_map_str.ToHandleChecked());
} }
...@@ -1702,11 +1705,10 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) { ...@@ -1702,11 +1705,10 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
Handle<FixedArray> export_wrappers; Handle<FixedArray> export_wrappers;
if (is_after_cache_hit) { if (is_after_cache_hit) {
// TODO(thibaudm): Look into sharing wrappers. // TODO(thibaudm): Look into sharing wrappers.
CompileJsToWasmWrappers(isolate_, module_object_->module(), CompileJsToWasmWrappers(isolate_, module, &export_wrappers);
&export_wrappers);
} else { } else {
compilation_state->FinalizeJSToWasmWrappers( compilation_state->FinalizeJSToWasmWrappers(isolate_, module,
isolate_, module_object_->module(), &export_wrappers); &export_wrappers);
} }
module_object_->set_export_wrappers(*export_wrappers); module_object_->set_export_wrappers(*export_wrappers);
} }
......
...@@ -33,6 +33,7 @@ constexpr char kNameString[] = "name"; ...@@ -33,6 +33,7 @@ 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"; constexpr char kDebugInfoString[] = ".debug_info";
constexpr char kExternalDebugInfoString[] = ".external_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]) {
...@@ -93,6 +94,8 @@ const char* SectionName(SectionCode code) { ...@@ -93,6 +94,8 @@ const char* SectionName(SectionCode code) {
return kSourceMappingURLString; return kSourceMappingURLString;
case kDebugInfoSectionCode: case kDebugInfoSectionCode:
return kDebugInfoString; return kDebugInfoString;
case kExternalDebugInfoSectionCode:
return kExternalDebugInfoString;
case kCompilationHintsSectionCode: case kCompilationHintsSectionCode:
return kCompilationHintsString; return kCompilationHintsString;
default: default:
...@@ -182,6 +185,11 @@ SectionCode IdentifyUnknownSectionInternal(Decoder* decoder) { ...@@ -182,6 +185,11 @@ SectionCode IdentifyUnknownSectionInternal(Decoder* decoder) {
strncmp(reinterpret_cast<const char*>(section_name_start), strncmp(reinterpret_cast<const char*>(section_name_start),
kDebugInfoString, num_chars(kDebugInfoString)) == 0) { kDebugInfoString, num_chars(kDebugInfoString)) == 0) {
return kDebugInfoSectionCode; return kDebugInfoSectionCode;
} else if (string.length() == num_chars(kExternalDebugInfoString) &&
strncmp(reinterpret_cast<const char*>(section_name_start),
kExternalDebugInfoString,
num_chars(kExternalDebugInfoString)) == 0) {
return kExternalDebugInfoSectionCode;
} }
return kUnknownSectionCode; return kUnknownSectionCode;
} }
...@@ -452,6 +460,9 @@ class ModuleDecoderImpl : public Decoder { ...@@ -452,6 +460,9 @@ class ModuleDecoderImpl : public Decoder {
// .debug_info is a custom section containing core DWARF information // .debug_info is a custom section containing core DWARF information
// if produced by compiler. Its presence likely means that Wasm was // if produced by compiler. Its presence likely means that Wasm was
// built in a debug mode. // built in a debug mode.
case kExternalDebugInfoSectionCode:
// .external_debug_info is a custom section containing a reference to an
// external symbol file.
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.
...@@ -508,11 +519,14 @@ class ModuleDecoderImpl : public Decoder { ...@@ -508,11 +519,14 @@ class ModuleDecoderImpl : public Decoder {
break; break;
case kDebugInfoSectionCode: case kDebugInfoSectionCode:
// If there is an explicit source map, prefer it over DWARF info. // If there is an explicit source map, prefer it over DWARF info.
if (!has_seen_unordered_section(kSourceMappingURLSectionCode)) { if (module_->debug_symbols.type == WasmDebugSymbols::Type::None) {
module_->source_map_url.assign("wasm://dwarf"); module_->debug_symbols = {WasmDebugSymbols::Type::EmbeddedDWARF, {}};
} }
consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info"); consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info");
break; break;
case kExternalDebugInfoSectionCode:
DecodeExternalDebugInfoSection();
break;
case kCompilationHintsSectionCode: case kCompilationHintsSectionCode:
if (enabled_features_.has_compilation_hints()) { if (enabled_features_.has_compilation_hints()) {
DecodeCompilationHintsSection(); DecodeCompilationHintsSection();
...@@ -1063,12 +1077,22 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1063,12 +1077,22 @@ class ModuleDecoderImpl : public Decoder {
Decoder inner(start_, pc_, end_, buffer_offset_); Decoder inner(start_, pc_, end_, buffer_offset_);
WireBytesRef url = wasm::consume_string(&inner, true, "module name"); WireBytesRef url = wasm::consume_string(&inner, true, "module name");
if (inner.ok() && if (inner.ok() &&
!has_seen_unordered_section(kSourceMappingURLSectionCode)) { module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) {
const byte* url_start = module_->debug_symbols = {WasmDebugSymbols::Type::SourceMap, url};
inner.start() + inner.GetBufferRelativeOffset(url.offset()); }
module_->source_map_url.assign(reinterpret_cast<const char*>(url_start), set_seen_unordered_section(kSourceMappingURLSectionCode);
url.length()); consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
set_seen_unordered_section(kSourceMappingURLSectionCode); }
void DecodeExternalDebugInfoSection() {
Decoder inner(start_, pc_, end_, buffer_offset_);
WireBytesRef url =
wasm::consume_string(&inner, true, "external symbol file");
// If there is an explicit source map, prefer it over DWARF info.
if (inner.ok() &&
module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) {
module_->debug_symbols = {WasmDebugSymbols::Type::ExternalDWARF, url};
set_seen_unordered_section(kExternalDebugInfoSectionCode);
} }
consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
} }
...@@ -2157,8 +2181,8 @@ FunctionResult DecodeWasmFunctionForTesting( ...@@ -2157,8 +2181,8 @@ FunctionResult DecodeWasmFunctionForTesting(
const byte* function_end, Counters* counters) { const byte* function_end, Counters* counters) {
size_t size = function_end - function_start; size_t size = function_end - function_start;
CHECK_LE(function_start, function_end); CHECK_LE(function_start, function_end);
auto size_histogram = SELECT_WASM_COUNTER(counters, module->origin, wasm, auto size_histogram =
function_size_bytes); SELECT_WASM_COUNTER(counters, module->origin, wasm, function_size_bytes);
// TODO(bradnelson): Improve histogram handling of ptrdiff_t. // TODO(bradnelson): Improve histogram handling of ptrdiff_t.
size_histogram->AddSample(static_cast<int>(size)); size_histogram->AddSample(static_cast<int>(size));
if (size > kV8MaxWasmFunctionSize) { if (size > kV8MaxWasmFunctionSize) {
......
...@@ -221,14 +221,19 @@ void WasmCode::LogCode(Isolate* isolate) const { ...@@ -221,14 +221,19 @@ void WasmCode::LogCode(Isolate* isolate) const {
VectorOf(native_module()->module()->export_table)); VectorOf(native_module()->module()->export_table));
WasmName name = wire_bytes.GetNameOrNull(name_ref); WasmName name = wire_bytes.GetNameOrNull(name_ref);
const std::string& source_map_url = native_module()->module()->source_map_url; const WasmDebugSymbols& debug_symbols =
native_module()->module()->debug_symbols;
auto load_wasm_source_map = isolate->wasm_load_source_map_callback(); auto load_wasm_source_map = isolate->wasm_load_source_map_callback();
auto source_map = native_module()->GetWasmSourceMap(); auto source_map = native_module()->GetWasmSourceMap();
if (!source_map && !source_map_url.empty() && load_wasm_source_map) { if (!source_map && debug_symbols.type == WasmDebugSymbols::Type::SourceMap &&
!debug_symbols.external_url.is_empty() && load_wasm_source_map) {
WasmName external_url =
wire_bytes.GetNameOrNull(debug_symbols.external_url);
std::string external_url_string(external_url.data(), external_url.size());
HandleScope scope(isolate); HandleScope scope(isolate);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
Local<v8::String> source_map_str = Local<v8::String> source_map_str =
load_wasm_source_map(v8_isolate, source_map_url.c_str()); load_wasm_source_map(v8_isolate, external_url_string.c_str());
native_module()->SetWasmSourceMap( native_module()->SetWasmSourceMap(
std::make_unique<WasmModuleSourceMap>(v8_isolate, source_map_str)); std::make_unique<WasmModuleSourceMap>(v8_isolate, source_map_str));
} }
......
...@@ -87,10 +87,11 @@ enum SectionCode : int8_t { ...@@ -87,10 +87,11 @@ enum SectionCode : int8_t {
// The following sections are custom sections, and are identified using a // The following sections are custom sections, and are identified using a
// string rather than an integer. Their enumeration values are not guaranteed // string rather than an integer. Their enumeration values are not guaranteed
// 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 kDebugInfoSectionCode, // DWARF section .debug_info
kCompilationHintsSectionCode, // Compilation hints section kExternalDebugInfoSectionCode, // Section encoding the external symbol path
kCompilationHintsSectionCode, // Compilation hints section
// Helper values // Helper values
kFirstSectionInModule = kTypeSectionCode, kFirstSectionInModule = kTypeSectionCode,
......
...@@ -707,10 +707,14 @@ Handle<Script> CreateWasmScript(Isolate* isolate, ...@@ -707,10 +707,14 @@ Handle<Script> CreateWasmScript(Isolate* isolate,
} }
script->set_source_url(*url_str.ToHandleChecked()); script->set_source_url(*url_str.ToHandleChecked());
auto source_map_url = VectorOf(module->source_map_url); const WasmDebugSymbols& debug_symbols =
if (!source_map_url.empty()) { native_module->module()->debug_symbols;
if (debug_symbols.type == WasmDebugSymbols::Type::SourceMap &&
!debug_symbols.external_url.is_empty()) {
Vector<const char> external_url =
ModuleWireBytes(wire_bytes).GetNameOrNull(debug_symbols.external_url);
MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8( MaybeHandle<String> src_map_str = isolate->factory()->NewStringFromUtf8(
source_map_url, AllocationType::kOld); external_url, AllocationType::kOld);
script->set_source_mapping_url(*src_map_str.ToHandleChecked()); script->set_source_mapping_url(*src_map_str.ToHandleChecked());
} }
......
...@@ -259,6 +259,12 @@ struct TypeDefinition { ...@@ -259,6 +259,12 @@ struct TypeDefinition {
}; };
}; };
struct V8_EXPORT_PRIVATE WasmDebugSymbols {
enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
Type type = Type::None;
WireBytesRef external_url;
};
// Static representation of a module. // Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule { struct V8_EXPORT_PRIVATE WasmModule {
std::unique_ptr<Zone> signature_zone; std::unique_ptr<Zone> signature_zone;
...@@ -333,7 +339,7 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -333,7 +339,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
ModuleOrigin origin = kWasmOrigin; // origin of the module ModuleOrigin origin = kWasmOrigin; // origin of the module
LazilyGeneratedNames lazily_generated_names; LazilyGeneratedNames lazily_generated_names;
std::string source_map_url; WasmDebugSymbols debug_symbols;
// Asm.js source position information. Only available for modules compiled // Asm.js source position information. Only available for modules compiled
// from asm.js. // from asm.js.
......
...@@ -52,12 +52,25 @@ InspectorTest.log( ...@@ -52,12 +52,25 @@ InspectorTest.log(
// Sample .debug_info section. // Sample .debug_info section.
// Content doesn't matter, as we don't try to parse it in V8, // Content doesn't matter, as we don't try to parse it in V8,
// but should be non-empty to check that we're skipping it correctly. // but should be non-empty to check that we're skipping it correctly.
const dwarfSection = { name: '.debug_info', value: [1, 2, 3, 4, 5] }; const embeddedDWARFSection = {
name: '.debug_info',
value: [1, 2, 3, 4, 5]
};
// Sample external_debug_info section set to "abc".
const externalDWARFSection = {
name: '.external_debug_info',
value: [3, 97, 98, 99]
};
// Sample sourceMappingURL section set to "abc". // Sample sourceMappingURL section set to "abc".
const sourceMapSection = { name: 'sourceMappingURL', value: [3, 97, 98, 99] }; const sourceMapSection = {
name: 'sourceMappingURL',
value: [3, 97, 98, 99]
};
sessions[0].Protocol.Runtime sessions[0]
.Protocol.Runtime
.evaluate({ .evaluate({
'expression': `//# sourceURL=v8://test/runTestRunction 'expression': `//# sourceURL=v8://test/runTestRunction
...@@ -67,24 +80,41 @@ sessions[0].Protocol.Runtime ...@@ -67,24 +80,41 @@ sessions[0].Protocol.Runtime
// shared script for identical modules // shared script for identical modules
testFunction([${createModule()}]); testFunction([${createModule()}]);
// DWARF // External DWARF
testFunction([${createModule(dwarfSection)}]); testFunction([${createModule(externalDWARFSection)}]);
// Embedded DWARF
testFunction([${createModule(embeddedDWARFSection)}]);
// Source map // Source map
testFunction([${createModule(sourceMapSection)}]); testFunction([${createModule(sourceMapSection)}]);
// DWARF + source map // SourceMap + External DWARF
testFunction([${createModule(dwarfSection, sourceMapSection)}]); testFunction([${createModule(sourceMapSection, externalDWARFSection)}]);
// External DWARF + SourceMap (different order)
testFunction([${createModule(externalDWARFSection, sourceMapSection)}]);
// Embedded DWARF + External DWARF
testFunction([${
createModule(embeddedDWARFSection, externalDWARFSection)}]);
// External + Embedded DWARF (different order)
testFunction([${
createModule(externalDWARFSection, embeddedDWARFSection)}]);
// Embedded DWARF + source map
testFunction([${createModule(embeddedDWARFSection, sourceMapSection)}]);
// Source map + DWARF (different order) // Source map + Embedded DWARF (different order)
testFunction([${createModule(sourceMapSection, dwarfSection)}]); testFunction([${createModule(sourceMapSection, embeddedDWARFSection)}]);
` `
}) })
.then(() => ( .then(
// At this point all scripts were parsed. () => (
// Stop tracking and wait for script sources in each session. // At this point all scripts were parsed.
Promise.all(sessions.map(session => session.getScripts())) // Stop tracking and wait for script sources in each session.
)) Promise.all(sessions.map(session => session.getScripts()))))
.catch(err => { .catch(err => {
InspectorTest.log(err.stack); InspectorTest.log(err.stack);
}) })
...@@ -97,12 +127,21 @@ function trackScripts(debuggerParams) { ...@@ -97,12 +127,21 @@ function trackScripts(debuggerParams) {
Protocol.Debugger.enable(debuggerParams); Protocol.Debugger.enable(debuggerParams);
Protocol.Debugger.onScriptParsed(handleScriptParsed); Protocol.Debugger.onScriptParsed(handleScriptParsed);
async function loadScript( async function loadScript({
{url, scriptId, sourceMapURL, startColumn, endColumn, codeOffset}) { url,
scriptId,
sourceMapURL,
startColumn,
endColumn,
codeOffset,
debugSymbols
}) {
let stableId = nextStableId(scriptId); let stableId = nextStableId(scriptId);
InspectorTest.log(`Session #${sessionId}: Script #${ InspectorTest.log(`Session #${sessionId}: Script #${
scripts.length} parsed. URL: ${url}. Script ID: ${stableId}, Source map URL: ${ scripts.length} parsed. URL: ${url}. Script ID: ${
sourceMapURL}, module begin: ${startColumn}, module end: ${endColumn}, code offset: ${codeOffset}`); stableId}, Source map URL: ${sourceMapURL}, debug symbols: ${
debugSymbols.type}:${debugSymbols.externalURL}. module begin: ${
startColumn}, module end: ${endColumn}, code offset: ${codeOffset}`);
let {result: {scriptSource, bytecode}} = let {result: {scriptSource, bytecode}} =
await Protocol.Debugger.getScriptSource({scriptId}); await Protocol.Debugger.getScriptSource({scriptId});
if (bytecode) { if (bytecode) {
...@@ -114,10 +153,17 @@ function trackScripts(debuggerParams) { ...@@ -114,10 +153,17 @@ function trackScripts(debuggerParams) {
bytecode = InspectorTest.decodeBase64(bytecode); bytecode = InspectorTest.decodeBase64(bytecode);
// Check that it can be parsed back to a WebAssembly module. // Check that it can be parsed back to a WebAssembly module.
let module = new WebAssembly.Module(bytecode); let module = new WebAssembly.Module(bytecode);
scriptSource = ` scriptSource =
`
Raw: ${Array.from(bytecode, b => ('0' + b.toString(16)).slice(-2)).join(' ')} 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(', ')}] Imports: [${
Exports: [${WebAssembly.Module.exports(module).map(e => `${e.name}: ${e.kind}`).join(', ')}] 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(); `.trim();
} }
InspectorTest.log(`Session #${sessionId}: Source for ${url}:`); InspectorTest.log(`Session #${sessionId}: Source for ${url}:`);
......
...@@ -2428,26 +2428,40 @@ TEST_F(WasmModuleCustomSectionTest, TwoKnownTwoUnknownSections) { ...@@ -2428,26 +2428,40 @@ TEST_F(WasmModuleCustomSectionTest, TwoKnownTwoUnknownSections) {
TEST_F(WasmModuleVerifyTest, SourceMappingURLSection) { TEST_F(WasmModuleVerifyTest, SourceMappingURLSection) {
static const byte data[] = { static const byte data[] = {
WASM_MODULE_HEADER,
SECTION_SRC_MAP('s', 'r', 'c', '/', 'x', 'y', 'z', '.', 'c')}; SECTION_SRC_MAP('s', 'r', 'c', '/', 'x', 'y', 'z', '.', 'c')};
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModuleNoHeader(data, data + sizeof(data));
EXPECT_TRUE(result.ok()); EXPECT_TRUE(result.ok());
EXPECT_EQ("src/xyz.c", result.value()->source_map_url); EXPECT_EQ(WasmDebugSymbols::Type::SourceMap,
result.value()->debug_symbols.type);
ModuleWireBytes wire_bytes(data, data + sizeof(data));
WasmName external_url =
wire_bytes.GetNameOrNull(result.value()->debug_symbols.external_url);
EXPECT_EQ("src/xyz.c", std::string(external_url.data(), external_url.size()));
} }
TEST_F(WasmModuleVerifyTest, BadSourceMappingURLSection) { TEST_F(WasmModuleVerifyTest, BadSourceMappingURLSection) {
static const byte data[] = { static const byte data[] = {
WASM_MODULE_HEADER,
SECTION_SRC_MAP('s', 'r', 'c', '/', 'x', 0xff, 'z', '.', 'c')}; SECTION_SRC_MAP('s', 'r', 'c', '/', 'x', 0xff, 'z', '.', 'c')};
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModuleNoHeader(data, data + sizeof(data));
EXPECT_TRUE(result.ok()); EXPECT_TRUE(result.ok());
EXPECT_EQ(0u, result.value()->source_map_url.size()); EXPECT_EQ(WasmDebugSymbols::Type::None, result.value()->debug_symbols.type);
EXPECT_EQ(0u, result.value()->debug_symbols.external_url.length());
} }
TEST_F(WasmModuleVerifyTest, MultipleSourceMappingURLSections) { TEST_F(WasmModuleVerifyTest, MultipleSourceMappingURLSections) {
static const byte data[] = {SECTION_SRC_MAP('a', 'b', 'c'), static const byte data[] = {WASM_MODULE_HEADER,
SECTION_SRC_MAP('a', 'b', 'c'),
SECTION_SRC_MAP('p', 'q', 'r')}; SECTION_SRC_MAP('p', 'q', 'r')};
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModuleNoHeader(data, data + sizeof(data));
EXPECT_TRUE(result.ok()); EXPECT_TRUE(result.ok());
EXPECT_EQ("abc", result.value()->source_map_url); EXPECT_EQ(WasmDebugSymbols::Type::SourceMap,
result.value()->debug_symbols.type);
ModuleWireBytes wire_bytes(data, data + sizeof(data));
WasmName external_url =
wire_bytes.GetNameOrNull(result.value()->debug_symbols.external_url);
EXPECT_EQ("abc", std::string(external_url.data(), external_url.size()));
} }
TEST_F(WasmModuleVerifyTest, MultipleNameSections) { TEST_F(WasmModuleVerifyTest, MultipleNameSections) {
......
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