日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GDB 调试多进程或者多线程应用

發(fā)布時間:2025/3/15 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GDB 调试多进程或者多线程应用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


GDB 是 linux 系統(tǒng)上常用的 c/c++ 調(diào)試工具, 功能十分強大. 對于較為復(fù)雜的系統(tǒng), 比如多進程系統(tǒng), 如何使用 GDB 調(diào)試呢?

考慮下面這個三進程系統(tǒng) :

進程 ProcessChild 是 ProcessParent 的子進程, ProcessParentThread 又是 ProcParent 的子線程. 如何使用 GDB 調(diào)試 子進程 ProcessChild 或者子線程 ProcessParentThread 呢 ?

實際上, GDB 沒有對多進程程序調(diào)試提供直接支持. 例如, 使用 GDB 調(diào)試某個進程, 如果該進程 fork 了子進程, GDB 會繼續(xù)調(diào)試該進程, 子進程會不受干擾地運行下去. 如果你事先在子進程代碼里設(shè)定了斷點, 子進程會收到SIGTRAP 信號并終止. 那么該如何調(diào)試子進程呢? 其實我們可以利用 GDB 的特點或者其他一些輔助手段來達到目的. 此外, GDB 也在較新內(nèi)核上加入一些多進程調(diào)試支持.

接下來我們詳細(xì)介紹幾種方法, 分別是 follow-fork-mode 方法, attach 子進程方法和 GDB wrapper 方法.

1 follow-fork-mode方法


參考 gdb調(diào)試多進程和多線程命令

1.1 follow-fork-mode方法簡介


默認(rèn)設(shè)置下, 在調(diào)試多進程程序時 GDB 只會調(diào)試主進程. 但是 GDB > V7.0 支持多進程的分別以及同時調(diào)試, 換句話說, GDB 可以同時調(diào)試多個程序. 只需要設(shè)置 follow-fork-mode (默認(rèn)值 parent) 和 detach-on-fork (默認(rèn)值 on )即可.

follow-fork-modedetach-on-fork說明
parenton只調(diào)試主進程( GDB 默認(rèn))
childon只調(diào)試子進程
parentoff同時調(diào)試兩個進程, gdb 跟主進程, 子進程 block 在 fork 位置
childoff同時調(diào)試兩個進程, gdb 跟子進程, 主進程 block 在 fork 位置

設(shè)置方法

set follow-fork-mode [parent|child]set detach-on-fork [on|off]
  • 1
  • 2
  • 3

查詢正在調(diào)試的進程

info inferiors
  • 1

切換調(diào)試的進程

inferior <infer number>
  • 1

添加新的調(diào)試進程

add-inferior [-copies n] [-exec executable]
  • 1

可以用 file executable 來分配給 inferior 可執(zhí)行文件.

其他 :

remove-inferiors infnodetach inferior
  • 1
  • 2
  • 3

GDB 默認(rèn)支持調(diào)試多線程, 跟主線程, 子線程 block 在 create thread

查詢線程

info threads
  • 1

切換調(diào)試線程

thread <thread number>
  • 1

1.2 示例程序例程

#include <stdio.h> #include <pthread.h>#include <unistd.h>void processParent( ); void processChild( );void * processParentworker(void *arg);int main(int argc, const char *argv[]) {int pid;pid = fork( );if(pid != 0) // fork return child pid in parent processprocessParent( );else // fork return 0 in child processprocessChild( );return 0; }void processParent( ) {pid_t pid = getpid();char prefix[] = "ProcessParent: ";//char tprefix[] = "thread ";int tstatus;pthread_t pt;printf("%s%d %s\n", prefix, pid, "step1");tstatus = pthread_create(&pt, NULL, processParentworker, NULL);if(tstatus != 0){printf("ProcessParent: Can not create new thread.");}processParentworker(NULL);sleep(1); }void * processParentworker(void *arg) {pid_t pid = getpid( );pthread_t tid = pthread_self( );char prefix[] = "ProcessParentThread: ";char tprefix[] = "thread ";printf("%s%d %s%ld %s\n", prefix, pid, tprefix, tid, "step2");printf("%s%d %s%ld %s\n", prefix, pid, tprefix, tid, "step3");return NULL; }void processChild( ) {pid_t pid = getpid( );char prefix[] = "ProcessChild: ";printf("%s%d %s\n", prefix, pid, "step1");printf("%s%d %s\n", prefix, pid, "step2");printf("%s%d %s\n", prefix, pid, "step3"); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

主程序 fork 出一個子進程, 然后父進程在執(zhí)行的過程中通過 pthread_create 創(chuàng)建出一個子線程.

輸出:

./test ProcessParent: 7993 step1 ProcessChild: 7994 step1 ProcessChild: 7994 step2 ProcessChild: 7994 step3 ProcessParentThread: 7993 thread 140427861296960 step2 ProcessParentThread: 7993 thread 140427861296960 step3 ProcessParentThread: 7993 thread 140427853031168 step2 ProcessParentThread: 7993 thread 140427853031168 step3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.3 調(diào)試


1.3.1 開始調(diào)試


首先調(diào)試主進程, block 子進程在 fork 的位置.

