v8/tools/wasm/module-inspector.cc
Jakob Kummerow 7f0c7fb074 [tools][wasm] Add "wami", the Wasm Module Inspector
Initial feature: list functions in a module, as follows:

$ gm x64.release wami
$ out/x64.release/wami --list-functions my_module.wasm

More to come.

Change-Id: I9580437d51153e1b5ccc291fdb6a6a67315be07d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3742700
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81515}
2022-07-05 04:45:28 +00:00

166 lines
4.9 KiB
C++

// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
#include "include/libplatform/libplatform.h"
#include "include/v8-initialization.h"
#include "src/wasm/module-decoder-impl.h"
#include "src/wasm/names-provider.h"
#include "src/wasm/string-builder.h"
#include "src/wasm/wasm-opcodes-inl.h"
int PrintHelp(char** argv) {
std::cerr << "Usage: Specify an action and a module name in any order.\n"
<< "The action can be any of:\n"
<< " --help\n"
<< " Print this help and exit.\n"
<< " --list-functions\n"
<< " List functions in the given module\n"
<< "The module name must be a file name.\n";
return 1;
}
namespace v8 {
namespace internal {
namespace wasm {
class FormatConverter {
public:
explicit FormatConverter(std::string path) {
std::ifstream input(path, std::ios::binary);
if (!input.is_open()) {
std::cerr << "Failed to open " << path << "!\n";
return;
}
raw_bytes_ = std::vector<byte>(std::istreambuf_iterator<char>(input), {});
if (raw_bytes_.size() < 8 || raw_bytes_[0] != 0 || raw_bytes_[1] != 'a' ||
raw_bytes_[2] != 's' || raw_bytes_[3] != 'm') {
std::cerr << "That's not a Wasm module!\n";
return;
}
base::Vector<const byte> wire_bytes(raw_bytes_.data(), raw_bytes_.size());
ModuleResult result =
DecodeWasmModuleForDisassembler(start(), end(), &allocator_);
if (result.failed()) {
WasmError error = result.error();
std::cerr << "Decoding error: " << error.message() << " at offset "
<< error.offset() << "\n";
// TODO(jkummerow): Show some disassembly.
return;
}
ok_ = true;
module_ = result.value();
names_provider_ =
std::make_unique<NamesProvider>(module_.get(), wire_bytes);
}
bool ok() const { return ok_; }
void ListFunctions() {
DCHECK(ok_);
const WasmModule* m = module();
uint32_t num_functions = static_cast<uint32_t>(m->functions.size());
std::cout << "There are " << num_functions << " functions ("
<< m->num_imported_functions << " imported, "
<< m->num_declared_functions
<< " locally defined); the following have names:\n";
for (uint32_t i = 0; i < num_functions; i++) {
StringBuilder sb;
names()->PrintFunctionName(sb, i);
if (sb.length() == 0) continue;
std::string name(sb.start(), sb.length());
std::cout << i << " " << name << "\n";
}
}
private:
byte* start() { return raw_bytes_.data(); }
byte* end() { return start() + raw_bytes_.size(); }
const WasmModule* module() { return module_.get(); }
NamesProvider* names() { return names_provider_.get(); }
AccountingAllocator allocator_;
bool ok_{false};
std::vector<byte> raw_bytes_;
std::shared_ptr<WasmModule> module_;
std::unique_ptr<NamesProvider> names_provider_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
using FormatConverter = v8::internal::wasm::FormatConverter;
enum class Action {
kUnset,
kHelp,
kListFunctions,
};
struct Options {
const char* filename = nullptr;
Action action = Action::kUnset;
};
void ListFunctions(const Options& options) {
FormatConverter fc(options.filename);
if (fc.ok()) fc.ListFunctions();
}
int ParseOptions(int argc, char** argv, Options* options) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0 ||
strcmp(argv[i], "help") == 0) {
options->action = Action::kHelp;
} else if (strcmp(argv[i], "--list-functions") == 0) {
options->action = Action::kListFunctions;
} else if (options->filename != nullptr) {
return PrintHelp(argv);
} else {
options->filename = argv[i];
}
}
if (options->action == Action::kUnset || options->filename == nullptr) {
return PrintHelp(argv);
}
return 0;
}
int main(int argc, char** argv) {
Options options;
if (ParseOptions(argc, argv, &options) != 0) return 1;
// Bootstrap the basics.
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
#ifdef V8_ENABLE_SANDBOX
if (!v8::V8::InitializeSandbox()) {
fprintf(stderr, "Error initializing the V8 sandbox\n");
return 1;
}
#endif
v8::V8::Initialize();
switch (options.action) {
// clang-format off
case Action::kHelp: PrintHelp(argv); break;
case Action::kListFunctions: ListFunctions(options); break;
case Action::kUnset: UNREACHABLE();
// clang-format on
}
v8::V8::Dispose();
v8::V8::DisposePlatform();
return 0;
}