軟體崩潰(crash)之後的報告(report)究竟是個什麼流程?

我用過Windows Mac 以及主流的Linux發行版 (Ubuntu, CentOS, Redhat, Fedora)。 在我印象中,軟體在這些操作系統上,一旦crash之後,都會提示「是否發送錯誤報告」。

我對這個過程比較感興趣,然而幾乎找不到什麼有價值的資料介紹,能否請大家講講在這個過程中:

1. 當一個軟體crash之後,究竟什麼信息被收集了呢? 我想general的說,不同操作系統應該區別不大吧,在這一點上?

2. 當軟體公司受到這些crash的信息之後,都會做哪些分析? 都是怎樣分析的?

3. 分析的結果會被怎麼使用呢?

方便的話,能否請大家在回答之餘提供一些資料來參考,多謝!


Windows的這個系統叫Watson,或者叫Windows Error Reporting。Report中最重要的內容就是dump file,或者叫內存轉儲文件,其他還有一些諸如系統基本信息之類。一般情況下收集的dump叫做mini-dump,其中只包含有限的信息,比如module list, call stack, 寄存器內容之類。

在Watson的伺服器端會根據callstack把這麼多error report分類歸併,我們稱為bucket。然後會有人去看這些bucket再決定需要做什麼,比如你可以設置以後再出現這類crash就需要上傳一個full dump(因為很多時候minidump中的內容不足以調試),甚至可以製作一個問卷調查讓遇到這個問題的用戶提供一些反饋。所以error reporting中的內容是可以控制的。


謝 @RednaxelaFX 邀!

1. 這個問題其他答案都有覆蓋,我也提不出更深的見解,就不硬答了。

2. 由於crash信息來自不同的平台,所以一開始得到的數據都是raw的,未經處理的。流程大概會是:

1)把crash的raw數據存起來。同時,為了定位具體是哪個模塊的哪個函數、偏移出了問題,開發者會提供一份符號表Symbol

2)根據Symbol就可以對raw數據進行處理了,大概是存成特定的數據結構存放在一張很大的表裡。這個表就像資料庫的表,有很多行,有很多列。當然為了加速後續的查找,還會做一個index。

3)有了index和處理過的「表」,就可以做常見的aggregation、combination等等操作啦。諸如,版本A的release在日期xxxx一共crash了多少次。從xxxx到yyyy幾天之中,哪個模塊的哪個函數出crash最多等等……

4)有了這些統計數據之後,就是提供各種Query API或者網頁介面供給開發人員查找和研究,追追bug,畫畫圖,跟經理開開會咯。

5)其他雜七雜八的問題當然還包括寫一寫配置文件,例如多長時間要清理一次上述的各種表啊,清理的順序啊等等。

3. 這個問題有 @Yu Ke 珠玉在前,我也不硬答了。


多年前剛好做過這個...手機碼字簡單說說...

分前端後端。假設程序跑著windows上,應用程序加個全局exception handler,碰到沒有捕獲的異常,跑到這個exception handler就minidump出程序棧,寄存器,以及部分內存信息。有可能還加上機器硬體配置以方便分析,然後壓縮發給伺服器。(通常信息的收集是在用戶同意之後進行,不排除無良軟體公司收集其它信息)。

伺服器那邊最重要的是設置symbol server,具體看windbg里關於symbol server的設置。對每個發布版本都要把pdb文件放到symbol server. 然後收到minidump用windbg打開,抽取crash call stack,存到資料庫,然後就可以進一步分析啦,比如最常crash的函數是哪個偏移多少,今年新上的feature crash發生情況,這個crash修過,就可以給提供郵箱的用戶發個patch啥的增加客戶滿意度,伺服器是不是要做負載均衡啊,要不要記錄客戶是那個地區的啊,等等。有了數據就可以生成無數fancy的報表了...

差不多就這樣。不同的os就客戶端有區別。在osx是什麼來著忘記了,也是類似minidump來著。

再下來,不是crash dump也可以發,比如很多軟體里的"是否願意發送用戶體驗數據",比如就可以記錄某個command執行頻率啊啥的,公司就可以用這些信息對系統做更好優化。我們之前甚至知道軟體是不是盜版的,不過根據協議,不能永這些信息反盜版哈哈


