通過Signal handling(信號處理)獲取任意線程調用棧

獲取任意線程調用棧目前有兩種方式。第一方式拿到棧的指針(StackPointer)以及棧幀指針(FramePointer),遞歸到棧底。

系統提供了 task_threads 方法,可以獲取到所有的線程,注意這里的線程是最底層的 mach 線程.

對于每一個線程,可以用 thread_get_state 方法獲取它的所有信息,信息填充在 _STRUCT_MCONTEXT 類型的參數中(這個方法中有兩個參數隨著 CPU 架構的不同而改變).

我們需要存儲線程的StackPointer以及 頂部的FramePointer, 通過遞歸獲取到整個調用棧.

根據棧幀的 Frame Pointer 獲取到這個函數調用的符號名

實現思路:

  1. 獲取線程的StackPointer 以及 FramePointer
  2. 找到FramePointer屬于哪一個鏡像文件(.m)
  3. 獲取鏡像文件的符號表
  4. 在符號表中找到函數調用地址對應的符號名
  5. return 到上一級調用函數的FramePointer, 重復第2步
  6. 到達棧底, 退出

這種方式是KSCrash的作者想到的,他曾提過一個問題Printing a stack trace from another thread,不過最后他自己想出這種方式給解決了。bestswifter基于此寫了BSBacktraceLogger,在OC中還是很好用的,但是在Swift沒法很好的打印出結果,不知道為什么,有知道的還希望能告知一下。

在這個提問下Printing a stack trace from another thread,有人通過Signal handling實現了。

Signal

這里介紹一下大致需要了解的知識點。

信號的本質
是軟件層次上對中斷的一種模擬。它是一種異步通信的處理機制,事實上,進程并不知道信號何時到來。

信號來源:

  1. 程序錯誤,如非法訪問內存
  2. 外部信號,如按下了CTRL+C
  3. 通過kill或sigqueue向另外一個進程發送信號

信號處理函數的過程

  1. 注冊信號處理函數
    信號的處理是由內核來代理的,首先程序通過sigal或sigaction函數為每個信號注冊處理函數,而內核中維護一張信號向量表,對應信號處理機制。這樣,在信號在進程中注銷完畢之后,會調用相應的處理函數進行處理。
  2. 信號的檢測與響應時機
  3. 處理過程

基本的信號處理函數

信號操作最常用的方法是信號的屏蔽,信號屏蔽主要用到以下幾個函數:

int sigemptyset(sigset_t *set): 函數初始化信號集set并將set設置為空

int sigfillset(sigset_t *set):函數初始化信號集,但將信號集set設置為所有信號的集合。

int sigaddset(sigset_t *set,int signo):將信號signo加入到信號集中去

int sigdelset(sigset_t *set,int signo):從信號集中刪除signo信號。

int sigismemeber(sigset_t* set,int signo):檢測信號是否被掛起。

int sigprocmask(int how,const sigset_t*set,sigset_t *oset):將指定的信號集合加入到進程的信號阻塞集合中去。如果提供了oset,那么當前的信號阻塞集合將會保存到oset集全中去。

對于信號集的初始化有兩種方法: 一種是用sigemptyset使信號集中不包含任何信號,然后用sigaddset把信號加入到信號集中去。
另一種是用sigfillset讓信號集中包含所有信號,然后用sigdelset刪除信號來初始化。

實現思路

1.通過sigaction注冊信號處理函數

private func setupCallStackSignalHandler() {
    let action = __sigaction_u(__sa_sigaction: signalHandler)
    var sigActionNew = sigaction(__sigaction_u: action, sa_mask: sigset_t(), sa_flags: SA_SIGINFO)

    if sigaction(SIGUSR2, &sigActionNew, nil) != 0 {
        return
    }
}

private func signalHandler(code: Int32, info: UnsafeMutablePointer<__siginfo>?, uap: UnsafeMutableRawPointer?) -> Void {
    guard pthread_self() == targetThread else {
        return
    }

    callstack = frame()
}

2.通過pthread_kill()向指定線程發送某個信號

if pthread_kill(threadId, SIGUSR2) != 0 {
     return nil
}

3.在信號處理函數中通過backtrace獲得函數調用棧(也可以使用NSThread.callstackSymbols)

  1. 然后遍歷通過dladdr獲得某個地址符號信息
  2. 使用swift_demangle函數進行符號名重整,這個是Swift特有的,可以看看Swift Name Mangling

6.用sigfillset讓信號集中包含所有信號,然后用sigdelset刪除信號來初始化

var mask = sigset_t()
sigfillset(&mask)
sigdelset(&mask, SIGUSR2)

3,4,5的代碼比較多,我就不貼了,可以看這里backtrace-swift,純Swift寫的,代碼也不是很多。

測試效果

注意在Xcode的時候,因為Xcode屏蔽了signal的回調,我們需要在lldb中輸入以下命令,signal的回調就可以進來了

pro hand -p true -s false SIGUSR2
Screen Shot 2019-08-19 at 10.34.25 PM.png

參考:

Getting a backtrace of other thread
Synchronization issue with usage of pthread_kill() to terminate thread blocked for I/O
Printing a stack trace from another thread
獲取任意線程調用棧的那些事

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,863評論 0 38
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,120評論 1 32
  • 計算機系統漫游 代碼從文本到可執行文件的過程(c語言示例):預處理階段,處理 #inlcude , #defin...
    willdimagine閱讀 3,607評論 0 5
  • 一、基礎知識:1、JVM、JRE和JDK的區別:JVM(Java Virtual Machine):java虛擬機...
    殺小賊閱讀 2,397評論 0 4
  • 親子日記第四天 3月21日陰轉晴星期三 昨天下午放學回家晚說了她幾句,今下午早早回家了,高興的和我說,語文老師在課...
    程文穎閱讀 191評論 0 0