Вы упомянули в моем другом ответе (теперь удаленном), что вы также хотите видеть номера строк. Я не уверен, как это сделать при вызове gdb из вашего приложения.
Но я собираюсь поделиться с вами несколькими способами напечатать простую трассировку стека с именами функций и соответствующими номерами строк без использования gdb . Большинство из них пришло из очень хорошей статьи из Linux Journal :
Первый способ - распространить его
с распечаткой и записью сообщений в порядке
точно определить путь выполнения. В
сложная программа, эта опция может
стать громоздким и утомительным, даже если,
с помощью некоторых GCC-специфичных
макросы, это можно немного упростить.
Рассмотрим, например, макрос отладки
такие как:
#define TRACE_MSG fprintf(stderr, __FUNCTION__ \
"() [%s:%d] here I am\n", \
__FILE__, __LINE__)
Вы можете быстро распространять этот макрос
на протяжении всей вашей программы путем резки и
вставлять это Когда тебе это не нужно
больше, просто выключите его
определяя его как неоперативный.
- Метод № 2: (Он ничего не говорит о номерах строк, но я говорю о методе 4)
Хороший способ получить трассировку стека,
Тем не менее, это использовать некоторые из
конкретные функции поддержки, предоставляемые
Glibc. Ключевым является backtrace (),
который перемещается по кадрам стека из
вызывающая точка к началу
программа и предоставляет массив
обратные адреса. Затем вы можете сопоставить
каждый адрес тела
конкретная функция в вашем коде
глядя на объектный файл с
команда нм. Или вы можете сделать это
более простой способ - используйте backtrace_symbols ().
Эта функция преобразует список
обратные адреса, возвращаемые
backtrace (), в список строк,
каждый из которых содержит имя функции
смещение в функции и
обратный адрес. Список строк
выделено из вашей кучи пространства (как будто
вы вызвали malloc ()), так что вы должны
free () это как только вы закончите с
это.
Я рекомендую вам прочитать его, поскольку на странице есть исходный код примеров. Чтобы преобразовать адрес в имя функции, вы должны скомпилировать приложение с опцией -rdynamic .
- Метод № 3: (лучший способ выполнения метода 2)
Еще более полезное приложение для
эта техника ставит стек
обратная трассировка внутри обработчика сигнала и
если последние поймают всех «плохих»
сигналы, которые ваша программа может получить
(SIGSEGV, SIGBUS, SIGILL, SIGFPE и
подобное, аналогичное, похожее). Таким образом, если ваша программа
к сожалению вылетает и тебя не было
запустив его с помощью отладчика, вы можете
получить трассировку стека и знать, где
ошибка произошла Эта техника также
может быть использован, чтобы понять, где ваш
программа зацикливается на случай остановки
отвечая
Реализация этой техники доступна здесь .
Небольшое улучшение в методе № 3 для печати номеров строк. Это можно было бы скопировать и для работы с методом № 2.
В основном, я следовал совету , который использует addr2line до
конвертировать адреса в имена файлов и
номера строк.
Исходный код ниже печатает номера строк для всех локальных функций. Если вызывается функция из другой библиотеки, вы можете увидеть пару ??:0
вместо имен файлов.
#include <stdio.h>
#include <signal.h>
#include <stdio.h>
#include <signal.h>
#include <execinfo.h>
void bt_sighandler(int sig, struct sigcontext ctx) {
void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;
if (sig == SIGSEGV)
printf("Got signal %d, faulty address is %p, "
"from %p\n", sig, ctx.cr2, ctx.eip);
else
printf("Got signal %d\n", sig);
trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
trace[1] = (void *)ctx.eip;
messages = backtrace_symbols(trace, trace_size);
/* skip first stack frame (points here) */
printf("[bt] Execution path:\n");
for (i=1; i<trace_size; ++i)
{
printf("[bt] #%d %s\n", i, messages[i]);
/* find first occurence of '(' or ' ' in message[i] and assume
* everything before that is the file name. (Don't go beyond 0 though
* (string terminator)*/
size_t p = 0;
while(messages[i][p] != '(' && messages[i][p] != ' '
&& messages[i][p] != 0)
++p;
char syscom[256];
sprintf(syscom,"addr2line %p -e %.*s", trace[i], p, messages[i]);
//last parameter is the file name of the symbol
system(syscom);
}
exit(0);
}
int func_a(int a, char b) {
char *p = (char *)0xdeadbeef;
a = a + b;
*p = 10; /* CRASH here!! */
return 2*a;
}
int func_b() {
int res, a = 5;
res = 5 + func_a(a, 't');
return res;
}
int main() {
/* Install our signal handler */
struct sigaction sa;
sa.sa_handler = (void *)bt_sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
/* ... add any other signal here */
/* Do something */
printf("%d\n", func_b());
}
Этот код должен быть скомпилирован как: gcc sighandler.c -o sighandler -rdynamic
Программа выводит:
Got signal 11, faulty address is 0xdeadbeef, from 0x8048975
[bt] Execution path:
[bt] #1 ./sighandler(func_a+0x1d) [0x8048975]
/home/karl/workspace/stacktrace/sighandler.c:44
[bt] #2 ./sighandler(func_b+0x20) [0x804899f]
/home/karl/workspace/stacktrace/sighandler.c:54
[bt] #3 ./sighandler(main+0x6c) [0x8048a16]
/home/karl/workspace/stacktrace/sighandler.c:74
[bt] #4 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x3fdbd6]
??:0
[bt] #5 ./sighandler() [0x8048781]
??:0
Обновление 2012/04/28 для последних версий ядра Linux, указанная выше sigaction
подпись устарела. Также я немного улучшил его, взяв имя исполняемого файла из этот ответ . Вот актуальная версия :
char* exe = 0;
int initialiseExecutableName()
{
char link[1024];
exe = new char[1024];
snprintf(link,sizeof link,"/proc/%d/exe",getpid());
if(readlink(link,exe,sizeof link)==-1) {
fprintf(stderr,"ERRORRRRR\n");
exit(1);
}
printf("Executable name initialised: %s\n",exe);
}
const char* getExecutableName()
{
if (exe == 0)
initialiseExecutableName();
return exe;
}
/* get REG_EIP from ucontext.h */
#define __USE_GNU
#include <ucontext.h>
void bt_sighandler(int sig, siginfo_t *info,
void *secret) {
void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t *)secret;
/* Do something useful with siginfo_t */
if (sig == SIGSEGV)
printf("Got signal %d, faulty address is %p, "
"from %p\n", sig, info->si_addr,
uc->uc_mcontext.gregs[REG_EIP]);
else
printf("Got signal %d\n", sig);
trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
messages = backtrace_symbols(trace, trace_size);
/* skip first stack frame (points here) */
printf("[bt] Execution path:\n");
for (i=1; i<trace_size; ++i)
{
printf("[bt] %s\n", messages[i]);
/* find first occurence of '(' or ' ' in message[i] and assume
* everything before that is the file name. (Don't go beyond 0 though
* (string terminator)*/
size_t p = 0;
while(messages[i][p] != '(' && messages[i][p] != ' '
&& messages[i][p] != 0)
++p;
char syscom[256];
sprintf(syscom,"addr2line %p -e %.*s", trace[i] , p, messages[i] );
//last parameter is the filename of the symbol
system(syscom);
}
exit(0);
}
и инициализировать так:
int main() {
/* Install our signal handler */
struct sigaction sa;
sa.sa_sigaction = (void *)bt_sighandler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
/* ... add any other signal here */
/* Do something */
printf("%d\n", func_b());
}