2011-07-14 5 views
3

以下は、「Red Hat Enterprise Linux 5.5(Tikanga)Kernel 2.6.18-194.el5xen x86_64」を実行しているマシンで使用したいソースの例です。 OS。Linux上でのバックトレース64ビットシグナルハンドラからmalloc/free on callstack

一般的な考え方は、あるスレッドのバックトレースが必要なので、そのスレッドのSIGUSR1信号を発生させ、ハンドラはbacktrace()呼び出しを行います。

以下のような私のシナリオでは、FrameTwo関数はmallocを呼び出し、ループでフリーになります。シグナルがこの特定のスレッドに対して呼び出され、freeまたはmallocがコールスタック上にあるときは、シグナルハンドラがbacktrace()を呼び出すとprogreamがクラッシュします。

(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() callstackでは、_int_freeのRBPが0x20のような有効なフレームのベースポインタではない値であるため、RBPレジスタのナビゲーションアルゴリズムが中断します。

どのようにコールスタックをレジスタからナビゲートできるか知っていますか?または、私の目的にバックトレースを使用するにはどうすればいいですか?一般x86_64のプログラムで

#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; 
} 
+0

IIRCシグナルハンドラには独自のスタックがあるため、プログラムのメインスタックは表示されず、シグナルハンドラのメインスタックは表示されません。 – DarkDust

+0

@DarkDust私はシナリオのほとんどでメインスタックを見ることができます..実際には一般的にはクラッシュ解析の人クラッシュのためのシグナルハンドラを実装し、メインスタックを見るためにそのハンドラからコールバックトレース()を提案する.. – sandeep

答えて

0

最適化がオンになっているとき、それはデフォルトであるようを使用して構築されている可能性があります。

つまり、RBPはスタックの巻き戻しには使用できません。また、DWARF巻き戻し情報(デバッグ情報がある場合)または例外巻き戻しテーブルを使用する必要があります。

+0

ほとんどのシナリオでRBP(コールスタックのフレームポインタレジスタ)を使用したこのアルゴリズムは、フリーとmallocのような非常に少ないシナリオで動作し、失敗します...私のスタックからフリーとmallocを削除すると..この実装は動作しています..実行中またはクラッシュ時にのみ使用可能な "例外巻き戻しテーブル"ですか? DWARF巻き戻し情報はどのように使用できますか?任意の参照? – sandeep

+0

あなたのコードはフレームポインタでビルドされているかもしれませんが、(mallocやfreeのような)システムライブラリルーチンはまだありません。だから、RBPを使ったバックトレースはあなたのコードではうまくいくが、あなたがCライブラリから追い出されているときは動かない。 – TomH

0

libunwindプロジェクトをご覧ください。

[libunwind]の第1の目的は、移植性の高い効率的なCプログラミングインターフェイス(API)を定義して、プログラムのコールチェーンを決定することです。 [...]このように、APIは多くのアプリケーションで有用です。いくつかの例が含まれます:

  • デバッガ
    libunwindのAPIは、それは些細なデバッガは、実行中のプログラム/特に

内のスレッドのコールチェーン(バックトレース)を生成できるようになり、持っていますthe local unwinding section of their documentationを見ると、説明と、現在の関数のバックトレースを出力する次のコード例(-lunwindとリンクする必要があります)が含まれています。

#define UNW_LOCAL_ONLY 
#include <libunwind.h> 

void show_backtrace (void) { 
    unw_cursor_t cursor; unw_context_t uc; 
    unw_word_t ip, sp; 

    unw_getcontext(&uc); 
    unw_init_local(&cursor, &uc); 
    while (unw_step(&cursor) > 0) { 
    unw_get_reg(&cursor, UNW_REG_IP, &ip); 
    unw_get_reg(&cursor, UNW_REG_SP, &sp); 
    printf ("ip = %lx, sp = %lx\n", (long) ip, (long) sp); 
    } 
}