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:
GanJingSaiyan 2024-05-08 16:31:39 +08:00 committed by GitHub
parent 1c2a8fca4e
commit 07eae7c424
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 2127 additions and 0 deletions

View File

@ -0,0 +1,88 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
cmake_minimum_required (VERSION 3.14)
project (aot-analyzer)
set (CMAKE_CXX_STANDARD 17)
################ runtime settings ################
string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM)
if (APPLE)
add_definitions(-DBH_PLATFORM_DARWIN)
endif ()
# Reset default linker flags
set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
set (WAMR_STRINGREF_IMPL_SOURCE "STUB")
# Set WAMR_BUILD_TARGET, currently values supported:
# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]",
# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]"
if (NOT DEFINED WAMR_BUILD_TARGET)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)")
set (WAMR_BUILD_TARGET "AARCH64")
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
set (WAMR_BUILD_TARGET "RISCV64")
elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
# Build as X86_64 by default in 64-bit platform
set (WAMR_BUILD_TARGET "X86_64")
elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
# Build as X86_32 by default in 32-bit platform
set (WAMR_BUILD_TARGET "X86_32")
else ()
message(SEND_ERROR "Unsupported build target platform!")
endif ()
endif ()
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif ()
if (NOT DEFINED WAMR_BUILD_INTERP)
# Enable Interpreter by default
set (WAMR_BUILD_INTERP 1)
endif ()
if (NOT DEFINED WAMR_BUILD_AOT)
# Enable AOT by default.
set (WAMR_BUILD_AOT 1)
endif ()
if (NOT DEFINED WAMR_BUILD_MEMORY_PROFILING)
# Enable reference types by default
set (WAMR_BUILD_MEMORY_PROFILING 1)
endif ()
if (NOT DEFINED WAMR_BIG_ENDIAN)
set (WAMR_BIG_ENDIAN 0)
endif ()
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security -Wshadow -Wno-unused-parameter")
# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion -Wsign-conversion")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wformat -Wformat-security -Wno-unused")
if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64")
if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register")
endif ()
endif ()
# build out vmlib
set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE})
################ application related ################
include_directories(./include)
include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
add_executable (aot-analyzer src/main.cc src/aot_file.cc src/binary_file.cc src/option_parser.cc src/wasm_file.cc ${UNCOMMON_SHARED_SOURCE})
target_link_libraries (aot-analyzer vmlib -lm -ldl -lpthread -lrt)

View File

@ -0,0 +1,55 @@
# AoT-Analyzer: The AoT Binary analysis tool
## Cloning
Clone as normal:
```console
$ git clone
$ cd aot-analyzer
```
## Building using CMake directly
You'll need [CMake](https://cmake.org). You can then run CMake, the normal way:
```console
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
```
To analyze AoT files with GC feature enabled, you need to enable GC feature when compiling this tool:
```console
$ mkdir build
$ cd build
$ cmake -DWAMR_BUILD_GC=1 ..
$ cmake --build .
```
## Running aot-analyzer
Some examples:
```sh
# 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
```
**NOTE**: Using `-c` for file comparison, must ensure that the AoT file is generated based on this Wasm file.
You can use `--help` to get additional help:
```console
$ ./aot-analyzer --help
```

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef ANALYZER_ERROR_H_
#define ANALYZER_ERROR_H_
#include <string>
#include <string_view>
#include <vector>
#include "config.h"
namespace analyzer {
enum class ErrorLevel {
Warning,
Error,
};
static inline const char *
GetErrorLevelName(ErrorLevel error_level)
{
switch (error_level) {
case ErrorLevel::Warning:
return "warning";
case ErrorLevel::Error:
return "error";
}
ANALYZER_UNREACHABLE;
}
class Error
{
public:
Error()
: error_level_(ErrorLevel::Error)
{}
Error(ErrorLevel error_level, std::string_view message)
: error_level_(error_level)
, message_(message)
{}
ErrorLevel error_level_;
std::string message_;
};
using Errors = std::vector<Error>;
} // namespace analyzer
#endif

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef AOT_FILE_H_
#define AOT_FILE_H_
#include "binary_file.h"
namespace analyzer {
class AoTFile : public BinaryFile
{
public:
AoTFile(const char *file_name);
Result Scan();
Result ParseTargetInfo();
AOTTargetInfo GetTargetInfo();
std::string GetBinTypeName(uint16_t bin_type);
std::string GetExectuionTypeName(uint16_t e_type);
std::string GetExectuionMachineName(uint16_t e_machine);
private:
AOTTargetInfo target_info_;
};
} // namespace analyzer
#endif

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef BINARY_FILE_H_
#define BINARY_FILE_H_
#include "aot_runtime.h"
#include "bh_read_file.h"
#include "common.h"
#include "config.h"
#include "wasm_export.h"
namespace analyzer {
class BinaryFile
{
public:
BinaryFile(const char *file_name);
~BinaryFile();
Result ReadModule();
virtual Result Scan();
void ANALYZER_PRINTF_FORMAT(2, 3) PrintError(const char *format, ...);
Result UpdateCurrentPos(uint32_t steps);
const char *GetFileName() { return file_name_; }
uint8_t *GetFileData() { return file_data_; }
uint32_t GetFileSize() { return file_size_; }
size_t GetCurrentPos() { return current_pos_; }
wasm_module_t GetModule() { return module_; }
WASMModuleMemConsumption GetMemConsumption() { return mem_conspn_; }
private:
const char *file_name_;
uint8_t *file_data_;
uint32_t file_size_;
size_t current_pos_;
wasm_module_t module_;
WASMModuleMemConsumption mem_conspn_;
};
template<typename T>
Result
ReadT(T *out_value, BinaryFile *file, const char *type_name)
{
if (file == NULL
|| file->GetCurrentPos() + sizeof(T) > file->GetFileSize()) {
return Result::Error;
}
#if WAMR_BIG_ENDIAN
uint8_t tmp[sizeof(T)];
memcpy(tmp, file->GetFileData() + file->GetCurrentPos(), sizeof(tmp));
SwapBytesSized(tmp, sizeof(tmp));
memcpy(out_value, tmp, sizeof(T));
#else
memcpy(out_value, file->GetFileData() + file->GetCurrentPos(), sizeof(T));
#endif
file->UpdateCurrentPos(sizeof(T));
return Result::Ok;
}
} // namespace analyzer
#endif

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef COMMON_H_
#define COMMON_H_
#include "string_format.h"
#define ANALYZER_FATAL(...) fprintf(stderr, __VA_ARGS__), exit(1)
#if WITH_EXCEPTIONS
#define ANALYZER_TRY try {
#define ANALYZER_CATCH_BAD_ALLOC \
} \
catch (std::bad_alloc &) {}
#define ANALYZER_CATCH_BAD_ALLOC_AND_EXIT \
} \
catch (std::bad_alloc &) { ANALYZER_FATAL("Memory allocation failure.\n"); }
#else
#define ANALYZER_TRY
#define ANALYZER_CATCH_BAD_ALLOC
#define ANALYZER_CATCH_BAD_ALLOC_AND_EXIT
#endif
namespace analyzer {
struct ObjdumpOptions {
bool info;
bool text_size;
bool details;
bool compare;
const char *file_name;
};
struct Result {
enum Enum {
Ok,
Error,
};
Result()
: Result(Ok)
{}
Result(Enum e)
: enum_(e)
{}
operator Enum() const { return enum_; }
Result &operator|=(Result rhs);
private:
Enum enum_;
};
inline Result
operator|(Result lhs, Result rhs)
{
return (lhs == Result::Error || rhs == Result::Error) ? Result::Error
: Result::Ok;
}
inline Result &
Result::operator|=(Result rhs)
{
enum_ = *this | rhs;
return *this;
}
inline bool
Succeeded(Result result)
{
return result == Result::Ok;
}
inline bool
Failed(Result result)
{
return result == Result::Error;
}
#define CHECK_RESULT(expr) \
do { \
if (Failed(expr)) { \
return Result::Error; \
} \
} while (0)
#define ERROR_IF(expr, ...) \
do { \
if (expr) { \
PrintError(__VA_ARGS__); \
return Result::Error; \
} \
} while (0)
#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__)
} // namespace analyzer
#endif

