wasm-micro-runtime/test-tools/aot-analyzer/src/option_parser.cc
GanJingSaiyan 07eae7c424
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>
2024-05-08 16:31:39 +08:00

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