mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-06-07 05:39:16 +00:00

Add aot binary analysis tool aot-analyzer, samples: ```bash # parse example.aot, and print basic information about AoT file $ ./aot-analyzer -i example.aot # parse example.aot, and print the size of text section of the AoT file $ ./aot-analyzer -t example.aot # compare these two files, and show the difference in function size between them $ ./aot-analyzer -c example.aot example.wasm ``` Signed-off-by: ganjing <ganjing@xiaomi.com>
346 lines
9.7 KiB
C++
346 lines
9.7 KiB
C++
/*
|
|
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
|
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
*/
|
|
|
|
#include "option_parser.h"
|
|
|
|
#include "common.h"
|
|
#include "config.h"
|
|
#include "string_format.h"
|
|
|
|
namespace analyzer {
|
|
|
|
OptionParser::Option::Option(char short_name, const std::string &long_name,
|
|
const std::string &metavar,
|
|
HasArgument has_argument, const std::string &help,
|
|
const Callback &callback)
|
|
: short_name(short_name)
|
|
, long_name(long_name)
|
|
, metavar(metavar)
|
|
, has_argument(has_argument == HasArgument::Yes)
|
|
, help(help)
|
|
, callback(callback)
|
|
{}
|
|
|
|
OptionParser::Argument::Argument(const std::string &name, ArgumentCount count,
|
|
const Callback &callback)
|
|
: name(name)
|
|
, count(count)
|
|
, callback(callback)
|
|
{}
|
|
|
|
OptionParser::OptionParser(const char *program_name, const char *description)
|
|
: program_name_(program_name)
|
|
, description_(description)
|
|
, on_error_([this](const std::string &message) { DefaultError(message); })
|
|
{
|
|
AddOption("help", "Print this help message", [this]() {
|
|
PrintHelp();
|
|
exit(0);
|
|
});
|
|
AddOption("version", "Print version information", []() {
|
|
printf("%s\n", ANALYZER_VERSION_STRING);
|
|
exit(0);
|
|
});
|
|
}
|
|
|
|
void
|
|
OptionParser::AddOption(const Option &option)
|
|
{
|
|
options_.emplace_back(option);
|
|
}
|
|
|
|
void
|
|
OptionParser::AddArgument(const std::string &name, ArgumentCount count,
|
|
const Callback &callback)
|
|
{
|
|
arguments_.emplace_back(name, count, callback);
|
|
}
|
|
|
|
void
|
|
OptionParser::AddOption(char short_name, const char *long_name,
|
|
const char *help, const NullCallback &callback)
|
|
{
|
|
Option option(short_name, long_name, std::string(), HasArgument::No, help,
|
|
[callback](const char *) { callback(); });
|
|
AddOption(option);
|
|
}
|
|
|
|
void
|
|
OptionParser::AddOption(const char *long_name, const char *help,
|
|
const NullCallback &callback)
|
|
{
|
|
Option option('\0', long_name, std::string(), HasArgument::No, help,
|
|
[callback](const char *) { callback(); });
|
|
AddOption(option);
|
|
}
|
|
|
|
void
|
|
OptionParser::AddOption(char short_name, const char *long_name,
|
|
const char *metavar, const char *help,
|
|
const Callback &callback)
|
|
{
|
|
Option option(short_name, long_name, metavar, HasArgument::Yes, help,
|
|
callback);
|
|
AddOption(option);
|
|
}
|
|
|
|
void
|
|
OptionParser::SetErrorCallback(const Callback &callback)
|
|
{
|
|
on_error_ = callback;
|
|
}
|
|
|
|
int
|
|
OptionParser::Match(const char *s, const std::string &full, bool has_argument)
|
|
{
|
|
int i;
|
|
for (i = 0;; i++) {
|
|
if (full[i] == '\0') {
|
|
if (s[i] == '\0') {
|
|
return i + 1;
|
|
}
|
|
|
|
if (!(has_argument && s[i] == '=')) {
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
if (s[i] == '\0') {
|
|
break;
|
|
}
|
|
if (s[i] != full[i]) {
|
|
return -1;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
void
|
|
OptionParser::Errorf(const char *format, ...)
|
|
{
|
|
ANALYZER_SNPRINTF_ALLOCA(buffer, length, format);
|
|
std::string msg(program_name_);
|
|
msg += ": ";
|
|
msg += buffer;
|
|
msg += "\nTry '--help' for more information.";
|
|
on_error_(msg.c_str());
|
|
}
|
|
|
|
void
|
|
OptionParser::DefaultError(const std::string &message)
|
|
{
|
|
ANALYZER_FATAL("%s\n", message.c_str());
|
|
}
|
|
|
|
void
|
|
OptionParser::HandleArgument(size_t *arg_index, const char *arg_value)
|
|
{
|
|
if (*arg_index >= arguments_.size()) {
|
|
Errorf("unexpected argument '%s'", arg_value);
|
|
return;
|
|
}
|
|
Argument &argument = arguments_[*arg_index];
|
|
argument.callback(arg_value);
|
|
argument.handled_count++;
|
|
|
|
if (argument.count == ArgumentCount::One) {
|
|
(*arg_index)++;
|
|
}
|
|
}
|
|
|
|
void
|
|
OptionParser::Parse(int argc, char *argv[])
|
|
{
|
|
size_t arg_index = 0;
|
|
bool processing_options = true;
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
const char *arg = argv[i];
|
|
if (!processing_options || arg[0] != '-') {
|
|
HandleArgument(&arg_index, arg);
|
|
continue;
|
|
}
|
|
|
|
if (arg[1] == '-') {
|
|
if (arg[2] == '\0') {
|
|
processing_options = false;
|
|
continue;
|
|
}
|
|
int best_index = -1;
|
|
int best_length = 0;
|
|
int best_count = 0;
|
|
for (size_t j = 0; j < options_.size(); ++j) {
|
|
const Option &option = options_[j];
|
|
if (!option.long_name.empty()) {
|
|
int match_length =
|
|
Match(&arg[2], option.long_name, option.has_argument);
|
|
if (match_length > best_length) {
|
|
best_index = j;
|
|
best_length = match_length;
|
|
best_count = 1;
|
|
}
|
|
else if (match_length == best_length && best_length > 0) {
|
|
best_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_count > 1) {
|
|
Errorf("ambiguous option '%s'", arg);
|
|
continue;
|
|
}
|
|
else if (best_count == 0) {
|
|
Errorf("unknown option '%s'", arg);
|
|
continue;
|
|
}
|
|
|
|
const Option &best_option = options_[best_index];
|
|
const char *option_argument = nullptr;
|
|
if (best_option.has_argument) {
|
|
if (arg[best_length + 1] != 0 && arg[best_length + 2] == '=') {
|
|
option_argument = &arg[best_length + 3];
|
|
}
|
|
else {
|
|
if (i + 1 == argc || argv[i + 1][0] == '-') {
|
|
Errorf("option '--%s' requires argument",
|
|
best_option.long_name.c_str());
|
|
continue;
|
|
}
|
|
++i;
|
|
option_argument = argv[i];
|
|
}
|
|
}
|
|
best_option.callback(option_argument);
|
|
}
|
|
else {
|
|
if (arg[1] == '\0') {
|
|
HandleArgument(&arg_index, arg);
|
|
continue;
|
|
}
|
|
|
|
for (int k = 1; arg[k]; ++k) {
|
|
bool matched = false;
|
|
for (const Option &option : options_) {
|
|
if (option.short_name && arg[k] == option.short_name) {
|
|
const char *option_argument = nullptr;
|
|
if (option.has_argument) {
|
|
if (arg[k + 1] != '\0') {
|
|
Errorf("option '-%c' requires argument",
|
|
option.short_name);
|
|
break;
|
|
}
|
|
|
|
if (i + 1 == argc || argv[i + 1][0] == '-') {
|
|
Errorf("option '-%c' requires argument",
|
|
option.short_name);
|
|
break;
|
|
}
|
|
++i;
|
|
option_argument = argv[i];
|
|
}
|
|
option.callback(option_argument);
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!matched) {
|
|
Errorf("unknown option '-%c'", arg[k]);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!arguments_.empty() && arguments_.back().handled_count == 0) {
|
|
for (size_t i = arg_index; i < arguments_.size(); ++i) {
|
|
if (arguments_[i].count != ArgumentCount::ZeroOrMore) {
|
|
Errorf("expected %s argument.", arguments_[i].name.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
OptionParser::PrintHelp()
|
|
{
|
|
printf("usage: %s [options]", program_name_.c_str());
|
|
|
|
for (size_t i = 0; i < arguments_.size(); ++i) {
|
|
Argument &argument = arguments_[i];
|
|
switch (argument.count) {
|
|
case ArgumentCount::One:
|
|
printf(" %s", argument.name.c_str());
|
|
break;
|
|
|
|
case ArgumentCount::OneOrMore:
|
|
printf(" %s+", argument.name.c_str());
|
|
break;
|
|
|
|
case ArgumentCount::ZeroOrMore:
|
|
printf(" [%s]...", argument.name.c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("\n\n");
|
|
printf("%s\n", description_.c_str());
|
|
printf("options:\n");
|
|
|
|
const size_t kExtraSpace = 8;
|
|
size_t longest_name_length = 0;
|
|
for (const Option &option : options_) {
|
|
size_t length;
|
|
if (!option.long_name.empty()) {
|
|
length = option.long_name.size();
|
|
if (!option.metavar.empty()) {
|
|
length += option.metavar.size() + 1;
|
|
}
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
|
|
if (length > longest_name_length) {
|
|
longest_name_length = length;
|
|
}
|
|
}
|
|
|
|
for (const Option &option : options_) {
|
|
if (!option.short_name && option.long_name.empty()) {
|
|
continue;
|
|
}
|
|
|
|
std::string line;
|
|
if (option.short_name) {
|
|
line += std::string(" -") + option.short_name + ", ";
|
|
}
|
|
else {
|
|
line += " ";
|
|
}
|
|
|
|
std::string flag;
|
|
if (!option.long_name.empty()) {
|
|
flag = "--";
|
|
if (!option.metavar.empty()) {
|
|
flag += option.long_name + '=' + option.metavar;
|
|
}
|
|
else {
|
|
flag += option.long_name;
|
|
}
|
|
}
|
|
|
|
size_t remaining = longest_name_length + kExtraSpace + 2 - flag.size();
|
|
line += flag + std::string(remaining, ' ');
|
|
|
|
if (!option.help.empty()) {
|
|
line += option.help;
|
|
}
|
|
printf("%s\n", line.c_str());
|
|
}
|
|
}
|
|
|
|
} // namespace analyzer
|