View File

@ -0,0 +1,168 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef CONFIG_H_
#define CONFIG_H_
#include <stdint.h>
#include <stdlib.h>
#define ANALYZER_VERSION_STRING "1.0.0"
#define WASM_MAGIC_NUMBER 0x6d736100
#define WASM_CURRENT_VERSION 1
#define AOT_MAGIC_NUMBER 0x746f6100
#define AOT_CURRENT_VERSION 3
/* Legal values for bin_type */
#define BIN_TYPE_ELF32L 0 /* 32-bit little endian */
#define BIN_TYPE_ELF32B 1 /* 32-bit big endian */
#define BIN_TYPE_ELF64L 2 /* 64-bit little endian */
#define BIN_TYPE_ELF64B 3 /* 64-bit big endian */
#define BIN_TYPE_COFF32 4 /* 32-bit little endian */
#define BIN_TYPE_COFF64 6 /* 64-bit little endian */
/* Legal values for e_type (object file type). */
#define E_TYPE_NONE 0 /* No file type */
#define E_TYPE_REL 1 /* Relocatable file */
#define E_TYPE_EXEC 2 /* Executable file */
#define E_TYPE_DYN 3 /* Shared object file */
#define E_TYPE_XIP 4 /* eXecute In Place file */
/* Legal values for e_machine (architecture). */
#define E_MACHINE_386 3 /* Intel 80386 */
#define E_MACHINE_MIPS 8 /* MIPS R3000 big-endian */
#define E_MACHINE_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
#define E_MACHINE_ARM 40 /* ARM/Thumb */
#define E_MACHINE_AARCH64 183 /* AArch64 */
#define E_MACHINE_ARC 45 /* Argonaut RISC Core */
#define E_MACHINE_IA_64 50 /* Intel Merced */
#define E_MACHINE_MIPS_X 51 /* Stanford MIPS-X */
#define E_MACHINE_X86_64 62 /* AMD x86-64 architecture */
#define E_MACHINE_ARC_COMPACT 93 /* ARC International ARCompact */
#define E_MACHINE_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */
#define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */
#define E_MACHINE_RISCV 243 /* RISC-V 32/64 */
#define E_MACHINE_WIN_I386 0x14c /* Windows i386 architecture */
#define E_MACHINE_WIN_X86_64 0x8664 /* Windows x86-64 architecture */
/* Whether <alloca.h> is available */
#define HAVE_ALLOCA_H 1
/* Whether snprintf is defined by stdio.h */
#define HAVE_SNPRINTF 1
/* Whether ssize_t is defined by stddef.h */
#define HAVE_SSIZE_T 1
/* Whether strcasecmp is defined by strings.h */
#define HAVE_STRCASECMP 1
#define COMPILER_IS_CLANG 0
#define COMPILER_IS_GNU 1
#define COMPILER_IS_MSVC 0
#define WITH_EXCEPTIONS 0
#define SIZEOF_SIZE_T 8
#if HAVE_ALLOCA_H
#include <alloca.h>
#elif COMPILER_IS_MSVC
#include <malloc.h>
#define alloca _alloca
#elif defined(__MINGW32__)
#include <malloc.h>
#endif
#if COMPILER_IS_CLANG || COMPILER_IS_GNU
#if __MINGW32__
#define ANALYZER_PRINTF_FORMAT(format_arg, first_arg) \
__attribute__((format(gnu_printf, (format_arg), (first_arg))))
#else
#define ANALYZER_PRINTF_FORMAT(format_arg, first_arg) \
__attribute__((format(printf, (format_arg), (first_arg))))
#endif
#ifdef __cplusplus
#define ANALYZER_STATIC_ASSERT(x) static_assert((x), #x)
#else
#define ANALYZER_STATIC_ASSERT(x) _Static_assert((x), #x)
#endif
#elif COMPILER_IS_MSVC
#include <intrin.h>
#include <string.h>
#define ANALYZER_STATIC_ASSERT(x) _STATIC_ASSERT(x)
#define ANALYZER_PRINTF_FORMAT(format_arg, first_arg)
#else
#error unknown compiler
#endif
#define ANALYZER_UNREACHABLE abort()
#ifdef __cplusplus
#if COMPILER_IS_MSVC
#elif COMPILER_IS_CLANG || COMPILER_IS_GNU
/* print format specifier for size_t */
#define PRIzd "zd"
#define PRIzx "zx"
#else
#error unknown compiler
#endif
#if HAVE_SNPRINTF
#define analyzer_snprintf snprintf
#elif COMPILER_IS_MSVC
#include <cstdarg>
int
analyzer_snprintf(char *str, size_t size, const char *format, ...);
#else
#error no snprintf
#endif
#if COMPILER_IS_MSVC
int
analyzer_vsnprintf(char *str, size_t size, const char *format, va_list ap);
#else
#define analyzer_vsnprintf vsnprintf
#endif
#if !HAVE_SSIZE_T
#if COMPILER_IS_MSVC
#if defined(_WIN64)
typedef signed __int64 ssize_t;
#else
typedef signed int ssize_t;
#endif
#else
typedef long ssize_t;
#endif
#endif
#if !HAVE_STRCASECMP
#if COMPILER_IS_MSVC
#define strcasecmp _stricmp
#else
#error no strcasecmp
#endif
#endif
#endif
#endif

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef OPTION_PARSER_H_
#define OPTION_PARSER_H_
#include <functional>
#include <string>
#include <vector>
#include "config.h"
namespace analyzer {
class OptionParser
{
public:
enum class HasArgument { No, Yes };
enum class ArgumentCount { One, OneOrMore, ZeroOrMore };
struct Option;
using Callback = std::function<void(const char *)>;
using NullCallback = std::function<void()>;
struct Option {
Option(char short_name, const std::string &long_name,
const std::string &metavar, HasArgument has_argument,
const std::string &help, const Callback &);
char short_name;
std::string long_name;
std::string metavar;
bool has_argument;
std::string help;
Callback callback;
};
struct Argument {
Argument(const std::string &name, ArgumentCount, const Callback &);
std::string name;
ArgumentCount count;
Callback callback;
int handled_count = 0;
};
explicit OptionParser(const char *program_name, const char *description);
void AddOption(const Option &);
void AddOption(char short_name, const char *long_name, const char *help,
const NullCallback &);
void AddOption(const char *long_name, const char *help,
const NullCallback &);
void AddOption(char short_name, const char *long_name, const char *metavar,
const char *help, const Callback &);
void AddArgument(const std::string &name, ArgumentCount, const Callback &);
void SetErrorCallback(const Callback &);
void Parse(int argc, char *argv[]);
void PrintHelp();
private:
static int Match(const char *s, const std::string &full, bool has_argument);
void ANALYZER_PRINTF_FORMAT(2, 3) Errorf(const char *format, ...);
void HandleArgument(size_t *arg_index, const char *arg_value);
void DefaultError(const std::string &);
std::string program_name_;
std::string description_;
std::vector<Option> options_;
std::vector<Argument> arguments_;
Callback on_error_;
};
} // namespace analyzer
#endif

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef STRING_FORMAT_H_
#define STRING_FORMAT_H_
#include <cstdarg>
#include <string>
#include <vector>
#include "config.h"
#define PRIstringview "%.*s"
#define ANALYZER_PRINTF_STRING_VIEW_ARG(x) \
static_cast<int>((x).length()), (x).data()
#define PRItypecode "%s%#x"
#define ANALYZER_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE 128
#define ANALYZER_SNPRINTF_ALLOCA(buffer, len, format) \
va_list args; \
va_list args_copy; \
va_start(args, format); \
va_copy(args_copy, args); \
char fixed_buf[ANALYZER_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE]; \
char *buffer = fixed_buf; \
size_t len = \
analyzer_vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); \
va_end(args); \
if (len + 1 > sizeof(fixed_buf)) { \
buffer = static_cast<char *>(alloca(len + 1)); \
len = analyzer_vsnprintf(buffer, len + 1, format, args_copy); \
} \
va_end(args_copy)
namespace analyzer {
inline std::string ANALYZER_PRINTF_FORMAT(1, 2)
StringPrintf(const char *format, ...)
{
va_list args;
va_list args_copy;
va_start(args, format);
va_copy(args_copy, args);
size_t len = analyzer_vsnprintf(nullptr, 0, format, args) + 1;
std::vector<char> buffer(len);
va_end(args);
analyzer_vsnprintf(buffer.data(), len, format, args_copy);
va_end(args_copy);
return std::string(buffer.data(), len - 1);
}
} // namespace analyzer
#endif

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef WASM_FILE_H_
#define WASM_FILE_H_
#include "binary_file.h"
namespace analyzer {
class WasmFile : public BinaryFile
{
public:
WasmFile(const char *file_name);
Result Scan();
};
} // namespace analyzer
#endif