前兩天正好碰到一個崩潰,給你舉個例子。

下面貼出的信息是在安卓設備上發生的一個真實的APP崩潰。我的APP集成了bugly,可以自動捕捉崩潰,並上傳到bugly的服務端,然後我可以通過QQ帳號登錄到它的web portal查看。

一般來說,崩潰信息中最有用的部分是崩潰線程的調用棧,很多時候僅憑這個調用棧就能分析出bug。不管哪個系統,如果它捕捉的崩潰信息里沒有調用棧,肯定是不合格的。像bugly抓到的,基本上有用的也就是調用棧。其他有用的信息還有:各個寄存器的值,崩潰線程的棧數據,進程堆數據,進程內已載入的模塊等。

觀察這個調用棧,我們可以得到一些基本信息:崩潰的原因是調用了assert函數,調用的地方在x264_encoder_encode 里,在函數入口的1028位元組偏移處。

1#00 pc 00024c98 /system/lib/libc.so [armeabi-v7a]

2#01 pc 00031694 /system/lib/libc.so (__assert2 +100) [armeabi-v7a]

3#02 pc 0043d555 /data/app-lib/xxx.so (x264_encoder_encode +1028) [armeabi-v7a]

4#03 pc 00437ae7 /data/app-lib/xxx.so (AVCEncoder_EncodeOneFrame +110) [armeabi-v7a]

接下來的問題是,如何從這些信息對應到源代碼去?方法不只一種,我的習慣是用IDA Pro反彙編,找到x264_encoder_encode的函數入口,然後找到1028偏移處。這是IDA Pro反彙編出來的代碼。可以看到這裡確實調用了assert,在這行:

.text:0043D554 BLX __assert2

怎麼定位到源代碼呢?觀察這段代碼,前面不遠處調用了一個函數x264_frame_push_unused,後面不遠處調用了另一個函數x264_frame_pop_unused。結合這幾個信息,在x264_encoder_encode 函數里找,基本上就能找到了。

.text:0043D534 BL x264_frame_push_unused

.text:0043D538 B loc_43D558

.text:0043D53A ; ---------------------------------------------------------------------------

.text:0043D53A

.text:0043D53A loc_43D53A ; CODE XREF: x264_encoder_encode+3D8j

.text:0043D53A LDR.W R2, =(x264_ue_size_tab - 0x43D54E)

.text:0043D53E MOVW R1, #0xCED

.text:0043D542 LDR.W R0, =(aEncoderEncoder - 0x43D550)

.text:0043D546 LDR.W R3, =(a0_0 - 0x43D556)

.text:0043D54A ADD R2, PC ; x264_ue_size_tab

.text:0043D54C ADD R0, PC ; "encoder/encoder.c"

.text:0043D54E ADDW R2, R2, #0x211

.text:0043D552 ADD R3, PC ; "0"

.text:0043D554 BLX __assert2

.text:0043D558

.text:0043D558 loc_43D558 ; CODE XREF: x264_encoder_encode+3D2j

.text:0043D558 ; x264_encoder_encode+3E8j

.text:0043D558 MOV R0, R6

.text:0043D55A MOVS R1, #1

.text:0043D55C BL x264_frame_pop_unused

這個例子雖然講的是安卓,原理上跟其它系統是差不多的。分析崩潰信息有很多隱含的技巧和知識點。比如說你需要了解一些彙編指令,調用約定等,C/C++編譯後的代碼大概長什麼樣,調試符號怎麼用,如果沒有調試符號又該怎麼辦,等等等等。

有同學問日誌記錄的機制,這個處理過程跟具體的CPU架構和操作系統有關。比如在Android上大概是這樣:

1,操作系統在初始化時給CPU設置異常處理入口(exception handlers,在vector table里);

2,觸發崩潰的指令首先被CPU捕捉到。很常見的一類崩潰是引用到了無效的內存地址(比如空指針),由於CPU沒法從這個地址獲取數據,這個線程走不下去了,CPU進入異常處理模式,跳轉到Data Abort Exception Handler執行;

3,Linux內核向觸發崩潰的進程發送 SIGSEGV信號;

4,進程通過signal handler接收這個信號,然後通知debuggerd處理崩潰(通過unix domain socket);

