Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You mentioned on my other answer (now deleted) that you also want to see line numbers. I'm not sure how to do that when invoking gdb from inside your application.</p> <p>But I'm going to share with you a couple of ways to print a simple stacktrace with function names and their respective line numbers <strong>without using gdb</strong>. Most of them came from a <em>very nice</em> article from <a href="http://www.linuxjournal.com/article/6391" rel="noreferrer">Linux Journal</a>:</p> <ul> <li><strong>Method #1:</strong></li> </ul> <blockquote> <p>The first method is to disseminate it with print and log messages in order to pinpoint the execution path. In a complex program, this option can become cumbersome and tedious even if, with the help of some GCC-specific macros, it can be simplified a bit. Consider, for example, a debug macro such as:</p> </blockquote> <pre><code> #define TRACE_MSG fprintf(stderr, __FUNCTION__ \ "() [%s:%d] here I am\n", \ __FILE__, __LINE__) </code></pre> <blockquote> <p>You can propagate this macro quickly throughout your program by cutting and pasting it. When you do not need it anymore, switch it off simply by defining it to no-op.</p> </blockquote> <ul> <li><strong>Method #2:</strong> (It doesn't say anything about line numbers, but I do on method 4)</li> </ul> <blockquote> <p>A nicer way to get a stack backtrace, however, is to use some of the specific support functions provided by glibc. The key one is backtrace(), which navigates the stack frames from the calling point to the beginning of the program and provides an array of return addresses. You then can map each address to the body of a particular function in your code by having a look at the object file with the nm command. Or, you can do it a simpler way--use backtrace_symbols(). This function transforms a list of return addresses, as returned by backtrace(), into a list of strings, each containing the function name offset within the function and the return address. The list of strings is allocated from your heap space (as if you called malloc()), so you should free() it as soon as you are done with it.</p> </blockquote> <p>I encourage you to read it since the page has <a href="http://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/063/6391/6391l1.html" rel="noreferrer">source code</a> examples. In order to convert an address to a function name you must compile your application with the <strong>-rdynamic</strong> option.</p> <ul> <li><strong>Method #3:</strong> (A better way of doing method 2)</li> </ul> <blockquote> <p>An even more useful application for this technique is putting a stack backtrace inside a signal handler and having the latter catch all the "bad" signals your program can receive (SIGSEGV, SIGBUS, SIGILL, SIGFPE and the like). This way, if your program unfortunately crashes and you were not running it with a debugger, you can get a stack trace and know where the fault happened. This technique also can be used to understand where your program is looping in case it stops responding</p> </blockquote> <p>An implementation of this technique is available <a href="http://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/063/6391/6391l2.html" rel="noreferrer">here</a>.</p> <ul> <li><strong>Method #4:</strong></li> </ul> <p>A small improvement I've done on method #3 to print line numbers. This could be copied to work on method #2 also.</p> <p>Basically, I <a href="http://tlug.up.ac.za/wiki/index.php/Obtaining_a_stack_trace_in_C_upon_SIGSEGV#addr2line" rel="noreferrer">followed a tip</a> that uses <strong>addr2line</strong> to </p> <blockquote> <p>convert addresses into file names and line numbers.</p> </blockquote> <p>The source code below prints line numbers for all local functions. If a function from another library is called, you might see a couple of <code>??:0</code> instead of file names.</p> <pre><code>#include &lt;stdio.h&gt; #include &lt;signal.h&gt; #include &lt;stdio.h&gt; #include &lt;signal.h&gt; #include &lt;execinfo.h&gt; 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&lt;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] != '(' &amp;&amp; messages[i][p] != ' ' &amp;&amp; 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(&amp;sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGSEGV, &amp;sa, NULL); sigaction(SIGUSR1, &amp;sa, NULL); /* ... add any other signal here */ /* Do something */ printf("%d\n", func_b()); } </code></pre> <p>This code should be compiled as: <code>gcc sighandler.c -o sighandler -rdynamic</code></p> <p>The program outputs:</p> <pre><code>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 </code></pre> <hr> <p><strong>Update 2012/04/28</strong> for recent linux kernel versions, the above <code>sigaction</code> signature is obsolete. Also I improved it a bit by grabbing the executable name from <a href="https://stackoverflow.com/a/10365300/170521">this answer</a>. Here is an <a href="http://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/063/6391/6391l3.html" rel="noreferrer">up to date version</a>:</p> <pre><code>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 &lt;ucontext.h&gt; 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-&gt;si_addr, uc-&gt;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-&gt;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&lt;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] != '(' &amp;&amp; messages[i][p] != ' ' &amp;&amp; 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); } </code></pre> <p>and initialise like this:</p> <pre><code>int main() { /* Install our signal handler */ struct sigaction sa; sa.sa_sigaction = (void *)bt_sighandler; sigemptyset (&amp;sa.sa_mask); sa.sa_flags = SA_RESTART | SA_SIGINFO; sigaction(SIGSEGV, &amp;sa, NULL); sigaction(SIGUSR1, &amp;sa, NULL); /* ... add any other signal here */ /* Do something */ printf("%d\n", func_b()); } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload