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

[tools][wasm] wami: full-module annotated hex dump

This adds a bunch of tracing hooks to the module decoder and uses
them to support "annotated hexdump" output for full modules in wami:

$ out/x64.release/wami my_module.wasm --full-hexdump

Change-Id: I5821d940b5ec236df9708eecd0124172d8893ffd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3754741Reviewed-by: 's avatarManos Koukoutos <manoskouk@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81791}
parent 4cd8ebdc
...@@ -52,7 +52,7 @@ void Histogram::AddSample(int sample) { ...@@ -52,7 +52,7 @@ void Histogram::AddSample(int sample) {
} }
} }
V8_EXPORT_PRIVATE void* Histogram::CreateHistogram() const { void* Histogram::CreateHistogram() const {
return counters_->CreateHistogram(name_, min_, max_, num_buckets_); return counters_->CreateHistogram(name_, min_, max_, num_buckets_);
} }
......
...@@ -155,7 +155,7 @@ class StatsCounter { ...@@ -155,7 +155,7 @@ class StatsCounter {
class Histogram { class Histogram {
public: public:
// Add a single sample to this histogram. // Add a single sample to this histogram.
void AddSample(int sample); V8_EXPORT_PRIVATE void AddSample(int sample);
// Returns true if this histogram is enabled. // Returns true if this histogram is enabled.
bool Enabled() { return histogram_ != nullptr; } bool Enabled() { return histogram_ != nullptr; }
......
...@@ -27,7 +27,7 @@ namespace wasm { ...@@ -27,7 +27,7 @@ namespace wasm {
// which is used in module-instantiate. We merge two distinct functionalities // which is used in module-instantiate. We merge two distinct functionalities
// in one class to reduce the number of WasmFullDecoder instantiations, and thus // in one class to reduce the number of WasmFullDecoder instantiations, and thus
// V8 binary code size. // V8 binary code size.
class ConstantExpressionInterface { class V8_EXPORT_PRIVATE ConstantExpressionInterface {
public: public:
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation; static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
static constexpr DecodingMode decoding_mode = kConstantExpression; static constexpr DecodingMode decoding_mode = kConstantExpression;
......
...@@ -183,17 +183,26 @@ class Decoder { ...@@ -183,17 +183,26 @@ class Decoder {
// Reads a 8-bit unsigned integer (byte) and advances {pc_}. // Reads a 8-bit unsigned integer (byte) and advances {pc_}.
uint8_t consume_u8(const char* name = "uint8_t") { uint8_t consume_u8(const char* name = "uint8_t") {
return consume_little_endian<uint8_t>(name); return consume_little_endian<uint8_t, kTrace>(name);
}
template <class Tracer>
uint8_t consume_u8(const char* name, Tracer& tracer) {
tracer.Bytes(pc_, sizeof(uint8_t));
tracer.Description(name);
return consume_little_endian<uint8_t, kNoTrace>(name);
} }
// Reads a 16-bit unsigned integer (little endian) and advances {pc_}. // Reads a 16-bit unsigned integer (little endian) and advances {pc_}.
uint16_t consume_u16(const char* name = "uint16_t") { uint16_t consume_u16(const char* name = "uint16_t") {
return consume_little_endian<uint16_t>(name); return consume_little_endian<uint16_t, kTrace>(name);
} }
// Reads a single 32-bit unsigned integer (little endian) and advances {pc_}. // Reads a single 32-bit unsigned integer (little endian) and advances {pc_}.
uint32_t consume_u32(const char* name = "uint32_t") { template <class Tracer>
return consume_little_endian<uint32_t>(name); uint32_t consume_u32(const char* name, Tracer& tracer) {
tracer.Bytes(pc_, sizeof(uint32_t));
tracer.Description(name);
return consume_little_endian<uint32_t, kNoTrace>(name);
} }
// Reads a LEB128 variable-length unsigned 32-bit integer and advances {pc_}. // Reads a LEB128 variable-length unsigned 32-bit integer and advances {pc_}.
...@@ -204,6 +213,16 @@ class Decoder { ...@@ -204,6 +213,16 @@ class Decoder {
pc_ += length; pc_ += length;
return result; return result;
} }
template <class Tracer>
uint32_t consume_u32v(const char* name, Tracer& tracer) {
uint32_t length = 0;
uint32_t result =
read_leb<uint32_t, kFullValidation, kNoTrace>(pc_, &length, name);
tracer.Bytes(pc_, length);
tracer.Description(name);
pc_ += length;
return result;
}
// Reads a LEB128 variable-length signed 32-bit integer and advances {pc_}. // Reads a LEB128 variable-length signed 32-bit integer and advances {pc_}.
int32_t consume_i32v(const char* name = "var_int32") { int32_t consume_i32v(const char* name = "var_int32") {
...@@ -215,10 +234,13 @@ class Decoder { ...@@ -215,10 +234,13 @@ class Decoder {
} }
// Reads a LEB128 variable-length unsigned 64-bit integer and advances {pc_}. // Reads a LEB128 variable-length unsigned 64-bit integer and advances {pc_}.
uint64_t consume_u64v(const char* name = "var_uint64") { template <class Tracer>
uint64_t consume_u64v(const char* name, Tracer& tracer) {
uint32_t length = 0; uint32_t length = 0;
uint64_t result = uint64_t result =
read_leb<uint64_t, kFullValidation, kTrace>(pc_, &length, name); read_leb<uint64_t, kFullValidation, kNoTrace>(pc_, &length, name);
tracer.Bytes(pc_, length);
tracer.Description(name);
pc_ += length; pc_ += length;
return result; return result;
} }
...@@ -242,6 +264,12 @@ class Decoder { ...@@ -242,6 +264,12 @@ class Decoder {
pc_ = end_; pc_ = end_;
} }
} }
template <class Tracer>
void consume_bytes(uint32_t size, const char* name, Tracer& tracer) {
tracer.Bytes(pc_, size);
tracer.Description(name);
consume_bytes(size, nullptr);
}
// Check that at least {size} bytes exist between {pc_} and {end_}. // Check that at least {size} bytes exist between {pc_} and {end_}.
bool checkAvailable(uint32_t size) { bool checkAvailable(uint32_t size) {
...@@ -397,9 +425,9 @@ class Decoder { ...@@ -397,9 +425,9 @@ class Decoder {
return base::ReadLittleEndianValue<IntType>(reinterpret_cast<Address>(pc)); return base::ReadLittleEndianValue<IntType>(reinterpret_cast<Address>(pc));
} }
template <typename IntType> template <typename IntType, TraceFlag trace>
IntType consume_little_endian(const char* name) { IntType consume_little_endian(const char* name) {
TRACE(" +%u %-20s: ", pc_offset(), name); TRACE_IF(trace, " +%u %-20s: ", pc_offset(), name);
if (!checkAvailable(sizeof(IntType))) { if (!checkAvailable(sizeof(IntType))) {
traceOffEnd(); traceOffEnd();
pc_ = end_; pc_ = end_;
...@@ -407,7 +435,7 @@ class Decoder { ...@@ -407,7 +435,7 @@ class Decoder {
} }
IntType val = read_little_endian<IntType, kNoValidation>(pc_, name); IntType val = read_little_endian<IntType, kNoValidation>(pc_, name);
traceByteRange(pc_, pc_ + sizeof(IntType)); traceByteRange(pc_, pc_ + sizeof(IntType));
TRACE("= %d\n", val); TRACE_IF(trace, "= %d\n", val);
pc_ += sizeof(IntType); pc_ += sizeof(IntType);
return val; return val;
} }
......
...@@ -293,9 +293,6 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc, ...@@ -293,9 +293,6 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
} }
} }
HeapType consume_heap_type(Decoder* decoder, const WasmModule* module,
const WasmFeatures& enabled);
// Read a value type starting at address {pc} using {decoder}. // Read a value type starting at address {pc} using {decoder}.
// No bytes are consumed. // No bytes are consumed.
// The length of the read value type is written in {length}. // The length of the read value type is written in {length}.
......
...@@ -16,17 +16,6 @@ namespace v8 { ...@@ -16,17 +16,6 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
namespace value_type_reader {
HeapType consume_heap_type(Decoder* decoder, const WasmModule* module,
const WasmFeatures& enabled) {
uint32_t length;
HeapType result = value_type_reader::read_heap_type<Decoder::kFullValidation>(
decoder, decoder->pc(), &length, module, enabled);
decoder->consume_bytes(length, "heap type");
return result;
}
} // namespace value_type_reader
bool DecodeLocalDecls(const WasmFeatures& enabled, BodyLocalDecls* decls, bool DecodeLocalDecls(const WasmFeatures& enabled, BodyLocalDecls* decls,
const WasmModule* module, const byte* start, const WasmModule* module, const byte* start,
const byte* end) { const byte* end) {
......
This diff is collapsed.
...@@ -200,7 +200,8 @@ size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder, ...@@ -200,7 +200,8 @@ size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder,
SectionCode* result) { SectionCode* result) {
if (!decoder->ok()) return 0; if (!decoder->ok()) return 0;
decoder->impl_->Reset(bytes, offset); decoder->impl_->Reset(bytes, offset);
*result = IdentifyUnknownSectionInternal(decoder->impl_.get()); NoTracer no_tracer;
*result = IdentifyUnknownSectionInternal(decoder->impl_.get(), no_tracer);
return decoder->impl_->pc() - bytes.begin(); return decoder->impl_->pc() - bytes.begin();
} }
...@@ -337,7 +338,8 @@ bool FindNameSection(Decoder* decoder) { ...@@ -337,7 +338,8 @@ bool FindNameSection(Decoder* decoder) {
static constexpr int kModuleHeaderSize = 8; static constexpr int kModuleHeaderSize = 8;
decoder->consume_bytes(kModuleHeaderSize, "module header"); decoder->consume_bytes(kModuleHeaderSize, "module header");
WasmSectionIterator section_iter(decoder); NoTracer no_tracer;
WasmSectionIterator section_iter(decoder, no_tracer);
while (decoder->ok() && section_iter.more() && while (decoder->ok() && section_iter.more() &&
section_iter.section_code() != kNameSectionCode) { section_iter.section_code() != kNameSectionCode) {
......
...@@ -55,6 +55,10 @@ class StringBuilder { ...@@ -55,6 +55,10 @@ class StringBuilder {
const char* start() const { return start_; } const char* start() const { return start_; }
const char* cursor() const { return cursor_; } const char* cursor() const { return cursor_; }
size_t length() const { return static_cast<size_t>(cursor_ - start_); } size_t length() const { return static_cast<size_t>(cursor_ - start_); }
void rewind_to_start() {
remaining_bytes_ += length();
cursor_ = start_;
}
protected: protected:
enum OnGrowth : bool { kKeepOldChunks, kReplacePreviousChunk }; enum OnGrowth : bool { kKeepOldChunks, kReplacePreviousChunk };
...@@ -62,10 +66,6 @@ class StringBuilder { ...@@ -62,10 +66,6 @@ class StringBuilder {
// Useful for subclasses that divide the text into ranges, e.g. lines. // Useful for subclasses that divide the text into ranges, e.g. lines.
explicit StringBuilder(OnGrowth on_growth) : on_growth_(on_growth) {} explicit StringBuilder(OnGrowth on_growth) : on_growth_(on_growth) {}
void start_here() { start_ = cursor_; } void start_here() { start_ = cursor_; }
void rewind_to_start() {
remaining_bytes_ += length();
cursor_ = start_;
}
private: private:
void Grow() { void Grow() {
......
...@@ -580,6 +580,23 @@ class OffsetsProvider { ...@@ -580,6 +580,23 @@ class OffsetsProvider {
void DataOffset(uint32_t offset) { data_offsets_.push_back(offset); } void DataOffset(uint32_t offset) { data_offsets_.push_back(offset); }
// Unused by this tracer:
void Bytes(const byte* start, uint32_t count) {}
void Description(const char* desc) {}
void Description(const char* desc, size_t length) {}
void Description(uint32_t number) {}
void Description(ValueType type) {}
void Description(HeapType type) {}
void Description(const FunctionSig* sig) {}
void NextLine() {}
void NextLineIfFull() {}
void NextLineIfNonEmpty() {}
void InitializerExpression(const byte* start, const byte* end,
ValueType expected_type) {}
void FunctionBody(const WasmFunction* func, const byte* start) {}
void FunctionName(uint32_t func_index) {}
void NameSection(const byte* start, const byte* end, uint32_t offset) {}
#define GETTER(name) \ #define GETTER(name) \
uint32_t name##_offset(uint32_t index) { \ uint32_t name##_offset(uint32_t index) { \
if (!enabled_) return 0; \ if (!enabled_) return 0; \
......
...@@ -85,7 +85,7 @@ inline uint64_t max_mem_bytes() { ...@@ -85,7 +85,7 @@ inline uint64_t max_mem_bytes() {
return uint64_t{max_mem_pages()} * kWasmPageSize; return uint64_t{max_mem_pages()} * kWasmPageSize;
} }
uint32_t max_table_init_entries(); V8_EXPORT_PRIVATE uint32_t max_table_init_entries();
size_t max_module_size(); size_t max_module_size();
} // namespace wasm } // namespace wasm
......
...@@ -713,7 +713,8 @@ struct WasmFunctionName { ...@@ -713,7 +713,8 @@ struct WasmFunctionName {
const WasmName name_; const WasmName name_;
}; };
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const WasmFunctionName& name);
V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
Handle<Context> context); Handle<Context> context);
......
...@@ -82,7 +82,7 @@ function assertConversionError(bytes, imports, msg) { ...@@ -82,7 +82,7 @@ function assertConversionError(bytes, imports, msg) {
]).end().toBuffer(), f_error('invalid local index: 0 @+24')); ]).end().toBuffer(), f_error('invalid local index: 0 @+24'));
assertCompileError( assertCompileError(
builder().addStart(0).toBuffer(), builder().addStart(0).toBuffer(),
'start function index 0 out of bounds (0 entries) @+10'); 'function index 0 out of bounds (0 entries) @+10');
})(); })();
function import_error(index, module, func, msg) { function import_error(index, module, func, msg) {
......
...@@ -46,7 +46,7 @@ assertThrows(() => {instantiate(kSig_i_v, [kExprI32Const, 0]);}); ...@@ -46,7 +46,7 @@ assertThrows(() => {instantiate(kSig_i_v, [kExprI32Const, 0]);});
assertThrows( assertThrows(
() => builder.instantiate(), WebAssembly.CompileError, () => builder.instantiate(), WebAssembly.CompileError,
'WebAssembly.Module(): ' + 'WebAssembly.Module(): ' +
'start function index 1 out of bounds (1 entry) @+20'); 'function index 1 out of bounds (1 entry) @+20');
})(); })();
......
...@@ -3344,7 +3344,7 @@ TEST_F(WasmModuleVerifyTest, DeclarativeElementSegmentWithInvalidIndex) { ...@@ -3344,7 +3344,7 @@ TEST_F(WasmModuleVerifyTest, DeclarativeElementSegmentWithInvalidIndex) {
U32V_1(1)), // func index U32V_1(1)), // func index
// code ------------------------------------------------------------------ // code ------------------------------------------------------------------
ONE_EMPTY_BODY}; ONE_EMPTY_BODY};
EXPECT_FAILURE_WITH_MSG(data, "element function index 1 out of bounds"); EXPECT_FAILURE_WITH_MSG(data, "function index 1 out of bounds");
} }
TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) { TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) {
......
...@@ -28,6 +28,12 @@ struct MockStreamingResult { ...@@ -28,6 +28,12 @@ struct MockStreamingResult {
MockStreamingResult() = default; MockStreamingResult() = default;
}; };
class NoTracer {
public:
void Bytes(const byte* start, uint32_t count) {}
void Description(const char* desc) {}
};
class MockStreamingProcessor : public StreamingProcessor { class MockStreamingProcessor : public StreamingProcessor {
public: public:
explicit MockStreamingProcessor(MockStreamingResult* result) explicit MockStreamingProcessor(MockStreamingResult* result)
...@@ -36,12 +42,13 @@ class MockStreamingProcessor : public StreamingProcessor { ...@@ -36,12 +42,13 @@ class MockStreamingProcessor : public StreamingProcessor {
bool ProcessModuleHeader(base::Vector<const uint8_t> bytes, bool ProcessModuleHeader(base::Vector<const uint8_t> bytes,
uint32_t offset) override { uint32_t offset) override {
Decoder decoder(bytes.begin(), bytes.end()); Decoder decoder(bytes.begin(), bytes.end());
uint32_t magic_word = decoder.consume_u32("wasm magic"); NoTracer no_tracer;
uint32_t magic_word = decoder.consume_u32("wasm magic", no_tracer);
if (decoder.failed() || magic_word != kWasmMagic) { if (decoder.failed() || magic_word != kWasmMagic) {
result_->error = WasmError(0, "expected wasm magic"); result_->error = WasmError(0, "expected wasm magic");
return false; return false;
} }
uint32_t magic_version = decoder.consume_u32("wasm version"); uint32_t magic_version = decoder.consume_u32("wasm version", no_tracer);
if (decoder.failed() || magic_version != kWasmVersion) { if (decoder.failed() || magic_version != kWasmVersion) {
result_->error = WasmError(4, "expected wasm version"); result_->error = WasmError(4, "expected wasm version");
return false; return false;
......
This diff is collapsed.
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