軟體偵錯的藝術筆記:GDB

2020-10-20 15:00:48

很久之前,在https://blog.csdn.net/fengbingchun/article/details/41413381中簡單整理過gdb中常用的一些命令,不齊全,這裡按照《軟體偵錯的藝術》一書中關於gdb的介紹再做次整理。《軟體偵錯的藝術》於2009年由人民郵電出版社出版。

1. 預備知識

GDB:Unix程式設計師最常用的偵錯工具,是由Richard Stallman開發的GNU專案偵錯程式(GNU Project Debugger)。

DDD:Data Display Debugger,資料顯示偵錯程式,GDB的GUI(圖形化使用者介面)前端:使用者通過GUI發出命令,GUI將這些命令傳遞給GDB。

Eclipse:IDE(整合式開發環境)。

GDB的命令允許在不產生歧義的情況下使用縮寫

從版本6.1以來,GDB已經以名為TUI(Terminal User Interface, 終端使用者介面)的模式提供了基於文字互動和圖形使用者互動之間的折中方法。在這一模式中,GDB將終端螢幕劃分為類似於DDD的源文字視窗和控制檯的多個子視窗:可以在類似於源文字視窗的子視窗中跟蹤程式執行的進展過程,同時在類似於控制檯的子視窗中發出GDB命令。也可以使用另一個程式CGDB,該程式也提供了類似的功能。

為了以TUI模式執行GDB,可以在呼叫GDB時在命令列上指定-tui選型,如gdb -tui main,或者處於非TUI模式時在GDB中使用Ctrl+X+A組合鍵。如果當前處於TUI模式,後一種命令方式就會使你離開TUI模式。TUI模式中,GDB視窗劃分為兩個子視窗:一個用於輸入GDB命令,而另一個用於檢視原始碼。需要注意的是,在一些情況下TUI可能不能按照使用者所需要的方式運作。

1.5 主要偵錯程式操作

單步偵錯原始碼:

(1).斷點:偵錯工具會在指定斷點處暫停程式的執行,在GDB中是通過break命令及其行號完成的。

(2).單步偵錯:GDB的next命令讓GDB執行下一行,然後暫停。step命令的作用與此類似,只是在函數呼叫時step命令會進入函數,而next導致程式執行的暫停出現在下次呼叫函數時。

(3).恢復操作:在GDB中,continue命令通知偵錯程式恢復執行並繼續,直到遇到斷點為止。

(4).臨時斷點:在GDB中,tbreak命令與break相似,但是這一命令設定的斷點的有效期限只到首次到達指定行時為止。GDB中還有建立特殊型別的一次性斷點的命令until和finish。

檢查變數:當偵錯程式暫停了程式的執行後,可以執行一些命令來顯示程式變數的值。這些變數可以是區域性變數、全域性變數、陣列的元素和C語言的struct、C++類中的成員變數等。如果發現某個變數有一個出乎意料的值,那往往是找出某個程式錯誤的位置和性質的重要線索。最基本的變數顯示型別是僅僅輸出當前值。在GDB中使用print命令輸出當前值,如檢視變數j的當前值執行」(gdb) print j」。

設定監視點以應對變數值的改變:監視點結合了斷點和變數檢查的概念。最基本形式的監視點通知偵錯程式,每當指定變數的值發生變化時都暫停程式的執行。例如,在程式執行期間,假設要在變數z改變值時檢視程式的狀態,在GDB中,可以執行如下命令:」(gdb) watch z」,當執行程式時,每當z的值發生變化,GDB都會暫停執行。更好的方法是,可以基於條件表示式來設定監視點。例如,假設要查詢程式執行期間z的值大於28的第一個位置,在GDB中,輸入」(gdb) watch (z>28)」。監視點對區域性變數的用途一般沒有對作用域更寬的變數的用途大,因為一旦變數超出作用域(即當在其中定義變數的函數結束時),在區域性變數上設定的監視點就會被取消。

