From 6a95189cfdf9a4703ab4a9d29300e9ca023b2de9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 30 Dec 2024 16:33:03 +0000 Subject: [PATCH] c-api: Enable debugger DWARF export for guest code (#9915) * c-api: Enable debugger DWARF export for guest code In order to allow source level debugging of hosted webassembly code in C-based embeddings, we must enable the debug-builtins cargo feature when building the C api. * c-api test: assert that the debug info is created Previously we were testing and confirming only that the options can be set using the API, not that they actually have any effect. In order to (simply) confirm that enabling debug information actually created some debug information, we can somewhat hackily inspect the generated GDB JIT descriptor to see that it's populated. --- crates/c-api/Cargo.toml | 1 + crates/c-api/artifact/Cargo.toml | 2 + crates/c-api/build.rs | 1 + crates/c-api/cmake/features.cmake | 1 + crates/c-api/include/wasmtime/conf.h.in | 1 + examples/CMakeLists.txt | 2 + examples/fib-debug/main.c | 53 +++++++++++++++++++++++++ 7 files changed, 61 insertions(+) diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 84b7d45ede1e..d77312f42788 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -56,5 +56,6 @@ gc-drc = ["wasmtime/gc-drc"] gc-null = ["wasmtime/gc-null"] cranelift = ['wasmtime/cranelift'] winch = ['wasmtime/winch'] +debug-builtins = ['wasmtime/debug-builtins'] # ... if you add a line above this be sure to change the other locations # marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/artifact/Cargo.toml b/crates/c-api/artifact/Cargo.toml index 1eb6659ec01b..8aa846d23344 100644 --- a/crates/c-api/artifact/Cargo.toml +++ b/crates/c-api/artifact/Cargo.toml @@ -38,6 +38,7 @@ default = [ 'gc-null', 'cranelift', 'winch', + 'debug-builtins', # ... if you add a line above this be sure to change the other locations # marked WASMTIME_FEATURE_LIST ] @@ -58,5 +59,6 @@ gc-drc = ["wasmtime-c-api/gc-drc"] gc-null = ["wasmtime-c-api/gc-null"] cranelift = ["wasmtime-c-api/cranelift"] winch = ["wasmtime-c-api/winch"] +debug-builtins = ["wasmtime-c-api/debug-builtins"] # ... if you add a line above this be sure to read the comment at the end of # `default` diff --git a/crates/c-api/build.rs b/crates/c-api/build.rs index b500fd1bf4d1..12bc0b5016e8 100644 --- a/crates/c-api/build.rs +++ b/crates/c-api/build.rs @@ -19,6 +19,7 @@ const FEATURES: &[&str] = &[ "GC_NULL", "CRANELIFT", "WINCH", + "DEBUG_BUILTINS", ]; // ... if you add a line above this be sure to change the other locations // marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/cmake/features.cmake b/crates/c-api/cmake/features.cmake index 385c139659e1..727412a0433c 100644 --- a/crates/c-api/cmake/features.cmake +++ b/crates/c-api/cmake/features.cmake @@ -43,5 +43,6 @@ feature(gc-null ON) feature(async ON) feature(cranelift ON) feature(winch ON) +feature(debug-builtins ON) # ... if you add a line above this be sure to change the other locations # marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/include/wasmtime/conf.h.in b/crates/c-api/include/wasmtime/conf.h.in index 3dff987acc4f..03f4debf92d1 100644 --- a/crates/c-api/include/wasmtime/conf.h.in +++ b/crates/c-api/include/wasmtime/conf.h.in @@ -25,6 +25,7 @@ #cmakedefine WASMTIME_FEATURE_ASYNC #cmakedefine WASMTIME_FEATURE_CRANELIFT #cmakedefine WASMTIME_FEATURE_WINCH +#cmakedefine WASMTIME_FEATURE_DEBUG_BUILTINS // ... if you add a line above this be sure to change the other locations // marked WASMTIME_FEATURE_LIST diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 84a02e46fb56..660edf07532a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,8 @@ function(CREATE_TARGET TARGET TARGET_PATH) target_compile_options(wasmtime-${TARGET} PRIVATE /W3) endif() + target_compile_definitions(wasmtime-${TARGET} PRIVATE WASMTIME_TEST_ONLY) + set_target_properties(wasmtime-${TARGET} PROPERTIES OUTPUT_NAME wasmtime-${TARGET} RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/$<0:> diff --git a/examples/fib-debug/main.c b/examples/fib-debug/main.c index 8633fb030129..0d901caffa2a 100644 --- a/examples/fib-debug/main.c +++ b/examples/fib-debug/main.c @@ -5,6 +5,41 @@ #include #include +#ifdef WASMTIME_TEST_ONLY +// These are the declarations provided from GDB documentation, used to validate +// that we actually added some DWARF info: +// https://sourceware.org/gdb/current/onlinedocs/gdb.html/Declarations.html#Declarations +// +// NOTE: These are not required in your code, rather they are used for wasmtime +// testing only. +typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} jit_actions_t; + +struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; +}; + +struct jit_descriptor { + uint32_t version; + /* This type should be jit_actions_t, but we use uint32_t + to be explicit about the bitwidth. */ + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +/* + * Import the descriptor, defined elsewhere in wasmtime + */ +extern struct jit_descriptor __jit_debug_descriptor; +#endif + #define own static void exit_with_error(const char *message, wasmtime_error_t *error, @@ -24,6 +59,15 @@ int main(int argc, const char *argv[]) { wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); wasmtime_context_t *context = wasmtime_store_context(store); +#ifdef WASMTIME_TEST_ONLY + // NOTE: This validation is for wasmtime testing and should not be included in + // your code. + if (__jit_debug_descriptor.first_entry != NULL) { + fprintf(stderr, "FAIL: JIT descriptor is already initialized\n"); + return 1; + } +#endif + // Load binary. printf("Loading binary...\n"); FILE *file = fopen("target/wasm32-unknown-unknown/debug/fib.wasm", "rb"); @@ -60,6 +104,15 @@ int main(int argc, const char *argv[]) { exit_with_error("failed to instantiate", error, trap); wasmtime_module_delete(module); +#ifdef WASMTIME_TEST_ONLY + // NOTE: This validation is for wasmtime testing and should not be included in + // your code. + if (__jit_debug_descriptor.first_entry == NULL) { + fprintf(stderr, "FAIL: JIT descriptor is NOT initialized\n"); + return 1; + } +#endif + // Extract export. wasmtime_extern_t fib; bool ok = wasmtime_instance_export_get(context, &instance, "fib", 3, &fib);