(1) overview.
簡述了整個breakpad工作的基本流程:
1)程式編譯完後,先用工具事先把程式的相關debug資訊dump下來(to symbol file)。
這些dump下來的東西主要是一些符號與源碼的對應,如各個符號在程式中對應的地址等。
有了它,breadpad通過一個地址就能找出這個地址對應的是變數,還是函數等。
2)把符號dump出來後,程式就可以pstrip一下,把debug資訊去除,然後發布程式。
3)程式在啟動並執行過程中,如果發生了崩潰,程式就會進入breakpad的異常處理,這個異常處理會把當前程式的上下文,
棧的內容,線程的資訊等,有選擇性的dump下來,儲存為一個mini dump 格式的檔案。
4)mini dump 與symbol file結合,就可以把程式崩潰那一刻的call stack 重建出來。
(2)exception handler.
breakpad通過響應訊號進行異常處理,Linux下,程式崩潰能被捕的訊號有幾種:
SIGSEGV,SIGABRT,SIGFPE,SIGILL,SIGBUS.
breadpad 在初始化的時候,就給程式設定了這些訊號的回調。
具體可以參看:src/client/linux/handler/exception_handler.cc
這裡代碼比較少,結構也很清晰,欲深究的讀者可以去讀代碼,作者在檔案裡畫了一個這樣流程圖:
由上可知,在處理signal的時候,breakpad 調用clone()建立了一個新的進程(clone一個新進程使得接下來的操作能夠在一個相對穩定的環境裡進行)。
然後在這個新的進程裡通過ptrace與父進程進行互動,把一些context,stack等,以mini dump 的形式儲存下來。
所以,整個breakpad在client端的運行流程是比較簡單的。
3)mini dump file format.
client 程式崩潰之後中,dump出來的內容主要包含以下幾個內容:
a)當前cput context.這裡主要是一堆寄存器中的內容,cpu的狀態字等。
有了它們我們就可以知道當前是在哪線程上運行,運行到了哪裡,堆棧的指標等。
這裡主要與當前的硬體體系相關。
b)關於每個線程的資訊。這裡就與具體的作業系統相關了。
Linux下的話,LinuxPtraceDumper這個類中有說明:
關於線程的資訊來自於/proc/$pid/status/,主要包括:tgid,ppid,pid.
此外還有棧的內容,以及與一些與線程相關的寄存器等。
具體細節可以參看:src/client/linux/minidump_writer.cpp.
c)被各線程當作棧用的記憶體地區,這個裡的東西是重建call stack的關鍵資料所在,
這裡的資料往往佔據了最後dump 檔案的大部分。
d)載入進來的各個模組的列表。
這裡主要包括:檔案的名字(.dll,.so,.exe etc),模組所佔用的記憶體地區,以前可能存在的debug資訊等。
這裡的內容,並不保證都能擷取的了。
以上這些程式運行時的細節內容,都是以後重建call stack時用來與symbol file進行匹配的關鍵資料。
4) symbol file
符號檔案是一個包含程式源碼與可執行檔裡的機器碼相對應的東西。
gcc編譯器開了-g進行編譯時間,會產生一堆的用於debug的資訊,這些資訊能通常以某種格式(DWARF,STABS)組織起來,存放在可執行檔的某個段位裡。
這些debug資訊主要是:machine code to source mapping data。
breakpad所產生的symbol file就是來自這些debug資訊。
所不同的只是:
1)它更簡單,資料量少
2)它不放在可執行程式中。
對於很多怕泄密的產品來說,第二點尤為重要,它避免了產品的源碼資訊向外暴露。
下面具體來說一下,這個symbol file中都儲存了些什麼:
1)全部內容都是ascii文本。
2)每一行就是一條記錄,每條記錄中有多個欄位,每個欄位以空格分開。
3)每條記錄的開頭是一個串字元,這個字元標記這條記錄是什麼類型的記錄。
但Line record除外,這種類型的記錄,預設省略掉標記符,也就是如果有一行沒有標記類型,這一行就是一個Line record.
4)記錄中有些欄位是10進位或16進位的字串,16進位也沒有以0x開頭,要分清某個數字具體是哪種進位,就要看這些數字是在哪種記錄裡,
屬於哪個欄位,這些都是規定死了的。
具體都有哪些欄位,請看:
這裡結合一些具體的例子:
檔案記錄:
函數類型:
行記錄,函數記錄:
具體其中每條記錄的各個欄位代表什麼意思,可以參看這裡:
https://code.google.com/p/google-breakpad/wiki/SymbolFiles
就我自己使用過程中的研究來看,breakpad最麻煩的工作在於write dump & dump symbol, 這兩部分工作都很與具體的細節相關,
如Dwarf的格式,mini dump 的格式,進程的cpu context,stack 等,一系列與程式運行很相關的底層知識。
因此必須對Linux作業系統,程式編譯等有一定功力才能看懂這些細節。