上下移動呼叫棧:在函數呼叫期間,與呼叫關聯的執行時資訊儲存在稱為棧幀(stack frame)的記憶體區域中。幀中包含函數的區域性變數的值、其形參,以及呼叫該函數的位置的記錄。每次發生函數呼叫時,都會建立一個新幀,並將其推到一個系統維護的棧上;棧最上方的幀表示當前正在執行的函數,當函數退出時,這個幀被彈出棧,並且被釋放。在GDB中可用用如下命令檢視以前的幀:「(gdb) frame 1」。當執行GDB的frame命令時,當前正在執行的函數的幀被編號為0,其父幀(即該函數的呼叫者的棧幀)被編號為1,父幀的父幀被編號為2,以此類推。GDB的up命令將你帶到呼叫棧中的下一個父幀(例如,從幀0到幀1),down則引向相反反向。這樣的操作非常有用,因為根據以前的一部分棧幀中的區域性變數的值,可能發現一些關於引起程式錯誤的程式碼的線索。遍歷呼叫棧不會修改執行路徑,但是它確實允許檢視幀的祖先幀,因此可以檢查通向當前幀的函數呼叫的區域性變數的值。GDB的backtrace命令會顯示整個棧,即當前存在的所有幀的集合

在GDB中,可以通過help命令存取檔案。例如,」(gdb) help breakpoints」,將顯示關於斷點的檔案。不帶引數的GDB命令help提供了一個可用來作為help的引數的命令類別的選單。

1.7 初涉偵錯對談:

注意,GCC中可以用-g選項讓編譯器將符號表(即對應於程式的變數和程式碼行的記憶體地址列表)儲存在生成的可執行檔案中。這是一個絕對必要的步驟,這樣才能在偵錯對談過程中參照原始碼中的變數名和行號

與接受行號(或函數名)的break命令不同,condition接受斷點號。總是可以用命令info break來查詢要查詢的斷點的編號。用break if可以將break和condition命令組合成一個步驟,如下所示」(gdb) break 30 if num_y==1」,然後用run命令再次執行程式。如果要重用老的命令列引數,就不必再次指定命令列引數,可以簡單地輸入run

我們在重新編譯程式之前仍然不必退出GDB。這樣做提供了很大的方便,第一,不需要重新指出命令列引數,只要鍵入run重新執行程式即可。其次,GDB保留了你設定的斷點,因此不需要再次鍵入它。同樣,在偵錯對談期間不要退出再重新啟動文字編輯器,這件事也會分心而且浪費時間。只要將文字編輯器放在一個視窗中,GDB放在另一個視窗中,用第三個視窗偵錯程式即可。

當執行程式試圖存取不允許存取的記憶體時發生了段錯誤。原因通常是由於陣列索引超出了邊界,或者採用了錯誤的指標值。段錯誤也可能由每月顯式地包含指標或陣列變數的記憶體參照產生。

1.8 啟動檔案的使用:

在重新編譯程式碼時,最好不要退出GDB。這樣,你的斷點和建立的其它各種動作都會保留。要是退出GDB,就不得不再次重複鍵入所有這些內容為了不丟失它們,可以將斷點和設定的其它命令放在一個GDB啟動檔案中,然後每次啟動GDB時會自動載入它們

GDB的啟動檔案預設名為.gdbinit。可以將一個檔案放在主目錄中用於一般用途,另一個檔案放在包含該專案特有用途的特定專案的目錄中。GDB在載入可執行檔案之前會讀取主目錄中的啟動檔案。在呼叫GDB時可以指定啟動檔案,例如」$ gdb -command=z x」,表示要在可執行檔案x上執行GDB,首先要從檔案z中讀取命令。

2. 停下來環顧程式

2.1 暫停機制:有3種方式可以通知GDB暫停程式的執行:

(1).斷點:通知GDB在程式中的特定位置暫停執行。

(2).監視點:通知GDB當特定記憶體位置(或者涉及一個或多個位置的表示式)的值發生變化時暫停執行。

(3).捕獲點:通知GDB當特定事件發生時暫停執行。

