diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 12fc06bd7..3d0d6bef7 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -404,6 +404,10 @@ endif () if (WAMR_BUILD_DEBUG_AOT EQUAL 1) message (" Debug AOT enabled") endif () +if (WAMR_BUILD_DYNAMIC_AOT_DEBUG EQUAL 1) + add_definitions (-DWASM_ENABLE_DYNAMIC_AOT_DEBUG=1) + message (" Dynamic AOT debug enabled") +endif () if (WAMR_BUILD_LOAD_CUSTOM_SECTION EQUAL 1) add_definitions (-DWASM_ENABLE_LOAD_CUSTOM_SECTION=1) message (" Load custom section enabled") diff --git a/core/config.h b/core/config.h index a25eb543e..50f798922 100644 --- a/core/config.h +++ b/core/config.h @@ -75,6 +75,10 @@ #define WASM_ENABLE_AOT 0 #endif +#ifndef WASM_ENABLE_DYNAMIC_AOT_DEBUG +#define WASM_ENABLE_DYNAMIC_AOT_DEBUG 0 +#endif + #ifndef WASM_ENABLE_WORD_ALIGN_READ #define WASM_ENABLE_WORD_ALIGN_READ 0 #endif diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 13664ca0e..f1e63802a 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -5072,6 +5072,18 @@ aot_const_str_set_insert(const uint8 *str, int32 len, AOTModule *module, return c_str; } +#if WASM_ENABLE_DYNAMIC_AOT_DEBUG != 0 +AOTModule *g_dynamic_aot_module = NULL; + +void __attribute__((noinline)) __enable_dynamic_aot_debug(void) +{ + /* empty implementation. */ +} + +void (*__enable_dynamic_aot_debug_ptr)(void) + __attribute__((visibility("default"))) = __enable_dynamic_aot_debug; +#endif + bool aot_set_module_name(AOTModule *module, const char *name, char *error_buf, uint32_t error_buf_size) @@ -5085,6 +5097,12 @@ aot_set_module_name(AOTModule *module, const char *name, char *error_buf, false, #endif error_buf, error_buf_size); +#if WASM_ENABLE_DYNAMIC_AOT_DEBUG != 0 + /* export g_dynamic_aot_module for dynamic aot debug */ + g_dynamic_aot_module = module; + /* trigger breakpoint __enable_dynamic_aot_debug */ + (*__enable_dynamic_aot_debug_ptr)(); +#endif return module->name != NULL; } diff --git a/product-mini/platforms/nuttx/CMakeLists.txt b/product-mini/platforms/nuttx/CMakeLists.txt index ca07a5d55..27ddd9fa9 100644 --- a/product-mini/platforms/nuttx/CMakeLists.txt +++ b/product-mini/platforms/nuttx/CMakeLists.txt @@ -49,6 +49,12 @@ else() add_definitions(-DWASM_ENABLE_WORD_ALIGN_READ=0) endif() +if(CONFIG_INTERPRETERS_WAMR_DYNAMIC_AOT_DEBUG) + add_definitions(-DWASM_ENABLE_DYNAMIC_AOT_DEBUG=1) +else() + add_definitions(-DWASM_ENABLE_DYNAMIC_AOT_DEBUG=0) +endif() + if(CONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE) add_definitions(-DWASM_STACK_GUARD_SIZE=0) else() diff --git a/product-mini/platforms/nuttx/wamr.mk b/product-mini/platforms/nuttx/wamr.mk index 0ee76c7dd..44d8694e5 100644 --- a/product-mini/platforms/nuttx/wamr.mk +++ b/product-mini/platforms/nuttx/wamr.mk @@ -148,6 +148,12 @@ else CFLAGS += -DWASM_ENABLE_WORD_ALIGN_READ=0 endif +ifeq ($(CONFIG_INTERPRETERS_WAMR_DYNAMIC_AOT_DEBUG),y) +CFLAGS += -DWASM_ENABLE_DYNAMIC_AOT_DEBUG=1 +else +CFLAGS += -DWASM_ENABLE_DYNAMIC_AOT_DEBUG=0 +endif + ifeq ($(CONFIG_INTERPRETERS_WAMR_MEM_DUAL_BUS_MIRROR),y) CFLAGS += -DWASM_MEM_DUAL_BUS_MIRROR=1 else diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index 14dc01f6b..af50223a4 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -928,6 +928,15 @@ main(int argc, char *argv[]) goto fail2; } +#if WASM_ENABLE_DYNAMIC_AOT_DEBUG != 0 + if (!wasm_runtime_set_module_name(wasm_module, wasm_file, error_buf, + sizeof(error_buf))) { + printf("set aot module name failed in dynamic aot debug mode, %s\n", + error_buf); + goto fail3; + } +#endif + #if WASM_ENABLE_LIBC_WASI != 0 libc_wasi_init(wasm_module, argc, argv, &wasi_parse_ctx); #endif diff --git a/test-tools/dynamic-aot-debug/README.md b/test-tools/dynamic-aot-debug/README.md new file mode 100644 index 000000000..6325a803a --- /dev/null +++ b/test-tools/dynamic-aot-debug/README.md @@ -0,0 +1,139 @@ +# Dynamic AOT Module Debugging + +> Note: Dynamic AOT debugging is experimental and only a few debugging capabilities are supported. + +This guide explains how to debug WAMR AOT modules with dynamic AOT features. Follow these steps to set up and run your debugging environment. + +## 1. Test source code + +The following c program file is used as a debugging test file. + +```bash +#include + +int main() { + printf("hello, world!\n"); + int a = 1024; + printf("a is %d\n",a); + int b = 42; + printf("b is %d\n",b); + return 0; +} +``` + +## 2. Build iwasm with dynamic aot debugging feature + +To enable dynamic AOT debugging, ensure the following +compile options are enabled when you [build iwasm](../../product-mini/README.md): + +```bash +cmake -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_DYNAMIC_AOT_DEBUG=1 -DCMAKE_BUILD_TYPE=Debug +``` + +## 3. Build wamrc + +Developer may need to build out two versions of wamrc, one is to compile the wasm binary into the AOT file, the other is to compile the wasm binary into an object file. To build out the former, just build wamrc as normal, see [wamrc-compiler/README.md](../../wamr-compiler/README.md). To build out the latter, the `WAMR_BUILD_DEBUG_AOT` flag must be added to cmake, please refer to the first two steps in [doc/source_debugging_aot.md](../../doc/source_debugging_aot.md), and if you encounter the error “‘eLanguageTypeC17’ not declared in this scope”, you can bypass it by commenting out the case judgments. This will not affect the debugging results. + +## 4. Dynamic aot debugging and verification across various platforms + +You can adjust the compiler options for different architectures and instruction sets. + +### 4.1 Linux + +#### Compile test.c to test.wasm + +```bash +/opt/wasi-sdk/bin/clang -O0 -g -gdwarf-2 -o test.wasm test.c +``` + +#### Compile test.wasm to test.aot + +```bash +./wamrc --opt-level=0 -o test.aot test.wasm +``` + +#### Compile test.wasm to test object file + +> Note: please use the version wamrc which was built with `cmake -DWAMR_BUILD_DEBUG_AOT` flag. + +```bash +./wamrc --opt-level=0 --format=object -o test.obj test.wasm +``` + +#### Launch the program using gdbserver on the remote linux host + +```bash +cd ~/aot_debug # This directory contains iwasm and test.aot +gdbserver hostip:port ./iwasm test.aot +``` + +#### Local remote debugging + +```bash +expport OBJ_PATH=~/aot_debug +cd ~/aot_debug # This directory contains iwasm, test.c, test obj file and dynamic_aot_debug.py +gdb ./iwasm +(gdb) target remote hostip:port +(gdb) source dynamic_aot_debug.py +(gdb) c +(gdb) b test.c:main +(gdb) n +``` + +### 4.2 ARMv7 + +#### Compile test.c to test.wasm + +```bash +/opt/wasi-sdk/bin/clang -O0 -nostdlib -z stack-size=8192 -Wl,--initial-memory=65536 +-g -gdwarf-2 -o test.wasm test.c -Wl,--export=main -Wl,--export=__main_argc_argv +-Wl,--export=__heap_base -Wl,--export=__data_end -Wl,--no-entry -Wl,--allow-undefined +``` + +#### Compile test.wasm to test.aot + +```bash +./wamrc --opt-level=0 --target=thumbv7 --target-abi=gnueabihf --cpu=cortex-a7 +--cpu-features=-neon -o test.aot test.wasm +``` + +#### Compile test.wasm to test object file + +> Note: please use the version wamrc which was built with `cmake -DWAMR_BUILD_DEBUG_AOT` flag. + +```bash +./wamrc --opt-level=0 --format=object --target=thumbv7 --target-abi=gnueabihf +--cpu=cortex-a7 --cpu-features=-neon -o test.obj test.wasm +``` + +#### Start Emulator + +In Terminal 1, start the emulator in debug mode and launch the GDB server: + +```bash +# start emulator on debug mode, and will start gdb server, set port as 1234 +./emulator.sh vela -qemu -S -s +ap> iwasm test.aot +``` + +#### Start NuttX Using GDB + +In Terminal 2, set the path to your object file and start NuttX with GDB: + +```bash +# You can save test.obj file in this path +export OBJ_PATH=~/work/data/aot_debug +gdb-multiarch nuttx -ex "tar remote:1234" -ex "source dynamic_aot_debug.py" +``` + +In the GDB prompt: + +```bash +(gdb) c +(gdb) b test.c:main +(gdb) n +``` + +## 5. Workflow + +Refer to the workflow diagram (wasm-micro-runtime/test-tools/dynamic-aot-debug) for an overview of the debugging process. In addition, the implementation of this dynamic aot debugging solution is not complete yet. It only supports breakpoints and single-step execution, and it is not yet known to view detailed information such as variables. diff --git a/test-tools/dynamic-aot-debug/dynamic_aot_debug.py b/test-tools/dynamic-aot-debug/dynamic_aot_debug.py new file mode 100644 index 000000000..1548954f4 --- /dev/null +++ b/test-tools/dynamic-aot-debug/dynamic_aot_debug.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 XiaoMi Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +import gdb + +# Get object file path from environment variable or use default value +path_objs = os.getenv("OBJ_PATH", "~/objects/") + +# Expand user directory symbol (~) +path_objs = os.path.expanduser(path_objs) +print(f"Object files will be loaded from: {path_objs} on localhost") + + +def add_symbol_with_aot_info(aot_module_info): + """Add symbol file with AOT information to GDB and list current breakpoints.""" + try: + text_addr = aot_module_info.get("code") + file_name = aot_module_info.get("name") + + if not text_addr or not file_name: + print("Error: 'code' or 'name' missing in AOT module info.") + return + + # Extract base file name without extension + file_name_without_extension, _ = os.path.splitext(file_name) + + # Remove directory part if present + file_name = os.path.basename(file_name_without_extension) + + # Add .obj extension to the file name + file_name = file_name + ".obj" + + # Construct the path for the symbol file + path_symfile = os.path.join(path_objs, file_name) + + # Construct the command to add the symbol file + cmd = f"add-symbol-file {path_symfile} {text_addr}" + gdb.execute(cmd) + + # Print current breakpoints + breakpoints = gdb.execute("info breakpoints", to_string=True) + print("Current breakpoints:", breakpoints) + + except gdb.error as e: + print(f"GDB error: {e}") + except Exception as e: + print(f"Unexpected error: {e}") + + +class ReadGDynamicAotModule(gdb.Command): + """Command to read the g_dynamic_aot_module structure and extract information.""" + + def __init__(self): + super(self.__class__, self).__init__("read_gda", gdb.COMMAND_USER) + + def invoke(self, args, from_tty): + """Retrieve and process the g_dynamic_aot_module structure.""" + try: + aot_module = gdb.parse_and_eval("g_dynamic_aot_module") + aot_module_info = {} + + # Ensure aot_module is a pointer and dereference it + if aot_module.type.code == gdb.TYPE_CODE_PTR: + aot_module = aot_module.dereference() + + # Check if it's a structure type + if aot_module.type.strip_typedefs().code == gdb.TYPE_CODE_STRUCT: + for field in aot_module.type.fields(): + field_name = field.name + var = aot_module[field_name] + + if field_name == "name": + aot_module_info["name"] = var.string() + elif field_name == "code": + aot_module_info["code"] = str(var) + + if "name" in aot_module_info and "code" in aot_module_info: + add_symbol_with_aot_info(aot_module_info) + else: + print("Could not find 'name' or 'code' in Aot_module.") + else: + print("Aot_module is not of struct type.") + else: + print("Aot_module is not a pointer type.") + except gdb.error as e: + print(f"An error occurred: {e}") + + +def init(): + """Initialize environment and set up debugger.""" + # Register the command to gdb + ReadGDynamicAotModule() + + # Set a breakpoint at function __enable_dynamic_aot_debug + breakpoint = gdb.Breakpoint("__enable_dynamic_aot_debug") + # Attach the self-defined command to the created breakpoint, read_gda means read global dynamic aot info. + breakpoint.commands = "read_gda" + + +init() diff --git a/test-tools/dynamic-aot-debug/dynamic_aot_debug_workflow.svg b/test-tools/dynamic-aot-debug/dynamic_aot_debug_workflow.svg new file mode 100644 index 000000000..fc81cb87a --- /dev/null +++ b/test-tools/dynamic-aot-debug/dynamic_aot_debug_workflow.svg @@ -0,0 +1,17 @@ + + + + + + + + load python scriptPCrun python initset breakpoint $1 on __enable_dynamic_aot_debugRegister "read_gda" command into gdbBind readl_gda to breakpoint $1trigger breakpointexcute "read_gda" commandnoyesRead and parse code_addr and module name from g_dynamic_aot_moduleGet the file name, text segment address of the dynamically aot loaded moduleExecute add-symbol-file test -s text 0x408f10f8 to load the debugging information fileiwasm test.aotset breakpoint $2, b test.aot main contiuewasm_runtime_set_module_name__enable_dynamic_aot_debug_ptrtrigger breakpoint $1gdb continue ?run nuttxGDB run nuttx andconnect to gdb servertrigger breakpoint $2debug test.aot programnowaityes12nuttxexitcall load_aot_module_destroytest/obj filetest.wasmwamrccall aot_unloadWASM_ENABLE_DYNAMIC_AOT_DEBUG=1test.cwasi-SDKWAMR_BUILD_DEBUG_AOT=1aot_set_module_name \ No newline at end of file