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) {
}
}
V8_EXPORT_PRIVATE void* Histogram::CreateHistogram() const {
void* Histogram::CreateHistogram() const {
return counters_->CreateHistogram(name_, min_, max_, num_buckets_);
}
......
......@@ -155,7 +155,7 @@ class StatsCounter {
class Histogram {
public:
// 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.
bool Enabled() { return histogram_ != nullptr; }
......
......@@ -27,7 +27,7 @@ namespace wasm {
// which is used in module-instantiate. We merge two distinct functionalities
// in one class to reduce the number of WasmFullDecoder instantiations, and thus
// V8 binary code size.
class ConstantExpressionInterface {
class V8_EXPORT_PRIVATE ConstantExpressionInterface {
public:
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
static constexpr DecodingMode decoding_mode = kConstantExpression;
......
......@@ -183,17 +183,26 @@ class Decoder {
// Reads a 8-bit unsigned integer (byte) and advances {pc_}.
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_}.
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_}.
uint32_t consume_u32(const char* name = "uint32_t") {
return consume_little_endian<uint32_t>(name);
template <class Tracer>
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_}.
......@@ -204,6 +213,16 @@ class Decoder {
pc_ += length;
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_}.
int32_t consume_i32v(const char* name = "var_int32") {
......@@ -215,10 +234,13 @@ class Decoder {
}
// 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;
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;
return result;
}
......@@ -242,6 +264,12 @@ class Decoder {
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_}.
bool checkAvailable(uint32_t size) {
......@@ -397,9 +425,9 @@ class Decoder {
return base::ReadLittleEndianValue<IntType>(reinterpret_cast<Address>(pc));
}
template <typename IntType>
template <typename IntType, TraceFlag trace>
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))) {
traceOffEnd();
pc_ = end_;
......@@ -407,7 +435,7 @@ class Decoder {
}
IntType val = read_little_endian<IntType, kNoValidation>(pc_, name);
traceByteRange(pc_, pc_ + sizeof(IntType));
TRACE("= %d\n", val);
TRACE_IF(trace, "= %d\n", val);
pc_ += sizeof(IntType);
return val;
}
......
......@@ -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}.
// No bytes are consumed.
// The length of the read value type is written in {length}.
......
......@@ -16,17 +16,6 @@ namespace v8 {
namespace internal {
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,
const WasmModule* module, const byte* start,
const byte* end) {
......
This diff is collapsed.
......@@ -200,7 +200,8 @@ size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder,
SectionCode* result) {
if (!decoder->ok()) return 0;
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();
}
......@@ -337,7 +338,8 @@ bool FindNameSection(Decoder* decoder) {
static constexpr int kModuleHeaderSize = 8;
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() &&
section_iter.section_code() != kNameSectionCode) {
......
......@@ -55,6 +55,10 @@ class StringBuilder {
const char* start() const { return start_; }
const char* cursor() const { return cursor_; }
size_t length() const { return static_cast<size_t>(cursor_ - start_); }
void rewind_to_start() {
remaining_bytes_ += length();
cursor_ = start_;
}
protected:
enum OnGrowth : bool { kKeepOldChunks, kReplacePreviousChunk };
......@@ -62,10 +66,6 @@ class StringBuilder {
// Useful for subclasses that divide the text into ranges, e.g. lines.
explicit StringBuilder(OnGrowth on_growth) : on_growth_(on_growth) {}
void start_here() { start_ = cursor_; }
void rewind_to_start() {
remaining_bytes_ += length();
cursor_ = start_;
}
private:
void Grow() {
......
......@@ -580,6 +580,23 @@ class OffsetsProvider {
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) \
uint32_t name##_offset(uint32_t index) { \
if (!enabled_) return 0; \
......
......@@ -85,7 +85,7 @@ inline uint64_t max_mem_bytes() {
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();
} // namespace wasm
......
......@@ -713,7 +713,8 @@ struct WasmFunctionName {
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,
Handle<Context> context);
......
......@@ -82,7 +82,7 @@ function assertConversionError(bytes, imports, msg) {
]).end().toBuffer(), f_error('invalid local index: 0 @+24'));
assertCompileError(
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) {
......
......@@ -46,7 +46,7 @@ assertThrows(() => {instantiate(kSig_i_v, [kExprI32Const, 0]);});
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
'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) {
U32V_1(1)), // func index
// code ------------------------------------------------------------------
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) {
......
......@@ -28,6 +28,12 @@ struct MockStreamingResult {
MockStreamingResult() = default;
};
class NoTracer {
public:
void Bytes(const byte* start, uint32_t count) {}
void Description(const char* desc) {}
};
class MockStreamingProcessor : public StreamingProcessor {
public:
explicit MockStreamingProcessor(MockStreamingResult* result)
......@@ -36,12 +42,13 @@ class MockStreamingProcessor : public StreamingProcessor {
bool ProcessModuleHeader(base::Vector<const uint8_t> bytes,
uint32_t offset) override {
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) {
result_->error = WasmError(0, "expected wasm magic");
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) {
result_->error = WasmError(4, "expected wasm version");
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