2.2 斷點概述:在程式中的特定」位置」設定斷點,當到達那一點時,偵錯程式會暫停程式的執行(在GDB這樣的基於文字的偵錯程式的情況下,會出現命令列提示符)。GDB中關於」位置」的含義是非常靈活的,它可以指各種原始碼行、程式碼地址、原始碼檔案中的行號或者函數的入口等。

GDB命令列提示符上面顯式的語句資訊是將要執行的程式碼行,而不是GDB最後執行的程式碼行,如下圖所示,接下來將要執行的是Messy_Test.cpp的第6行。GDB的工作針對的是機器語言指令,而不是原始碼行,一行程式碼可能對應於數行機器語言。GDB之所以可以使用原始碼,是因為可執行檔案中包含了額外的資訊。

2.3 跟蹤斷點:程式設計師建立的每個斷點(包括斷點、監視點和捕獲點)都被標識為從1開始的唯一整數識別符號。這個識別符號用來執行該斷點上的各種操作。偵錯程式還包括一種列出所有斷點及其屬性的方式。當建立斷點時,GDB會告知你分配給該斷點的編號。如果忘記了分配給哪個斷點的編號是什麼,可以使用」(gdb) info breakpoints」命令來提示。通過使用delete命令以及斷點識別符號,可以刪除斷點、監視點及捕獲點。

2.4 設定斷點:GDB中有許多指定斷點的方式,下面是一些最常見的方法:

(1).break funciton:在函數function的入口(第一行可執行程式碼)處設定斷點。

(2).break line_number:在當前活動原始碼檔案的line_number處設定斷點

(3).break filename:line_number:在原始碼檔案filename的line_number處設定斷點。如果filename不在當前工作目錄中,則可以給出相對路徑名或者完全路徑名來幫助GDB查詢該檔案

(4).break filename:function:在檔案filename中的函數function的入口處設定斷點。過載函數或者使用同名靜態函數的程式可能需要使用這種形式。

當設定一個斷點時,該斷點的有效性會持續到刪除、禁用或退出GDB時。然而,臨時斷點是首次到達後就會被自動刪除的斷點。臨時斷點使用tbreak命令設定,它與break採用相同型別的引數GDB實際設定斷點的位置可能與請求將斷點放置的位置不同,當開啟優化來編譯程式時,這個問題可能變得更糟糕。GDB最終使用的是機器指令。在偵錯完成前不應當優化程式碼。

當GDB使用多個斷點中斷一行原始碼時,它只會中斷一次。換言之,當它到達該行程式碼時,如果恢復執行,會忽略恰好在同一行上的其它斷點。事實上,GDB知道是哪個斷點」觸發」了程式停止執行。在具有多個斷點的程式碼行上,觸發中斷的斷點將是識別符號編號最小的斷點。

2.5 展開GDB範例:

啟動偵錯對談時在main()中設定斷點是非常普遍的。這一操作在該函數的第一行上設定斷點:」(gdb) break main」。

在任何給定時間,GDB都有一個焦點,可以將它看作當前」活動」檔案。這意味著除非對命令做了限定,否則都是在具有GDB的焦點的檔案上執行命令。預設情況下,具有GDB的初始焦點的檔案是包含main()函數的檔案,但是當發生如下任一動作時,焦點會轉移到不同的檔案上:

(1).向不同的原始檔應用list命令。

(2).進入位於不同的原始碼檔案中的程式碼。

(3).當在不同的原始碼檔案中執行程式碼時GDB遇到斷點。

使用quit命令離開GDB。

2.6 斷點的永續性:如果在修改和重新編譯程式碼時沒有退出GDB,那麼在下次執行GDB的run命令時,GDB會感知到程式碼已修改,並自動重新載入新版本。然而要注意,斷點是會」移動」的。

2.7 刪除和禁用斷點:這裡提到的一切方法都同樣適用於監視點。

GDB中有兩個用來刪除斷點的命令:delete和clear。delete命令用來基於識別符號刪除斷點。clear命令使用與建立斷點相同的語法刪除斷點。

