GDB多線程調試分析

0x00: 在Linux系統上Gdb提供了一組多線程調試命令,如表所示:linux

多線程調試的主要任務是準確及時地捕捉被調試程序線程狀態的變化的事件,而且GDB針對根據捕捉到的事件作出相應的操做,其實最終的結果就是維護一根叫thread list的鏈表。上面的調試命令都是基於thread list鏈表來實現的,後面會有講到。數據結構

0x01:Gdb在linux平臺多線程調試實現主要依賴下面三個文件多線程

thread.c:文件它的任務很是簡單,就是多線程調試命令子集的實現,好比info threads。
當用戶在gdb命令行敲入多線程調試命令子集中的命令時,就會調用thread.c中對應的函數。Thread.c中的實現都是基於thread list的。而gdb對於thread list的維護工做主要在另外兩個文件中實現。
Linux-nat.c:它實現了一般的調試功能,好比讀寫寄存器、讀寫內存、resume程序、wait程序、attach、detach等功能。更重要的是,在linux-nat.c中會維護一個lwp_list鏈表,表示當前進程全部的內核線程。框架

linux-thread-db.c:基於thread_db庫的一組功能函數,用在多線程調試環境下的函數,好比to_get_thread_local_address。這些函數的任務就是獲取用戶態線程的產生、消亡事件,雙及獲取用戶態線程相關的數據。Linux-thread-db.c獲取用戶線程的發生的事件和獲取的信息、結合linux-nat.c中維護的lwp_list內核線程鏈表中提供的信息,以此維護一個完整的thread_list,該鏈表存放線程全部的信息。函數

0x02:Gdb功能函數分層:命令行

Gdb中的功能函數使用struct target_ops數據結構來組織。不一樣功能的函數集抽象成不一樣的target_ops。好比用於處理coredump文件的」core」 target_ops,而linux-nat.c中實現的linux應用程序本地調試功能也抽象成一個ops」child」 target_ops,linux-thread-db.c中實現的基於libpthread庫的調試功能抽象成」multi-thread」target_ops。
整個linux多線程應用程序本地調試的結構框架以下:線程

從上圖能夠看到當調試linux多線程程序時,就會使用thread_db_ops中的相應的函數。那麼問題來了,對於resume和wait這些Linux_ops中也實現的函數,會調用哪一個呢?Gdb中實現了不少的target_ops,有功能相近也有徹底不一樣功能的,好比linux_ops和file_ops。那麼對於功能相近的target_ops怎樣使用呢?功能不一樣的target_ops之間又有怎樣的關係呢?這些問題gdb分層機制能解釋。
Gdb中把target_ops分爲了7層,每一層負責不一樣的功能。如圖所示:調試

0x03:GDB調試多線程server

調試進程創建具體的流程下圖所示:blog

在建立好被調試進程以後,gdb經過ptrace(PTRACE_SETOPTIONS)設置PTRACE_O_TRACECLONE,設置事後,當被調試進程建立線程的時候,就會給本身發送一個SIGTRAP信號,讓被調試進程進入stop狀態,使得gdb可以捕捉到這些事件,獲取tid添加到lwp_list中後,gdb會讓程序繼續運行,直到被調試程序發生一些須要通知gdb用戶的事件,好比觸發了用戶設置的斷點,下面是流程圖

Lwp_list鏈表
被調試進程建立線程最終是經過clone()系統調用實現的。要捕捉子線程的建立和死亡事件,這個捕捉事件由ptrace提供的機制實現。具體機制以下圖所示。

Thread_list鏈表:
Thread_list是struct thread_info類型的一個鏈表,記錄的是被調試進程的全部線程的信息,裏面包含線程用戶態和內核態的一些信息。線程用戶態信息的捕獲基於libthread_db庫,該庫提供了一組調試接口。這麼一組libpthread_db調試接口在gdb中使用struct thread_db_info進行管理,該數據主要結構的具體信息以下表:

在被調試進程加載libpthread庫時,會爲該進程建立這麼一個struct thread_db_info記錄該進程要使用到的libthread_db提供的調試接口。其中比較重要的是:
td_create_bp_addr和td_death_bp_addr。這兩個地址是對應libpthread庫中的某個位置。當調用libpthread庫建立線程或者線程死亡時,必定會分別調用這麼兩個addr處的代碼。Gdb經過在這兩個位置設置斷點來捕獲libpthread庫的線程建立和死亡事件,斷點的類型爲bp_thread_event.
被調試程序建立子進程或者子進程死亡,會執行到libpthread庫的td_create_bp_addr或td_death_bp_addr地址處,觸發斷點。線程進入stop狀態
gdb 經過waitpid()監測到被調試進程的狀態改變,分析子進程發生的事件,判斷爲bp_thread_event的斷點觸發。若是是create,獲取新建立線程struct thread_info的相關的信息,而且加入到thread_list中;若是是death,從thread_list中刪除該線程。

0x04:總結

GDB肯定咱們調試的程序是否爲多線程, 經過判斷被調試程序是否加載libpthread庫來判斷的。(捕捉裝載libpthread庫這個事件)也就是一旦被調試程序裝在libpthread庫,( Observer觀察者模式)咱們就應作初始化多線程調試功能。

相關文章
相關標籤/搜索