1. 程式人生 > >Linux中backtrace()系列函式的應用例項

Linux中backtrace()系列函式的應用例項

一、引言 backtrace()系列函式可用來輸出程式碼出錯時的函式呼叫關係。

A backtrace is the series of currently active function calls for the program. 

#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);

二、示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <execinfo.h> 

#define SIZE	(100)
static sigset_t signals_handled;
	
static void dbg_backtrace(void)
{
	int i, nline;
	void *buffer[SIZE];
	char **strings;
	
	nline = backtrace(buffer, SIZE);
	printf("addr: %d = backtrace()\n", nline);
	
	strings = backtrace_symbols(buffer, nline);
	if (NULL == strings) {
		perror("backtrace_symbols()\n");
		exit(EXIT_FAILURE);
	}
	
	for (i = 0; i < nline; i ++) {
		printf("callback: %s\n", strings[j]);
	}
	/* backtrace_symbols() 內部有呼叫malloc函式 */
	free(strings);
}

static void sig_handler_hup(int sig)
{
	printf("%s() %d\n\n", __func__, sig);
}

static void sig_handler_debug(int sig)
{
	printf("%s() %d\n\n", __func__, sig);
}

static void sig_handler_usr2(int sig)
{
	printf("%s() %d\n\n", __func__, sig);
}

static void sig_handler_fatal(int sig)
{
	printf("Fatal signal %d\n\n", sig);
	
	dbg_backtrace();
	exit(127);
}

void signals_setup(void)
{
	struct sigaction sa;
	
	sigemptyset(&signals_handled);
	sigaddset(&signals_handled, SIGHUP);
	sigaddset(&signals_handled, SIGINT);
	sigaddset(&signals_handled, SIGTERM);
	sigaddset(&signals_handled, SIGUSR2);
	
#define SIGNAL(s, handler)	do { \
	sa.sa_handler = handler;	\
	if (sigaction(s, &sa, NULL) < 0) \
		printf("could not set signal handler (%d): %m", s); \
	} while(0);
	
	sa.sa_mask = signals_handled;
	sa.sa_flags = 0;
	
	SIGNAL(SIGHUP, sig_handler_hup);
	SIGNAL(SIGUSR1, sig_handler_debug);
	SIGNAL(SIGUSR2, sig_handler_usr2);
	SIGNAL(SIGABRT, sig_handler_fatal);
	SIGNAL(SIGALRM, sig_handler_fatal);
	SIGNAL(SIGFPE, sig_handler_fatal);
	SIGNAL(SIGILL, sig_handler_fatal);
	SIGNAL(SIGPIPE, sig_handler_fatal);
	SIGNAL(SIGQUIT, sig_handler_fatal);
	SIGNAL(SIGSEGV, sig_handler_fatal);
	
	signal(SIGPIPE, SIG_IGN);
}

void mem_err(void)
{
	char *prt = NULL;
	printf("%s()\n", __func__);
	
	*prt = '1';
}

int main(int argc, char **argv)
{
	signals_setup();
	int i = 0;
	
	for (; ;) {
		i ++;
		if (i > 10) {
			mem_err();
		}
		
		// printf("i: %d\n", i);
		sleep(2);
	}
		
	return 0;
}
執行結果: mem_err() Fatal signal 11 addr: 7 = backtrace() callback: ./sig_setup() [0x804867b] callback: ./sig_setup() [0x8048791] callback: [0xb77af400] callback: ./sig_setup() [0x8048ae7] callback: ./sig_setup() [0x8048b13] callback: /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0xb764ce66] callback: ./sig_setup() [0x8048591]
三、backtrace()系列函式含義:
int backtrace(void **buffer, int size);
backtrace()返回程式呼叫的backtrace資訊,結果(即地址:address from the corresponding stack frame)存放在buffer指標指向的陣列中。 size引數指定了buffer指向的陣列可儲存的結果的最大個數;若結果個數大於size,陣列將儲存那些最近執行的函式呼叫。 返回值代表buffer指向的陣列實際元素的個數。
char **backtrace_symbols(void *const *buffer, int size);
backtrace()返回的陣列中存放的結果是地址值,backtrace_symbols()則把這些地址轉化成對應的字串。 每一個元素(字串)包含地址值對應的函式名(若不能獲取,則不存在)、內部語句相對函式的偏移地址(hexadecimal)、語句的實際地址(hexadecimal)。 如:./prog(myfunc3+0x5c) [0x80487f0] backtrace_symbols()函式內部進行了malloc操作,所以呼叫者必須free記憶體。 操作執行失敗返回NULL。
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
功能和backtrace_symbols()函式一樣,只是它把返回結果按行寫到了fd代表的檔案裡。 backtrace_symbols_fd函式內部沒有進行malloc操作! 四、結果分析: 先執行反彙編:objdump -d test > dump.txt
callback: ./sig_setup() [0x804867b]:
	0804865c <dbg_backtrace>:
	 804865c:	55                   	push   %ebp
	 804865d:	89 e5                	mov    %esp,%ebp
	 804865f:	81 ec b8 01 00 00    	sub    $0x1b8,%esp
	 8048665:	c7 44 24 04 64 00 00 	movl   $0x64,0x4(%esp)
	 804866c:	00 
	 804866d:	8d 85 5c fe ff ff    	lea    -0x1a4(%ebp),%eax
	 8048673:	89 04 24             	mov    %eax,(%esp)
	 8048676:	e8 e5 fe ff ff       	call   8048560 <[email protected]>
	 804867b:	89 45 f0             	mov    %eax,-0x10(%ebp)

