gdb常用命令及gdb調試多進程/線程程序&coredump


一、常用普通調試命令

1.簡單介紹GDB

介紹: gdbLinux環境下的代碼調試⼯具。
使⽤:需要在源代碼⽣成的時候加上 -g 選項
開始使⽤: gdb binFile
退出: ctrl + d quit

2.調試過程

(1)list命令

  • list  linenum        顯⽰binFile第linenum行周圍的源代碼,接着上次的位置往下列,每次列10⾏。
  • list  function       顯示函數名為function的函數的源程序
  • list                     顯示當前行后面的源程序
  • list -                  顯示當前行前面的源程序

(2)run或r

    運行程序。
  • run args         run命令可以直接接命令行參數值,也可以在執行run之前通過 set args + 參數值實現。

(3)break(b)

打斷點,使用方法:
  • b  linenum                           在某行打斷點
  • b  +offset/-offset                  在當前行號的前面或后面offset停住
  • b  filename:linenum             在某文件的某行打斷點
  • b  filename:function            在某文件某個函數入口停住
  • b  *address                         在程序的運行地址處停住
  • b                                         沒有參數在下一句停住
  • b where if condition            當某個條件滿足時,在某一行停住(這個很有用,比如b 10 if ret == 5)
  • breaktrace(或bt)                 查看各級函數調⽤及參數
  • delete breakpoints            刪除所有斷點
  • delete breakpoints n         刪除序號為n的斷點
  • disable breakpoints           禁⽤斷點
  • enable breakpoints            啟⽤斷點
對於break命令,我們要靈活使用。例如打多個斷點。多線程程序中我們可以主函數中線程創建后立即打斷點,執行線程函數入口打斷點等。

(4)單步命令

    普通用法就不說了。
  • step count         一次性執行count步,如果有函數會進入函數
  • next count         一次執行count,不進入函數
  • finish                 運行程序,直到當前函數完成返回,並打印函數返回時的堆棧地址和返回值以及參數信息
  • until                   退出循環體(尤其是針對for循環這種,很煩的)

(5)continue命令
continue(或c):從當前位置開始連續⽽⾮單步執⾏程序

    當程序被停住之后,可以使用continue(c)命令,恢復程序的運行直到程序結束,或到達下一個斷點。 這里要注意如果沒有斷點程序是會直接結束的。

(6)print(p)命令

    這個命令比較常用,用來查看我們想看的內容。比如有關數組可以看全部,也可以看從左到右某一部分:


    print命令針對變量查看的輸出格式有:
  • x 按十六進制格式顯示變量
  • d 按十進制格式顯示變量
  • u 按十六進制格式顯示無符號整型
  • o 按八進制格式顯示變量
  • t 按二進制格式顯示變量t 按二進制格式顯示變量
  • a 按十六進制格式顯示變量
  • c 按字符格式顯示變量
  • f 按浮點數格式顯示變量