set follow-fork-mode parent set detach-on-fork off
  • 1
  • 2
show follow-fork-mode show detach-on-fork
  • 1
  • 2

接著在主進程 fork 的時候和三個進程(線程)的內(nèi)部均設(shè)置斷電. 下面分別跟蹤三個進程

# 在主進程fork的時候設(shè)置斷點 b 16 # 在主進程pthread_create的時候設(shè)置斷點 b 36 # 在父進程執(zhí)行的開始位置設(shè)置斷點 b 28 # 在子進程執(zhí)行的開始位置設(shè)置斷點 b 48 # 在子線程執(zhí)行的開始位置設(shè)置斷點 b 61
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.3.2 調(diào)試父進程


首先開始調(diào)試父進程

# run 運行程序 r
  • 1
  • 2

進程停在了 fork 的位置

查看進程和線程的信息, 由于進程停在了 fork 的地址, 還沒有創(chuàng)建子進程, 因此只有主進程

由于設(shè)置了 follow-fork-mode = parent 和 detach-on-fork = off. 因此在 fork 之后, gdb 進程會開始調(diào)試主進程, 而子進程會阻塞在 fork 之后的位置. 而我們在每個進程的執(zhí)行函數(shù)路徑的開始都打了斷點, 那么我們繼續(xù)執(zhí)行, 進程將運行主程序, 走到 ProcessParent 的位置.

下面我們來驗證一下

#繼續(xù)程序的執(zhí)行 c OR continue #也可以next執(zhí)行 next
  • 1
  • 2
  • 3
  • 4

可以看到程序停在了主進程 ProcessParent 的開始位置.

現(xiàn)在我們查看進程和線程的信息, 此時父進程(pid = 9766)創(chuàng)建了子進程(pid = 10036), gdb 目前正在調(diào)試父進程

info inferiors info threads
  • 1
  • 2

1.3.3 調(diào)試子進程


下面我們開始跟進子進程, 之前通過 info inferiors/threads 來查看進程信息的時候可以看到, 進程的編號信息, 父進程(pid = 9766)編號為 1, 子進程(pid = 10036)編號為 2.

我們接著將 gdb attach 到子進程.

inferiors 2
  • 1

然后 continue 運行程序, 程序會停止在子進程 ProcessChild 的位置.

continue
  • 1

1.3.4 調(diào)試子線程


現(xiàn)在我們繼續(xù)回到主線程然后待其創(chuàng)建子線程之后, 跟蹤子線程.

由于在父進程的程序邏輯處 pthread_create 處設(shè)置了斷點, 將會停在 pthread_create 的地方

接著往下執(zhí)行, 這樣子線程就會被創(chuàng)建, 然后我們查看進程和線程的信息

info inferiors info threads
  • 1
  • 2

我們可以查看到 info inferiors 不能看到父進程創(chuàng)建的子線程, 但是 info threads 可以看到.

然后開始調(diào)試子線程

thread 3
  • 1

2 Attach子進程


https://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/

眾所周知, GDB 有附著(attach)到正在運行的進程的功能, 即 attach <pid> 命令. 因此我們可以利用該命令 attach 到子進程然后進行調(diào)試.

例如我們要調(diào)試某個進程 process, 首先得到該進程的 pid

ps -ef | grep process
  • 1

然后可以通過 pstree 可以看到該進程下的線程信息

pstree -H pid
  • 1

啟動 GDB attach 到該進程

現(xiàn)在就可以調(diào)試了. 一個新的問題是, 子進程一直在運行, attach 上去后都不知道運行到哪里了. 有沒有辦法解決呢?

一個辦法是, 在要調(diào)試的子進程初始代碼中, 比如 main 函數(shù)開始處, 加入一段特殊代碼, 使子進程在某個條件成立時便循環(huán)睡眠等待, attach 到進程后在該代碼段后設(shè)上斷點, 再把成立的條件取消, 使代碼可以繼續(xù)執(zhí)行下去.

至于這段代碼所采用的條件, 看你的偏好了. 比如我們可以檢查一個指定的環(huán)境變量的值, 或者檢查一個特定的文件存不存在. 以文件為例, 其形式可以如下 :

void debug_wait(char *tag_file) {while(1){if (tag_file存在)睡眠一段時間;elsebreak;} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

當(dāng) attach 到進程后, 在該段代碼之后設(shè)上斷點, 再把該文件刪除就 OK 了. 當(dāng)然你也可以采用其他的條件或形式, 只要這個條件可以設(shè)置檢測即可.

Attach 進程方法還是很方便的, 它能夠應(yīng)付各種各樣復(fù)雜的進程系統(tǒng), 比如孫子/曾孫進程, 比如守護進程(daemon process), 唯一需要的就是加入一小段代碼.

3 gdb swapper


Debugging with GDB學(xué)習(xí)記錄(二)

GDB調(diào)試及其調(diào)試腳本的使用

使用 GDB 調(diào)試多進程程序


  • 本作品/博文 ( AderStep-紫夜闌珊-青伶巷草 Copyright ?2013-2017 ), 由 成堅(gatieme) 創(chuàng)作,

總結(jié)

以上是生活随笔為你收集整理的GDB 调试多进程或者多线程应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。