Vous avez mentionné sur mon autre réponse (aujourd'hui supprimée) que vous aussi vous voulez voir les numéros de ligne. Je ne suis pas sûr de la façon de le faire lors de l'invocation de gdb à partir de l'intérieur de votre application.
Mais je vais partager avec vous quelques-unes des façons d'imprimer une simple stacktrace avec les noms de fonction et de leurs numéros de ligne sans l'utilisation de gdb. La plupart d'entre eux sont venus à partir d'un très bel article de Linux Journal:
La première méthode consiste à diffuser
avec d'imprimer et d'enregistrer les messages dans l'ordre
pour repérer le chemin d'exécution. Dans un
programme complexe, cette option peut
devenu lourd et fastidieux, même si,
avec l'aide de GCC spécifiques
les macros, il peut être simplifié un peu.
Considérons, par exemple, une version de débogage de macro
tels que:
#define TRACE_MSG fprintf(stderr, __FUNCTION__ \
"() [%s:%d] here I am\n", \
__FILE__, __LINE__)
Vous pouvez propager cette macro rapidement
tout au long de votre programme par la coupe et
le collant. Lorsque vous n'en avez pas besoin
de plus, l'éteindre, tout simplement par
la définition n'-op.
-
Méthode #2: (Il ne dit rien sur les numéros de ligne, mais je n'sur la méthode 4)
Une jolie manière à obtenir un stack trace,
toutefois, pour utiliser certains des
soutien spécifique aux fonctions fournies par
la glibc. L'une des clés est le backtrace(),
qui navigue dans la pile des images à partir de
l'appel de point pour le début de
le programme et fournit un tableau de
des adresses de retour. Ensuite, vous pouvez mapper
chaque adresse dans le corps d'un
fonction spécifique dans votre code par
avoir un coup d'oeil au fichier objet avec
le nm de commande. Ou, vous pouvez faire une
plus simple d'utilisation backtrace_symbols().
Cette fonction transforme une liste de
des adresses de retour, tel que retourné par la
backtrace(), dans une liste de chaînes de caractères,
chaque contenant le nom de la fonction
décalage de la fonction et de la
adresse de retour. La liste des chaînes est
allouées à partir de votre espace de tas (comme si
vous avez appelé la fonction malloc()), de sorte que vous devrait
free() dès que vous avez terminé avec
c'.
Je vous encourage à le lire depuis la page a le code source des exemples. Afin de convertir une adresse sur un nom de fonction, vous devez compiler votre application avec l' -rdynamic option.
-
Méthode n ° 3: (Une meilleure façon de faire la méthode 2)
Encore plus utiles de l'application pour
cette technique est de mettre une pile
trace à l'intérieur d'un gestionnaire de signal et
avoir le dernier attraper tous les "mauvais"
les signaux de votre programme peut recevoir
(SIGSEGV, SIGBUS, SIGILL, SIGFPE et
la comme). De cette façon, si votre programme
malheureusement, les accidents et vous n'étiez pas
l'exécution avec un débogueur, vous pouvez
obtenir une trace de la pile et de savoir où l'
la faute qui s'est passé. Cette technique a également
peut être utilisé pour comprendre où votre
le programme est une boucle dans le cas où il s'arrête
répondre
Une mise en œuvre de cette technique est disponible ici.
Une petite amélioration, je l'ai fait sur la méthode #3 pour imprimer les numéros de ligne. Cela pourrait être copié à travailler sur la méthode #2 aussi.
En gros, j'ai suivi une astuce qui utilise addr2line à
convertir les adresses en noms de fichiers et de
les numéros de ligne.
Le code source ci-dessous imprime les numéros de ligne pour toutes les fonctions. Si une fonction à partir d'une autre bibliothèque est appelée, vous pouvez voir un couple de ??:0
au lieu des noms de fichier.
#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]);
char syscom[256];
sprintf(syscom,"addr2line %p -e sighandler", trace[i]); //last parameter is the name of this app
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());
}
Ce code doit être compilé comme: gcc sighandler.c -o sighandler -rdynamic
Le programme des sorties:
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
Mise à jour 2012/04/28 pour les dernières versions du noyau linux, le ci-dessus sigaction
signature est obsolète. Aussi j'ai amélioré un peu en saisissant le nom de l'exécutable à partir de cette réponse. Voici une version à jour:
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#92;\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:#92;\n");
for (i=1; i<trace_size; ++i)
{
printf("[bt] %s#92;\n", messages[i]);
char syscom[256];
sprintf(syscom,"addr2line %p -e %s", trace[i] , getExecutableName() ); //last parameter is the name of this app
system(syscom);
}
exit(0);
}
et initialiser comme ceci:
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#92;n", func_b());
}