From 1c2a12eb053553e9039faf2e9729d5d3c85b86dd Mon Sep 17 00:00:00 2001 From: Chris Lee <@klee93> Date: Thu, 31 May 2018 06:34:02 +0000 Subject: [PATCH] Prefer libunwind to libbacktrace --- configure | 34 ++++-- src/util/bt.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/bt.h | 20 ++++ src/util/common.c | 44 +------- 4 files changed, 314 insertions(+), 48 deletions(-) create mode 100644 src/util/bt.c create mode 100644 src/util/bt.h diff --git a/configure b/configure index f08d469..6899f7e 100755 --- a/configure +++ b/configure @@ -368,14 +368,28 @@ except: print("No startup notification support.") # Add library dependencies detected with using successful compilation test -if check_c_compiles([CC, '-lbacktrace'], '''#include - int main() { - return 0; - }'''): - CFLAGS += ['-DHAS_BACKTRACE'] - LIBS += ['-lbacktrace'] -else: - print("No libbacktrace support.") +bt = False +if not bt: + try: + lib, cf, lf = pkg_config('libunwind') + LIBS += lib + CFLAGS += cf + ['-DHAS_LIBUNWIND'] + LFLAGS += lf + bt = True + print("Backtrace support via libunwind.") + except: + print("No backtrace support via libunwind.") +if not bt: + if check_c_compiles([CC, '-lbacktrace'], '''#include + int main() { + return 0; + }'''): + CFLAGS += ['-DHAS_BACKTRACE'] + LIBS += ['-lbacktrace'] + bt = True + print("Backtrace support via libbacktrace.") + else: + print("No backtrace support via libbacktrace.") # Add option-dependent flags if not args.debug: @@ -434,6 +448,7 @@ tint2.sources = ['src/config.c', 'src/separator/separator.c', 'src/tint2rc.c', 'src/util/area.c', + 'src/util/bt.c', 'src/util/common.c', 'src/util/fps_distribution.c', 'src/util/strnatcmp.c', @@ -497,7 +512,8 @@ tint2conf.cflags += ['-DTINT2CONF', '-DGETTEXT_PACKAGE=\\"tint2conf\\"', '-DHAVE_VERSION_H'] -tint2conf.sources = ['src/util/common.c', +tint2conf.sources = ['src/util/bt.c', + 'src/util/common.c', 'src/util/strnatcmp.c', 'src/util/cache.c', 'src/util/timer.c', diff --git a/src/util/bt.c b/src/util/bt.c new file mode 100644 index 0000000..c750080 --- /dev/null +++ b/src/util/bt.c @@ -0,0 +1,264 @@ +/************************************************************************** +* +* Tint2 : backtrace +* +* Copyright (C) 2007 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "bt.h" +#include "bool.h" + +static void bt_add_frame(struct backtrace *bt, const char *fname) +{ + if (bt->frame_count >= BT_MAX_FRAMES) + return; + struct backtrace_frame *frame = &bt->frames[bt->frame_count]; + if (fname && *fname) { + strncpy(frame->name, fname, BT_FRAME_SIZE); + } else { + strncpy(frame->name, "??", BT_FRAME_SIZE); + } + bt->frame_count++; +} + +#ifdef HAS_BACKTRACE + +#include + +static const char *get_exe() +{ + static char buf[256] = {0}; + ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf)-1); + if (ret > 0) + return buf; + ret = readlink("/proc/curproc/file", buf, sizeof(buf)-1); + if (ret > 0) + return buf; + return buf; +} + +static void bt_error_callback(void *data, const char *msg, int errnum) +{ +} + +static int bt_full_callback(void *data, uintptr_t pc, + const char *filename, int lineno, + const char *function) +{ + struct backtrace *bt = (struct backtrace *)data; + bt_add_frame(bt, function); + return 0; +} + +void get_backtrace(struct backtrace *bt, int skip) +{ + static struct backtrace_state *state = NULL; + + if (!state) { + const char *exe = get_exe(); + if (exe) + state = backtrace_create_state(exe, 1, bt_error_callback, NULL); + } + bzero(bt, sizeof(*bt)); + if (state) { + backtrace_full(state, skip + 1, bt_full_callback, bt_error_callback, bt); + } +} + +#elif HAS_LIBUNWIND + +#define UNW_LOCAL_ONLY +#include + +struct bt_mapping { + unw_word_t ip; + char fname[BT_FRAME_SIZE]; +}; + +#define BT_BUCKET_SIZE 3 +struct bt_bucket { + struct bt_mapping mappings[BT_BUCKET_SIZE]; +}; + +struct bt_cache { + struct bt_bucket *buckets; + size_t count; + size_t size; +}; + +static unsigned oat_hash(void *key, int len) +{ + unsigned char *p = key; + unsigned h = 0; + int i; + + for (i = 0; i < len; i++) + { + h += p[i]; + h += (h << 10); + h ^= (h >> 6); + } + + h += (h << 3); + h ^= (h >> 11); + h += (h << 15); + + return h; +} + +static struct bt_bucket *cached_proc_name_bucket(struct bt_cache *cache, unw_word_t ip) +{ + if (!cache->size) + return NULL; + unsigned h = oat_hash(&ip, sizeof(ip)); + return &cache->buckets[h % cache->size]; +} + +static void bt_cache_init(struct bt_cache *cache) +{ + if (!cache->size) { + cache->size = 119; + cache->buckets = calloc(cache->size, sizeof(*cache->buckets)); + if (!cache->buckets) + cache->size = 0; + cache->count = 0; + } +} + +static void cached_proc_name_store(struct bt_cache *cache, unw_word_t ip, const char *fname); + +static void bt_cache_rebalance(struct bt_cache *cache) +{ + struct bt_cache bigger = {}; + bigger.size = cache->size * 2 + 1337; + bigger.buckets = calloc(bigger.size, sizeof(*bigger.buckets)); + bigger.count = 0; + for (size_t b = 0; b < cache->size; b++) { + for (size_t i = 0; i < BT_BUCKET_SIZE; i++) { + struct bt_mapping *map = &cache->buckets[b].mappings[i]; + if (map->ip) { + cached_proc_name_store(&bigger, map->ip, map->fname); + } + } + } + free(cache->buckets); + *cache = bigger; +} + +static void cached_proc_name_store(struct bt_cache *cache, unw_word_t ip, const char *fname) +{ + bt_cache_init(cache); + if (!cache->size) + return; + struct bt_bucket *bucket = cached_proc_name_bucket(cache, ip); + bool stored = false; + for (size_t i = 0; i < BT_BUCKET_SIZE; i++) { + if (bucket->mappings[i].ip == ip) + return; + if (bucket->mappings[i].ip != 0) + continue; + bucket->mappings[i].ip = ip; + strncpy(bucket->mappings[i].fname, fname, sizeof(bucket->mappings[i].fname)); + cache->count++; + stored = true; + break; + } + if (cache->count > cache->size / 4 || !stored) { + bt_cache_rebalance(cache); + fprintf(stderr, "tint2: proc_name cache: ratio %f, count %lu, size %lu, (%lu bytes)\n", + cache->count / (double)cache->size, + cache->count, (size_t)cache->size, cache->size * sizeof(*cache->buckets)); + } +} + +const char *cached_proc_name_get(struct bt_cache *cache, unw_word_t ip) +{ + struct bt_bucket *bucket = cached_proc_name_bucket(cache, ip); + if (!bucket) + return NULL; + for (size_t i = 0; i < BT_BUCKET_SIZE; i++) { + if (bucket->mappings[i].ip != ip) + continue; + return bucket->mappings[i].fname; + } + return NULL; +} + +void get_backtrace(struct backtrace *bt, int skip) +{ + static struct bt_cache bt_cache = {}; + bzero(bt, sizeof(*bt)); + + unw_cursor_t cursor; + unw_context_t context; + unw_getcontext(&context); + unw_init_local(&cursor, &context); + + while (unw_step(&cursor) > 0) { + if (skip > 0) { + skip--; + continue; + } + unw_word_t offset; + char fname[BT_FRAME_SIZE] = {0}; + unw_word_t ip; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + const char *fname_cached = cached_proc_name_get(&bt_cache, ip); + if (fname_cached) { + bt_add_frame(bt, fname_cached); + } else { + unw_get_proc_name(&cursor, fname, sizeof(fname), &offset); + cached_proc_name_store(&bt_cache, ip, fname); + bt_add_frame(bt, fname); + } + } +} + +#elif HAS_EXECINFO + +#include + +void get_backtrace(struct backtrace *bt, int skip) +{ + bzero(bt, sizeof(*bt)); + + void *array[BT_MAX_FRAMES]; + int size = backtrace(array, BT_MAX_FRAMES); + char **strings = backtrace_symbols(array, size); + + for (int i = 0; i < size; i++) { + bt_add_frame(bt, strings[i]); + } + + free(strings); +} + + +#else + +void get_backtrace(struct backtrace *bt, int skip) +{ + bzero(bt, sizeof(*bt)); +} + +#endif diff --git a/src/util/bt.h b/src/util/bt.h new file mode 100644 index 0000000..ce02d23 --- /dev/null +++ b/src/util/bt.h @@ -0,0 +1,20 @@ +#ifndef BT_H +#define BT_H + +#include + +#define BT_FRAME_SIZE 64 +#define BT_MAX_FRAMES 64 + +struct backtrace_frame { + char name[BT_FRAME_SIZE]; +}; + +struct backtrace { + struct backtrace_frame frames[BT_MAX_FRAMES]; + size_t frame_count; +}; + +void get_backtrace(struct backtrace *bt, int skip); + +#endif // BT_H diff --git a/src/util/common.c b/src/util/common.c index 7b38650..032b433 100644 --- a/src/util/common.c +++ b/src/util/common.c @@ -48,18 +48,10 @@ #include #endif -#ifdef ENABLE_LIBUNWIND -#define UNW_LOCAL_ONLY -#include -#else -#ifdef ENABLE_EXECINFO -#include -#endif -#endif - #include "../panel.h" #include "timer.h" #include "signals.h" +#include "bt.h" void write_string(int fd, const char *s) { @@ -83,39 +75,13 @@ void log_string(int fd, const char *s) void dump_backtrace(int log_fd) { -#ifndef DISABLE_BACKTRACE + struct backtrace bt; + get_backtrace(&bt, 1); log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n"); - -#ifdef ENABLE_LIBUNWIND - unw_cursor_t cursor; - unw_context_t context; - unw_getcontext(&context); - unw_init_local(&cursor, &context); - - while (unw_step(&cursor) > 0) { - unw_word_t offset; - char fname[128]; - fname[0] = '\0'; - (void)unw_get_proc_name(&cursor, fname, sizeof(fname), &offset); - log_string(log_fd, fname); + for (size_t i = 0; i < bt.frame_count; i++) { + log_string(log_fd, bt.frames[i].name); log_string(log_fd, "\n"); } -#else -#ifdef ENABLE_EXECINFO -#define MAX_TRACE_SIZE 128 - void *array[MAX_TRACE_SIZE]; - size_t size = backtrace(array, MAX_TRACE_SIZE); - char **strings = backtrace_symbols(array, size); - - for (size_t i = 0; i < size; i++) { - log_string(log_fd, strings[i]); - log_string(log_fd, "\n"); - } - - free(strings); -#endif // ENABLE_EXECINFO -#endif // ENABLE_LIBUNWIND -#endif // DISABLE_BACKTRACE } // sleep() returns early when signals arrive. This function does not.