(1).delete breakpoint_list:刪除斷點使用數值識別符號。斷點可以是一個數位,比如delete 2刪除第二個斷點;也可以是數位列表,比如delete 2 4刪除第二個和第四個斷點。

(2).delete:刪除所有斷點。

(3).clear:清除GDB將執行的下一個指令處的斷點。這種方法適用於要刪除GDB已經到達的斷點的情況

(4).clear function、clear filename:funciton、clear line_number和clear filename:line_number:這些命令根據位置清除斷點,工作方式與對應的break命令相似。

每個斷點都可以被啟用或禁用。只有當GDB遇到啟用的斷點時,才會暫停程式的執行;它會忽略禁用的斷點。預設情況下,斷點的生命期從啟用時開始。在偵錯對談期間,會遇到大量斷點。對於經常重複的迴圈結構或函數,這種情況使得偵錯極不方便。如果要保留斷點以便以後使用,暫時又不希望GDB停止執行,可以禁用它們,在以後需要時再啟用。

使用disable breakpoint-list命令禁用斷點,使用enable breakpoint-list命令啟用斷點,其中breakpoint-list是使用空格分隔的列表,其中有一個或多個斷點識別符號。不帶任何引數地執行disable命令將禁用所有現有斷點。類似地,不帶引數地執行enable命令會啟用所有現有斷點。還有一個enable once命令」enable once breakpoint-list」,在斷點下次引起GDB暫停執行後被禁用。這個命令與tbreak命令非常類似,但是當遇到斷點時,它是禁用斷點,而不是刪除斷點。

2.8 進一步介紹瀏覽斷點屬性:每個斷點都有各種屬性----行號、加在斷點上的條件(如果有的話)、當前啟用/禁用狀態等。

建立的每個斷點都被賦予了唯一的整數識別符號。設定的第一個斷點被賦予」1」,其後的每個斷點都被賦予所賦的上一個識別符號加1.每個斷點也有一些控制調整其行為的屬性。使用唯一識別符號,可以分別調整各個斷點的屬性。可以使用」(gdb) info breakpoints」命令(簡寫為」i b」)來獲得設定的所有斷點的清單,以及它們的屬性。info breakpoints的輸出看上去或多或少類似於如下所示:

(1).識別符號(Num):斷點的唯一識別符號。

(2).型別(Type):這個欄位指出該斷點是斷點、監視點還是捕獲點。

(3).部署(Disp):每個斷點都有一個部署,指示斷點下次引起GDB暫停程式的執行後該斷點上會發生什麼事情。可能的部署有3種:

a.保持(keep):下次到達斷點後不改變斷點。這是新建斷點的預設部署。

b.刪除(del):下次到達斷點後刪除該斷點。使用tbreak命令建立的任何斷點都是這樣的斷點。

c.禁用(dis):下次到達斷點時會禁用該斷點。這是使用enable once命令設定的斷點。

(4).啟用狀態(Enb):這個欄位說明斷點當前是啟用還是禁用的。

(5).地址(Address):這是記憶體中設定斷點的位置。它主要用於組合語言程式設計師,或者試圖偵錯沒有用擴充的符號表編譯的可執行檔案的人。

(6).位置(What):各個斷點位於原始碼中的特定行上。What欄位顯示了斷點所在位置的行號和檔案號。

2.9 恢復執行:方法有3類:

(1).使用step和next」單步」偵錯程式,僅執行程式碼的下一行然後再次暫停。一旦GDB在斷點處停止,可以使用next(簡寫為n)和step(簡寫為s)命令來單步偵錯程式碼。當觸發了斷點,並且GDB暫停後,可以使用next和step來執行緊接著的下一行程式碼。當執行了這一行後,GDB會再次暫停,並給出一個命令列提示符。這兩個命令的區別是它們如何處理常式呼叫:next執行函數,不會在其中暫停,然後在呼叫之後的第一條語句處暫停。step在函數中的第一個語句處暫停。next將函數呼叫看作一行程式碼,並在一個操作中執行整個函數,這稱為單步越過(stepping over)函數。next和step命令都採用一個可選的數值引數,表示要使用next或step執行的額外行數。換言之,next 3與在一行中鍵入next三次(或者鍵入next一次,然後按下ENTER鍵兩次)的作用相同。如果是在沒有函數呼叫的那部分程式碼中,那麼使用next還是step並沒有關係,在這種情況下,兩個命令是完全相同的

