Gdb 調試core文件詳解

一,什么是coredump

??????? 我們經常聽到大家說到程序core掉了,需要定位解決,這里說的大部分是指對應程序由于各種異常或者bug導致在運行過程中異常退出或者中止,并且在滿足一定條件下(這里為什么說需要滿足一定的條件呢?下面會分析)會產生一個叫做core的文件。

??????? 通常情況下,core文件會包含了程序運行時的內存,寄存器狀態,堆棧指針,內存管理信息還有各種函數調用堆棧信息等,我們可以理解為是程序工作當前狀態存儲生成第一個文件,許多的程序出錯的時候都會產生一個core文件,通過工具分析這個文件,我們可以定位到程序異常退出的時候對應的堆棧調用等信息,找出問題所在并進行及時解決。

?

二,coredump文件的存儲位置

?? core文件默認的存儲位置與對應的可執行程序在同一目錄下,文件名是core,大家可以通過下面的命令看到core文件的存在位置:

?? cat? /proc/sys/kernel/core_pattern

?? 缺省值是core

?

注意:這里是指在進程當前工作目錄的下創建。通常與程序在相同的路徑下。但如果程序中調用了chdir函數,則有可能改變了當前工作目錄。這時core文件創建在chdir指定的路徑下。有好多程序崩潰了,我們卻找不到core文件放在什么位置。和chdir函數就有關系。當然程序崩潰了不一定都產生 core文件。

如下程序代碼:則會把生成的core文件存儲在/data/coredump/wd,而不是大家認為的跟可執行文件在同一目錄。

?

?

通過下面的命令可以更改coredump文件的存儲位置,若你希望把core文件生成到/data/coredump/core目錄下:

?? echo “/data/coredump/core”> /proc/sys/kernel/core_pattern

?

注意,這里當前用戶必須具有對/proc/sys/kernel/core_pattern的寫權限。

?

缺省情況下,內核在coredump時所產生的core文件放在與該程序相同的目錄中,并且文件名固定為core。很顯然,如果有多個程序產生core文件,或者同一個程序多次崩潰,就會重復覆蓋同一個core文件,因此我們有必要對不同程序生成的core文件進行分別命名。

?

我們通過修改kernel的參數,可以指定內核所生成的coredump文件的文件名。例如,使用下面的命令使kernel生成名字為core.filename.pid格式的core dump文件:

echo “/data/coredump/core.%e.%p” >/proc/sys/kernel/core_pattern

這樣配置后,產生的core文件中將帶有崩潰的程序名、以及它的進程ID。上面的%e和%p會被替換成程序文件名以及進程ID。

如果在上述文件名中包含目錄分隔符“/”,那么所生成的core文件將會被放到指定的目錄中。 需要說明的是,在內核中還有一個與coredump相關的設置,就是/proc/sys/kernel/core_uses_pid。如果這個文件的內容被配置成1,那么即使core_pattern中沒有設置%p,最后生成的core dump文件名仍會加上進程ID。

三,如何判斷一個文件是coredump文件?

在類unix系統下,coredump文件本身主要的格式也是ELF格式,因此,我們可以通過readelf命令進行判斷。

???

?????可以看到ELF文件頭的Type字段的類型是:CORE (Core file)

???? 可以通過簡單的file命令進行快速判斷:?????

四,產生coredum的一些條件總結

1,? 產生coredump的條件,首先需要確認當前會話的ulimit –c,若為0,則不會產生對應的coredump,需要進行修改和設置。

ulimit? -c?unlimited? (可以產生coredump且不受大小限制)

?

若想甚至對應的字符大小,則可以指定:

ulimit –c [size]

???????????????

?

?????? 可以看出,這里的size的單位是blocks,一般1block=512bytes

??????? 如:

??????? ulimit –c 4? (注意,這里的size如果太小,則可能不會產生對應的core文件,筆者設置過ulimit –c 1的時候,系統并不生成core文件,并嘗試了1,2,3均無法產生core,至少需要4才生成core文件)

