C Debugging Techniques
Table of Contents
1 backtrace(3)
#include <stdio.h> #include <execinfo.h> #include <stdlib.h> void handler(char *caller) { void *array[10]; size_t size; printf("Stack trace start for %s\n", caller); size = backtrace(array, 10); backtrace_symbols_fd(array, size, 2); printf("Stack Trace End\n"); } void car() { handler("char()"); printf("continue"); } void baz() {car();} void bar() { baz();} void foo() {bar();} int main(int argc, char **argv) { foo(); }
compile:
gcc -g -rdynamic a.c
Output:
Stack trace start for char() ./b.out(handler+0x33)[0x400969] ./b.out(car+0xe)[0x4009a2] ./b.out(baz+0xe)[0x4009c1] ./b.out(bar+0xe)[0x4009d1] ./b.out(foo+0xe)[0x4009e1] ./b.out(main+0x19)[0x4009fc] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7ffff7a52b45] ./b.out[0x400869] Stack Trace End continue
2 -finstrument-funcitons
See gcc options.
Generate instrumentation calls for entry and exit to functions. Just after function entry and just before function exit, the following profiling functions are called with the address of the current function and its call site. (On some platforms, _builtinreturnaddress does not work beyond the current function, so the call site information may not be available to the profiling functions otherwise.) void _cygprofilefuncenter (void *thisfn, void *callsite); void _cygprofilefuncexit (void *thisfn, void *callsite);
trace.c:
#include <stdio.h> #include <time.h> static FILE *fp_trace; void __attribute__ ((constructor)) trace_begin (void) { fp_trace = fopen("trace.out", "w"); } void __attribute__ ((destructor)) trace_end (void) { if (fp_trace != NULL) { fclose(fp_trace); } } void __cyg_profile_func_enter (void *func, void *caller) { if (fp_trace != NULL) { fprintf(fp_trace, "e %p %p %lu\n", func, caller, time(NULL)); } } void __cyg_profile_func_exit(void *func, void *caller) { if (fp_trace != NULL) { fprintf(fp_trace, "x %p %p %lu\n", func, caller, time(NULL)); } }
a.c:
#include <stdio.h> #include <string.h> void foo() { printf("foo\n"); } int main() { foo(); }
Compile:
gcc -finstrument-functions -g -c -o a.o a.c gcc -c -o trace.o trace.c gcc a.o trace.o -o a.out ./a.out
The trace.out
will be generated:
e 0x4006c8 0x7ffff7a52b45 1470343711 e 0x400696 0x4006e7 1470343711 x 0x400696 0x4006e7 1470343711 x 0x4006c8 0x7ffff7a52b45 1470343711
For those addrees:
nm a.out | grep 4006c8 addr2line -f -e a.out 0x4006c8 | head -1 # function name addr2line -s -e a.out 0x4006c8 # filename:linum
3 Callgrind
valgrind --tool=callgrind ./a.out
Open another terminal and callgrindcontrol -b to see the trace. The program must be running.