在編程調試中,經常出現段錯誤,此時可用gdb調試。具體方法為注冊段錯誤信號處理函數,在處理函數中啟動gdb。
具體代碼如下:
void segv_handler(int no) { char buf[512]; char cmd[512]; FILE *file; snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid()); if(!(file = fopen(buf, "r"))) { exit(EXIT_FAILURE); } if(!fgets(buf, sizeof(buf), file)) { eixt(EXIT_FAILURE); } if(buf[strlen(buf) - 1 ] == '\n') { buf[strlen(buf) -1] = '\0'; } snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid()); system(cmd); }
注冊函數:
signal(SIGSEGV, segv_handler);
下面轉自一些總結:
作為一名程序猿,日常開發中解決各種bug是不可避免的。對於簡單的bug通過日志分析,或者增加打印信息就能很快定位到原因並解決。但是對於某些比較復雜的情況,想要定位到bug往往十分困難。查閱了很多資料,經過不斷嘗試,我發現gdb調試能夠起到很大的幫助。下面我將對使用gdb的一些常用技巧和實例做下總結。
下面總結下比較關鍵的幾個用法:
一、啟動GDB調試
使用gdb調試首先在編譯程序時加上-g參數:$ gcc –g –o foo foo.c
啟動gdb調試有多種方法,可以根據不同的場景選擇合適的方式,這也是gdb比較好用的地方。
1. 程序沒有運行時,gdb +<program> 直接用gdb運行程序;
2. 程序運行中的gdb調試有兩種方式:
a.ps查看程序的PID,gdb + <program> + PID ,自動掛接到已運行的程序;
b.ps產看程序的PID,gdb + <program>運行gdb后,用attach + PID指令掛接到程序, 並用detach來取消掛接的進程。
3. 程序已經死掉后,gdb +<program> + core文件進行調試,core文件是程序非法執行后產生的“核心轉儲”文件。有些情況下core不能生成,需要用ulimit -c unlimited指令先設置系統環境。
二、針對第二種gdb啟動方式,可以有如下實現方式。
程序發生段錯誤,但是該異常發生有一定的隨機性,為了捕獲異常並進行gdb調試,在程序中捕獲SIGSEGV信號並進行如下處理,這樣當程序運行出現段錯誤時直接進入gdb調試環境。
三、gdb調試常用指令
關於gdb調試的常用指令及介紹可以參考這里,http://blog.csdn.net/liwf616/article/details/46833107
Gdb環境下直接按下回車表示執行上一條命令
1. break 設置斷點,
break10 設置斷點,在源程序第10行
breakfunc 設置斷點,在func函數入口處
infobreak 查看斷點信息
2. run 運行程序,可簡寫為r
3. next 單步跟蹤,函數調用當作一條簡單語句執行,可簡寫為n
step 單步跟蹤,函數調進入被調用函數體內,可簡寫為s
stepi 或si單步跟蹤一條機器指令
nexti 或ni單步跟蹤一條機器指令
4. continue 繼續運行程序,可簡寫為c
5. print 打印變量、字符串、表達式等的值,可簡寫為p
p count 打印count的值
p cou1+cou2+cou3 打印表達式值
6. bt 查看函數堆棧
7. finish 退出函數
8. quit 退出GDB
9. shell 不退出GDB就使用shell命令
10. make <make-args>不退出GDB就重新編譯程序
11. set args指定運行時參數。(如:set args10 20 30 40 50)
showargs查看設置好的運行參數。
12. path <dir>設定程序的運行路徑。
showpaths 查看程序的運行路徑。
13. set environment varname [=value] 設置環境變量。如:setenv USER=hchen
showenvironment [varname] 查看環境變量。
14. 其他
cd<dir> 相當於shell的cd命令。
pwd 顯示當前的所在目錄。
infoterminal 顯示程序用到的終端模式。
tty指寫輸入輸出的終端設備。如:tty /dev/ttyb
until在一個循環體內單步跟蹤時,該命令運行程序到退出循環體。簡寫u
四、多線程調試
用gdb調試的好處是可以查看程序運行時的堆棧等信息,可以很直觀的發現問題。
info threads //顯示當前可調試的所有線程,每個線程前面有一個gdb為其分配的ID;
thread ID //切換到該ID對應的線程;
bt //顯示該線程的堆棧信息
thread apply all bt //顯示所有線程堆棧信息
break fun.c:123 thread ID //設置指定線程的斷點
set scheduler-locking off|on|step //off表示不鎖定任何線程,所有線程都執行;on只有當前線程執行;step在單步時除了next過一個函數外,只有當前線程執行
五、示例
當本目錄文件wang大小超過2*1024時可能出現段錯誤(超過很大時,小時短時間不會出現),會進入segv_handler(),但不會觸發gdb。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <syslog.h> #include <fcntl.h> #include <stdarg.h> #include <sys/stat.h> #include <signal.h> #include <string.h> #include <execinfo.h> #define SYSLOG_LINE_BUFFER_SIZE 2*1024 #define BUFFER_SIZE 4*1024 #define FILEN "wang" char bbuf[BUFFER_SIZE]={0}; void m_syslog_dbg(char *format, ...) { va_list ptr; char buf[SYSLOG_LINE_BUFFER_SIZE] = {0}; openlog("log", 0, LOG_DAEMON); // put log va_start(ptr, format); vsprintf(buf, format, ptr); va_end(ptr); syslog(LOG_DEBUG, "%s", buf); closelog(); return; } void segv_handler(int no) { printf("seg....\n"); #if 1 void * array[10]; /* 25 層,太夠了 : ),你也可以自己設定個其他值 */ char **strings; int nSize = backtrace(array, sizeof(array)/sizeof(array[0])); for (int i=nSize-1; i>=0; i--){ /* 頭尾幾個地址不必輸出,看官要是好奇,輸出來看看就知道了 */ /* 修正array使其指向正在執行的代碼 */ // printf("SIGSEGV catched when running code at %x\n", (char*)array[i] - 1); printf("SIGSEGV catched when running code at %x\n", array[i]); } #if 1 strings = backtrace_symbols(array, nSize); printf("backtrace...%d\n", nSize); if(strings == NULL){ printf("strings NULL\n"); exit(EXIT_FAILURE); } for(int i=nSize-1; i>=0; i--){ printf("%s\n", strings[i]); } free(strings); #endif #endif #if 0 char buf[128]; char cmd[128]; FILE *file; pid_t pid = getpid(); snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); if((file = fopen(buf, "r")) == NULL){ exit(EXIT_FAILURE); } if(fgets(buf, sizeof(buf), file) == NULL ){ exit(EXIT_FAILURE); } if(buf[strlen(buf)-1] == '\n'){ buf[strlen(buf)-1] = '\0'; } snprintf(cmd, sizeof(cmd), "/usr/bin/gdb %s %d", buf, pid); printf("receive signo %d-->%s\n", no, cmd); // system(cmd); // while(1); #endif } int main(void ) { signal(SIGSEGV, segv_handler); struct stat st; int ret = 0; ret = stat(FILEN, &st); if(ret != 0){ perror("stat"); return -1; } int fd = open(FILEN, O_RDONLY); if(fd < 0){ printf("open error\n"); return -1; } //read(fd, bbuf, st.st_size); ret = read(fd, bbuf, sizeof(bbuf)); printf("file size = %d, %d\n", ret, getpid()); m_syslog_dbg("recevice [%s]\n", bbuf); printf("the end\n"); while(1){ sleep(1); printf("running...\n"); } close(fd); return 0; }
參考:
1. backtrace、backtrace_symbols、backtrace_symbols_fd-support for application self-debugging
3. C/C++捕獲段錯誤,打印出錯的具體位置(精確到哪一行)_轉
4. gdb調試段錯誤及使用