???????

但當前設置的ulimit只對當前會話有效,若想系統均有效,則需要進行如下設置:

?? 在/etc/profile中加入以下一行,這將允許生成coredump文件

ulimit-c unlimited

?? 在rc.local中加入以下一行,這將使程序崩潰時生成的coredump文件位于/data/coredump/目錄下:

echo /data/coredump/core.%e.%p> /proc/sys/kernel/core_pattern?

注意rc.local在不同的環境,存儲的目錄可能不同,susu下可能在/etc/rc.d/rc.local

??????更多ulimit的命令使用,可以參考:http://baike.baidu.com/view/4832100.htm

??????這些需要有root權限, 在ubuntu下每次重新打開中斷都需要重新輸入上面的ulimit命令, 來設置core大小為無限.

2, 當前用戶,即執行對應程序的用戶具有對寫入core目錄的寫權限以及有足夠的空間。

3, 幾種不會產生core文件的情況說明:

The?core?file?will?not?be?generated?if

(a)????the?process?was?set-user-ID?and?the?current?user?is?not?the?owner?of?the?program?file,?or

(b)?????the?process?was?set-group-ID?and?the?current?user?is?not?the?group?owner?of?the?file,

(c)?????the?user?does?not?have?permission?to?write?in?the?current?working?directory,?

(d)?????the?file?already?exists?and?the?user?does?not?have?permission?to?write?to?it,?or?

(e)?????the?file?is?too?big?(recall?the?RLIMIT_CORE?limit?in?Section?7.11).?The?permissions?of?the?core?file?(assuming?that?the?file?doesn't?already?exist)?are?usually?user-read?and?user-write,?although?Mac?OS?X?sets?only?user-read.

?

五,coredump產生的幾種可能情況

造成程序coredump的原因有很多,這里總結一些比較常用的經驗吧:

?1,內存訪問越界

? a) 由于使用錯誤的下標,導致數組訪問越界。

? b) 搜索字符串時,依靠字符串結束符來判斷字符串是否結束,但是字符串沒有正常的使用結束符。

? c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函數,將目標字符串讀/寫爆。應該使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函數防止讀寫越界。

?2,多線程程序使用了線程不安全的函數。

應該使用下面這些可重入的函數,它們很容易被用錯:

asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n)ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c)getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c)fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c)getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3)getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n)nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3)getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c)getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c)getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

?3,多線程讀寫的數據未加鎖保護。

對于會被多個線程同時訪問的全局數據,應該注意加鎖保護,否則很容易造成coredump

?4,非法指針

? a) 使用空指針

? b) 隨意使用指針轉換。一個指向一段內存的指針,除非確定這段內存原先就分配為某種結構或類型,或者這種結構或類型的數組,否則不要將它轉換為這種結構或類型的指針,而應該將這段內存拷貝到一個這種結構或類型中,再訪問這個結構或類型。這是因為如果這段內存的開始地址不是按照這種結構或類型對齊的,那么訪問它時就很容易因為bus error而core dump。

?5,堆棧溢出

不要使用大的局部變量(因為局部變量都分配在棧上),這樣容易造成堆棧溢出,破壞系統的棧和堆結構,導致出現莫名其妙的錯誤。??

六,利用gdb進行coredump的定位

? 其實分析coredump的工具有很多,現在大部分類unix系統都提供了分析coredump文件的工具,不過,我們經常用到的工具是gdb。

? 這里我們以程序為例子來說明如何進行定位。

1,? 段錯誤 – segmentfault

?? 我們寫一段代碼往受到系統保護的地址寫內容。

?

?

?? 按如下方式進行編譯和執行,注意這里需要-g選項編譯。

?

可以看到,當輸入12的時候,系統提示段錯誤并且core dumped

?