callback: ./sig_setup() [0x8048791]
	08048773 <sig_handler_fatal>:
	 8048773:	55                   	push   %ebp
	 8048774:	89 e5                	mov    %esp,%ebp
	 8048776:	83 ec 18             	sub    $0x18,%esp
	 8048779:	8b 45 08             	mov    0x8(%ebp),%eax
	 804877c:	89 44 24 04          	mov    %eax,0x4(%esp)
	 8048780:	c7 04 24 15 8c 04 08 	movl   $0x8048c15,(%esp)
	 8048787:	e8 14 fd ff ff       	call   80484a0 <[email protected]>
	 804878c:	e8 cb fe ff ff       	call   804865c <dbg_backtrace>
	 8048791:	c7 04 24 7f 00 00 00 	movl   $0x7f,(%esp)

callback: ./sig_setup() [0x8048ae7]
08048ac3 <mem_err>:
 8048ac3:	55                   	push   %ebp
 8048ac4:	89 e5                	mov    %esp,%ebp
 8048ac6:	83 ec 28             	sub    $0x28,%esp
 8048ac9:	c7 45 f4 00 00 00 00 	movl   $0x0,-0xc(%ebp)
 8048ad0:	c7 44 24 04 5b 8c 04 	movl   $0x8048c5b,0x4(%esp)
 8048ad7:	08 
 8048ad8:	c7 04 24 4e 8c 04 08 	movl   $0x8048c4e,(%esp)
 8048adf:	e8 bc f9 ff ff       	call   80484a0 <[email protected]>
 8048ae4:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048ae7:	c6 00 31             	movb   $0x31,(%eax)
至此可知,這裡的函式呼叫,造成了非法的記憶體操作。 五、backtrace()系列函式注意事項: 這三個函式都假設函式的返回地址按它認為的方式儲存在棧上,故使用時應注意: 1、幀指標(Frame  pointers)的遮蔽/忽略可能導致上述假設失效 2、inline關鍵字描述的函式沒有棧幀(stack frames) 3、尾呼叫(Tail-call)優化會造成一個棧幀被另一個替換掉 4、對於特定編譯器,應該指定連結選項,否則函式名欄位可能是無效的;對於使用GNU linker的系統,使用-rdynamic連結項 5、static關鍵字描述的函式不會被顯示,and won't be available in the backtrace PS: 棧幀(stack frames): In computer science, a stack frame is a memory management strategy used to create and destroy temporary (automatic) variables in some programming languages. Stack frames only exist at run-time. 在計算機科學裡,棧幀是一種記憶體管理策略,在某些程式語言裡它用來建立/銷燬臨時(自動)變數。棧幀只在執行時存在。 尾呼叫(Tail-call): In computer science, a tail call is a subroutine call performed as the final action of a procedure. 在計算機科學裡,尾呼叫是指一個函式裡的最後一個動作是一個函式呼叫的情形:即這個呼叫的返回值直接被當前函式返回的情形。 參考資料: 幀指標(Frame  pointers):http://stackoverflow.com/questions/579262/what-is-the-purpose-of-the-frame-pointer http://blog.chinaunix.net/uid-25871104-id-2938389.html 棧幀(stack frames):http://en.citizendium.org/wiki/Stack_frame 尾呼叫(Tail-call):http://en.wikipedia.org/wiki/Tail_call

相關推薦

Linuxbacktrace()系列函式應用例項

一、引言 backtrace()系列函式可用來輸出程式碼出錯時的函式呼叫關係。 A backtrace is the series of currently active function calls for the program.  #include <ex

Linuxexec系列函式應用

這段時間在研究linux中使用者登陸和shell執行程式的原理。我們知道,shell命令分為內部命令和外部命令,內部命令有諸如cd,history,exit,echo等,常見的外部命令有ls,ping,netstat等,通過type命令可以檢視一個命令是內部命令還是外部命令

python方法、函式例項方法、類方法、靜態方法的理解

python中的方法、函式、例項方法、類方法、靜態方法的理解 方法與函式 例項方法、類方法、靜態方法 例項方法 類方法 靜態方法 為了便於理解,我舉個栗子: 方法與函式 def func():

Linux的時間函式

