4

以下は、「Red Hat Enterprise Linux 5.5 (Tikanga) Kernel 2.6.18-194.el5xen x86_64」OS を実行しているマシンで使用したいソースの例です。

一般的な考え方は、いくつかのスレッドのバックトレースが必要なため、そのスレッドの SIGUSR1 シグナルを発生させ、ハンドラーが backtrace() 呼び出しを行うというものです。

以下の私のシナリオでは、FrameTwo 関数は malloc と free をループで呼び出します。この特定のスレッドに対してシグナルが発生し、free または malloc が呼び出しスタックにある場合は常に、シグナル ハンドラーが 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 ?? ()

バックトレースをシグナルハンドラから呼び出すべきではないことを他のソースから学んだので、この場合のために独自の関数 grok_and_print_thread_stack() を作成しました。

RBP レジスタを使用してスタックをナビゲートします (RBP には、前のフレームのベース ポインタを指す現在のフレームのベース ポインタが含まれています) が、このアルゴリズムはこの場合も機能しません: _int_free () がコールスタックにある場合、RBP _int_free の RBP が有効なフレームのベース ポインターではない 0x20 のような値であるため、レジスタ ナビゲーション アルゴリズムが壊れます。

レジスタからコールスタックをナビゲートする方法を知っている人はいますか? または、バックトレースを目的に使用するにはどうすればよいですか?

#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;
}
4

2 に答える 2

1

一般に、x86_64 プログラムは-fomit-frame-pointer、最適化がオンになっている場合のデフォルトであるため、ビルドされている可能性があります。

つまりRBP、スタックのアンワインドには使用できず、DWARF アンワインド情報 (デバッグ情報が利用可能な場合) または例外アンワインド テーブルを使用する必要があります。

于 2011-07-14T06:40:08.337 に答える