Linux gdb多进程、多线程调试
目錄
常用命令
堆棧相關命令
更為詳細的斷點調試
gdb多進程調試
gdb多線程調試
前言
gdb 是?linux?平臺下進行程序調試的最常用的工具。簡單的程序調試就是加斷點,然后一步一步讓程序運行,直到找到 bug 。一般的程序調試起來比較簡單,但是在多進程或多線程情況下調試起來就比較麻煩。
若 test.c 是你想要調試的程序,那么在編譯時需要加 -g,即 gcc test.c -g -o test。完成編譯后使用命令:gdb test。
?
?
常用命令
| 命令 | 命令縮寫及例子 | 說明 |
| list + n | l + n | 顯示源碼第n行前后的代碼,顯示范圍有限。 |
| break + n | b + n | 在第n行設置斷點 |
| info | i | 描述程序狀態 |
| run | r | 開始運行程序 |
| display | disp | 跟蹤查看某個變量的值 |
| info display | ? | 用于顯示當前所有要顯示值的表達式的情況 |
| undisplay | undisplay + 編號 | 用于結束某個表達式值的顯示 |
| step | s | 執行下一條語句,如果該語句為函數調用,則進入函數執行其中的第一條語句 |
| next | n | 執行下一條語句,如果該語句為函數調用,不會進入函數內部執行 |
| | p | 打印內部變量的值 |
| continue | c | 繼續運行,直到遇到下一個斷點 |
| start | st | 開始執行程序,在main函數的第一條語句前面停下來 |
| kill | k | 終止正在調試的程序 |
| quit | q | 退出gdb |
| set args | set args arg1 arg2 | 設置運行參數 |
| show args | show args | 查看運行參數 |
| finish | finish | 一直運行到函數返回并打印函數返回時的堆棧地址和返回值及參數值等信息 |
?
堆棧相關命令
| 命令 | 例子 | 說明 |
| backtrace | bt | 查看堆棧信息 |
| frame | f 1 | 查看棧幀 |
| info reg | info reg/ i r | 查看寄存器使用情況 |
| info stack | info stack | 查看堆棧使用情況 |
| up/down | up/down | 跳到上一層/下一層函數 |
這里以一個簡單的程序為例,進行調試。
#include <bits/stdc++.h> using namespace std; #define M 5int fact(int n) //線性遞歸 {if (n < 0)return 0;else if(n == 0 || n == 1)return 1;elsereturn n * fact(n - 1); }int facttail(int n, int a) //尾遞歸 {if (n < 0)return 0;else if (n == 0)return 1;else if (n == 1)return a;elsereturn facttail(n - 1, n * a); }int facttail1(int n, int a) {while(n > 0){a = n * a;n--;}return a; }int main() {//printf("%p", facttail);int a = fact(M);int b = facttail(M, 1);cout << "A:" << a <<endl;cout << "B:" << b <<endl; }?
(1)開始 gdb 調試
?
(2)設置斷點
?
(3)查看棧的使用情況
?
?
更為詳細的斷點調試
| 命令 | 例子 | 說明 |
| break + 設置斷點的行號 | break n | 在n行處設置斷點 |
| tbreak + 行號或函數名 | tbreak n/func | 設置臨時斷點,到達后被自動刪除 |
| break + filename + 行號 | break main.c:10 | 用于在指定文件對應行設置斷點 |
| break + <0x...> | break 0x3400a | 用于在內存某一位置處暫停 |
| break + 行號 + if + 條件 | break 10 if i==3 | 用于設置條件斷點,在循環中使用非常方便 |
| info breakpoints/watchpoints [n] | info break | n表示斷點編號,查看斷點/觀察點的情況 |
| clear + 要清除的斷點行號 | clear 10 | 用于清除對應行的斷點,要給出斷點的行號,清除時GDB會給出提示 |
| delete + 要清除的斷點編號 | delete 3 | 用于清除斷點和自動顯示的表達式的命令,要給出斷點的編號,清除時GDB不會給出任何提示 |
| disable/enable + 斷點編號 | disable 3 | 讓所設斷點暫時失效/使能,如果要讓多個編號處的斷點失效/使能,可將編號之間用空格隔開 |
| awatch/watch + 變量 | awatch/watch i | 設置一個觀察點,當變量被讀出或寫入時程序被暫停 |
| rwatch + 變量 | rwatch i | 設置一個觀察點,當變量被讀出時,程序被暫停 |
| catch | ? | 設置捕捉點來補捉程序運行時的一些事件。如:載入共享庫(動態鏈接庫)或是C++的異常 |
| tcatch | ? | 只設置一次捕捉點,當程序停住以后,應點被自動刪除 |
?
gdb多進程調試
| 命令 | 例子 | 說明 |
| set follow-fork-mode?[parent|child] ? ? | set follow-fork-mode?parent or child | 設置調試器的模式,mode參數可以是: parent :fork之后調試原進程,子進程不受影響,這是缺省的方式 child :fork之后調試新的進程,父進程不受影響。 ? |
| show?follow-fork-mode | show follow-fork-mode | 顯示當前調試器的模式 |
| set detach-on-fork [on|off] ? | set detach-on-fork on or off | 設置gdb在fork之后是否detach進程中的其中一個,或者繼續保留控制這兩個進程?。 on:子進程(或者父進程,依賴于follow-fork-mode的值)會detach然后獨立運行,這是缺省的mode off:兩個進程都受gdb控制,一個進程(子進程或父進程,依賴于follow-fork-mode)被調試,另外一個進程被掛起 |
| info?inferiors | info?inferiors | 顯示所有進程 |
| inferiors processid | inferiors 2 | 切換進程 |
| detach inferiors processid | detach inferiors processid | detach 一個由指定的進程,然后從fork 列表里刪除。這個進程會被允許繼續獨立運行。 |
| kill?inferiors? processid | kill inferiors? processid | 殺死一個由指定的進程,然后從fork 列表里刪除。 |
| catch fork | catch fork | 讓程序在fork,vfork或者exec調用的時候中斷 |
調試多進程時,需要設置?detach-on-fork?的值,默認值為?on,設置為?off?的含義:一個進程被調試,另外一個進程被掛起,這樣就可以交替的調試進程。follow-fork-mode?的默認值為?parent,即默認調試父進程。調試代碼:
#include <stdio.h> #include <unistd.h> #include <stdlib.h>int main() {int num = 0;pid_t pid = fork();if(pid == 0) //子進程{while(1){num++;printf("child:pid:[%d] num:[%d]\n", getpid(), num);sleep(2);}}else{while(1){num = num + 2;printf("parent:pid:[%d] num:[%d]\n", getpid(), num);sleep(2);}}return 0; }?
(1)?查看系統默認的 follow-fork-mode 和 detach-on-fork 及 設置follow-fork-mode 和 detach-on-fork
show follow-fork-mode show detach-on-fork set follow-fork-mode [parent|child] ?? set detach-on-fork [on|off]?
(2) 設置斷點并查看斷點信息
?
(3) 運行程序并使用?info inferiors 命令 (顯示GDB調試的所有進程,其中帶有*的進程是正在調試的進程)
?
(4) 使用 inferior + [編號]?切換進程,對子進程進行調試
?
(5) 繼續運行程序,觀察子進程的輸出
?
(6) 中斷子進程運行后,開始逐步調試
?
(7) 再次切換回父進程完成調試
?
?
gdb多線程調試
| 命令 | 例子 | 說明 |
| info threads | info threads | 查詢線程信息 |
| thread + 線程號 | thread 2 | 切換線程 |
| thread apply [threadno] [all] + 命令 | thread apply [threadno] [all] bt | 線程根據相應的命令完成操作 |
| set print thread-events | set print thread-events | 控制線程開始和結束時的打印信息 |
| show print thread-events | show print thread-events | 顯示線程打印信息的開關狀態 |
調試代碼:
#include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <errno.h> #include <unistd.h>/*全局變量*/ int sum = 0; /*互斥量 */ pthread_mutex_t mutex; /*聲明線程運行服務程序*/ void* pthread_function1 (void*); void* pthread_function2 (void*);int main (void) {/*線程的標識符*/pthread_t pt_1 = 0;pthread_t pt_2 = 0;int ret = 0;/*互斥初始化*/pthread_mutex_init (&mutex, NULL);/*分別創建線程1、2*/ret = pthread_create( &pt_1, //線程標識符指針NULL, //默認屬性pthread_function1, //運行函數NULL); //無參數if (ret != 0){perror ("pthread_1_create");}ret = pthread_create( &pt_2, //線程標識符指針NULL, //默認屬性pthread_function2, //運行函數NULL); //無參數if (ret != 0){perror ("pthread_2_create");}/*等待線程1、2的結束*/pthread_join (pt_1, NULL);pthread_join (pt_2, NULL);printf ("main programme exit!\n");return 0; }/*線程1的服務程序*/ void* pthread_function1 (void*a) {int i = 0;printf ("This is pthread_1!\n");for( i=0; i<3; i++ ){pthread_mutex_lock(&mutex); /*獲取互斥鎖*//*臨界資源*/sum++;printf ("Thread_1 add one to num:%d\n",sum);pthread_mutex_unlock(&mutex); /*釋放互斥鎖*//*注意,這里以防線程的搶占,以造成一個線程在另一個線程sleep時多次訪問互斥資源,所以sleep要在得到互斥鎖后調用*/sleep (1);}pthread_exit ( NULL ); }/*線程2的服務程序*/ void* pthread_function2 (void*a) {int i = 0;printf ("This is pthread_2!\n");for( i=0; i<5; i++ ){pthread_mutex_lock(&mutex); /*獲取互斥鎖*//*臨界資源*/sum++;printf ("Thread_2 add one to num:%d\n",sum);pthread_mutex_unlock(&mutex); /*釋放互斥鎖*//*注意,這里以防線程的搶占,以造成一個線程在另一個線程sleep時多次訪問互斥資源,所以sleep要在得到互斥鎖后調用*/sleep (1);}pthread_exit ( NULL ); }?
(1) 設置斷點并查看信息
?
(2) 運行程序,這里可以不設第一個斷點
?
(3) 兩個線程交替運行,觀察不同線程的輸出結果
?
(4) 使用 info threads 查看線程信息,使用 thread +?[編號] 切換線程
?
(5) 使用 thread apply + [編號] + 命令?
?
鎖定其他線程,只讓當前線程運行
(1) 設置?set scheduler-locking on
?
(2)? 觀察線程 1 運行的情況并完成調試
其他命令
(1) thread apply ID1 ID2 IDN command: 讓線程編號是ID1,ID2…等等的線程都執行command命令。
(2) thread apply all command:所有線程都執行command命令。
(3)?set scheduler-locking off|on|step: 在調試某一個線程時,其他線程是否執行。在使用step或continue命令調試當前被調試線程的時候,其他線程也是同時執行的,如果我們只想要被調試的線程執行,而其他線程停止等待,那就要鎖定要調試的線程,只讓他運行。
? ? ? ? off:不鎖定任何線程,默認值。
? ? ? ? on:鎖定其他線程,只有當前線程執行。
? ? ? ? step:在step(單步)時,只有被調試線程運行。
(4)?set non-stop on/off:當調試一個線程時,其他線程是否運行。
(5) set pagination on/off: 在使用backtrace時,在分頁時是否停止。
(6) set target-async on/ff: 同步和異步。同步,gdb在輸出提示符之前等待程序報告一些線程已經終止的信息。而異步的則是直接返回。
(7) show scheduler-locking: 查看當前鎖定線程的模式
?
參考:
https://blog.csdn.net/snow_5288/article/details/72982594
https://www.cnblogs.com/euphie/p/9781482.html
總結
以上是生活随笔為你收集整理的Linux gdb多进程、多线程调试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ deque 底层原理及 queu
- 下一篇: C++易被忽略的知识点:移动语义 左值