(2).由使用continue組成,使GDB無條件地恢復程式的執行,直到它遇到另一個斷點或者程式結束。簡寫為c,與僅執行一行程式碼的step和next相反,這個命令使GDB恢復程式的執行,直到觸發斷點或者程式結束。continue命令可以接受一個可選的整數引數n,這個數位要求GDB忽略下面n個斷點。

(3).涉及條件,用finish或until命令恢復。在這種情況下,GDB會恢復執行,程式繼續執行直到遇到某個預先確定的條件(比如,到達函數的末尾),到達另一個斷點,或者程式完成。

使用finish恢復程式執行:想返回到單步進入被呼叫函數之前GDB所在的呼叫函數,這時應該使用finish命令finish命令(簡寫為fin)指示GDB恢復執行,直到恰好在當前棧幀完成之後為止。也就是說,這意味著如果你在一個不是main的函數中,finish命令會導致GDB恢復執行,直到恰好在函數返回之後為止。finish的另一個常見用途是當不小心單步進入原本希望單步越過的函數時(換言之,當需要使用next時使用了step),在這種情況下,使用finish可以將你正好放回到使用next會位於的位置。

如果在一個遞迴函數中,finish只會將你帶到遞迴的上一層。這是因為每次呼叫都被看作在它自己許可權內的函數呼叫,因為每個函數都有各自的棧幀。如果要在遞迴層次較高時完全退出遞迴函數,那麼更適合使用臨時斷點及continue,或者用until命令。

使用until恢復程式執行:finish命令在不進一步在函數中暫停(除了中間斷點)的情況下完成當前函數的執行。類似地,until命令(簡寫為u)通常用來在不進一步在迴圈中暫停(除了迴圈中的中間斷點)的情況下完成正在執行的迴圈,使用until會執行迴圈的其餘部分,讓GDB在迴圈後面的第一行程式碼處暫停。如果在迴圈的末尾,執行until命令導致回跳到迴圈頂部,這時只要再次執行until,就可以離開當前的迴圈。until命令也可以接受原始碼中的位置作為引數,事實上,該命令接受的引數與break命令相同。

2.10 條件斷點:這類似於監視點的工作方式,但是有一個重要區別。如果懷疑某個變數得到了一個偽值,那麼條件斷點相當適用於監視點。每當該變數的值發生變化時,監視點都會中斷。條件斷點只會在懷疑有問題的程式碼處當變數呈現該懷疑值時才中斷。設定條件斷點的語法為」break break-args if (condition)」。其中break-args是可以傳遞給break以指定斷點位置的任何引數,condition是定義的布林表示式。括著condition的圓括號是可選的。如」(gdb) break main if argc > 1」:說明了如果使用者向程式中鍵入一些命令列引數,則在main()處中斷。條件中斷極其有用,尤其適用於索引變數的特定值出了問題的迴圈結構。條件中斷也極其靈活,不僅可以測試相等或不相等的變數。在有效的C條件語句中幾乎可以使用任何表示式。無論使用什麼表示式,都需要具有布林值,即真(非0)或假(0)

(1).相等、邏輯和不相等運運算元(<、<=、==、!=、>、>=、&&、||等),如」(gdb) break 180 if string==nullptr && i < 0」

(2).按位元和移位運運算元(&、|、^、>>、<<等),如」(gdb) break test.c:34 if (x & y) == 1」

(3).算術運運算元(+、-、*、/、%),如」(gdb) break myfunc if i % (j + 3) != 0」

(4).你自己的函數,只要它們被連結到程式中,如」(gdb) break test.c:myfunc if ! check_variable_sanity(i)」

(5).庫函數,只要該庫被連結到程式碼中,例如:」(gdb) break 44 if strlen(mystring) == 0」