?? 我們進入對應的core文件生成目錄,優先確認是否core文件格式并啟用gdb進行調試。

?

從紅色方框截圖可以看到,程序中止是因為信號11,且從bt(backtrace)命令(或者where)可以看到函數的調用棧,即程序執行到coremain.cpp的第5行,且里面調用scanf 函數,而該函數其實內部會調用_IO_vfscanf_internal()函數。

接下來我們繼續用gdb,進行調試對應的程序。

記住幾個常用的gdb命令:

l(list)?,顯示源代碼,并且可以看到對應的行號;

b(break)x, x是行號,表示在對應的行號位置設置斷點;

p(print)x, x是變量名,表示打印變量x的值

r(run),?表示繼續執行到斷點的位置

n(next),表示執行下一步

c(continue),表示繼續執行

q(quit),表示退出gdb

?

啟動gdb,注意該程序編譯需要-g選項進行。

?

注:? SIGSEGV?????11?????? Core??? Invalid memoryreference

?

七,附注:

1,? gdb的查看源碼

顯示源代碼

GDB 可以打印出所調試程序的源代碼,當然,在程序編譯時一定要加上-g的參數,把源程序信息編譯到執行文件中。不然就看不到源程序了。當程序停下來以后,GDB會報告程序停在了那個文件的第幾行上。你可以用list命令來打印程序的源代碼。還是來看一看查看源代碼的GDB命令吧。

list<linenum>

顯示程序第linenum行的周圍的源程序。

list<function>

顯示函數名為function的函數的源程序。

list

顯示當前行后面的源程序。

list -

顯示當前行前面的源程序。

一般是打印當前行的上5行和下5行,如果顯示函數是是上2行下8行,默認是10行,當然,你也可以定制顯示的范圍,使用下面命令可以設置一次顯示源程序的行數。

setlistsize <count>

設置一次顯示源代碼的行數。

showlistsize

查看當前listsize的設置。

list命令還有下面的用法:

list<first>, <last>

顯示從first行到last行之間的源代碼。

list ,<last>

顯示從當前行到last行之間的源代碼。

list +

往后顯示源代碼。

一般來說在list后面可以跟以下這些參數:

?

<linenum>?? 行號。

<+offset>?? 當前行號的正偏移量。

<-offset>?? 當前行號的負偏移量。

<filename:linenum>? 哪個文件的哪一行。

<function>? 函數名。

<filename:function>哪個文件中的哪個函數。

<*address>? 程序運行時的語句在內存中的地址。

?

2,? 一些常用signal的含義

SIGABRT:調用abort函數時產生此信號。進程異常終止。

SIGBUS:指示一個實現定義的硬件故障。

SIGEMT:指示一個實現定義的硬件故障。EMT這一名字來自PDP-11的emulator trap 指令。

SIGFPE:此信號表示一個算術運算異常,例如除以0,浮點溢出等。

SIGILL:此信號指示進程已執行一條非法硬件指令。4.3BSD由abort函數產生此信號。SIGABRT現在被用于此。

SIGIOT:這指示一個實現定義的硬件故障。IOT這個名字來自于PDP-11對于輸入/輸出TRAP(input/outputTRAP)指令的縮寫。系統V的早期版本,由abort函數產生此信號。SIGABRT現在被用于此。

SIGQUIT:當用戶在終端上按退出鍵(一般采用Ctrl-/)時,產生此信號,并送至前臺進

程組中的所有進程。此信號不僅終止前臺進程組(如SIGINT所做的那樣),同時產生一個core文件。

SIGSEGV:指示進程進行了一次無效的存儲訪問。名字SEGV表示“段違例(segmentationviolation)”。

SIGSYS:指示一個無效的系統調用。由于某種未知原因,進程執行了一條系統調用指令,但其指示系統調用類型的參數卻是無效的。

SIGTRAP:指示一個實現定義的硬件故障。此信號名來自于PDP-11的TRAP指令。