(7)watch命令

    這個命令比較有用。watch一般用來觀察某個表達式(變量也是一種表達式)的值是否有變化,如果有變化,馬上停住程序。我們有一下幾種方法設置觀察點:
  • watch   expr                 為表達式expr設置一個觀察點,一旦表達式值有變化,馬上停住程序
  • rwatch  expr                  當表達式expr被讀時,停住程序
  • awatch expr                  當表達式的值被讀或被寫時,停住程序。
  • info      watchpoints       列出所有觀察點(info指令通常可以去套 舉例如下,演示觀測*i的值,一旦變化停下來:

    在循環中我們也可以使用watch,配合ignore,它是除了until命令之外又一個可以讓我們跳出循環的方法,不過watch+ignore更強大,可以任意跳轉到第i次循環。它們的意思就是觀察一個變量,可以理解為斷點,ignore這個斷點多少次,然后用continue就可以直接跳過了。


 

 

 

 

(8)examine命令

    使用該命令來查看內存地址中的值。語法是:x/u addr 
    addr表示一個內存地址。“x/”后的n、f、u都是可選的參數,n 是一個正整數,表示顯示內存的長度,也就是說從當前地址向后顯示幾個地址的內容;f 表示顯示的格式,如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i;u 表示從當前地址往后請求的字節數,如果不指定的話,GDB默認是4字節。u參數可以被一些字符代替:b表示單字節,h表示雙字節,w表示四字節,g表示八字節。當我們指定了字節長度后,GDB會從指定的內存地址開始,讀寫指定字節,並把其當作一個值取出來。n、f、u這3個參數可以一起使用,例如命令“x/3uh 0x54320”表示從內存地址0x54320開始以雙字節為1個單位(h)、16進制方式(u)顯示3個單位(3)的內存。

 

 

(9)jump命令

    jump命令不會改變程序棧的內容,一般只在同一函數內跳轉。
  • jump linespec     指定下一條語句的運行點,linespec可以是linenum,filename+linenum,+offset這幾種形式
  • jump address      跳到代碼行的地址

(10)signal命令

使用signal 信號名(如SIGINT)這種方式把信號發送給程序,如果程序注冊了signal_handler函數,還可以進行相應的處理,幫助調試程序。

(11)set命令

  • set args       設置命令行參數
  • set env environmentVarname=value 設置環境變量。如:set env USER=benben

(12)call命令

  • call function     強制調用某函數
    強制調用某函數,它會顯示函數返回值(如果函數返回值不是void)。print命令也可以完成該功能。

(13)disassemble命令

    反匯編命令,查看執行時源代碼的機器碼。

 

 

 
 
 
 
 
 
 
(14)其他命令
  • infoi) locals:  查看當前棧幀局部變量的值
  • info break :      查看斷點信息
  • info(i) breakpoints: 查看當前設置了哪些斷點
  • finish:               執⾏到當前函數返回,然后挺下來等待命令
  • set var:             修改變量的值
  • display 變量名: 跟蹤查看⼀個變量,每次停下來都顯⽰它的值
  • undisplay:        取消對先前設置的那些變量的跟蹤
  • until X⾏號:      跳⾄X 直接回
  • n next:         單條執⾏
  • p 變量:             打印變量值。

(14)另外一些常用調試命令

until 跳出循環。在執行完循環體內的最后一條語句之后執行 until, 就會執行完循環體到后面的一個語句停下。

finish 跳出當前函數,執行完當前的函數。

tui是一個命令行的界面,能同時把代碼顯示出來。

二、使用gdb調試core文件

1.認識core文件

linux下程序異常中止退出或崩潰的時,內核將程序在運行期間的內存狀態等相關信息轉存到磁盤,這個轉存的文件叫core dump文件。文件中記錄程序當時的內存調用、堆棧引用、進程和線程調用等信息,以幫助開發或維護人員了解異常發生當時的環境參數和信息。
段錯誤(segfault)也稱為核心轉儲;指程序試圖操作不允許訪問或試圖訪問的不合法內存的情況。可能導致段錯誤的原因主要有:
1、試圖解引用空指針(你不允許訪問內存地址0)
2、試圖解引用不在你內存中的其他指針
3、一個C++ vtable虛表指針被破壞並指向錯誤的地方,這導致程序試圖去執行一些不可執行的內存。
4、其他情況,比如未對齊的內存訪問也可能會出現段錯誤。

2.配置生成core文件

編譯時需要加 -g 選項使程序生成調試信息: gcc -g core_test.c -o core_test

①使用 ulimit -c 查看core開關,如果為0表示關閉,不會生成core文件;
②使用 ulimit -c [filesize] 設置core文件大小,當最小設置為4之后才會生成core文件;
③使用 ulimit -c unlimited 設置core文件大小為不限制,這是常用的做法;
④如果需要開機就執行,則需要將這句命令寫到 /etc/profile 等文件。
core文件命名和保存路徑:

①core文件有默認的名稱和路徑;
自己命名和指定保存路徑,可以通過 /proc/sys/kernel/core_pattern 設置 core 文件名和保存路徑,方法如下:
    echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
    命名的參數列表:
    %p - insert pid into filename 添加pid
    %u - insert current uid into filename 添加當前uid
    %g - insert current gid into filename 添加當前gid
    %s - insert signal that caused the coredump into the filename 添加導致產生core的信號
    %t - insert UNIX time that the coredump occurred into filename 添加core文件生成時的unix時間
    %h - insert hostname where the coredump happened into filename 添加主機名
    %e - insert coredumping executable name into filename 添加命令名。

3.調試core文件

1)方法1: gdb [exec file] [core file] 然后執行bt看堆棧信息(或者where命令)

(2)方法1:gdb -c [core file],然后 file [exec file],最后再使用 bt 查看錯誤位置

https://blog.csdn.net/sunxiaopengsun/article/details/72974548

三、使用gdb調試多進程多線程程序

1.設置

