Prefer libunwind to libbacktrace
This commit is contained in:
parent
ec5bda6ddf
commit
1c2a12eb05
4 changed files with 314 additions and 48 deletions
20
configure
vendored
20
configure
vendored
|
@ -368,14 +368,28 @@ except:
|
||||||
print("No startup notification support.")
|
print("No startup notification support.")
|
||||||
|
|
||||||
# Add library dependencies detected with using successful compilation test
|
# Add library dependencies detected with using successful compilation test
|
||||||
|
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>
|
if check_c_compiles([CC, '-lbacktrace'], '''#include <backtrace.h>
|
||||||
int main() {
|
int main() {
|
||||||
return 0;
|
return 0;
|
||||||
}'''):
|
}'''):
|
||||||
CFLAGS += ['-DHAS_BACKTRACE']
|
CFLAGS += ['-DHAS_BACKTRACE']
|
||||||
LIBS += ['-lbacktrace']
|
LIBS += ['-lbacktrace']
|
||||||
|
bt = True
|
||||||
|
print("Backtrace support via libbacktrace.")
|
||||||
else:
|
else:
|
||||||
print("No libbacktrace support.")
|
print("No backtrace support via libbacktrace.")
|
||||||
|
|
||||||
# Add option-dependent flags
|
# Add option-dependent flags
|
||||||
if not args.debug:
|
if not args.debug:
|
||||||
|
@ -434,6 +448,7 @@ tint2.sources = ['src/config.c',
|
||||||
'src/separator/separator.c',
|
'src/separator/separator.c',
|
||||||
'src/tint2rc.c',
|
'src/tint2rc.c',
|
||||||
'src/util/area.c',
|
'src/util/area.c',
|
||||||
|
'src/util/bt.c',
|
||||||
'src/util/common.c',
|
'src/util/common.c',
|
||||||
'src/util/fps_distribution.c',
|
'src/util/fps_distribution.c',
|
||||||
'src/util/strnatcmp.c',
|
'src/util/strnatcmp.c',
|
||||||
|
@ -497,7 +512,8 @@ tint2conf.cflags += ['-DTINT2CONF',
|
||||||
'-DGETTEXT_PACKAGE=\\"tint2conf\\"',
|
'-DGETTEXT_PACKAGE=\\"tint2conf\\"',
|
||||||
'-DHAVE_VERSION_H']
|
'-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/strnatcmp.c',
|
||||||
'src/util/cache.c',
|
'src/util/cache.c',
|
||||||
'src/util/timer.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>
|
#include <librsvg/rsvg.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_LIBUNWIND
|
|
||||||
#define UNW_LOCAL_ONLY
|
|
||||||
#include <libunwind.h>
|
|
||||||
#else
|
|
||||||
#ifdef ENABLE_EXECINFO
|
|
||||||
#include <execinfo.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../panel.h"
|
#include "../panel.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "signals.h"
|
#include "signals.h"
|
||||||
|
#include "bt.h"
|
||||||
|
|
||||||
void write_string(int fd, const char *s)
|
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)
|
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");
|
log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n");
|
||||||
|
for (size_t i = 0; i < bt.frame_count; i++) {
|
||||||
#ifdef ENABLE_LIBUNWIND
|
log_string(log_fd, bt.frames[i].name);
|
||||||
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);
|
|
||||||
log_string(log_fd, "\n");
|
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.
|
// sleep() returns early when signals arrive. This function does not.
|
||||||
|
|
Loading…
Reference in a new issue