From e8965ed5afaa9bc7833cf38474dfc309a7dcd31f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 13 Nov 2024 20:45:51 +0100 Subject: [PATCH] Use libbacktrace for bracktrace generation Signed-off-by: DL6ER --- src/CMakeLists.txt | 15 +++---- src/args.c | 8 ++++ src/main.c | 2 +- src/signals.c | 110 ++++++++++++++++++--------------------------- src/signals.h | 2 +- 5 files changed, 60 insertions(+), 77 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 815273af8..efb177410 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -326,15 +326,12 @@ find_library(LIBUNISTRING NAMES libunistring${LIBRARY_SUFFIX} unistring) target_link_libraries(pihole-FTL rt Threads::Threads ${LIBHOGWEED} ${LIBGMP} ${LIBNETTLE} ${LIBIDN2} ${LIBUNISTRING}) -# for unwinding we need the libunwind library which in turn depends on the lzma -# library. We only enable unwinding support if both libraries are available -find_library(LIBUNWIND NAMES libunwind${CMAKE_STATIC_LIBRARY_SUFFIX} unwind) -find_library(LIBLZMA NAMES liblzma${LIBRARY_SUFFIX} lzma) -if(LIBUNWIND AND LIBLZMA) - message(STATUS "Building FTL with unwinding support: YES") - target_compile_definitions(core PRIVATE USE_UNWIND) - target_compile_definitions(pihole-FTL PRIVATE USE_UNWIND) - target_link_libraries(pihole-FTL ${LIBUNWIND} ${LIBLZMA}) +find_library(LIBBACKTRACE NAMES libbacktrace${LIBRARY_SUFFIX} backtrace) +if(LIBBACKTRACE) + message(STATUS "Building FTL with backtrace support: YES") + target_compile_definitions(core PRIVATE USE_BACKTRACE) + target_compile_definitions(pihole-FTL PRIVATE USE_BACKTRACE) + target_link_libraries(pihole-FTL ${LIBBACKTRACE}) else() message(STATUS "Building FTL with unwinding support: NO") endif() diff --git a/src/args.c b/src/args.c index 70705b0f3..20a14d864 100644 --- a/src/args.c +++ b/src/args.c @@ -505,6 +505,14 @@ void parse_args(int argc, char *argv[]) exit(EXIT_FAILURE); } + if(argc == 2 && strcmp(argv[1], "backtrace") == 0) + { + // Enable stdout printing + cli_mode = true; + generate_backtrace(); + exit(EXIT_SUCCESS); + } + // IDN2 conversion mode if(argc > 1 && strcmp(argv[1], "idn2") == 0) { diff --git a/src/main.c b/src/main.c index 50af5c4da..b89857ae6 100644 --- a/src/main.c +++ b/src/main.c @@ -41,7 +41,7 @@ int main (int argc, char *argv[]) timer_start(EXIT_TIMER); // Set binary name - set_bin_name(argv[0]); + init_backtrace(argv[0]); // Initialize locale (needed for libidn) init_locale(); diff --git a/src/signals.c b/src/signals.c index 62ba9163c..5b84e9505 100644 --- a/src/signals.c +++ b/src/signals.c @@ -24,9 +24,9 @@ #include "config/config.h" // For backtrace -#if defined(USE_UNWIND) -#define UNW_LOCAL_ONLY -#include +#if defined(USE_BACKTRACE) +#include +#include #include #elif defined(__GLIBC__) #include @@ -53,14 +53,28 @@ const char * const thread_names[THREADS_MAX] = { "ntp-server6", }; -static void *get_base_addr(void); +//static void *get_base_addr(void); -void set_bin_name(const char *name) +#if defined(USE_BACKTRACE) +struct backtrace_state *state = NULL; + +static void error_callback_create(void *data, const char *msg, int errnum) +{ + (void)data; + fprintf(stderr, "libbacktrace: %s (%d, %s)", msg, errnum, strerror (errnum)); +} +#endif + +void init_backtrace(const char *name) { strncpy(bin_name, name, sizeof(bin_name)-1); bin_name[sizeof(bin_name)-1] = '\0'; - get_base_addr(); +// get_base_addr(); + +#if defined(USE_BACKTRACE) + state = backtrace_create_state(name, BACKTRACE_SUPPORTS_THREADS, error_callback_create, NULL); +#endif } // Return the (null-terminated) name of the calling thread @@ -71,7 +85,7 @@ static char * __attribute__ ((nonnull (1))) getthread_name(char buffer[16]) return buffer; } -#if defined(USE_UNWIND) || defined(__GLIBC__) +#if !defined(USE_BACKTRACE) && defined(__GLIBC__) static void print_addr2line(const char *symbol, const void *addr) { // Only do this analysis for our own binary (skip trying to analyse libc.so, etc.) @@ -168,71 +182,35 @@ static void *get_base_addr(void) #endif */ -void generate_backtrace(void) { -#ifdef USE_UNWIND - unw_cursor_t cursor; unw_context_t uc; - unw_word_t ip, sp; - - log_info(" "); - - // Get the base address of the main executable - log_info("Generating backtrace (unwinding, base address: %p)...", base_addr); +#ifdef USE_BACKTRACE +static void error_callback(void *data, const char *msg, int errnum) +{ + (void)data; + log_err("libbacktrace: %s (%d, %s)", msg, errnum, strerror(errnum)); +} - // Get unwind context - unw_getcontext(&uc); - unw_init_local(&cursor, &uc); +static int backtrace_callback(void *data, uintptr_t pc, + const char *filename, int lineno, + const char *function) +{ + (void)data; + log_info(" %s:%d -> %s() [%p]", filename, lineno, function, (void*)pc); + return 0; +} +#endif - // Skip the first frame (this function) - unw_step(&cursor); +void generate_backtrace(void) +{ +#ifdef USE_BACKTRACE - // Iterate over the stack frames - unsigned int i = 1; - do - { - // Get the program counter - unw_get_reg(&cursor, UNW_REG_IP, &ip); - // Get the stack pointer - unw_get_reg(&cursor, UNW_REG_SP, &sp); - - // Get the name of the shared object - char sname[256]; - unw_word_t offset; - int ret = unw_get_proc_name(&cursor, sname, sizeof(sname), &offset); - if (ret && ret != -UNW_ENOMEM) - { - if (ret != -UNW_EUNSPEC && ret != -UNW_ENOINFO) - log_err(" * unw_get_proc_name: %s [%d]", unw_strerror(ret), ret); - strcpy(sname, "?"); - } + log_info(" "); - // Get the procedure information - unw_proc_info_t pip; - ret = unw_get_proc_info(&cursor, &pip); - if (ret) - { - log_err("unw_get_proc_info: %s [%d]", unw_strerror(ret), ret); - continue; - } + // Get the base address of the main executable + log_info("Generating backtrace (base address: %p)...", base_addr); - // Get the file name of defining object (binary/library name, - // fname_dl) and the name of the nearest symbol (sname_dl) - void *ptr = (void *)(pip.start_ip + offset); - Dl_info dlinfo; - const char *fname_dl = bin_name, *sname_dl = sname; - if (dladdr(ptr, &dlinfo)) - { - if(dlinfo.dli_fname && *dlinfo.dli_fname) - fname_dl = dlinfo.dli_fname; - if(dlinfo.dli_sname && *dlinfo.dli_sname) - sname_dl = dlinfo.dli_sname; - } + // Print backtrace + backtrace_full(state, 1, backtrace_callback, error_callback, NULL); - // Print this stack frame's details - log_info(" %02u: %s(%s+%p) [%p / %p]", i++, fname_dl, sname_dl, (void*)offset, (void*)ip, ptr); - print_addr2line(fname_dl, ptr); - print_addr2line(fname_dl, (void*)ip); - log_info(" "); - } while(unw_step(&cursor) > 0); #elif defined(__GLIBC__) // Try to obtain backtrace. This may not always be helpful, but it is better than nothing void *buffer[255]; diff --git a/src/signals.h b/src/signals.h index 927a601f1..0b25f14ae 100644 --- a/src/signals.h +++ b/src/signals.h @@ -20,7 +20,7 @@ void handle_realtime_signals(void); pid_t main_pid(void); void thread_sleepms(const enum thread_types thread, const int milliseconds); void generate_backtrace(void); -void set_bin_name(const char *name); +void init_backtrace(const char *name); int sigtest(void); void restart_ftl(const char *reason);