1 Reply Latest reply: Aug 29, 2012 2:19 PM by verpra
verpra Level 1 Level 1 (0 points)

I am trying to use libunwind to crawl stacktraces from within my signal handler. The problem is that the frame with an IP of 0 stops unwinding process.

 

example:

void (*fp)() = NULL;

fp();

 

The testcase is both clang and g++ friendly. You can see from the output of the posted testcase that backtrace does this job.

Can we somehow force libunwind (unw_step) to ignore NULL IP and keep unwinding?

    

Here is the testcase:

 

#define UNW_LOCAL_ONLY

#include <libunwind.h>

#include <iostream>

#include <signal.h>

#include <execinfo.h>

#include <cassert>

   

void gen_segv_bad_ip(){

    printf("\nInside gen_segv_bad_ip --> %p", gen_segv_bad_ip); fflush(stdout);

    int (*fp)(int) = (int (*)(int)) NULL;

   

    //Bad IP!!!

    int g = fp(100);

}

 

void bail(){

    printf("\nInside bail --> %p", bail); fflush(stdout);

    gen_segv_bad_ip();

}

 

void foo3(){ printf("\nInside foo3 --> %p", foo3); fflush(stdout); bail(); }

void foo2(){ printf("\nInside foo2 --> %p", foo2); fflush(stdout); foo3(); }

void foo1(){ printf("\nInside foo1 --> %p", foo1); fflush(stdout); foo2(); }

void foo(){ printf("\nInside foo --> %p", foo); fflush(stdout); foo1(); }

 

/* Modified example given by libunwind */

void show_backtrace (unw_cursor_t *cursor) {

    printf("\n\nInside show_backtrace --> %p\n", show_backtrace);

    fflush(stdout);

 

 

    unw_word_t ip, sp;

    char buf[1000];

    size_t len = sizeof(buf);

    unw_word_t off;

    do {

        unw_get_reg(cursor, UNW_REG_IP, &ip);

        unw_get_reg(cursor, UNW_REG_SP, &sp);

        printf("ip = %llX sp = %llX  ",ip, sp);

        buf[0] = '\0';

        unw_get_proc_name(cursor, buf, len, &off);

        std::cout << "Procedure name returned --> " << buf << std::endl;

    } while (unw_step(cursor) > 0);

    printf("Leaving show_backtrace\n");

}

 

 

void segv_helper(void *uc){

    printf("\nInside segv_helper --> %p\n", segv_helper);

    fflush(stdout);

 

    unw_cursor_t cursor;

    unw_word_t ip, sp;

   

    std::cout << "Getting local context " << std::endl;

    int a = unw_getcontext((unw_context_t*) uc);

    std::cout << "unw_init_local returned    " << unw_init_local(&cursor, (unw_context_t*) uc) << std::endl;

 

     unw_get_reg(&cursor, UNW_REG_IP, &ip);

    unw_get_reg(&cursor, UNW_REG_SP, &sp);

    printf("IP = %llX SP = %llX", ip, sp);

    fflush(stdout);

   

    show_backtrace(&cursor);

 

     printf("Leaving segv_helper\n");

    fflush(stdout); 

}

 

 

void segv_handler (int signum, siginfo_t* info, void *old_context){

   

    //Show stacktrace using backtrace....

    void *buff[300];

    int size = 300, nptrs;

    char **pbuff;

   

    nptrs = backtrace((void**) &buff, size);

    printf("\n\nbacktrace() returned %d addresses\n", nptrs);

    assert(NULL != (pbuff = backtrace_symbols(buff, nptrs)));

    for (int i = 0; i < nptrs; i++) {

        printf ("%s\n", pbuff[i]);

        fflush(stdout);   

    }

    free(pbuff);

 

    printf("\nInside segv_handler --> %p\n", segv_handler);

    fflush(stdout);

 

     segv_helper( old_context );

 

    std::cout << "\n\nBAILING...\n";

    exit(1);

}

 

 

void test_signal_handler(int signum){

    std::cout << "\nInside test_signal_handler. I should not be here...\n";

    signal(SIGSEGV, SIG_DFL);

}

 

 

int main()

{

    //Install signal handler...

    struct sigaction new_sigaction, old_sigaction;

    new_sigaction.sa_sigaction = segv_handler;

    new_sigaction.sa_flags = SA_SIGINFO;

    sigemptyset (&new_sigaction.sa_mask);

    sigaddset(&new_sigaction.sa_mask, SIGSEGV);   

    sigaction (SIGSEGV, NULL, &new_sigaction);

   

     if (old_sigaction.sa_handler != SIG_IGN){

        new_sigaction.sa_handler = test_signal_handler;

        new_sigaction.sa_sigaction = segv_handler;

        sigaction (SIGSEGV, &new_sigaction, &old_sigaction);

    }

 

    printf("\nInside main --> %p", main);

    foo();

 

    return 0;

}


OS X Mountain Lion (10.8)
  • verpra Level 1 Level 1 (0 points)

    Sorry I didn't post the output before. Here it is:

     

    Inside main --> 0x10e50a720

    Inside foo --> 0x10e50a030

    Inside foo1 --> 0x10e509fe0

    Inside foo2 --> 0x10e509f90

    Inside foo3 --> 0x10e509f40

    Inside bail --> 0x10e509ef0

    Inside gen_segv_bad_ip --> 0x10e509e90

     

     

    backtrace() returned 10 addresses

    0   unwind_nullip                       0x000000010e50a509 _Z12segv_handleriP9__siginfoPv + 105

    1   libsystem_c.dylib                   0x00007fff94c87cfa _sigtramp + 26

    2   ???                                 0x00007fff6e140062 0x0 + 140735040192610

    3   unwind_nullip                       0x000000010e509f2f _Z4bailv + 63

    4   unwind_nullip                       0x000000010e509f7f _Z4foo3v + 63

    5   unwind_nullip                       0x000000010e509fcf _Z4foo2v + 63

    6   unwind_nullip                       0x000000010e50a01f _Z4foo1v + 63

    7   unwind_nullip                       0x000000010e50a06f _Z3foov + 63

    8   unwind_nullip                       0x000000010e50a7f4 main + 212

    9   unwind_nullip                       0x000000010e509e84 start + 52

     

     

    Inside segv_handler --> 0x10e50a4a0

     

     

    Inside segv_helper --> 0x10e50a270

    Getting local context

    unw_init_local returned    0

    IP = 10E50A394 SP = 7FFF6E107470

     

     

    Inside show_backtrace --> 0x10e50a080

    ip = 10E50A394 sp = 7FFF6E107470  Procedure name returned --> _Z11segv_helperPv

    ip = 10E50A696 sp = 7FFF6E1079A0  Procedure name returned --> _Z12segv_handleriP9__siginfoPv

    ip = 7FFF94C87CFA sp = 7FFF6E1083C0  Procedure name returned --> _sigtramp

    Leaving show_backtrace

    Leaving segv_helper

     

     

     

     

    BAILING...