gdb常用命令及使用gdb調試多進程多線程程序
一、常用普通調試命令1.簡單介紹GDB
介紹: gdb是Linux環境下的代碼調試?具。
使?:需要在源代碼?成的時候加上 -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)其他命令
- info(i) locals: 查看當前棧幀局部變數的值
- info break : 查看斷點信息
- info(或i) breakpoints: 參看當前設置了哪些斷點
- finish: 執?到當前函數返回,然後挺下來等待命令
- set var: 修改變數的值
- display 變數名: 跟蹤查看?個變數,每次停下來都顯?它的值
- undisplay: 取消對先前設置的那些變數的跟蹤
- until X?號: 跳?X? 直接回
- n 或 next: 單條執?
- p 變數: 列印變數值。
二、使用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
添加新的調試進程: add-inferior [-copies n] [-exec executable] ,可以用file executable來分配給inferior可執行文件。
其他:remove-inferiors infno, detach inferior
查看gdb默認的參數設置:
2.演示代碼下面這段代碼的主要流程就是在main函數中fork創建一個子進程,然後在父進程中又創建一個線程,接著就使用gdb進行調試(block子進程)。
1 #include 如果直接運行程序,那麼輸出的結果如下: 3.1設置調試模式和Catchpoint 設置調試父子進程,gdb跟主進程,子進程block在fork位置。
2 #include
4 #include
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]
", cParent, pid, "step1");
26
27 if (pthread_create(&pt, NULL, (void *)*ParentDo, cThread))
28 {
29 printf("[%s]: Can not create a thread.
", 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]
", argv, pid, tprefix, tid, "step2"); //add the second breakpoint.
43 printf("[%s]: [%d] [%s] [%lu] [%s]
", 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]
", prefix, pid, "step1");
53 return;
54 }
這時可以另開一個終端,使用如下命令查看當前CentOS系統所有進程的狀態:發現父進程PID為10062,通過fork產生的子進程為10065:
同時,可以使用命令cat /proc/10062/status
查看當前進程的詳細信息:進程PID為10060,它的父進程(即GDB進程)為10062,同時這也是追蹤進程ID,線程數Threads為1(共享使用該信號描述符的線程數,在POSIX多線程序應用程序中,線程組中的所有線程使用同一個信號描述符)。
在程序的第46行設置斷點,並運行到斷點處:
這時再次使用命令pstree -pul查看當前系統進程的狀態:發現此時仍然只有父進程13162和子進程13159。
3.3 執行到第一個斷點此時如果切換到子進程13162重新切換到父進程131593.4 設置第二個斷點並調試
在第50行設置斷點繼續調試主進程(使父進程產生線程),其中父進程和線程到底是誰先執行是由內核調度控制的。
這時使用命令pstree -pul查看當前系統進程的狀態:存在父進程13159和子進程13162以及父進程創建的一個線程14208(線程用大括弧{}表示)。同時,使用命令cat /proc/13159/status
查看當前進程的詳細信息:進程PID為13159,它的父進程(即GDB進程)為13154,同時這也是追蹤進程ID,線程數Threads為2(當前父進程13159+線程14208)。
這時使用命令查看當前系統進程的狀態:存在父進程13159和子進程13162,其中線程14208已經結束了。
本文部分參考:https://typecodes.com/cseries/multilprocessthreadgdb.html
※Spring Data JPA與PostgreSQL的jsonb類型集成與支持
※細說Nullable
※Javascript 「繼承」
TAG:達人科技 |
※Python多進程編程
※入門Python多線程/多進程編程
※Android 進程和線程
※深入Python多進程編程基礎
※用 Python 管理系統進程
※Python 並發編程之線程池/進程池
※Python學習之進程和線程
※Python網路編程——進程
※一篇文章學會使用 Android IPC 多進程
※Python的分散式進程
※python 進程實現多任務
※走進Node.js之多進程模型
※python threading中處理主進程和子線程的關係
※深入Python多進程通信原理與實戰——圖文
※linux進程信息查詢命令lsof詳解
※蘋果iOS 13將限制VolP後台進程 或影響Facebook等應用通話功能
※Perl 進程管理
※Python學習之多進程詳解
※linux中如何用命令結束一個進程
※豐田使用HoloLens來加快造車進程