由於優先順序的次序規則在起作用,因此可能需要使用圓括號將一些結構括起來。此外,如果在GDB表示式中使用庫函數,而該庫不是用偵錯符號編譯的(幾乎肯定是這種情況),那麼唯一能在斷點條件中使用的返回值型別為int。換言之,如果沒有偵錯資訊,GDB會假設函數的返回值是int型別。當這種假設不正確時,函數的返回值會被曲解。

可以對正常斷點設定條件以將它們轉變為條件斷點。例如,如果設定了斷點3作為無條件斷點,但是現在希望新增條件i==3,只要鍵入」(gdb) cond 3 i == 3」,如果以後要刪除條件,但是保持該斷點,只要鍵入」(gdb) cond 3」。

2.11 斷點命令列表:使用」斷點命令列表」可以讓GDB在每次到達某個斷點時自動執行一組命令,從而自動完成反覆檢視相同的變數這一過程。使用commands命令設定命令列表。逐條輸入命令,然後鍵入end表示輸入命令完畢,從那以後,每當GDB在這個斷點處中斷時,它都會執行你輸入的任何命令。

可以使用GDB的define命令建立宏。可以將宏儲存在.gdbinit檔案中,以便將它用於其它程式。鍵入」(gdb) show user」可以得到所有宏的列表。

2.12 監視點:是一種特殊型別的斷點,它類似於正常斷點,是要求GDB暫停程式執行的指令。區別在於監視點沒有」住在」某一行原始碼中,取而代之的是,監視點是指示GDB每當某個表示式改變了值就暫停執行的命令,如」(gdb) watch (i | j > 12) && i > 24 && strlen(name) > 6」。可以將監視點看作被」附加」在表示式上,當表示式的值改變時,GDB會暫停程式的執行。

雖然管理監視點和斷點的方式相同,但是兩者之間有一個重要區別。斷點與原始碼中的一個位置關聯。只要程式碼沒有改變,就不存在某行程式碼」超出作用域」的風險。因為C語言有嚴格的作用域規則,所以只能監視存在且在作用域內的變數。一旦變數不再存在於呼叫棧的任何幀中(當包含區域性變數的函數返回時),GDB會自動刪除監視點。GDB只能監視單個執行緒中的變數

當變數var存在且在作用域中時,可以通過使用如下命令來設定監視點:」(gdb) watch var」,該命令會導致每當var改變值時GDB都中斷。GDB實際上是在var的記憶體位置改變值時中斷。監視點不僅僅限於監視變數,事實上,可以監視涉及變數的表示式,每當表示式修改值時,GDB都會中斷。

GDB中的表示式可以包含:

(1).GDB方便變數(convenience variables)。

(2).程式中的任何在作用域內的變數。

(3).任何種類的字串、數值或字元常數。

(4).前處理器宏(如果程式被編譯為包括前處理器偵錯資訊)。

(5).條件、函數呼叫、型別強制轉換和所用語言定義的運運算元。

3. 檢查和設定變數

3.2 變數的高階檢查和設定:

GDB的display命令(簡寫為disp)在執行中每次有暫停(由於有斷點,使用next或step命令等)時就輸出指定條目的值,只有當變數在作用域中時才生效。

在GDB中可以輸出整個陣列的值,如」(gdb) p x」或」(gdb) *pointer@number_of_elements」。

在GDB中,可以通過呼叫info locals命令得到當前棧幀中所有區域性變數的值列表

在有些情況下,可能希望檢查給定地址的記憶體,而不是通過變數的名稱。GDB為這種目的提供了x命令。

print和display命令允許指定可選的格式,如」(gdb) p/x y」,會以十六進位制格式顯示變數,而不是十進位制格式。其它常用的格式為c表示字元(character),s表示字串(string),f表示浮點(floating-point)。

可以臨時禁用某個顯示項,例如」(gdb) dis disp 1」,臨時禁用顯示列表中的條目1.如果不知道條目號,可以通過info disp命令檢查。要重新啟用條目,使用enable,例如」(gdb) enable disp 1」。要完全刪除顯示的條目,使用undisplay,例如」(gdb) undisp 1」。