SIGXCPUSVR4和4.3+BSD支持資源限制的概念。如果進程超過了其軟C P U時間限制,則產生此信號。

SIGXFSZ:如果進程超過了其軟文件長度限制,則SVR4和4.3+BSD產生此信號。

?

3,? Core_pattern的格式

可以在core_pattern模板中使用變量還很多,見下面的列表:

%% 單個%字符

%p 所dump進程的進程ID

%u 所dump進程的實際用戶ID

%g 所dump進程的實際組ID

%s 導致本次core dump的信號

%t core dump的時間 (由1970年1月1日計起的秒數)

%h 主機名

%e 程序文件名

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384917.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384917.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384917.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux之GDB命令(二)

gdb命令&#xff1a; 前提條件&#xff1a;可執行文件必須包含調試信息 gcc -ggdb 文件名 –啟動gdb調試查看代碼命令 當前文件&#xff1a; list 行號&#xff08;函數名&#xff09; 指定文件&#xff1a; list 文件名&#xff1a;行號&#xff08;函數名&#x…

Windows下編譯openssl庫

1、概述 OpenSSL是一個開放源代碼的軟件庫包&#xff0c;它實現了 SSL&#xff08;Secure SocketLayer&#xff09;和 TLS&#xff08;Transport Layer Security&#xff09;協議&#xff0c;所以應用程序可以使用這個包來進行安全通信&#xff0c;避免竊聽&#xff0c;同時確…

Makefile規則介紹

Makefile 一個規則 三要素&#xff1a;目標&#xff0c;依賴&#xff0c;命令 目標&#xff1a;依賴命令 1、第一條規則是用來生成終極目標的規則 如果規則中的依賴不存在&#xff0c;向下尋找其他的規則 更新機制&#xff1a;比較的是目標文件和依賴文件的時間 兩個函…

windows環境下C語言socket編程

最近由于實驗需要&#xff0c;要求寫一個c程序與java程序通信的軟件&#xff0c;為了測試首先寫了一個windows環境下c語言的socket&#xff08;tcp&#xff09;通信程序。 首先socket通信的步驟&#xff1a; 圖一 socket通信步驟&#xff08;轉載) 圖二 三次握手協議&…

進程控制塊(PCB)

進程控制塊PCB 我們知道&#xff0c;每個進程在內核中都有一個進程控制塊&#xff08;PCB&#xff09;來維護進程相關的信息&#xff0c;Linux內核的進程控制塊是task_struct結構體。 /usr/src/linux-headers-3.16.0-30/include/linux/sched.h文件中可以查看struct task_struct…

網絡層攻擊防御

網絡層攻擊防御 網絡層攻擊防御主要分為以下三類&#xff1a; TCP類報文攻擊防御 UDP類報文攻擊防御 ICMP類報文攻擊防御 TCP類報文攻擊防御 TCP正常的交互過程&#xff1a; 圖&#xff1a;TCP正常交互過程 在TCP/IP協議中&#xff0c;TCP協議提供可靠的連接服務&#xff0c…

Linux之環境變量

常見環境變量 按照慣例&#xff0c;環境變量字符串都是namevalue這樣的形式&#xff0c;大多數name由大寫字母加下劃線組成&#xff0c;一般把name的部分叫做環境變量&#xff0c;value的部分則是環境變量的值。環境變量定義了進程的運行環境&#xff0c;一些比較重要的環境變量…

環境變量操作函數

getenv獲取環境變量值的函數&#xff1b; setenv改變或者添加環境變量函數&#xff1b; unsetenv 取消環境變量&#xff1b; &#xff08;可以在終端上man 函數名來獲取詳細的函數信息&#xff09; #include<stdio.h> #include<stdlib.h> #include<string.h>…

Makefile(三)