View File

@ -0,0 +1,259 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "aot_file.h"
#include <cstring>
#include "analyzer_error.h"
#include "common.h"
namespace analyzer {
AoTFile::AoTFile(const char *file_name)
: BinaryFile(file_name)
{
memset(&target_info_, 0, sizeof(AOTTargetInfo));
}
Result
AoTFile::Scan()
{
CHECK_RESULT(ParseTargetInfo());
WASMModuleMemConsumption mem_conspn = GetMemConsumption();
aot_get_module_mem_consumption((AOTModule *)GetModule(), &mem_conspn);
return Result::Ok;
}
Result
AoTFile::ParseTargetInfo()
{
uint32_t section_id = 0;
uint32_t section_size = 0;
// skip AOT_MAGIC_NUMBER + AOT_CURRENT_VERSION
UpdateCurrentPos(sizeof(uint32_t) + sizeof(uint32_t));
ReadT(&section_id, this, "uint32_t");
ReadT(&section_size, this, "uint32_t");
// bin type
uint16_t bin_type = 0;
CHECK_RESULT(ReadT(&bin_type, this, "uint16_t"));
target_info_.bin_type = bin_type;
// abi type
uint16_t abi_type = 0;
CHECK_RESULT(ReadT(&abi_type, this, "uint16_t"));
target_info_.abi_type = abi_type;
// exectuion type
uint16_t e_type = 0;
CHECK_RESULT(ReadT(&e_type, this, "uint16_t"));
target_info_.e_type = e_type;
// exectuion machine
uint16_t e_machine = 0;
CHECK_RESULT(ReadT(&e_machine, this, "uint16_t"));
target_info_.e_machine = e_machine;
// exectuion version
uint32_t e_version = 0;
CHECK_RESULT(ReadT(&e_version, this, "uint32_t"));
target_info_.e_version = e_version;
// exectuion flags
uint32_t e_flags = 0;
CHECK_RESULT(ReadT(&e_flags, this, "uint32_t"));
target_info_.e_flags = e_flags;
// feature flags
uint64_t feature_flags = 0;
CHECK_RESULT(ReadT(&feature_flags, this, "uint64_t"));
target_info_.feature_flags = feature_flags;
// reserved
uint64_t reserved = 0;
CHECK_RESULT(ReadT(&reserved, this, "uint64_t"));
target_info_.reserved = reserved;
// Arch name
const uint32_t section_end =
section_size - (GetCurrentPos() - sizeof(uint32_t) - sizeof(uint32_t));
for (size_t i = 0; i < section_end; ++i) {
ReadT(&target_info_.arch[i], this, "uint8_t");
}
return Result::Ok;
}
AOTTargetInfo
AoTFile::GetTargetInfo()
{
return target_info_;
}
std::string
AoTFile::GetBinTypeName(uint16_t bin_type)
{
std::string name = "";
switch (bin_type) {
case BIN_TYPE_ELF32L:
{
name = "ELF32L";
break;
}
case BIN_TYPE_ELF32B:
{
name = "ELF32B";
break;
}
case BIN_TYPE_ELF64L:
{
name = "ELF64L";
break;
}
case BIN_TYPE_ELF64B:
{
name = "ELF64B";
break;
}
case BIN_TYPE_COFF32:
{
name = "COFF32";
break;
}
case BIN_TYPE_COFF64:
{
name = "COFF64";
break;
}
default:
name = "bad bin type";
}
return name;
}
std::string
AoTFile::GetExectuionTypeName(uint16_t e_type)
{
std::string name = "";
switch (e_type) {
case E_TYPE_NONE:
{
name = "NONE";
break;
}
case E_TYPE_REL:
{
name = "REL";
break;
}
case E_TYPE_EXEC:
{
name = "EXEC";
break;
}
case E_TYPE_DYN:
{
name = "DYN";
break;
}
case E_TYPE_XIP:
{
name = "XIP";
break;
}
default:
name = "bad exectuion type";
}
return name;
}
std::string
AoTFile::GetExectuionMachineName(uint16_t e_machine)
{
std::string machine = "";
switch (e_machine) {
case E_MACHINE_386:
{
machine = "386";
break;
}
case E_MACHINE_MIPS:
{
machine = "MIPS";
break;
}
case E_MACHINE_MIPS_RS3_LE:
{
machine = "MIPS_RS3_LE";
break;
}
case E_MACHINE_ARM:
{
machine = "ARM";
break;
}
case E_MACHINE_AARCH64:
{
machine = "AARCH64";
break;
}
case E_MACHINE_ARC:
{
machine = "ARC";
break;
}
case E_MACHINE_IA_64:
{
machine = "IA_64";
break;
}
case E_MACHINE_MIPS_X:
{
machine = "MIPS_X";
break;
}
case E_MACHINE_X86_64:
{
machine = "X86_64";
break;
}
case E_MACHINE_ARC_COMPACT:
{
machine = "ARC_COMPACT";
break;
}
case E_MACHINE_ARC_COMPACT2:
{
machine = "ARC_COMPACT2";
break;
}
case E_MACHINE_XTENSA:
{
machine = "XTENSA";
break;
}
case E_MACHINE_RISCV:
{
machine = "RISCV";
break;
}
case E_MACHINE_WIN_I386:
{
machine = "WIN_I386";
break;
}
case E_MACHINE_WIN_X86_64:
{
machine = "WIN_X86_64";
break;
}
default:
machine = "bad exectuion machine type";
}
return machine;
}
} // namespace analyzer

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "binary_file.h"
#include <cctype>
#include <cstring>
#include "analyzer_error.h"
#if HAVE_ALLOCA
#include <alloca.h>
#endif
namespace analyzer {
BinaryFile::BinaryFile(const char *file_name)
: file_name_(file_name)
, file_data_(NULL)
, file_size_(0)
, current_pos_(0)
, module_(NULL)
{
memset(&mem_conspn_, 0, sizeof(WASMModuleMemConsumption));
}
BinaryFile::~BinaryFile()
{
if (module_) {
wasm_runtime_unload(module_);
wasm_runtime_free(file_data_);
wasm_runtime_destroy();
}
}
Result
BinaryFile::ReadModule()
{
char error_buf[128];
#if WASM_ENABLE_GC != 0
uint32_t gc_heap_size = GC_HEAP_SIZE_DEFAULT;
#endif
uint32_t buf_size, stack_size = DEFAULT_WASM_STACK_SIZE,
heap_size = GC_HEAP_SIZE_DEFAULT;
RuntimeInitArgs init_args;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0
static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 };
init_args.mem_alloc_type = Alloc_With_Pool;
init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
#else
init_args.mem_alloc_type = Alloc_With_Allocator;
init_args.mem_alloc_option.allocator.malloc_func = (void *)malloc;
init_args.mem_alloc_option.allocator.realloc_func = (void *)realloc;
init_args.mem_alloc_option.allocator.free_func = (void *)free;
#endif
/* initialize runtime environment */
if (!wasm_runtime_full_init(&init_args)) {
printf("Init runtime environment failed.\n");
return Result::Error;
}
file_data_ = (uint8 *)bh_read_file_to_buffer(file_name_, &file_size_);
if (!file_data_) {
printf("Open Binary file [%s] failed.\n", file_name_);
wasm_runtime_destroy();
return Result::Error;
}
module_ =
wasm_runtime_load(file_data_, file_size_, error_buf, sizeof(error_buf));
if (!module_) {
printf("Load Binary module failed. error: %s\n", error_buf);
wasm_runtime_free(file_data_);
wasm_runtime_destroy();
return Result::Error;
}
return Result::Ok;
}
Result
BinaryFile::Scan()
{
return Result::Ok;
}
void ANALYZER_PRINTF_FORMAT(2, 3) BinaryFile::PrintError(const char *format,
...)
{
ErrorLevel error_level = ErrorLevel::Error;
ANALYZER_SNPRINTF_ALLOCA(buffer, length, format);
Error error(error_level, buffer);
fprintf(stderr, "%07" PRIzx ": %s: %s\n", current_pos_,
GetErrorLevelName(error_level), buffer);
}
Result
BinaryFile::UpdateCurrentPos(uint32_t steps)
{
if (current_pos_ + steps > file_size_) {
return Result::Error;
}
current_pos_ += steps;
return Result::Ok;
}
} // namespace analyzer

