在Linux 64位上从Signal Handler进行回溯,并在调用堆栈上使用malloc/free [英] Backtracing on Linux 64 bit from Signal Handler with malloc/free on callstack

查看:398
本文介绍了在Linux 64位上从Signal Handler进行回溯,并在调用堆栈上使用malloc/free的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是我想在运行"Red Hat Enterprise Linux 5.5(Tikanga)内核2.6.18-194.el5xen x86_64" OS的计算机上使用的源示例.

Below is an example of source I want to use on a machine running "Red Hat Enterprise Linux 5.5 (Tikanga) Kernel 2.6.18-194.el5xen x86_64" OS.

一般的想法是我想要某个线程的回溯,因此我正在为该线程引发SIGUSR1信号,并且处理程序进行backtrace()调用.

The general idea is that I want to have backtrace of some thread, so I am raising a SIGUSR1 signal for that thread and a handler does a backtrace() call.

在以下情况下,FrameTwo函数在循环中调用malloc和free.每当为此特定线程发出信号并且free或malloc在调用堆栈上时,当信号处理程序调用backtrace()时,程序就会崩溃.

In my scenario as below, FrameTwo function calls malloc and free in a loop. Whenever the signal is raised for this particular thread and free or malloc is on the callstack, the progream crashes when the signal handler calls backtrace().

(gdb) where (stack from gdb)
0  0x0000003e67207638 in ?? () 
1  0x0000003e672088bb in _Unwind_Backtrace
2  0x00000037ba0e5fa8 in backtrace () 
3  0x000000000040071a in handler ()
4  <signal handler called>
5  0x00000037ba071fac in _int_free () 
6  0x0000000a33605000 in ?? ()
7  0x000000004123b130 in ?? ()
8  0x00000000004007d4 in ThreadFunction ()
9  0x000000001f039020 in ?? ()
10 0x000000004123b940 in ?? ()
11 0x0000000000000001 in ?? ()
12 0x0000000000000000 in ?? ()

我从其他来源获悉,不应从信号处理程序中调用backtrace,因此我为这种情况编写了自己的函数grok_and_print_thread_stack().

I learned from other sources that backtrace shouldn't be called from a signal handler, so I have written my own function grok_and_print_thread_stack() for this case.

它使用RBP寄存器导航堆栈(RBP包含当前帧的基址指针指向前一帧的基址指针),但是这种算法在这种情况下也不起作用:当_int_free()在调用堆栈上时,RBP寄存器导航算法就中断了,因为_int_free的RBP是某个值,例如0x20,它不是有效帧的基本指针.

It uses the RBP register to navigate the stack (RBP contains the base pointer of the current frame points to the previous frame's base pointer), but this algorithm does not work in this case either: when _int_free () is on the callstack, the RBP register navigation algorithm breaks, because the RBP of _int_free is some value like 0x20 which is not a valid frame's base pointer.

有人知道如何从寄存器中导航调用堆栈吗?或者我该如何使用backtrace呢?

#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "signal.h"
#include "syscall.h"
#include "string.h"
#include "inttypes.h"

//####################################################################

//gcc BacktraceTestProgram.c -o backtracetest -lpthread
//./backtracetest
//gdb -c core backtracetest

//####################################################################
volatile sig_atomic_t flag = 1;
int thlist[6] = {0};
int cnt = 0;
int *memory = NULL;

//####################################################################

void raiseUserSignal(int tid)
{
    union sigval value;
    value.sival_int = 1;
    sigqueue(tid,SIGUSR1, value);
}

//####################################################################

int grok_and_print_thread_stack()
{
    int ret = 0;
    register uint64_t* rbp asm("rbp");
    /*if buffer was built before, add separator */
    uint64_t *previous_bp;

    /*save pointers*/
    previous_bp = rbp;

    /* stack Traversal */
    while(previous_bp)
    {
        uint64_t *next_bp;

        next_bp = (uint64_t*)*previous_bp;
        printf("Read BP: %lx \n", next_bp);

        if ( NULL == (void*)next_bp )
        {
            printf("Reached the top of the stack\n");
            fflush(stdout);
            break;
        }

        previous_bp = next_bp;
    }
    return ret;
}

//####################################################################

void handler(int signum, siginfo_t *info, void *context)
{

    int nptrs = 0 ;
    void *buffer[100] = {NULL};
    char **strings = NULL;

    nptrs = backtrace(buffer, 100);

    flag = 1;
}

//####################################################################

void FrameTwo(const char A)
{
    do{
        if( memory == NULL)
            memory = (int *)malloc(sizeof(int) *5);

        if(memory != NULL) {
            free(memory);
            memory = NULL;
        }
    }while(1);
}

//####################################################################

void FrameOne(int no)
{
    FrameTwo('A');
}

//####################################################################

void *ThreadFunction( void *ptr )
{
    int tid = syscall(SYS_gettid);
    thlist[cnt++] = tid;

    FrameOne(10);
}

//####################################################################

void RegisterSignalHandler()
{
    /* Register a Signal Handler */
    struct sigaction usrsig_action;
    usrsig_action.sa_flags = SA_SIGINFO;
    usrsig_action.sa_sigaction = &handler;
    sigaction (SIGUSR1, &usrsig_action, NULL);
}

//####################################################################

int main(int no , char *argc[] )
{
    int iret1;
    pthread_t thread1;
    RegisterSignalHandler();

    /* Create independent threads each of which will execute function */
    iret1 = pthread_create( &thread1, NULL, ThreadFunction, NULL);

    while(cnt == 0);

    while(1) {
        if(flag == 1){
            flag = 0;
            raiseUserSignal(thlist[0]);
        }
    }

    pthread_join( thread1, NULL);
    return 0;
}

推荐答案

通常,x86_64程序可能是使用-fomit-frame-pointer构建的,因为它是启用优化后的默认设置.

In general x86_64 programs are likely to have been built with -fomit-frame-pointer as it is the default when optimisation is on.

这意味着RBP不可用于展开堆栈,您将需要使用DWARF展开信息(如果有调试信息)或异常展开表.

What that means is that RBP is not usable for unwinding the stack and you will either need to use the DWARF unwind information (if you have debugging information available) or the exception unwind table.

这篇关于在Linux 64位上从Signal Handler进行回溯,并在调用堆栈上使用malloc/free的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