3.3 設定變數:」(gdb) set x = 12」,會將x的當前值改成12。可以通過GDB的set args命令設定程式的命令列引數,如」(gdb) set args 1 52 19 11」,並不會立即將argv[1]改成1,argv[2]改成52,等等,直到下次執行run命令時才會發生這些變化。GDB有用來檢查當前函數引數的info args命令

3.4 GDB自己的變數:

(1).使用值歷史:GDB的print命令的輸出值被標為$1、$2等,這些值統稱為值歷史。在將來執行print命令時使用這樣的值歷史會比較方便,如」(gdb) p *$1」。

(2).方便變數(convenience variable):會根據C規則改變值。

4. 程式崩潰處理

4.2 核心檔案:如果在程式執行期間建立了核心檔案(core file,俗稱轉儲核心),則可以開啟偵錯程式(比如GDB)上的該檔案,然後開始常規GDB操作。

核心檔案包含程式崩潰時對程式狀態的詳細描述:棧的內容(或者,如果程式是多執行緒的,則是各個執行緒的棧),CPU暫存器的內容(同樣,如果程式是多執行緒的,則是每個執行緒上的一組暫存器值),程式的靜態分配變數的值(全域性與static變數),等等。

大多數現代shell都會在一開始就防止編寫核心檔案。在bash中,可以使用ulimit命令控制核心檔案的建立。」$ ulimit -c n」,其中n是核心檔案的最大大小,以千位元組為單位。超過nKB的任何核心檔案都不會被編寫。如果沒有指定n,shell就會顯示核心檔案上的當前限制。如果允許任意大小的核心檔案,可以使用:」$ ulimit -c unlimited」。

如果程式的原始碼不可用,或者可執行檔案不是用增強的符號表編譯的,甚或當我們沒有計劃偵錯可執行檔案,核心檔案可能都不會有太大的用處。

core file的使用範例可參考:https://blog.csdn.net/fengbingchun/article/details/97113451

4.3 擴充套件範例:

在偵錯對談期間,修改程式碼時永遠不要退出GDB。這樣就不必費時間來啟動,可以保留我們的斷點,等等。類似地,我們要保持文字編輯器開啟。在偵錯時的兩次編譯之間留在同一個編輯器對談中,我們可以充分利用編輯器的」復原」功能。例如,偵錯過程中的一個常見策略是臨時刪除部分程式碼,以便集中精力於你認為存在程式錯誤的餘下部分。完成檢查後,只要使用編輯器的復原功能恢復被刪除的程式碼行即可。因此,在螢幕上我們通常有一個GDB視窗,以及一個編輯器視窗。我們還開啟了第三個視窗用於執行編譯器命令,甚至最好是通過編輯器執行命令。

記住,當GDB注意到重新編譯了程式後,它會自動載入新的可執行檔案,因此同樣不需要退出和重新啟動GDB

5. 多活動上下文中的偵錯

5.1 偵錯客戶/伺服器網路程式:socket

5.2 偵錯多執行緒程式碼:

檢視指定執行緒的棧,如執行緒3,可執行」(gdb) thread 3」,再執行」(gdb) bt」。多執行緒程式的執行有一定的隨機性。

下面是與執行緒相關的GDB命令用法彙總:

(1).info threads:給出關於當前所有執行緒的資訊,帶有」*」號表示當前執行緒;

(2).thread 3:改成執行緒3;

(3).break 88 thread 3:當執行緒3到達原始碼行88時停止執行;

(4).break 88 thread 3 if x == y:當執行緒3到達原始碼行88,並且變數x和y相等時停止執行。

5.3 偵錯並行應用程式:共用記憶體和訊息傳遞、MPI

5.4 擴充套件範例:OpenMP

6. 特殊主題

6.1 根本無法編譯或載入:

6.2 偵錯GUI程式:

7. 其它工具

8. 對其它語言使用GDB

GitHubhttps://github.com/fengbingchun/Messy_Test