在平時使用中&#xff0c;可以使用以下的makefile來編譯單獨的代碼 src $(wildcard *.c) obj $(patsubst %.c, %.o, $(src))CC gcc CFLAGS -Wall -gall:$(target)$(target):%:%.c$(CC) $< -o $ $(CFLAGS).PHONY: clean all clean:-rm -rf $(target) 使用方法就是make 后…

位運算(C++)

C輸出十六進制 #include<iostream> #include<iomanip> using namespace std;int main() {int a 60;int b 13;int c a &b;cout << "a : hex "<<hex << a << endl;cout << "b : hex "<<hex <<…

數學函數(C/C++)

C中包含頭文件<math.h> C包含頭文件<cmath> 函數 double cos&#xff08;double&#xff09; 該函數返回弧度角&#xff08;double型&#xff09;的余弦 double tan&#xff08;double&#xff09; 該函數返回弧度角&#xff08;double型&#xff09;的正切…

數據類型(C++)

不同系統會有不同差異&#xff1a; 類型 位(byte) 范圍 char 1 -128—127 or 0 – 255 unsigned char 1 0 – 255 signed int 1 -128—127 int 4 -2^31 – 2^32-1 unsigned int 4 0 – 2^32 signed int 4 -2^31 – 2^32-1 short int 2 2^15 – 2^15-1 …

日期與時間(C/C++)

C繼承了C語言用于日期和時間操作的結構和函數&#xff0c;使用之前程序要引用<ctime>頭文件 有四個與時間相關的類型:clock_t、time_t、size_t、和tm。類型clock_t、size_t、和time_t能夠把系統時間和日期表示為某種整數。 結構體tm把時間和日期以C結構的形式保存&#x…

標準輸入輸出(C++)

輸入輸出流函數&#xff08;模板&#xff09; #include<iostream> #include<iomanip> using namespace std; int main() {cout << setiosflags(ios::left|ios::showpoint); //設左對齊cout.precision(5); //設置除小數…

拷貝函數和構造函數

類的構造函數&#xff1a; 類的構造函數是類的一種特殊的成員函數&#xff0c;它會在每次創建類的新對象時執行。 構造函數的名稱與類的名稱是完全相同的&#xff0c;并且不會返回任何類型&#xff0c;也不會返回void。 構造函數可用于為某些成員變量初始值。 默認的構造函數是…

拷貝構造函數

拷貝構造函數是一種特殊的構造函數&#xff0c;它在創建對象時&#xff0c;使用的是同一類中之前創建的對象來初始化新創建的對象。拷貝構造函數通常用于&#xff1a; l 通過使用另一個同類型的對象來初始化新創建的對象&#xff1b; l 復制對象把它作為參數傳遞給函數&#…

Linux進程通信之管道

進程間完成數據傳遞需要借助操作系統提供的特殊的方法&#xff0c;比如&#xff1a;文件、管道、信號、共享內存、消息隊列、套接字、命名管道等。但現在常用的進程間通信方式有&#xff1a; 管道 – 使用最簡單 pipe 管道一般讀寫行為 FIFO&#xff08;有名管道&#xff09;&a…

Linux進程通信之文件

父子進程共享打開的文件描述符------使用文件完成進程間通信. /*** fork_share_fd.c***/ #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <sys/wait.h>int main(void) {in…

dup2函數

將當前系統中的進程信息打印到文件中 命令行&#xff1a;ps aux > out 將ps得到的信息重定向到out文件中 使用dup2文件在程序中完成。 int dup2(int oldfd,int newfd); /*** dup2.c ***/ #include<stdio.h> #include<fcntl.h> #include<unistd.h> #includ…

wait()函數

wait()函數&#xff1a;回收僵尸進程 父進程調用wait函數可以回收子進程終止信息。該函數有三個功能&#xff1a; 1&#xff09; 阻塞等待子進程退出 2&#xff09; 回收子進程殘留資源 3&#xff09; 獲取子進程結束狀態&#xff08;退出原因&#xff09; /*** zoom_test.c **…