5,debuggerd是Android的一個daemon程序,專門用於收集崩潰信息(通過ptrace, /proc文件系統等)。

幾個關鍵點,異常首先由CPU捕獲,CPU通過Data Abort/Prefetch Abort等異常處理入口把控制權交給操作系統內核,內核通過signal通知進程,進程直接處理異常或者通知其他程序處理。


馬一個

OK今天放假休息,我來回答這個問題。

我先說一下數據,之前在老東家負責宇宙第一客戶端的性能,穩定性,以及數據上報工作。穩定性工作就是robustness 擼棒性啦。在這個taem里做了一年,期間團隊KPI目標一直是降低Crash率,我剛加入時候大概是0.3%左右,然後我們把Crash率從0.3降到0.2,降到0.17,離職前團隊最後一次KPI目標是Crash率降低到0.15%,沒錯,100次啟動中,最多只能出現0.15次crash,團隊目標勝利達成。可能這小數點後數字的變動很多人都無法體會到有多難,你一定要想想基數,大家可能了解的是每天活躍人數,同時在線人數。但是我們的基數是啟動次數。每天五六億啟動次數都是輕鬆鬆鬆,開了關關了開的。要在這五六億啟動中保證這麼一個由兩三百人開發的客戶端程序的崩潰率低於1.5‰,你們覺得好不好搞呢……

好搞!但是前提是有一套完善的Crash上報系統!

先吃個早飯。


謝邀。 基本如王翦所說。

1,一般是部分內存的快照(主要是棧內存)和寄存器上下文。

2,具體分析多是查看現場,然後根據棧回溯找到具體的錯誤代碼。win下可以把dump文件導入windbg分析。

3,定位到bug點以後修復。


Windows可以參考這個

http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus

基本原理就是捕獲被拋到最外層的異常,然後生成內存dump,發送到伺服器用http的multipart post就行,可以把dump文件加上日誌以及其他有用信息一起壓縮後發送。


沒研究過這個。猜測可能記錄了一份崩潰時的內存快照,包括寄存器上下文等信息。linux上有core dump,安卓上有tombstone,安全研究員可以通過這個分析崩潰的原因,快速定位可能的漏洞位置(比如可能會觀察到棧幀被破壞了,堆棧被破壞了,可能是發生了溢出)。模糊測試比較需要這個。Windows的這份報告可能還包含了其他一些信息。


程序的每一個函數在執行之前會在程序棧中放一個棧幀,這個幀裡面有臨時變數,引用等。當這個函數正常的執行完成之後會彈出這個棧幀,回到上一個幀,即完成本次函數調用,回到調用他的那個函數。

如果程序出現異常會首先在這個函數中尋找處理異常的方法,如果沒有,則會在上一層函數中查找,如果一路都找不到,就把異常和現在調用的程序棧狀態扔給系統。系統捕捉異常和函數調用順序並顯示出來。

這應該就是整個崩潰過程了。

像我們軟體遇到的異常,我們會發送到伺服器,這樣就能遠程定位問題了。通過剛剛的調用棧和異常基本能遠程的查到程序崩潰的位置和原因。

這樣也就能修改程序了。

手機碼字不易。


搜一下 google breakpad ,這玩意兒跨平台 對不同平台有不同平台的處理方法


其餘平台不清楚。

在windows平台下,軟體編碼時可以利用系統API進行全局崩潰處理;

在自定義的處理中,可以生成dump(內存轉儲文件,包含了軟體的堆棧、寄存器等一些信息);

利用windbg或者高版本VS,加上dump文件(自定義崩潰處理中生成) 和 pdb文件(軟體對應版本的符號資料庫,由軟體開發者預先生成), 可以得到崩潰時的上下文(包括崩潰代碼、行數、調用堆棧等),基本就能定位崩潰原因並進行代碼修復。

當然,如果崩潰機器在用戶那裡,還需要將dump文件上傳至伺服器的機制。


推薦閱讀:

C++11中能否顯式聲明一個lambda類型的變數,而不用auto?
QQ上發送么么噠時候,彈出彈跳錶情,是如何實現的?
求一個數學公式:要求生成一個可控制分布的隨機數?
Python的for使用問題?

TAG:逆向工程 | 軟體開發 | 編程 | 軟體工程 | 軟體測試 |