View File

@ -0,0 +1,663 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "aot_file.h"
#include "common.h"
#include "option_parser.h"
#include "wasm_file.h"
using namespace analyzer;
static const char s_description[] =
R"( Print information about the contents of AoT binaries.
examples:
$ aot-analyzer example.aot
)";
struct func_info {
uint32_t idx;
void *ptr;
};
static int
compare_func_ptrs(const void *f1, const void *f2)
{
return (intptr_t)((struct func_info *)f1)->ptr
- (intptr_t)((struct func_info *)f2)->ptr;
}
static struct func_info *
sort_func_ptrs(const AOTModule *module)
{
uint64_t content_len;
struct func_info *sorted_func_ptrs;
unsigned i;
content_len = (uint64_t)sizeof(struct func_info) * module->func_count;
sorted_func_ptrs = (struct func_info *)wasm_runtime_malloc(content_len);
if (!sorted_func_ptrs) {
return NULL;
}
for (i = 0; i < module->func_count; i++) {
sorted_func_ptrs[i].idx = i;
sorted_func_ptrs[i].ptr = module->func_ptrs[i];
}
qsort(sorted_func_ptrs, module->func_count, sizeof(struct func_info),
compare_func_ptrs);
return sorted_func_ptrs;
}
static ObjdumpOptions s_objdump_options;
static std::vector<const char *> s_infiles;
static void
ParseOptions(int argc, char **argv)
{
OptionParser parser("aot-analyzer", s_description);
parser.AddOption('i', "info", "Print basic information about AoT",
[]() { s_objdump_options.info = true; });
parser.AddOption('t', "text-size", "Print text size",
[]() { s_objdump_options.text_size = true; });
parser.AddOption('x', "details", "Show AoT details",
[]() { s_objdump_options.details = true; });
parser.AddOption('c', "compare",
"Show the differences between AoT and WASM",
[]() { s_objdump_options.compare = true; });
parser.AddArgument(
"filename", OptionParser::ArgumentCount::OneOrMore,
[](const char *argument) { s_infiles.push_back(argument); });
parser.Parse(argc, argv);
}
void
InitStdio()
{
#if COMPILER_IS_MSVC
int result = _setmode(_fileno(stdout), _O_BINARY);
if (result == -1) {
perror("Cannot set mode binary to stdout");
}
result = _setmode(_fileno(stderr), _O_BINARY);
if (result == -1) {
perror("Cannot set mode binary to stderr");
}
#endif
}
void
dump_value_type(uint8 type)
{
switch (type) {
case VALUE_TYPE_I32:
printf("i32");
break;
case VALUE_TYPE_I64:
printf("i64");
break;
case VALUE_TYPE_F32:
printf("f32");
break;
case VALUE_TYPE_F64:
printf("f64");
break;
case VALUE_TYPE_V128:
printf("v128");
break;
case PACKED_TYPE_I8:
printf("i8");
break;
case PACKED_TYPE_I16:
printf("i16");
break;
case REF_TYPE_FUNCREF:
printf("funcref");
break;
case REF_TYPE_EXTERNREF:
printf("externref");
break;
case REF_TYPE_ANYREF:
printf("anyref");
break;
case REF_TYPE_EQREF:
printf("eqref");
break;
case REF_TYPE_I31REF:
printf("i31ref");
break;
case REF_TYPE_STRUCTREF:
printf("structref");
break;
case REF_TYPE_ARRAYREF:
printf("arrayref");
break;
case REF_TYPE_NULLREF:
printf("nullref");
break;
case REF_TYPE_NULLFUNCREF:
printf("nullfuncref");
break;
case REF_TYPE_NULLEXTERNREF:
printf("nullexternref");
break;
default:
printf("unknown");
}
}
void
DumpInfo(AoTFile *aot)
{
const AOTTargetInfo target_info = aot->GetTargetInfo();
printf("AOT File Information:\n\n");
printf("Binary type: %s\n",
aot->GetBinTypeName(target_info.bin_type).c_str());
printf("ABI type: %d\n", target_info.abi_type);
printf("Exectuion type: %s\n",
aot->GetExectuionTypeName(target_info.e_type).c_str());
printf("Exectuion machine: %s\n",
aot->GetExectuionMachineName(target_info.e_machine).c_str());
printf("Exectuion version: %u\n", target_info.e_version);
printf("Exectuion flags: %u\n", target_info.e_flags);
printf("Feature flags: %ld\n", target_info.feature_flags);
printf("Reserved: %ld\n", target_info.reserved);
printf("Arch: %s\n", target_info.arch);
}
void
DumpTextSize(AoTFile *aot)
{
const AOTTargetInfo target_info = aot->GetTargetInfo();
printf("%s: file format <%s>\n\n", aot->GetFileName(),
aot->GetBinTypeName(target_info.bin_type).c_str());
printf("Text size:\n");
const uint32_t literal_size =
((AOTModule *)(aot->GetModule()))->literal_size;
const uint32_t code_size = ((AOTModule *)(aot->GetModule()))->code_size;
printf(" literal size= %u Bytes\n", literal_size);
printf(" code size= %u Bytes\n", code_size);
}
void
DumpDetails(AoTFile *aot)
{
const AOTTargetInfo target_info = aot->GetTargetInfo();
printf("%s: file format <%s>\n\n", aot->GetFileName(),
aot->GetBinTypeName(target_info.bin_type).c_str());
printf("Details:\n\n");
// Types
const uint32_t type_count = ((AOTModule *)(aot->GetModule()))->type_count;
AOTType **types = ((AOTModule *)(aot->GetModule()))->types;
printf("Types[%u]\n", type_count);
#if WASM_ENABLE_GC != 0
const char *wasm_type[] = { "function", "struct", "array" };
for (uint32_t index = 0; index < type_count; index++) {
AOTType *type = types[index];
const uint16_t type_flag = type->type_flag;
printf(" -[%u] ", index);
if (type_flag == WASM_TYPE_FUNC) {
wasm_dump_func_type(((AOTFuncType *)type));
}
else if (type_flag == WASM_TYPE_STRUCT) {
wasm_dump_struct_type(((AOTStructType *)type));
}
else if (type_flag == WASM_TYPE_ARRAY) {
wasm_dump_array_type(((AOTArrayType *)type));
}
else {
printf(" -[%u] unknown type\n", index);
}
}
#else
for (uint32_t index = 0; index < type_count; index++) {
printf(" -[%u] ", index);
AOTType *type = types[index];
uint32_t i = 0;
printf("func [");
for (i = 0; i < type->param_count; i++) {
dump_value_type(type->types[i]);
if (i < (uint32)type->param_count - 1)
printf(" ");
}
printf("] -> [");
for (; i < (uint32)(type->param_count + type->result_count); i++) {
dump_value_type(type->types[i]);
if (i < (uint32)type->param_count + type->result_count - 1)
printf(" ");
}
printf("]\n");
}
#endif
printf("\n\n");
// Imports
const uint32_t import_memory_count =
((AOTModule *)(aot->GetModule()))->import_memory_count;
AOTImportMemory *import_memories =
((AOTModule *)(aot->GetModule()))->import_memories;
const uint32_t import_table_count =
((AOTModule *)(aot->GetModule()))->import_table_count;
AOTImportTable *import_tables =
((AOTModule *)(aot->GetModule()))->import_tables;
const uint32_t import_global_count =
((AOTModule *)(aot->GetModule()))->import_global_count;
AOTImportGlobal *import_globals =
((AOTModule *)(aot->GetModule()))->import_globals;
const uint32_t import_func_count =
((AOTModule *)(aot->GetModule()))->import_func_count;
AOTImportFunc *import_funcs =
((AOTModule *)(aot->GetModule()))->import_funcs;
printf("Imports[%u]\n", import_memory_count + import_table_count
+ import_global_count + import_func_count);
// import memories
printf(" -import_memories[%u]\n", import_memory_count);
for (uint32_t index = 0; index < import_memory_count; index++) {
AOTImportMemory memory = import_memories[index];
printf(" -[%u] num_bytes_per_page:%5u init_page_count:%5u "
"max_page_count:%5u module_name: %s memory_name: %s\n",
index, memory.num_bytes_per_page, memory.mem_init_page_count,
memory.mem_max_page_count, memory.module_name,
memory.memory_name);
}
printf("\n");
// import tables
printf(" -import_tables[%u]\n", import_table_count);
for (uint32_t index = 0; index < import_table_count; index++) {
AOTImportTable table = import_tables[index];
printf(" -[%u] ", index);
printf("elem_type: ");
#if WASM_ENABLE_GC != 0
wasm_dump_value_type(table.elem_type, table.elem_ref_type);
#else
dump_value_type(table.elem_type);
#endif
printf(" init_size:%5u max_size:%5u "
"module_name: %s table_name: %s\n",
table.table_init_size, table.table_max_size, table.module_name,
table.table_name);
}
printf("\n");
// import globals
printf(" -import_globals[%u]\n", import_global_count);
for (uint32_t index = 0; index < import_global_count; index++) {
AOTImportGlobal global = import_globals[index];
printf(" -[%u] ", index);
printf("type: ");
dump_value_type(global.type);
printf(" module_name: %s global_name: %s\n", global.module_name,
global.global_name);
}
printf("\n");
// import functions
printf(" -import_functions[%u]\n", import_func_count);
for (uint32_t index = 0; index < import_func_count; index++) {
AOTImportFunc func = import_funcs[index];
printf(" -[%u] module_name: %s function_name: %s\n", index,
func.module_name, func.func_name);
}
printf("\n\n");
// Functions
const uint32_t func_count = ((AOTModule *)(aot->GetModule()))->func_count;
const uint32_t code_size = ((AOTModule *)(aot->GetModule()))->code_size;
struct func_info *sorted_func_ptrs = NULL;
sorted_func_ptrs = sort_func_ptrs(((AOTModule *)(aot->GetModule())));
if (sorted_func_ptrs) {
printf("Function[%u]\n", func_count);
for (uint32_t index = 0; index < func_count; index++) {
const uint32_t func_size =
index + 1 < func_count
? (uintptr_t)(sorted_func_ptrs[index + 1].ptr)
- (uintptr_t)(sorted_func_ptrs[index].ptr)
: code_size
+ (uintptr_t)(((AOTModule *)(aot->GetModule()))->code)
- (uintptr_t)(sorted_func_ptrs[index].ptr);
printf(" -[%u] code_size= %u Bytes\n", index, func_size);
}
wasm_runtime_free(sorted_func_ptrs);
printf("\n\n");
}
// Tables
const uint32_t table_count = ((AOTModule *)(aot->GetModule()))->table_count;
AOTTable *tables = ((AOTModule *)(aot->GetModule()))->tables;
printf("Tables[%u]\n", table_count);
for (uint32_t index = 0; index < table_count; index++) {
AOTTable table = tables[index];
printf(" -[%u] ", index);
printf("elem_type: ");
#if WASM_ENABLE_GC != 0
wasm_dump_value_type(table.elem_type, table.elem_ref_type);
#else
dump_value_type(table.elem_type);
#endif
printf(" init_size:%5u max_size:%5u\n", table.table_init_size,
table.table_max_size);
}
printf("\n\n");
// Memories
const uint32_t memory_count =
((AOTModule *)(aot->GetModule()))->memory_count;
AOTMemory *memories = ((AOTModule *)(aot->GetModule()))->memories;
printf("Memories[%u]\n", memory_count);
for (uint32_t index = 0; index < memory_count; index++) {
AOTMemory memory = memories[index];
printf(" -[%u] memory_flags:%5u bytes_per_page:%5u "
"init_page_count:%5u max_page_count:%5u\n",
index, memory.memory_flags, memory.num_bytes_per_page,
memory.mem_init_page_count, memory.mem_max_page_count);
}
printf("\n\n");
// Globals
const uint32_t global_count =
((AOTModule *)(aot->GetModule()))->global_count;
AOTGlobal *globals = ((AOTModule *)(aot->GetModule()))->globals;
printf("Globals[%u]\n", global_count);
for (uint32_t index = 0; index < global_count; index++) {
AOTGlobal global = globals[index];
printf(" -[%u] ", index);
printf("type: ");
dump_value_type(global.type);
printf(" is_mutable: %d size: %u data_offset: %u\n",
global.is_mutable, global.size, global.data_offset);
}
printf("\n\n");
// Exports
const uint32_t export_count =
((AOTModule *)(aot->GetModule()))->export_count;
AOTExport *exports = ((AOTModule *)(aot->GetModule()))->exports;
printf("Exports[%u]\n", export_count);
for (uint32_t index = 0; index < export_count; index++) {
AOTExport expt = exports[index];
printf(" -[%u] kind:%5d index:%5u name: %s\n", index, expt.kind,
expt.index, expt.name);
}
printf("\n\n");
// Code
const uint32_t aot_code_size = (aot->GetMemConsumption()).aot_code_size;
const uint32_t literal_size =
((AOTModule *)(aot->GetModule()))->literal_size;
const uint32_t data_section_count =
((AOTModule *)(aot->GetModule()))->data_section_count;
printf("Codes[%u]\n", aot_code_size);
printf(" -code\n");
printf(" -code_size: %u Bytes\n", code_size);
printf("\n");
printf(" -literal\n");
printf(" -literal_size: %u Bytes\n", literal_size);
printf("\n");
printf(" -data section\n");
for (uint32_t index = 0; index < data_section_count; index++) {
AOTObjectDataSection *obj_data =
((AOTModule *)(aot->GetModule()))->data_sections + index;
printf(" -[%u] code_size:%5u Bytes name: %s\n", index,
obj_data->size, obj_data->name);
}
printf("\n\n");
}
void
DumpCompare(AoTFile *aot, WasmFile *wasm)
{
const AOTTargetInfo target_info = aot->GetTargetInfo();
AOTModule *aot_module = (AOTModule *)(aot->GetModule());
WASMModuleMemConsumption aot_mem_conspn = aot->GetMemConsumption();
WASMModule *wasm_module = (WASMModule *)(wasm->GetModule());
const uint32_t aot_func_count = aot_module->func_count;
const uint32_t aot_code_size = aot_module->code_size;
struct func_info *sorted_func_ptrs = NULL;
sorted_func_ptrs = sort_func_ptrs(((AOTModule *)(aot->GetModule())));
if (!sorted_func_ptrs) {
printf("sort AoT functions failed.\n");
return;
}
const uint32_t wasm_func_count = wasm_module->function_count;
WASMFunction **wasm_functions = wasm_module->functions;
if (aot_func_count != wasm_func_count) {
printf("The number of AoT functions does not match the number of Wasm "
"functions.\n");
wasm_runtime_free(sorted_func_ptrs);
return;
}
uint32_t wasm_code_size = 0;
// print function Comparison Details
printf(
"|--------------------------------------------------------------------"
"-------------------|\n");
printf(
"| Function Code Size Compare "
" |\n");
printf(
"|--------------------------------------------------------------------"
"-------------------|\n");
printf(
"| ID | AoT Function Code Size | Wasm Function Code Size | "
"expansion multiple |\n");
printf(
"|--------------------------------------------------------------------"
"-------------------|\n");
for (uint32_t index = 0; index < aot_func_count; index++) {
const uint32_t aot_func_size =
index + 1 < aot_func_count
? (uintptr_t)(sorted_func_ptrs[index + 1].ptr)
- (uintptr_t)(sorted_func_ptrs[index].ptr)
: aot_code_size + (uintptr_t)(aot_module->code)
- (uintptr_t)(sorted_func_ptrs[index].ptr);
const uint32_t wasm_func_size = wasm_functions[index]->code_size;
wasm_code_size += wasm_func_size;
printf(
"| %4d | %10d Bytes | %10d Bytes | %10.2f "
" "
" |\n",
index, aot_func_size, wasm_func_size,
(aot_func_size * 1.0) / wasm_func_size);
printf(
"|-----------------------------------------------------------------"
"-"
"---------------------|\n");
}
wasm_runtime_free(sorted_func_ptrs);
printf("\n\n");
printf(
"|--------------------------------------------------------------------"
"---|\n");
printf(
"| Total Code Size Compare "
" |\n");
printf(
"|--------------------------------------------------------------------"
"---|\n");
printf("| AoT code size= %10d Bytes | Wasm code size= %10d Bytes |\n",
aot_code_size, wasm_code_size);
printf(
"|--------------------------------------------------------------------"
"---|\n");
}
int
ProgramMain(int argc, char **argv)
{
InitStdio();
ParseOptions(argc, argv);
if (!s_objdump_options.info && !s_objdump_options.text_size
&& !s_objdump_options.details && !s_objdump_options.compare) {
fprintf(stderr,
"At least one of the following switches must be given:\n");
fprintf(stderr, " -i/ --info\n");
fprintf(stderr, " -t/ --text-size\n");
fprintf(stderr, " -x/ --details\n");
fprintf(stderr, " -c/ --compare\n");
return 1;
}
std::vector<BinaryFile *> readers;
for (const char *filename : s_infiles) {
BinaryFile *reader = NULL;
const char *dot = strrchr(filename, '.');
if (!dot) {
printf("bad file name: %s\n", filename);
continue;
}
if (strncmp(dot, ".aot", 4) == 0) {
reader = new AoTFile(filename);
}
else if (strncmp(dot, ".wasm", 4) == 0) {
reader = new WasmFile(filename);
}
else {
printf("unkown file extension: %s\n", dot);
continue;
}
if (reader && reader->ReadModule() == Result::Error) {
printf("read module failed.\n");
continue;
}
CHECK_RESULT(reader->Scan());
readers.push_back(reader);
}
// -i/ --info
if (s_objdump_options.info == 1) {
for (size_t i = 0; i < readers.size(); ++i) {
printf("\n");
BinaryFile *reader = readers[i];
const uint32_t module_type = reader->GetModule()->module_type;
if (module_type == Wasm_Module_AoT) {
AoTFile *aot = dynamic_cast<AoTFile *>(reader);
if (!aot) {
printf("[DumpInfo]: Reader cast failed.\n");
continue;
}
DumpInfo(aot);
}
else {
printf("[DumpInfo]: Wrong file format, not an AoT file.\n");
}
}
}
// -t/ --text-size
if (s_objdump_options.text_size == 1) {
for (size_t i = 0; i < readers.size(); ++i) {
printf("\n");
BinaryFile *reader = readers[i];
const uint32_t module_type = reader->GetModule()->module_type;
if (module_type == Wasm_Module_AoT) {
AoTFile *aot = dynamic_cast<AoTFile *>(reader);
if (!aot) {
printf("[DumpTextSize]: Reader cast failed.\n");
continue;
}
DumpTextSize(aot);
}
else {
printf("[DumpTextSize]: Wrong file format, not an AoT file.\n");
}
}
}
// -x/ --details
if (s_objdump_options.details == 1) {
for (size_t i = 0; i < readers.size(); ++i) {
printf("\n");
BinaryFile *reader = readers[i];
const uint32_t module_type = reader->GetModule()->module_type;
if (module_type == Wasm_Module_AoT) {
AoTFile *aot = dynamic_cast<AoTFile *>(reader);
if (!aot) {
printf("[DumpDetails]: Reader cast failed.\n");
continue;
}
DumpDetails(aot);
}
else {
printf("[DumpDetails]: Wrong file format, not an AoT file.\n");
}
}
}
// -c/ --compare
if (s_objdump_options.compare == 1) {
printf("\n");
if (readers.size() != 2) {
printf("[DumpCompare]: Illegal number of file parameters.\n");
return 1;
}
AoTFile *aot = NULL;
WasmFile *wasm = NULL;
for (size_t i = 0; i < readers.size(); ++i) {
BinaryFile *reader = readers[i];
const uint32_t module_type = reader->GetModule()->module_type;
if (module_type == Wasm_Module_AoT) {
aot = dynamic_cast<AoTFile *>(reader);
}
else if (module_type == Wasm_Module_Bytecode) {
wasm = dynamic_cast<WasmFile *>(reader);
}
}
if (!aot) {
printf("[DumpCompare]: an aot file is required for comparison.\n");
return 1;
}
if (!wasm) {
printf("[DumpCompare]: a wasm file is required for comparison.\n");
return 1;
}
DumpCompare(aot, wasm);
}
return 0;
}
int
main(int argc, char **argv)
{
ANALYZER_TRY
return ProgramMain(argc, argv);
ANALYZER_CATCH_BAD_ALLOC_AND_EXIT
}

View 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

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 Xiaomi Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "wasm_file.h"
#include <cstring>
#include "analyzer_error.h"
#include "common.h"
namespace analyzer {
WasmFile::WasmFile(const char *file_name)
: BinaryFile(file_name)
{}
Result
WasmFile::Scan()
{
WASMModuleMemConsumption mem_conspn = GetMemConsumption();
wasm_get_module_mem_consumption((WASMModule *)GetModule(), &mem_conspn);
return Result::Ok;
}
} // namespace analyzer