默認設置下,在調試多進程程序時GDB只會調試主進程。但是GDB(>V7.0)支持多進程的分別以及同時調試,換句話說,GDB可以同時調試多個程序。只需要設置follow-fork-mode(默認值:parent)和detach-on-fork(默認值:on)即可。兩者結合起來構成了GDB的調試模式。
      follow-fork-mode  detach-on-fork    說明
parent                   on                     只調試主進程(GDB默認)
child                     on                     只調試子進程
parent                   off                    同時調試兩個進程,gdb跟主進程,子進程block在fork位置
child                     off                    同時調試兩個進程,gdb跟子進程,主進程block在fork位置
   設置方法:set follow-fork-mode [parent|child]   set detach-on-fork [on|off]
   查詢正在調試的進程:info inferiors
   切換調試的進程: inferior <infer number>
   添加新的調試進程: add-inferior [-copies n] [-exec executable] ,可以用file executable來分配給inferior可執行文件。
   其他:remove-inferiors infno, detach inferior
查看gdb默認的參數設置:

2.演示代碼

下面這段代碼的主要流程就是在main函數中fork創建一個子進程,然后在父進程中又創建一個線程,接着就使用gdb進行調試(block子進程)。

 1 #include <stdio.h>
 2 #include <pthread.h>
 3 #include <sys/types.h>
 4 #include <unistd.h>
 5 
 6 int main(int argc, const char **argv)
 7 {
 8     int pid;
 9     pid = fork();
10     if (pid != 0)    //add the first breakpoint.
11         Parent();
12     else
13         Child();
14     return 0;
15 }
16 
17 //Parent process handle.
18 void Parent()
19 {
20     pid_t pid = getpid();
21     char cParent[] = "Parent";
22     char cThread[] = "Thread";
23     pthread_t pt;
24 
25     printf("[%s]: [%d] [%s]\n", cParent, pid, "step1");
26 
27     if (pthread_create(&pt, NULL, (void *)*ParentDo, cThread))
28     {
29         printf("[%s]: Can not create a thread.\n", cParent);
30     }
31 
32     ParentDo(cParent);
33     sleep(1);
34 }
35 //Parent process handle after generate a thread.
36 void * ParentDo(char *argv)
37 {
38     pid_t pid = getpid();
39     pthread_t tid = pthread_self();   //Get the thread-id selfly.
40     char tprefix[] = "thread";
41 
42     printf("[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step2");  //add the second breakpoint.
43     printf("[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step3");
44 
45     return NULL;
46 }
47 //Child process handle.
48 void Child()
49 {
50     pid_t pid = getpid();
51     char prefix[] = "Child";
52     printf("[%s]: [%d] [%s]\n", prefix, pid, "step1");
53     return;
54 }

如果直接運行程序,那么輸出的結果如下:

3.gdb調試

3.1設置調試模式和Catchpoint

設置調試父子進程,gdb跟主進程,子進程block在fork位置。

這時可以另開一個終端,使用如下命令查看當前CentOS系統所有進程的狀態:發現父進程PID為10062,通過fork產生的子進程為10065:

 同時,可以使用命令cat /proc/10062/status查看當前進程的詳細信息:進程PID為10060,它的父進程(即GDB進程)為10062,同時這也是追蹤進程ID,線程數Threads為1(共享使用該信號描述符的線程數,在POSIX多線程序應用程序中,線程組中的所有線程使用同一個信號描述符)。

3.2 設置第一個斷點

在程序的第46行設置斷點,並運行到斷點處:

這時再次使用命令pstree -pul查看當前系統進程的狀態:發現此時仍然只有父進程13162和子進程13159。

3.3 執行到第一個斷點此時如果切換到子進程13162

重新切換到父進程13159

3.4 設置第二個斷點並調試

在第50行設置斷點繼續調試主進程(使父進程產生線程),其中父進程和線程到底是誰先執行是由內核調度控制的。

這時使用命令pstree -pul查看當前系統進程的狀態:存在父進程13159和子進程13162以及父進程創建的一個線程14208(線程用大括號{}表示)。

同時,使用命令cat /proc/13159/status查看當前進程的詳細信息:進程PID為13159,它的父進程(即GDB進程)為13154,同時這也是追蹤進程ID,線程數Threads為2(當前父進程13159+線程14208)。

3.5 查看第二個斷點處的調試信息

3.6手動切換到線程
3.7 開始執行第二個斷點處的代碼

這時使用命令查看當前系統進程的狀態:存在父進程13159和子進程13162,其中線程14208已經結束了。

本文部分參考:https://typecodes.com/cseries/multilprocessthreadgdb.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM