Debugging: Difference between revisions

From miki
Jump to navigation Jump to search
Line 15: Line 15:
char ** backtrace_symbols (void *const *buffer, int size)
char ** backtrace_symbols (void *const *buffer, int size)
void backtrace_symbols_fd (void *const *buffer, int size, int fd)
void backtrace_symbols_fd (void *const *buffer, int size, int fd)
</source>

Object and executables need to build with '''<tt>gcc -g -rdynamic</tt>'''!
<source lang="bash">
gcc -O0 -g -rdynamic main.c -o myprogram
</source>

A very extensive answer on [http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes StackOverflow]:
* Using <tt>backtrace</tt>:
<source lang="c">
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
void * array[50];
void * caller_address;
char ** messages;
int size, i;
sig_ucontext_t * uc;

uc = (sig_ucontext_t *)ucontext;

/* Get the address at the time the signal was raised from the EIP (x86) */
caller_address = (void *) uc->uc_mcontext.eip;

fprintf(stderr, "signal %d (%s), address is %p from %p\n",
sig_num, strsignal(sig_num), info->si_addr,
(void *)caller_address);

size = backtrace(array, 50);

/* overwrite sigaction with caller's address */
array[1] = caller_address;

messages = backtrace_symbols(array, size);

/* skip first stack frame (points here) */
for (i = 1; i < size && messages != NULL; ++i)
{
fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
}

free(messages);

exit(EXIT_FAILURE);
}

int crash()
{
char * p = NULL;
*p = 0;
return 0;
}

int foo4()
{
crash();
return 0;
}

int foo3()
{
foo4();
return 0;
}

int foo2()
{
foo3();
return 0;
}

int foo1()
{
foo2();
return 0;
}

int main(int argc, char ** argv)
{
struct sigaction sigact;

sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;

if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
{
fprintf(stderr, "error setting signal handler for %d (%s)\n",
SIGSEGV, strsignal(SIGSEGV));

exit(EXIT_FAILURE);
}

foo1();

exit(EXIT_SUCCESS);
}
</source>

* Demangling symbols in C++ by calling <tt>__cxa_demangle</tt> (like is done in tool <tt>c++filt</tt>):
<source lang="cpp">
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

void * caller_address = (void *) uc->uc_mcontext.eip; // x86 specific

std::cerr << "signal " << sig_num
<< " (" << strsignal(sig_num) << "), address is "
<< info->si_addr << " from " << caller_address
<< std::endl << std::endl;

void * array[50];
int size = backtrace(array, 50);

array[1] = caller_address;

char ** messages = backtrace_symbols(array, size);

// skip first stack frame (points here)
for (int i = 1; i < size && messages != NULL; ++i)
{
char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

// find parantheses and +address offset surrounding mangled name
for (char *p = messages[i]; *p; ++p)
{
if (*p == '(')
{
mangled_name = p;
}
else if (*p == '+')
{
offset_begin = p;
}
else if (*p == ')')
{
offset_end = p;
break;
}
}

// if the line could be processed, attempt to demangle the symbol
if (mangled_name && offset_begin && offset_end &&
mangled_name < offset_begin)
{
*mangled_name++ = '\0';
*offset_begin++ = '\0';
*offset_end++ = '\0';

int status;
char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

// if demangling is successful, output the demangled function name
if (status == 0)
{
std::cerr << "[bt]: (" << i << ") " << messages[i] << " : "
<< real_name << "+" << offset_begin << offset_end
<< std::endl;

}
// otherwise, output the mangled function name
else
{
std::cerr << "[bt]: (" << i << ") " << messages[i] << " : "
<< mangled_name << "+" << offset_begin << offset_end
<< std::endl;
}
free(real_name);
}
// otherwise, print the whole line
else
{
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
}
std::cerr << std::endl;

free(messages);

exit(EXIT_FAILURE);
}
</source>
</source>



Revision as of 13:27, 30 September 2011

