Prefer libunwind to libbacktrace
This commit is contained in:
parent
ec5bda6ddf
commit
1c2a12eb05
4 changed files with 314 additions and 48 deletions
34
configure
vendored
34
configure
vendored
|
@ -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 <backtrace.h>
|
||||
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 <backtrace.h>
|
||||
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',
|
||||
|
|
264
src/util/bt.c
Normal file
264
src/util/bt.c
Normal file
|
@ -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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 <backtrace.h>
|
||||
|
||||
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 <libunwind.h>
|
||||
|
||||
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 <execinfo.h>
|
||||
|
||||
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
|
20
src/util/bt.h
Normal file
20
src/util/bt.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef BT_H
|
||||
#define BT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#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
|
|
@ -48,18 +48,10 @@
|
|||
#include <librsvg/rsvg.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#include <execinfo.h>
|
||||
#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.
|
||||
|
|
Loading…
Reference in a new issue