251 lines
5.0 KiB
C
251 lines
5.0 KiB
C
/*
|
|
* Copyright (C) 2006-2008 Martin Willi
|
|
* Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#ifdef HAVE_DLADDR
|
|
# include <dlfcn.h>
|
|
#endif /* HAVE_DLADDR */
|
|
|
|
#ifdef HAVE_BACKTRACE
|
|
# include <execinfo.h>
|
|
#endif /* HAVE_BACKTRACE */
|
|
|
|
#include <string.h>
|
|
|
|
#include "backtrace.h"
|
|
|
|
typedef struct private_backtrace_t private_backtrace_t;
|
|
|
|
/**
|
|
* Private data of an backtrace_t object.
|
|
*/
|
|
struct private_backtrace_t {
|
|
|
|
/**
|
|
* Public backtrace_t interface.
|
|
*/
|
|
backtrace_t public;
|
|
|
|
/**
|
|
* Number of stacks frames obtained in stack_frames
|
|
*/
|
|
int frame_count;
|
|
|
|
/**
|
|
* Recorded stack frames.
|
|
*/
|
|
void *frames[];
|
|
};
|
|
|
|
METHOD(backtrace_t, log_, void,
|
|
private_backtrace_t *this, FILE *file, bool detailed)
|
|
{
|
|
#ifdef HAVE_BACKTRACE
|
|
size_t i;
|
|
char **strings;
|
|
|
|
strings = backtrace_symbols(this->frames, this->frame_count);
|
|
|
|
fprintf(file, " dumping %d stack frame addresses:\n", this->frame_count);
|
|
for (i = 0; i < this->frame_count; i++)
|
|
{
|
|
#ifdef HAVE_DLADDR
|
|
Dl_info info;
|
|
|
|
if (dladdr(this->frames[i], &info))
|
|
{
|
|
char cmd[1024];
|
|
FILE *output;
|
|
int c;
|
|
void *ptr = this->frames[i];
|
|
|
|
if (strstr(info.dli_fname, ".so"))
|
|
{
|
|
ptr = (void*)(this->frames[i] - info.dli_fbase);
|
|
}
|
|
if (info.dli_sname)
|
|
{
|
|
fprintf(file, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%tx) [%p]\n",
|
|
info.dli_fname, info.dli_fbase, info.dli_sname,
|
|
this->frames[i] - info.dli_saddr, this->frames[i]);
|
|
}
|
|
else
|
|
{
|
|
fprintf(file, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname,
|
|
info.dli_fbase, this->frames[i]);
|
|
}
|
|
if (detailed)
|
|
{
|
|
fprintf(file, " -> \e[32m");
|
|
snprintf(cmd, sizeof(cmd), "addr2line -e %s %p",
|
|
info.dli_fname, ptr);
|
|
output = popen(cmd, "r");
|
|
if (output)
|
|
{
|
|
while (TRUE)
|
|
{
|
|
c = getc(output);
|
|
if (c == '\n' || c == EOF)
|
|
{
|
|
break;
|
|
}
|
|
fputc(c, file);
|
|
}
|
|
pclose(output);
|
|
}
|
|
else
|
|
{
|
|
#endif /* HAVE_DLADDR */
|
|
fprintf(file, " %s\n", strings[i]);
|
|
#ifdef HAVE_DLADDR
|
|
}
|
|
fprintf(file, "\n\e[0m");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(file, " %s\n", strings[i]);
|
|
}
|
|
#endif /* HAVE_DLADDR */
|
|
}
|
|
free (strings);
|
|
#else /* !HAVE_BACKTRACE */
|
|
fprintf(file, "C library does not support backtrace().\n");
|
|
#endif /* HAVE_BACKTRACE */
|
|
}
|
|
|
|
METHOD(backtrace_t, contains_function, bool,
|
|
private_backtrace_t *this, char *function[], int count)
|
|
{
|
|
#ifdef HAVE_DLADDR
|
|
int i, j;
|
|
|
|
for (i = 0; i< this->frame_count; i++)
|
|
{
|
|
Dl_info info;
|
|
|
|
if (dladdr(this->frames[i], &info) && info.dli_sname)
|
|
{
|
|
for (j = 0; j < count; j++)
|
|
{
|
|
if (streq(info.dli_sname, function[j]))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_DLADDR */
|
|
return FALSE;
|
|
}
|
|
|
|
METHOD(backtrace_t, equals, bool,
|
|
private_backtrace_t *this, backtrace_t *other_public)
|
|
{
|
|
private_backtrace_t *other = (private_backtrace_t*)other_public;
|
|
int i;
|
|
|
|
if (this == other)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (this->frame_count != other->frame_count)
|
|
{
|
|
return FALSE;
|
|
}
|
|
for (i = 0; i < this->frame_count; i++)
|
|
{
|
|
if (this->frames[i] != other->frames[i])
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Frame enumerator
|
|
*/
|
|
typedef struct {
|
|
/** implements enumerator_t */
|
|
enumerator_t public;
|
|
/** reference to backtrace */
|
|
private_backtrace_t *bt;
|
|
/** current position */
|
|
int i;
|
|
} frame_enumerator_t;
|
|
|
|
METHOD(enumerator_t, frame_enumerate, bool,
|
|
frame_enumerator_t *this, void **addr)
|
|
{
|
|
if (this->i < this->bt->frame_count)
|
|
{
|
|
*addr = this->bt->frames[this->i++];
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
|
|
private_backtrace_t *this)
|
|
{
|
|
frame_enumerator_t *enumerator;
|
|
|
|
INIT(enumerator,
|
|
.public = {
|
|
.enumerate = (void*)_frame_enumerate,
|
|
.destroy = (void*)free,
|
|
},
|
|
.bt = this,
|
|
);
|
|
return &enumerator->public;
|
|
}
|
|
|
|
METHOD(backtrace_t, destroy, void,
|
|
private_backtrace_t *this)
|
|
{
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
backtrace_t *backtrace_create(int skip)
|
|
{
|
|
private_backtrace_t *this;
|
|
void *frames[50];
|
|
int frame_count = 0;
|
|
|
|
#ifdef HAVE_BACKTRACE
|
|
frame_count = backtrace(frames, countof(frames));
|
|
#endif /* HAVE_BACKTRACE */
|
|
frame_count = max(frame_count - skip, 0);
|
|
this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
|
|
memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
|
|
this->frame_count = frame_count;
|
|
|
|
this->public = (backtrace_t) {
|
|
.log = _log_,
|
|
.contains_function = _contains_function,
|
|
.equals = _equals,
|
|
.create_frame_enumerator = _create_frame_enumerator,
|
|
.destroy = _destroy,
|
|
};
|
|
|
|
return &this->public;
|
|
}
|
|
|