Просмотр исходного кода

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>
GanJingSaiyan 1 год назад
Родитель
Сommit
07eae7c424

+ 88 - 0
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)

+ 55 - 0
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
+```

+ 52 - 0
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 <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

+ 32 - 0
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

+ 67 - 0
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<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

+ 100 - 0
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

+ 168 - 0
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 <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

+ 78 - 0
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 <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

+ 56 - 0
test-tools/aot-analyzer/include/string_format.h

@@ -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

+ 22 - 0
test-tools/aot-analyzer/include/wasm_file.h

@@ -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

+ 259 - 0
test-tools/aot-analyzer/src/aot_file.cc

@@ -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

+ 115 - 0
test-tools/aot-analyzer/src/binary_file.cc

@@ -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

+ 663 - 0
test-tools/aot-analyzer/src/main.cc

@@ -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
+}

+ 345 - 0
test-tools/aot-analyzer/src/option_parser.cc

@@ -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

+ 27 - 0
test-tools/aot-analyzer/src/wasm_file.cc

@@ -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