From 5ae7098ebab1d15fa903d8888a1a73058e5976ff Mon Sep 17 00:00:00 2001 From: "Kristian H. Kristensen" Date: Tue, 28 Jul 2020 21:24:45 -0700 Subject: [PATCH] gallium/android: Rewrite backtrace helper for android The previous implementation kept a hashtable of a Backtrace object per thread. debug_backtrace_capture is supposed to store a backtrace in the passed in debug_stack_frame array, but instead overwrote the per-thread Backtrace object. This new version works more like the libunwind based capture. We hash the file and symbol names and store pointers in the debug_stack_frame struct. This way debug_backtrace_capture doesn't overwrite previous captures or allocate memory that needs to be freed. Part-of: --- src/util/u_debug_stack.h | 8 +- src/util/u_debug_stack_android.cpp | 116 ++++++++++++++++------------- 2 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/util/u_debug_stack.h b/src/util/u_debug_stack.h index fff41a5a9ea..8a36a66a08e 100644 --- a/src/util/u_debug_stack.h +++ b/src/util/u_debug_stack.h @@ -59,10 +59,12 @@ extern "C" { */ struct debug_stack_frame { -#ifdef HAVE_LIBUNWIND - unw_word_t start_ip; - unsigned int off; +#if defined(HAVE_ANDROID_PLATFORM) || defined(HAVE_LIBUNWIND) const char *procname; + uint64_t start_ip; + unsigned off; + const char *map; + unsigned int map_off; #else const void *function; #endif diff --git a/src/util/u_debug_stack_android.cpp b/src/util/u_debug_stack_android.cpp index 879b0fb2e9d..061707c6790 100644 --- a/src/util/u_debug_stack_android.cpp +++ b/src/util/u_debug_stack_android.cpp @@ -28,84 +28,94 @@ #include "util/hash_table.h" #include "os/os_thread.h" -static hash_table *backtrace_table; +static hash_table *symbol_table; static mtx_t table_mutex = _MTX_INITIALIZER_NP; +static const char * +intern_symbol(const char *symbol) +{ + if (!symbol_table) + symbol_table = _mesa_hash_table_create(NULL, NULL, _mesa_key_string_equal); + + uint32_t hash = _mesa_hash_string(symbol); + hash_entry *entry = + _mesa_hash_table_search_pre_hashed(symbol_table, hash, symbol); + if (!entry) + entry = _mesa_hash_table_insert_pre_hashed(symbol_table, hash, symbol, strdup(symbol)); + + return (const char *) entry->data; +} + void -debug_backtrace_capture(debug_stack_frame *mesa_backtrace, +debug_backtrace_capture(debug_stack_frame *backtrace, unsigned start_frame, unsigned nr_frames) { - hash_entry *backtrace_entry; - Backtrace *backtrace; - pid_t tid = gettid(); + Backtrace *bt; if (!nr_frames) return; - /* We keep an Android Backtrace handler around for each thread */ + bt = Backtrace::Create(BACKTRACE_CURRENT_PROCESS, + BACKTRACE_CURRENT_THREAD); + if (bt == NULL) { + for (unsigned i = 0; i < nr_frames; i++) + backtrace[i].procname = NULL; + return; + } + + /* Add one to exclude this call. Unwind already ignores itself. */ + bt->Unwind(start_frame + 1); + mtx_lock(&table_mutex); - if (!backtrace_table) - backtrace_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer, - _mesa_key_pointer_equal); - - backtrace_entry = _mesa_hash_table_search(backtrace_table, (void*) (uintptr_t)tid); - if (!backtrace_entry) { - backtrace = Backtrace::Create(getpid(), tid); - _mesa_hash_table_insert(backtrace_table, (void*) (uintptr_t)tid, backtrace); - } else { - backtrace = (Backtrace *) backtrace_entry->data; + + for (unsigned i = 0; i < nr_frames; i++) { + const backtrace_frame_data_t* frame = bt->GetFrame(i); + if (frame) { + backtrace[i].procname = intern_symbol(frame->func_name.c_str()); + backtrace[i].start_ip = frame->pc; + backtrace[i].off = frame->func_offset; + backtrace[i].map = intern_symbol(frame->map.Name().c_str()); + backtrace[i].map_off = frame->rel_pc; + } else { + backtrace[i].procname = NULL; + } } + mtx_unlock(&table_mutex); - /* Add one to exclude this call. Unwind already ignores itself. */ - backtrace->Unwind(start_frame + 1); - - /* Store the Backtrace handler in the first mesa frame for reference. - * Unwind will generally return less frames than nr_frames specified - * but we have no good way of storing the real count otherwise. - * The Backtrace handler only stores the results until the next Unwind, - * but that is how u_debug_stack is used anyway. - */ - mesa_backtrace->function = backtrace; + delete bt; } void -debug_backtrace_dump(const debug_stack_frame *mesa_backtrace, +debug_backtrace_dump(const debug_stack_frame *backtrace, unsigned nr_frames) { - Backtrace *backtrace = (Backtrace *) mesa_backtrace->function; - size_t i; - - if (!nr_frames) - return; - - if (nr_frames > backtrace->NumFrames()) - nr_frames = backtrace->NumFrames(); - for (i = 0; i < nr_frames; i++) { - /* There is no prescribed format and this isn't interpreted further, - * so we simply use the default Android format. - */ - const std::string& frame_line = backtrace->FormatFrameData(i); - debug_printf("%s\n", frame_line.c_str()); + for (unsigned i = 0; i < nr_frames; i++) { + if (backtrace[i].procname) + debug_printf( + "%s(+0x%x)\t%012" PRIx64 ": %s+0x%x\n", + backtrace[i].map, + backtrace[i].map_off, + backtrace[i].start_ip, + backtrace[i].procname, + backtrace[i].off); } } void debug_backtrace_print(FILE *f, - const debug_stack_frame *mesa_backtrace, + const debug_stack_frame *backtrace, unsigned nr_frames) { - Backtrace *backtrace = (Backtrace *) mesa_backtrace->function; - size_t i; - - if (!nr_frames) - return; - - if (nr_frames > backtrace->NumFrames()) - nr_frames = backtrace->NumFrames(); - for (i = 0; i < nr_frames; i++) { - const std::string& frame_line = backtrace->FormatFrameData(i); - fprintf(f, "%s\n", frame_line.c_str()); + for (unsigned i = 0; i < nr_frames; i++) { + if (backtrace[i].procname) + fprintf(f, + "%s(+0x%x)\t%012" PRIx64 ": %s+0x%x\n", + backtrace[i].map, + backtrace[i].map_off, + backtrace[i].start_ip, + backtrace[i].procname, + backtrace[i].off); } } -- 2.30.2