This is a general page on debugging tools, techniques, tips, etc.

Linux tools

addr2line

See addr2line page.

gdb

See gdb page.

C/C++ - Debugging with gcc

backtrace, backtrace_symbols, backtrace_symbols_fd

See manual page, or gcc manual.

int     backtrace            (void **      buffer, int size)
char ** backtrace_symbols    (void *const *buffer, int size)
void    backtrace_symbols_fd (void *const *buffer, int size, int fd)

Object and executables need to build with gcc -g -rdynamic!

gcc -O0 -g -rdynamic main.c -o myprogram

A very extensive answer on StackOverflow:

  • Using backtrace:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
    unsigned long     uc_flags;
    struct ucontext   *uc_link;
    stack_t           uc_stack;
    struct sigcontext uc_mcontext;
    sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    void *             array[50];
    void *             caller_address;
    char **            messages;
    int                size, i;
    sig_ucontext_t *   uc;

    uc = (sig_ucontext_t *)ucontext;

    /* Get the address at the time the signal was raised from the EIP (x86) */
    caller_address = (void *) uc->uc_mcontext.eip;   

    fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
        sig_num, strsignal(sig_num), info->si_addr, 
        (void *)caller_address);

    size = backtrace(array, 50);

    /* overwrite sigaction with caller's address */
    array[1] = caller_address;

    messages = backtrace_symbols(array, size);

    /* skip first stack frame (points here) */
    for (i = 1; i < size && messages != NULL; ++i)
    {
        fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
    }

    free(messages);

    exit(EXIT_FAILURE);
}

int crash()
{
    char * p = NULL;
    *p = 0;
    return 0;
}

int foo4()
{
    crash();
    return 0;
}

int foo3()
{
    foo4();
    return 0;
}

int foo2()
{
    foo3();
    return 0;
}

int foo1()
{
    foo2();
    return 0;
}

int main(int argc, char ** argv)
{
    struct sigaction sigact;

    sigact.sa_sigaction = crit_err_hdlr;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;

    if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
    {
        fprintf(stderr, "error setting signal handler for %d (%s)\n",
        SIGSEGV, strsignal(SIGSEGV));

        exit(EXIT_FAILURE);
    }

    foo1();

    exit(EXIT_SUCCESS);
}
  • Demangling symbols in C++ by calling __cxa_demangle (like is done in tool c++filt):
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

    void * caller_address = (void *) uc->uc_mcontext.eip; // x86 specific

    std::cerr << "signal " << sig_num 
              << " (" << strsignal(sig_num) << "), address is " 
              << info->si_addr << " from " << caller_address 
              << std::endl << std::endl;

    void * array[50];
    int size = backtrace(array, 50);

    array[1] = caller_address;

    char ** messages = backtrace_symbols(array, size);    

    // skip first stack frame (points here)
    for (int i = 1; i < size && messages != NULL; ++i)
    {
        char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

        // find parantheses and +address offset surrounding mangled name
        for (char *p = messages[i]; *p; ++p)
        {
            if (*p == '(') 
            {
                mangled_name = p; 
            }
            else if (*p == '+') 
            {
                offset_begin = p;
            }
            else if (*p == ')')
            {
                offset_end = p;
                break;
            }
        }

        // if the line could be processed, attempt to demangle the symbol
        if (mangled_name && offset_begin && offset_end && 
            mangled_name < offset_begin)
        {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';

            int status;
            char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

            // if demangling is successful, output the demangled function name
            if (status == 0)
            {    
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << real_name << "+" << offset_begin << offset_end 
                          << std::endl;

            }
            // otherwise, output the mangled function name
            else
            {
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << mangled_name << "+" << offset_begin << offset_end 
                          << std::endl;
            }
            free(real_name);
        }
        // otherwise, print the whole line
        else
        {
            std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
        }
    }
    std::cerr << std::endl;

    free(messages);

    exit(EXIT_FAILURE);
}

libunwind (non-gnu)