多執行緒程式debug linux

2021-06-17 15:38:23 字數 2785 閱讀 3750

多執行緒程式可能存在很多潛在的bug,如data race,dead lock,訊號bug等,而這些bug一向很難除錯,現在有很多**都是基於多執行緒程式的除錯技術的,比如model check,死鎖檢測,replay技術等,也有很多對應的工具,如intel的pinplay,微軟的zing等。關於這些技術和工具,如果感興趣可以 google相應的**進一步了解。這裡我主要講述的是我在對二進位制翻譯下多執行緒程式除錯中經常使用的一些方法以及一些除錯經驗,雖然我的除錯的是二進位制翻譯器,但是這些方法也同樣適用於大多數多執行緒程式。

1、最直接的方法就是在源程式插入printf語句來列印出一些有用的變數。這種方法的優點是不用借助其他工具就可以對程式的執行進行觀察,缺點是插入語句的位置、粒度等都需要除錯者自己去權衡,如果插入過多的列印語句,則頻繁的io操作會使程式執行變慢,執行緒行為改變,有些bug甚至不會再出現。至於需要在什麼地方插入語句,首先,只列印有必要的變數,乙個語句可以列印多個變數;其次,在迴圈中,我們可以通過設定一些條件來降低列印的粒度,比如下面這段**:

123

4567

8

while

(flag)

假設我們對pc的取值很感興趣,需要列印出所有pc取到過的值,但是大多數情況下,getpc()的返回值都同上一次的返回值相同,這樣我們printf出來的就會有很多重複值。這種情況下我們可以用下面這種插樁方式來去處重複值:

123

4567

891011

int  lastpc;

//定義為全域性變數或區域性靜態變數

while

(flag)

......

......//do somthing using pc

}

這樣通過乙個簡單的判斷就可以省掉很多沒有必要的輸出。很多別的情形,比如我們只關心某一變數等於特定值(比如0)時其他變數的狀態,我們就沒有必要把改變量不等於0時的狀態列印出來。總之,能省則省,只列印我們需要的。

1
gdb attach 

這種方法的好處是能夠使gdb對程式執行的影響最小,而且可以只接管程式中某一條我們所關心的執行緒,而其他執行緒不受影響。

這時有人會問,如果執行緒執行過快,我們還沒來得及attach執行緒就已經執行完或者dump掉了,這種情況該怎麼辦?解決方法很簡單,既然執行緒執行過快,我們就讓它等一等,可以在源**中讓我們關心這個執行緒sleep()一小會兒,這樣我們就有足夠的時間來attach它,並且attach的位置我們也可以進行控制,想在**attach,就在**sleep。

3、第三種方法是利用訊號處理函式來獲取一些資訊。在多執行緒程式的壓力測試中,很多錯誤要每隔幾百幾千次執行才能出現一次,而這種錯誤的replay是很困難的,因此捕捉到這種錯誤的現場很重要。這裡我習慣利用訊號處理程式來儲存這樣的現場,這樣你可以晚上寫個指令碼讓程式無限跑,早上起來你會發現程式停在出錯的地方,這是很愜意的事情。

多數多執行緒程式出錯,都是訪問非法記憶體,也就是我們常說的「段錯誤」(segmentation fault),程式發生非法記憶體的訪問,系統會發給執行緒乙個sigsegv訊號,這個訊號預設處理為core掉該執行緒。我們可以對這個訊號進行利用,為其註冊乙個訊號處理函式:

123

4

struct sigaction act;

act.sa_flags

= sa_siginfo;

act.sa_sigaction

= signal_handler;

sigaction(sigsegv,

&act, null)

;//sigsegv表示該訊號的值

訊號處理函式如下:

123

4567

891011

1213

14

void signal_handler(

int host_signum, siginfo_t *info,

void

*puc)

函式引數中,puc是乙個體繫結構相關的指標,不同的體系結構,指標指向的結構不一樣,裡面存放了發生訊號時執行緒的暫存器的值,程式位址等資訊,函式內第一句話的目的就是把void型別轉換成ucontext結構型別,這樣在gdb中可以直接print出該結構的成員。

函式中sleep的作用是讓程式停在訊號處理程式中,以給我們足夠的時間進行attach。如果想讓程式繼續執行下去,手動把loopflag修改為1即可。用while迴圈的目的是我們可以在執行時手動控制sleep的時間。

這種方法同樣適用於其他訊號帶來的bug,比如sigbus等。在二進位制翻譯下,還可以使用這種方法對二進位制翻譯器訊號處理進行跟蹤和除錯,具體使用讀者可以自己去發掘。

4、利用strace得到我們關心的資訊。大多數情況下我們用strace的目的是跟蹤系統呼叫,但其實strace對多執行緒程式的除錯有很大的幫助,使用strace列印多執行緒程式資訊的命令如下:

1
strace

-f ./

test

如果我們對某些系統呼叫,如gettimeofday,ioctl不感興趣,可以遮蔽掉

1
strace

-f-etrace=\!gettimeofday,ioctl ./

test

通過strace列印出的資訊,我們可以對什麼時候產生了乙個子執行緒,那個執行緒在等待,哪個執行緒被喚醒,哪個執行緒收到訊號,哪個執行緒core掉有乙個綜合的了解,這些資訊對多執行緒除錯會起到很大的作用。

python程式多執行緒 PYTHON多執行緒

在單執行緒的情況下,程式是逐條指令順序執行的。同一時間只做乙個任務,完成了乙個任務再進行下乙個任務。比如有5個人吃飯,單執行緒一次只允許乙個人吃,乙個人吃完了另乙個人才能接著吃,假如每個人吃飯都需要1分鐘,5個人就需要5分鐘。多執行緒的情況下,程式就會同時進行多個任務,雖然在同一時刻也只能執行某個任...

多執行緒程式的除錯

gdb對於多執行緒程式的除錯有如下的支援 gdb r starting program root thread new thread 1073951360 lwp 12900 new thread 1082342592 lwp 12907 以下三個為新產生的執行緒 new thread 109073...

簡單的多執行緒程式

下面是一段在windows環境下,建立多個執行緒執行程式的一段 這段 我去年就見過了,雖然挺簡單的,但是一直沒有在意,這次重新看它,希望能夠看出點不一樣的東西,平行計算一直是我乙個非常喜歡的研究方向.在自己寫的過程當中,還有乙個地方出現了乙個小小的錯誤,我想這如果不是我自己重新又寫了一遍是不會發現這...