Commit d180d40d authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[wasm][devtools] Fix reported function body offsets

The DevTools frontend doesn't want the Wasm module's understanding of
function body offsets (i.e. including locals), but the ranges of
offsets where breakpoints can be set (i.e. only where instructions are).
This patch adjusts the reported offsets accordingly.
A consequence is that we have to report full (start,end) pairs for each
function, instead of being able to dedupe end1==start2 etc.

Bug: v8:12917
Change-Id: I0c7d2d96435cdac2c4553647b7bcc8783bc1798b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3780526
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarPhilip Pfaffe <pfaffe@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81887}
parent 376813df
...@@ -261,8 +261,8 @@ domain Debugger ...@@ -261,8 +261,8 @@ domain Debugger
optional string streamId optional string streamId
# The total number of lines in the disassembly text. # The total number of lines in the disassembly text.
integer totalNumberOfLines integer totalNumberOfLines
# The offsets of all function bodies plus one additional entry pointing # The offsets of all function bodies, in the format [start1, end1,
# one by past the end of the last function. # start2, end2, ...] where all ends are exclusive.
array of integer functionBodyOffsets array of integer functionBodyOffsets
# The first chunk of disassembly. # The first chunk of disassembly.
WasmDisassemblyChunk chunk WasmDisassemblyChunk chunk
......
...@@ -847,27 +847,8 @@ int WasmScript::GetContainingFunction(int byte_offset) const { ...@@ -847,27 +847,8 @@ int WasmScript::GetContainingFunction(int byte_offset) const {
return i::wasm::GetContainingWasmFunction(module, byte_offset); return i::wasm::GetContainingWasmFunction(module, byte_offset);
} }
void WasmScript::GetAllFunctionStarts(std::vector<int>& starts) const { void WasmScript::Disassemble(DisassemblyCollector* collector,
i::DisallowGarbageCollection no_gc; std::vector<int>* function_body_offsets) {
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();
size_t num_functions = module->functions.size();
starts.resize(num_functions + 1);
for (size_t i = 0; i < num_functions; i++) {
const i::wasm::WasmFunction& f = module->functions[i];
starts[i] = f.code.offset();
}
if (num_functions > 0) {
starts[num_functions] =
module->functions[num_functions - 1].code.end_offset();
} else {
starts[0] = 0;
}
}
void WasmScript::Disassemble(DisassemblyCollector* collector) {
i::DisallowGarbageCollection no_gc; i::DisallowGarbageCollection no_gc;
i::Handle<i::Script> script = Utils::OpenHandle(this); i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type()); DCHECK_EQ(i::Script::TYPE_WASM, script->type());
...@@ -875,7 +856,7 @@ void WasmScript::Disassemble(DisassemblyCollector* collector) { ...@@ -875,7 +856,7 @@ void WasmScript::Disassemble(DisassemblyCollector* collector) {
const i::wasm::WasmModule* module = native_module->module(); const i::wasm::WasmModule* module = native_module->module();
i::wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes()); i::wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes());
i::wasm::Disassemble(module, wire_bytes, native_module->GetNamesProvider(), i::wasm::Disassemble(module, wire_bytes, native_module->GetNamesProvider(),
collector); collector, function_body_offsets);
} }
uint32_t WasmScript::GetFunctionHash(int function_index) { uint32_t WasmScript::GetFunctionHash(int function_index) {
......
...@@ -258,11 +258,9 @@ class WasmScript : public Script { ...@@ -258,11 +258,9 @@ class WasmScript : public Script {
std::pair<int, int> GetFunctionRange(int function_index) const; std::pair<int, int> GetFunctionRange(int function_index) const;
int GetContainingFunction(int byte_offset) const; int GetContainingFunction(int byte_offset) const;
// For N functions, {starts} will have N+1 entries: the last is the offset of
// the first byte after the end of the last function.
void GetAllFunctionStarts(std::vector<int>& starts) const;
void Disassemble(DisassemblyCollector* collector); void Disassemble(DisassemblyCollector* collector,
std::vector<int>* function_body_offsets);
uint32_t GetFunctionHash(int function_index); uint32_t GetFunctionHash(int function_index);
......
...@@ -1214,11 +1214,10 @@ Response V8DebuggerAgentImpl::disassembleWasmModule( ...@@ -1214,11 +1214,10 @@ Response V8DebuggerAgentImpl::disassembleWasmModule(
} }
std::unique_ptr<DisassemblyCollectorImpl> collector = std::unique_ptr<DisassemblyCollectorImpl> collector =
std::make_unique<DisassemblyCollectorImpl>(); std::make_unique<DisassemblyCollectorImpl>();
script->Disassemble(collector.get()); std::vector<int> functionBodyOffsets;
script->Disassemble(collector.get(), &functionBodyOffsets);
*out_totalNumberOfLines = *out_totalNumberOfLines =
static_cast<int>(collector->total_number_of_lines()); static_cast<int>(collector->total_number_of_lines());
std::vector<int> functionBodyOffsets;
script->GetAllFunctionStarts(functionBodyOffsets);
*out_functionBodyOffsets = *out_functionBodyOffsets =
std::make_unique<protocol::Array<int>>(std::move(functionBodyOffsets)); std::make_unique<protocol::Array<int>>(std::move(functionBodyOffsets));
// Even an empty module would disassemble to "(module)", never to zero lines. // Even an empty module would disassemble to "(module)", never to zero lines.
......
...@@ -98,18 +98,13 @@ class ActualScript : public V8DebuggerScript { ...@@ -98,18 +98,13 @@ class ActualScript : public V8DebuggerScript {
return v8::Just(String16(external_url.data(), external_url.size())); return v8::Just(String16(external_url.data(), external_url.size()));
} }
void GetAllFunctionStarts(std::vector<int>& starts) const override { void Disassemble(v8::debug::DisassemblyCollector* collector,
std::vector<int>* function_body_offsets) const override {
v8::HandleScope scope(m_isolate); v8::HandleScope scope(m_isolate);
v8::Local<v8::debug::Script> script = this->script(); v8::Local<v8::debug::Script> script = this->script();
DCHECK(script->IsWasm()); DCHECK(script->IsWasm());
v8::debug::WasmScript::Cast(*script)->GetAllFunctionStarts(starts); v8::debug::WasmScript::Cast(*script)->Disassemble(collector,
} function_body_offsets);
void Disassemble(v8::debug::DisassemblyCollector* collector) const override {
v8::HandleScope scope(m_isolate);
v8::Local<v8::debug::Script> script = this->script();
DCHECK(script->IsWasm());
v8::debug::WasmScript::Cast(*script)->Disassemble(collector);
} }
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
......
...@@ -105,9 +105,8 @@ class V8DebuggerScript { ...@@ -105,9 +105,8 @@ class V8DebuggerScript {
getDebugSymbolsType() const = 0; getDebugSymbolsType() const = 0;
virtual v8::Maybe<String16> getExternalDebugSymbolsURL() const = 0; virtual v8::Maybe<String16> getExternalDebugSymbolsURL() const = 0;
void removeWasmBreakpoint(int id); void removeWasmBreakpoint(int id);
virtual void GetAllFunctionStarts(std::vector<int>& starts) const = 0; virtual void Disassemble(v8::debug::DisassemblyCollector* collector,
virtual void Disassemble( std::vector<int>* function_body_offsets) const = 0;
v8::debug::DisassemblyCollector* collector) const = 0;
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
protected: protected:
......
...@@ -90,7 +90,8 @@ class V8_EXPORT_PRIVATE FunctionBodyDisassembler ...@@ -90,7 +90,8 @@ class V8_EXPORT_PRIVATE FunctionBodyDisassembler
names_(names) {} names_(names) {}
void DecodeAsWat(MultiLineStringBuilder& out, Indentation indentation, void DecodeAsWat(MultiLineStringBuilder& out, Indentation indentation,
FunctionHeader include_header = kPrintHeader); FunctionHeader include_header = kPrintHeader,
uint32_t* first_instruction_offset = nullptr);
void DecodeGlobalInitializer(StringBuilder& out); void DecodeGlobalInitializer(StringBuilder& out);
...@@ -128,14 +129,13 @@ class V8_EXPORT_PRIVATE FunctionBodyDisassembler ...@@ -128,14 +129,13 @@ class V8_EXPORT_PRIVATE FunctionBodyDisassembler
class ModuleDisassembler { class ModuleDisassembler {
public: public:
enum ByteOffsets { kSkipByteOffsets = false, kIncludeByteOffsets = true }; V8_EXPORT_PRIVATE ModuleDisassembler(
MultiLineStringBuilder& out, const WasmModule* module,
V8_EXPORT_PRIVATE ModuleDisassembler(MultiLineStringBuilder& out, NamesProvider* names, const ModuleWireBytes wire_bytes,
const WasmModule* module, AccountingAllocator* allocator,
NamesProvider* names, // When non-nullptr, doubles as a sentinel that bytecode offsets should be
const ModuleWireBytes wire_bytes, // stored for each line of disassembly.
ByteOffsets byte_offsets, std::vector<int>* function_body_offsets = nullptr);
AccountingAllocator* allocator);
V8_EXPORT_PRIVATE ~ModuleDisassembler(); V8_EXPORT_PRIVATE ~ModuleDisassembler();
V8_EXPORT_PRIVATE void PrintTypeDefinition(uint32_t type_index, V8_EXPORT_PRIVATE void PrintTypeDefinition(uint32_t type_index,
...@@ -165,6 +165,7 @@ class ModuleDisassembler { ...@@ -165,6 +165,7 @@ class ModuleDisassembler {
const byte* start_; const byte* start_;
Zone zone_; Zone zone_;
std::unique_ptr<OffsetsProvider> offsets_; std::unique_ptr<OffsetsProvider> offsets_;
std::vector<int>* function_body_offsets_;
}; };
} // namespace wasm } // namespace wasm
......
...@@ -20,11 +20,12 @@ namespace wasm { ...@@ -20,11 +20,12 @@ namespace wasm {
void Disassemble(const WasmModule* module, ModuleWireBytes wire_bytes, void Disassemble(const WasmModule* module, ModuleWireBytes wire_bytes,
NamesProvider* names, NamesProvider* names,
v8::debug::DisassemblyCollector* collector) { v8::debug::DisassemblyCollector* collector,
std::vector<int>* function_body_offsets) {
MultiLineStringBuilder out; MultiLineStringBuilder out;
AccountingAllocator allocator; AccountingAllocator allocator;
ModuleDisassembler md(out, module, names, wire_bytes, ModuleDisassembler md(out, module, names, wire_bytes, &allocator,
ModuleDisassembler::kIncludeByteOffsets, &allocator); function_body_offsets);
md.PrintModule({0, 2}); md.PrintModule({0, 2});
out.ToDisassemblyCollector(collector); out.ToDisassemblyCollector(collector);
} }
...@@ -149,7 +150,8 @@ void PrintSignatureOneLine(StringBuilder& out, const FunctionSig* sig, ...@@ -149,7 +150,8 @@ void PrintSignatureOneLine(StringBuilder& out, const FunctionSig* sig,
void FunctionBodyDisassembler::DecodeAsWat(MultiLineStringBuilder& out, void FunctionBodyDisassembler::DecodeAsWat(MultiLineStringBuilder& out,
Indentation indentation, Indentation indentation,
FunctionHeader include_header) { FunctionHeader include_header,
uint32_t* first_instruction_offset) {
out_ = &out; out_ = &out;
int base_indentation = indentation.current(); int base_indentation = indentation.current();
// Print header. // Print header.
...@@ -184,6 +186,7 @@ void FunctionBodyDisassembler::DecodeAsWat(MultiLineStringBuilder& out, ...@@ -184,6 +186,7 @@ void FunctionBodyDisassembler::DecodeAsWat(MultiLineStringBuilder& out,
} }
consume_bytes(locals_length); consume_bytes(locals_length);
out.set_current_line_bytecode_offset(pc_offset()); out.set_current_line_bytecode_offset(pc_offset());
if (first_instruction_offset) *first_instruction_offset = pc_offset();
// Main loop. // Main loop.
while (pc_ < end_) { while (pc_ < end_) {
...@@ -662,16 +665,17 @@ ModuleDisassembler::ModuleDisassembler(MultiLineStringBuilder& out, ...@@ -662,16 +665,17 @@ ModuleDisassembler::ModuleDisassembler(MultiLineStringBuilder& out,
const WasmModule* module, const WasmModule* module,
NamesProvider* names, NamesProvider* names,
const ModuleWireBytes wire_bytes, const ModuleWireBytes wire_bytes,
ByteOffsets byte_offsets, AccountingAllocator* allocator,
AccountingAllocator* allocator) std::vector<int>* function_body_offsets)
: out_(out), : out_(out),
module_(module), module_(module),
names_(names), names_(names),
wire_bytes_(wire_bytes), wire_bytes_(wire_bytes),
start_(wire_bytes_.start()), start_(wire_bytes_.start()),
zone_(allocator, "disassembler zone"), zone_(allocator, "disassembler zone"),
offsets_(new OffsetsProvider()) { offsets_(new OffsetsProvider()),
if (byte_offsets == kIncludeByteOffsets) { function_body_offsets_(function_body_offsets) {
if (function_body_offsets != nullptr) {
offsets_->CollectOffsets(module, wire_bytes_.start(), wire_bytes_.end(), offsets_->CollectOffsets(module, wire_bytes_.start(), wire_bytes_.end(),
allocator); allocator);
} }
...@@ -920,6 +924,11 @@ void ModuleDisassembler::PrintModule(Indentation indentation) { ...@@ -920,6 +924,11 @@ void ModuleDisassembler::PrintModule(Indentation indentation) {
if (out_.length() != 0) out_.NextLine(0); if (out_.length() != 0) out_.NextLine(0);
// XI. Code / function bodies. // XI. Code / function bodies.
if (function_body_offsets_ != nullptr) {
size_t num_defined_functions =
module_->functions.size() - module_->num_imported_functions;
function_body_offsets_->reserve(num_defined_functions * 2);
}
for (uint32_t i = module_->num_imported_functions; for (uint32_t i = module_->num_imported_functions;
i < module_->functions.size(); i++) { i < module_->functions.size(); i++) {
const WasmFunction* func = &module_->functions[i]; const WasmFunction* func = &module_->functions[i];
...@@ -935,7 +944,13 @@ void ModuleDisassembler::PrintModule(Indentation indentation) { ...@@ -935,7 +944,13 @@ void ModuleDisassembler::PrintModule(Indentation indentation) {
FunctionBodyDisassembler d(&zone_, module_, i, &detected, func->sig, FunctionBodyDisassembler d(&zone_, module_, i, &detected, func->sig,
code.begin(), code.end(), func->code.offset(), code.begin(), code.end(), func->code.offset(),
names_); names_);
d.DecodeAsWat(out_, indentation, FunctionBodyDisassembler::kSkipHeader); uint32_t first_instruction_offset;
d.DecodeAsWat(out_, indentation, FunctionBodyDisassembler::kSkipHeader,
&first_instruction_offset);
if (function_body_offsets_ != nullptr) {
function_body_offsets_->push_back(first_instruction_offset);
function_body_offsets_->push_back(d.pc_offset());
}
} }
// XII. Data // XII. Data
......
...@@ -24,7 +24,8 @@ class NamesProvider; ...@@ -24,7 +24,8 @@ class NamesProvider;
void Disassemble(const WasmModule* module, ModuleWireBytes wire_bytes, void Disassemble(const WasmModule* module, ModuleWireBytes wire_bytes,
NamesProvider* names, NamesProvider* names,
v8::debug::DisassemblyCollector* collector); v8::debug::DisassemblyCollector* collector,
std::vector<int>* function_body_offsets);
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
...@@ -14,7 +14,7 @@ bytecode: ...@@ -14,7 +14,7 @@ bytecode:
0x02 0x66 0x32 0x02 0x08 0x01 0x00 0x01 ;; offset 72..79 0x02 0x66 0x32 0x02 0x08 0x01 0x00 0x01 ;; offset 72..79
0x01 0x03 0x78 0x79 0x7a 0x01 0x03 0x78 0x79 0x7a
streamid: undefined streamid: undefined
functionBodyOffsets: 28,40,44 functionBodyOffsets: 33,39,41,44
totalNumberOfLines: 13 totalNumberOfLines: 13
lines: lines:
(module $moduleName (module $moduleName
......
...@@ -795,8 +795,7 @@ class FormatConverter { ...@@ -795,8 +795,7 @@ class FormatConverter {
// Print any types that were used by the function. // Print any types that were used by the function.
out.NextLine(0); out.NextLine(0);
ModuleDisassembler md(out, module(), names(), wire_bytes_, ModuleDisassembler md(out, module(), names(), wire_bytes_, &allocator_);
ModuleDisassembler::kSkipByteOffsets, &allocator_);
for (uint32_t type_index : d.used_types()) { for (uint32_t type_index : d.used_types()) {
md.PrintTypeDefinition(type_index, {0, 1}, md.PrintTypeDefinition(type_index, {0, 1},
NamesProvider::kIndexAsComment); NamesProvider::kIndexAsComment);
...@@ -805,8 +804,7 @@ class FormatConverter { ...@@ -805,8 +804,7 @@ class FormatConverter {
void WatForModule(MultiLineStringBuilder& out) { void WatForModule(MultiLineStringBuilder& out) {
DCHECK(ok_); DCHECK(ok_);
ModuleDisassembler md(out, module(), names(), wire_bytes_, ModuleDisassembler md(out, module(), names(), wire_bytes_, &allocator_);
ModuleDisassembler::kSkipByteOffsets, &allocator_);
md.PrintModule({0, 2}); md.PrintModule({0, 2});
} }
......
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