From 07eae7c424a7bdf57be2345763901db6810abad4 Mon Sep 17 00:00:00 2001 From: GanJingSaiyan <130947810+Shanks0224@users.noreply.github.com> Date: Wed, 8 May 2024 16:31:39 +0800 Subject: [PATCH] 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 --- test-tools/aot-analyzer/CMakeLists.txt | 88 +++ test-tools/aot-analyzer/README.md | 55 ++ .../aot-analyzer/include/analyzer_error.h | 52 ++ test-tools/aot-analyzer/include/aot_file.h | 32 + test-tools/aot-analyzer/include/binary_file.h | 67 ++ test-tools/aot-analyzer/include/common.h | 100 +++ test-tools/aot-analyzer/include/config.h | 168 +++++ .../aot-analyzer/include/option_parser.h | 78 +++ .../aot-analyzer/include/string_format.h | 56 ++ test-tools/aot-analyzer/include/wasm_file.h | 22 + test-tools/aot-analyzer/src/aot_file.cc | 259 +++++++ test-tools/aot-analyzer/src/binary_file.cc | 115 +++ test-tools/aot-analyzer/src/main.cc | 663 ++++++++++++++++++ test-tools/aot-analyzer/src/option_parser.cc | 345 +++++++++ test-tools/aot-analyzer/src/wasm_file.cc | 27 + 15 files changed, 2127 insertions(+) create mode 100644 test-tools/aot-analyzer/CMakeLists.txt create mode 100644 test-tools/aot-analyzer/README.md create mode 100644 test-tools/aot-analyzer/include/analyzer_error.h create mode 100644 test-tools/aot-analyzer/include/aot_file.h create mode 100644 test-tools/aot-analyzer/include/binary_file.h create mode 100644 test-tools/aot-analyzer/include/common.h create mode 100644 test-tools/aot-analyzer/include/config.h create mode 100644 test-tools/aot-analyzer/include/option_parser.h create mode 100644 test-tools/aot-analyzer/include/string_format.h create mode 100644 test-tools/aot-analyzer/include/wasm_file.h create mode 100644 test-tools/aot-analyzer/src/aot_file.cc create mode 100644 test-tools/aot-analyzer/src/binary_file.cc create mode 100644 test-tools/aot-analyzer/src/main.cc create mode 100644 test-tools/aot-analyzer/src/option_parser.cc create mode 100644 test-tools/aot-analyzer/src/wasm_file.cc diff --git a/test-tools/aot-analyzer/CMakeLists.txt b/test-tools/aot-analyzer/CMakeLists.txt new file mode 100644 index 000000000..b1a11b549 --- /dev/null +++ b/test-tools/aot-analyzer/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/test-tools/aot-analyzer/README.md b/test-tools/aot-analyzer/README.md new file mode 100644 index 000000000..1ae3ca7b5 --- /dev/null +++ b/test-tools/aot-analyzer/README.md @@ -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 +``` \ No newline at end of file diff --git a/test-tools/aot-analyzer/include/analyzer_error.h b/test-tools/aot-analyzer/include/analyzer_error.h new file mode 100644 index 000000000..62126fdf7 --- /dev/null +++ b/test-tools/aot-analyzer/include/analyzer_error.h @@ -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 +#include +#include + +#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; + +} // namespace analyzer +#endif \ No newline at end of file diff --git a/test-tools/aot-analyzer/include/aot_file.h b/test-tools/aot-analyzer/include/aot_file.h new file mode 100644 index 000000000..fdb9c87f1 --- /dev/null +++ b/test-tools/aot-analyzer/include/aot_file.h @@ -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 diff --git a/test-tools/aot-analyzer/include/binary_file.h b/test-tools/aot-analyzer/include/binary_file.h new file mode 100644 index 000000000..d0cf00347 --- /dev/null +++ b/test-tools/aot-analyzer/include/binary_file.h @@ -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 +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 diff --git a/test-tools/aot-analyzer/include/common.h b/test-tools/aot-analyzer/include/common.h new file mode 100644 index 000000000..c4d25bc76 --- /dev/null +++ b/test-tools/aot-analyzer/include/common.h @@ -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 diff --git a/test-tools/aot-analyzer/include/config.h b/test-tools/aot-analyzer/include/config.h new file mode 100644 index 000000000..546b5b167 --- /dev/null +++ b/test-tools/aot-analyzer/include/config.h @@ -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 +#include + +#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 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 +#elif COMPILER_IS_MSVC +#include +#define alloca _alloca +#elif defined(__MINGW32__) +#include +#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 +#include + +#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 +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 diff --git a/test-tools/aot-analyzer/include/option_parser.h b/test-tools/aot-analyzer/include/option_parser.h new file mode 100644 index 000000000..a3e0f7d55 --- /dev/null +++ b/test-tools/aot-analyzer/include/option_parser.h @@ -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 +#include +#include + +#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; + using NullCallback = std::function; + + 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