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