Linux 中常用的函式有以下函式 sleep、usleep、ndelay、udelay、mdelay 等 Linux 系統程式設計下用到的延時函式在標頭檔案“#include <unistd.h>”中,包括函式 sleep、usleep。 Linux 核心中用到的延時函式在

javaMongoDB的簡單應用例項

1 首先載入 MongoDB的jar包。下載Jar包連結 2然後進行相應的配置。如我當前專案是在web-pom.xml 的的節點下配置。 <dependency> <groupId>org.mo

Linux下mount/umount函式應用

針對海思平臺system命令佔用資源較大的問題,因此程式中與系統呼叫相關的就只能用函式實現了。 demo mount.c: #include <sys/mount.h> int main

Linux程式設計之ioremap函式例項解析

void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 入口: phys_addr:要對映的起始的IO地址; size:要對映的空間的大小; flags:要對映

Linux的mmap函式

1,mmap函式功能mmap函式為記憶體對映函式。負責把檔案內容對映到程序的虛擬記憶體空間, 通過對這段記憶體的讀取和修改,來實現對檔案的讀取和修改,而不需要再呼叫read,write等操作。注意:mmap將一個檔案或者其它物件對映進記憶體。檔案被對映到多個頁上,如果檔

C++backtrace列印函式呼叫棧callstack-dbg_assert

1. 使用backtrace()函式獲取呼叫棧,是一個指標陣列,返回獲取到的呼叫棧個數,結果放到傳入的指標數組裡面; 2. 呼叫backtrace_symbols()把獲取的指標陣列和陣列中呼叫棧個數傳遞給該函式,會返回一個新的指標陣列,    裡面是已經轉換成符號表的呼叫棧資訊

linux使用pthread_kill函式測試執行緒是否存活的例子

/******************************* pthread_kill.c *******************************/#include <stdio.h>#include <stdlib.h>#include <pthread.h&g

phpob_start()系列函式的使用

    我在看《php設計模式》的時候看到一個有趣的函式ob_start(),查了一下網友的部落格,滿心歡喜,我們在平時做模版快取會選擇Smart,實際上我們可以自己來,不借助其他工具,對於我這樣對程式碼控制慾強的人來說簡直好東西。輸出控制函式可以幫助我們自由的控制php指

linuxshell的函式用法

1.語法 方法1: 函式名() { 函式體 return n } 方法2: function 函式名() { 函式體 return n } 2.呼叫函式 呼叫函式,在函式體外寫函式的名字即可,下面有一個簡單的指令碼來舉例 #!/bin/bash

UNIX /Linux的memcpy函式用法詳解

原型:extern void *memcpy(void *dest, void *src, unsigned int count); 用法:#include <string.h>

函式指標&回撥函式&linux的signal函式

1.  int (*func)();函式指標,指向的函式為空引數,返回整型; 2. 回撥函式是一個程式設計師不能顯式呼叫的函式;通過將回調函式的地址傳給被呼叫者從而實現呼叫。 回撥函式是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標

Linux程序間通訊——pipe應用例項

    管道(pipe):管道可用於具有親緣關係的程序間的通訊,是一種半雙工的方式,資料只能單向流動,允許一個程序和另一個與它有共同祖先的程序之間進行通訊。 PIPE模組程式一 下面模組程式碼是在主函式中創將一個程序,在子程序中往管道中寫資料,在父程序中讀取資料,也就是一對

isprint字串測試函式應用例項

原型:int isprint(int c); 標頭檔案:ctype.h 功能:檢查引數c是否為可列印字元(0x20-0x7e),若c所對映的ASCII(非控制字元)碼可列印,其中包含空格字元,則返回T

Linux的popen函式和system函式

說在前面:在實際程式設計中儘量減少使用system函式。int system(const char *command);說明: system()通過呼叫/ bin / sh -c命令執行命令中指定的命令,並在命令完成後返回。在執行該命令期間,SIGCHLD將被阻塞,並且SIG

access檔案操作函式應用例項

標頭檔案:unistd.h 功 能: 確定檔案或資料夾的訪問許可權。即,檢查某個檔案的存取方式,比如說是隻讀方式、只寫方式等。如果指定的存取方式有效,則函式返回0,否則函式返回-1。 用 法: int access(const char *filenpath, int m

php的魔術方法應用例項

<?php //獲取當前類名 echo __CLASS__ ; //當前函式名(confirm echo __FUNCTION__ ; //當前方法名 (bankcard::confirm) echo __METHOD__ ; //在P

小何講程序:Linux的exec函式族講解

1.  exec函式族-exec()函式族說明使用exec函式族主要有兩種情況當程序認為自己不能再為系統和使用者做出任何貢獻時,就可以呼叫exec函式族中的任意一個函式讓自己重生;如果一個程序想執行另一個程式,那麼它就可以呼叫fork()/vfork()函式新建一個程序,然