Add aot binary analysis tool aot-analyzer (#3379)
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>
This commit is contained in:
345
test-tools/aot-analyzer/src/option_parser.cc
Normal file
345
test-tools/aot-analyzer/src/option_parser.cc
Normal file
@ -0,0 +1,345 @@
|
||||
/*
|
||||
* 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
|
||||
Reference in New Issue
Block a user