2011-03-22 20 views
1

我在將堆棧跟蹤輸出轉儲到stderr或轉儲到日誌文件時遇到問題。我使用gcc編譯器(4.4.3)運行Kubuntu10.04中的代碼。問題是,在正常運行模式下(沒有gdb),程序不會輸出除「Segmentation Fault」外的任何內容。我希望按照下面的打印語句輸出回溯輸出。當我運行GDB與我的應用程序,它涉及到的printf/fprintf中/(函數調用)語句,然後用下面的語句崩潰:在gcc的SIGSEGV期間,C++程序不處理任何函數調用或printf

669  { 
(gdb) 
670  printf("Testing for stability.\n"); 
(gdb) 

Program received signal SIGTRAP, Trace/breakpoint trap. 
0x00007ffff68b1f45 in puts() from /lib/libc.so.6 

奇怪的事情是,它的工作原理,如果我中調用一個函數同樣的文件崩潰,它工作正常,並正確地噴出輸出。但是,如果該程序在該文件外的某個函數中崩潰,則不會打印任何輸出。 因此沒有處理printf或文件轉儲語句或函數調用。我正在使用以下示例代碼:

void bt_sighandler(int sig, siginfo_t *info, 
       void *secret) { 

void *trace[16]; 
char **messages = (char **)NULL; 
int i, trace_size = 0; 
ucontext_t *uc = (ucontext_t *)secret; 

/* Do something useful with siginfo_t */ 
if (sig == SIGSEGV) 
    printf("Got signal %d, faulty address is %p, " 
     "from %p\n", sig, info->si_addr, 
     uc->uc_mcontext.gregs[0]); 
else 
    printf("Got signal %d#92; \n", sig); 

trace_size = backtrace(trace, 16); 
/* overwrite sigaction with caller's address */ 
trace[1] = (void *) uc->uc_mcontext.gregs[0]; 

messages = backtrace_symbols(trace, trace_size); 
/* skip first stack frame (points here) */ 
printf("[bt] Execution path:#92; \n"); 
for (i=1; i<trace_size; ++i) 
    printf("[bt] %s#92; \n", messages[i]); 

exit(0); 
} 


int main() { 

/* Install our signal handler */ 
struct sigaction sa; 

sa.sa_sigaction = (void *)bt_sighandler; 
sigemptyset (&sa.sa_mask); 
sa.sa_flags = SA_RESTART | SA_SIGINFO; 

sigaction(SIGSEGV, &sa, NULL); 
sigaction(SIGUSR1, &sa, NULL); 
/* Do something */ 
printf("%d#92; \n", func_b()); 
} 

在此先感謝您的任何幫助。

回答

2

您應該在信號處理程序中做的很少,原則上只訪問sig_atomic_t類型和volatile數據的變量。

做I/O絕對是不可能的。海合會看到這個頁面:

http://www.gnu.org/s/libc/manual/html_node/Nonreentrancy.html#Nonreentrancy

+0

但我看到很多人從處理程序生成輸出,從這個線程ex:http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c -app -crashes – gunjan 2011-03-22 12:14:25

0

嘗試使用簡單的功能,如strcat的()和write()。

+0

strcat(),strcpy()工作。但是我無法打印它們或將信息轉儲到文件中,因爲fwrite()不起作用。此外,我需要使backtrace()調用工作,否則我無法獲取callstack信息。如果我在某個其他函數中模擬從同一文件中的應用程序崩潰,代碼有效,但由於這是一個大型項目,如果我使其從另一個文件中的其他函數中崩潰,它不起作用。完全卡在這裏:( – gunjan 2011-03-22 12:41:03

+0

不使用fwrite。使用write(1,buffer,size)(不知道args的順序)。1是stdout,2是stderr。 – Arkadiy 2011-03-22 14:28:22

3

不幸的是,你只是不能在SIGSEGV處理程序中可靠地執行任何操作。這樣想一想:你的程序有一個嚴重的錯誤,它的狀態(包括系統級狀態,如堆)處於不一致的狀態。

在這種情況下,您不能指望操作系統神奇地修復它需要的堆和其他內部信息,以便能夠在信號處理程序中執行任意代碼。

如果SEGV發生在您自己的代碼中,最好的解決方案是使用核心並修復根本問題。如果核心發生在其他代碼中,通過說共享庫,我會建議隔離在一個完全獨立的二進制代碼,並在兩個二進制文件之間進行通信。然後,如果圖書館崩潰你的主程序沒有。

+0

其實它的所有大項目的一部分包含超過20 -30庫,我需要創建一個處理程序,當它們中的任何一個崩潰時都可以轉儲callstack日誌。因此,您的參數可能是正確的,因爲一個庫可能不會將堆內部通信傳遞給其他處理程序(其中的處理程序在那裏)。我只是想知道爲什麼它打印的調用堆棧在一個案例中(在相同的庫調用),而不是在另一個(從另一個模塊/類調用)。 – gunjan 2011-03-22 15:02:31

0

有沒有理由不能使用valgrind?

+0

我從來沒有嘗試過使用Valgrind。實際上,而不是調試,我需要創建一個處理程序會在崩潰時轉儲callstack的日誌,試試Valgrind看看它是否對我有幫助。 – gunjan 2011-03-22 14:53:45

0

當應用程序崩潰時,Linux會在應用程序崩潰時創建一個核心轉儲。核心文件可以使用gdb進行檢查。

如果沒有核心文件被創建嘗試在相同的外殼,並在程序啓動之前

ulimit -c unlimited 

改變核心文件的大小。 核心文件的名稱通常是core.PID,其中PID是程序的pid。核心文件通常放在/ tmp或程序啓動的目錄中的某處。

有關核心文件的更多信息可在覈心的手冊頁上找到。使用

man core 

閱讀手冊頁。

+0

謝謝,會檢查這一點。 – gunjan 2011-03-22 14:54:27

0

我設法讓它部分工作。其實我是在'sudo'模式下運行應用程序。以用戶模式運行它給了我的調用堆棧。然而,在用戶模式下運行會禁用硬件加速(NVIDIA圖形驅動程序)。爲了解決這個問題,我將自己添加到'video'組中,這樣我就可以訪問/ dev/nvidia0 &/dev/nvidiactl。但是,當我獲得訪問堆棧不會生成了。只有當我處於用戶模式並且硬件加速被禁用時,堆棧纔會到來。但是我不能在沒有硬件加速的情況下運行我的應用程序(意味着某些重要功能會被禁用)。如果有人有任何想法,請讓我知道。

謝謝。

相關問題