2013-04-24 105 views
3

我正在嘗試執行以下操作 - 爲pthreads庫編寫一個封裝器,該封裝器將在每次調用其每個API時記錄一些信息。 我想記錄的一條信息是堆棧跟蹤。在Linux x86上調用backtrace()時發生分段錯誤

以下是原始代碼中的最小片段,可以按原樣編譯和運行。

初始化(文件libmutex.c):

#include <execinfo.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <dlfcn.h> 

static int (*real_mutex_lock)(pthread_mutex_t *) __attribute__((__may_alias__)); 
static void *pthread_libhandle; 

#ifdef _BIT64 
#define PTHREAD_PATH  "/lib64/libpthread.so.0" 
#else 
#define PTHREAD_PATH  "/lib/libpthread.so.0" 
#endif 

static inline void load_real_function(char* function_name, void** real_func) { 
    char* msg; 
    *(void**) (real_func) = dlsym(pthread_libhandle, function_name); 
    msg = dlerror(); 
    if (msg != NULL) 
    printf("init: real_%s load error %s\n", function_name, msg); 
} 

void __attribute__((constructor)) my_init(void) { 
    printf("init: trying to dlopen '%s'\n", PTHREAD_PATH); 
    pthread_libhandle = dlopen(PTHREAD_PATH, RTLD_LAZY); 
    if (pthread_libhandle == NULL) { 
    fprintf(stderr, "%s\n", dlerror()); 
    exit(EXIT_FAILURE); 
    } 
    load_real_function("pthread_mutex_lock", (void**) &real_mutex_lock); 
} 

的包裝和回溯呼叫。 我已經儘可能地從方法切碎了,所以是的,我知道我從來沒有調用原始的pthread_mutex_lock作爲例子。

void my_backtrace(void) { 
    #define SIZE 100 
    void *buffer[SIZE]; 
    int nptrs; 

    nptrs = backtrace(buffer, SIZE); 
    printf("backtrace() returned %d addresses\n", nptrs); 
} 

int pthread_mutex_lock(pthread_mutex_t *mutex) { 
    printf("In pthread_mutex_lock\n"); fflush(stdout); 
    my_backtrace(); 
    return 0; 
} 

爲了驗證這一點,我用這個二進制文件(文件tst_mutex.c):

#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 

int main (int argc, char *argv[]) { 
    pthread_mutex_t x; 

    printf("Before mutex\n"); fflush(stdout); 
    pthread_mutex_lock(&x); 
    printf("after mutex\n");fflush(stdout); 

    return 0; 
} 

下面是這一切的編譯方式:

rm -f *.o *.so tst_mutex 

cc -Wall -D_BIT64 -c -m64 -fPIC libmutex.c 
cc -m64 -o libmutex.so -shared -fPIC -ldl -lpthread libmutex.o 

cc -Wall -m64 tst_mutex.c -o tst_mutex 

和運行

LD_PRELOAD=$(pwd)/libmutex.so ./tst_mutex 

這與崩潰Linux x86上的分段錯誤。 在Linux上PPC一切都完美無瑕。 我已經嘗試了GCC編譯器,GLIBC庫和Linux發行版的幾個版本 - 都失敗了。

輸出是

init: trying to dlopen '/lib64/libpthread.so.0' 
Before mutex 
In pthread_mutex_lock 
In pthread_mutex_lock 
In pthread_mutex_lock 
... 
... 
./run.sh: line 1: 25023 Segmentation fault  LD_PRELOAD=$(pwd)/libmutex.so ./tst_mutex 

提示有一個遞歸在這裏。 我已經看過backtrace()的源代碼 - 有沒有調用鎖定機制。它所做的只是簡單地遍歷棧幀鏈表。 我也有,用objdump檢查庫代碼,但沒有透露任何異常。

這裏發生了什麼? 任何解決方案/解決方法?

哦,也許是最重要的事情。這隻發生在pthread_mutex_lock函數中! 從任何其他重寫的pthread_ *函數打印堆棧工作得很好...

+0

你用RTLD_NOW試過了嗎? – stark 2013-04-24 16:41:58

+0

問題不在於打開pthreads庫。 – 2013-04-24 22:00:18

+0

我試過了你的步驟,但是我得到一個錯誤'./tst_mutex:symbol lookup error:libmutex.so:undefined symbol:dlopen'。我現在不打算排除這個問題,但是也許有一些步驟不能解決問題。 – 2013-04-24 22:36:11

回答

0

這是一個堆棧溢出,由無窮遞歸引起(如@Chris Dodd所述)。 backtrace()函數運行從使用pthread庫編譯且不使用的程序調用的不同系統調用。即使程序沒有明確調用pthread函數。

這是一個使用backtrace()函數並且不使用任何pthread函數的簡單程序。

#include <stdio.h> 
#include <stdlib.h> 
#include <execinfo.h> 

int main(void) 
{ 
void* buffer[100]; 
int num_ret_addr; 

num_ret_addr=backtrace(buffer, 100); 
printf("returned number of addr %d\n", num_ret_addr); 

return 0; 
} 

允許在沒有鏈接到pthread的情況下進行編譯,並使用strace實用程序檢查程序系統調用。輸出中不會出現與互斥體相關的系統調用。

$ gcc -o backtrace_no_thread backtrace.c 
$ strace -o backtrace_no_thread.out backtrace_no_thread 

沒有讓編譯相同的代碼將其鏈接到pthread庫,運行strace的,看看它的輸出。

$ gcc -o backtrace_with_thread backtrace.c -lpthread 
$ strace -o backtrace_with_thread.out backtrace_with_thread 

這一次的輸出包含互斥相關的系統調用(它們的名稱可能依賴於平臺)。這是在X86 Linux機器上獲得的strace輸出文件的一個片段。

futex(0x3240553f80, FUTEX_WAKE_PRIVATE, 2147483647) = 0 
futex(0x324480d350, FUTEX_WAKE_PRIVATE, 2147483647) = 0 
相關問題