崩溃优化(一)
Android 兩種崩潰
- Java崩潰
- 在Java代碼中出現了未捕獲的異常,導致程序異常退出
- Native崩潰
- 在Native代碼中訪問非法地址或地址對齊出現問題或者程序主動 abort 異常退出 (默認的程序結束函數,這種方式可能會或可能不會以刷新與關閉打開的文件或刪除臨時文件)
Native崩潰捕獲機制以及實現
- 首先native crash因為具有上下文不全,出錯信息模糊,難以捕捉的特點,所以捕獲難比Java crash更難修復
- 所以捕獲組件需要達到以下特點
- 打印logcat和應用日志
- 上報crash次數
- 對不同的crash做不同的恢復措施
- 可以針對業務不斷改進和適應
- 所以捕獲組件需要達到以下特點
信號機制
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CkfcxK6b-1570331077566)(en-resource://database/8399:1)]
- 在Unix-like系統中,所有的崩潰都是編程錯誤或者硬件錯誤相關的,系統遇到不可恢復的錯誤時會觸發崩潰時機讓程序退出
- 異常發生時,CPU通過異常中斷的方式觸發異常處理流程,不同的處理器,有不同的異常中斷處理方式
- 在Linux系統中,這些中斷處理統稱為信號量,可以注冊信號量向量進行處理
- 信號(軟中斷信號)機制是進程之間信息傳遞的一種方法
- 當函數運行在用戶態時,當遇到系統調用、中斷或是異常的情況時,程序會進入內核態,信號的處理就在這兩種狀態之間相互轉換
信號類型
| SIGABRT | 由調用abort函數產生,進程非正常退出 |
| SIGALRM | 用alarm函數設置的timer超時或setitimer函數設置的interval timer超時 |
| SIGBUS | 某種特定的硬件異常,通常由內存訪問引起 |
| SIGCANCEL | 由Solaris Thread Library內部使用,通常不會使用 |
| SIGCHLD | 進程Terminate或Stop的時候,SIGCHLD會發送給它的父進程。缺省情況下該Signal會被忽略 |
| SIGCONT | 當被stop的進程恢復運行的時候,自動發送 |
| SIGEMT | 和實現相關的硬件異常 |
| SIGFPE | 數學相關的異常,如被0除,浮點溢出,等等 |
| SIGFREEZE | Solaris專用,Hiberate或者Suspended時候發送 |
| SIGHUP | 發送給具有Terminal的Controlling Process,當terminal被disconnect時候發送 |
| SIGILL | 非法指令異常 |
| SIGINFO | BSD signal。由Status Key產生,通常是CTRL+T。發送給所有Foreground Group的進程 |
| SIGINT | 由Interrupt Key產生,通常是CTRL+C或者DELETE。發送給所有ForeGround Group的進程 |
| SIGIO | 異步IO事件 |
| SIGIOT | 實現相關的硬件異常,一般對應SIGABRT |
| SIGKILL | 無法處理和忽略。中止某個進程 |
| SIGLWP | 由Solaris Thread Libray內部使用 |
| SIGPIPE | 在reader中止之后寫Pipe的時候發送 |
| SIGPOLL | 當某個事件發送給Pollable Device的時候發送 |
| SIGPROF | Setitimer指定的Profiling Interval Timer所產生 |
| SIGPWR | 和系統相關。和UPS相關。 |
| SIGQUIT | 輸入Quit Key的時候(CTRL+\)發送給所有Foreground Group的進程 |
| SIGSEGV | 非法內存訪問 |
| SIGSTKFLT | Linux專用,數學協處理器的棧異常 |
| SIGSTOP | 中止進程。無法處理和忽略。 |
| SIGSYS | 非法系統調用 |
| SIGTERM | 請求中止進程,kill命令缺省發送 |
| SIGTHAW | Solaris專用,從Suspend恢復時候發送 |
| SIGTRAP | 實現相關的硬件異常。一般是調試異常 |
| SIGTSTP | Suspend Key,一般是Ctrl+Z。發送給所有Foreground Group的進程 |
| SIGTTIN | 當Background Group的進程嘗試讀取Terminal的時候發送 |
| SIGTTOU | 當Background Group的進程嘗試寫Terminal的時候發送 |
| SIGURG | 當out-of-band data接收的時候可能發送 |
| SIGUSR1 | 用戶自定義signal 1 |
| SIGUSR2 | 用戶自定義signal 2 |
| SIGVTALRM | setitimer函數設置的Virtual Interval Timer超時的時候 |
| SIGWAITING | Solaris Thread Library內部實現專用 |
| SIGWINCH | 當Terminal的窗口大小改變的時候,發送給Foreground Group的所有進程 |
| SIGXCPU | 當CPU時間限制超時的時候 |
| SIGXFSZ | 進程超過文件大小限制 |
| SIGXRES | Solaris專用,進程超過資源限制的時候發送 |
信號處理流程
-
信號的接收
- 接收信號是由內核代理的,當內核接收到信號后,會將其放到對應的進程信號隊列中,同時向進程中發送一個中斷,使其陷入內核態,注意,此時信號還只是在隊列中,對進程來說暫時是不知道有信號的到來
-
信號的檢測
- 進程陷入內核態后,有兩種場景會對信號進行檢測:
- 進程從內核態返回到用戶態前進行信號檢測
- 進程在內核態中,從睡眠狀態到被喚醒時進行信號檢測
- 進程陷入內核態后,有兩種場景會對信號進行檢測:
-
信號的處理
- 運行在用戶態,調用函數處理前,內核會將當前內核棧的內容備份拷貝到用戶棧上,并且修改指令寄存器將其指向信號處理函數
- 接著進程返回到用戶態中,執行相應的信號處理函數
- 信號處理函數執行完成后,還需要返回內核態,檢查是否還有信號未作處理,如果所欲信號都處理完成,就會將內核棧恢復,同時恢復指令寄存器將其指向終端前的運行位置,最后回到用戶態繼續執行進程
捕捉native crash
- 注冊信號處理函數
- 設置額外棧空間
兼容其他signal處理
static void my_handler(const int code, siginfo_t *const si, void *const sc) { ... ? ?/* Call previous handler. */ ? ?old_handler.sa_sigaction(code, si, sc); ? }Breakpad
- Breakpad是目前Native崩潰中最成熟的方案,
- google-breakpad有兩種工作模式。進程內和進程外抓取
-
進程內模式通過使用exception_handle模塊完成,使用也非常簡單——實例化ExceptionHandler,在構造函數中注冊回調FilterCallback和MinidumpCallback以及dump文件路徑即可,前者Filter回調發生在崩潰之后,dump回調寫入dump文件之間,在exception_handler.h中有詳盡注釋,毋庸贅言
-
進程外模式采用一個守護進程與崩潰進程進程間通訊的模式實現,xl當年的后臺網頁查毒也是使用這種結構,只是實現更笨重。這兩種模式是排他的,進程外模式需要崩潰進程和監聽者進程分別引入crash_generation_client和crash_generation_server
-
安裝和編譯
- 編譯環境
- python 2.7
- windows sdk 7
- VS2005的補丁
- 資源下載
- 設置windows sdk 7
- 生成windows工程文件
使用Breakpad
- 進程內抓取Dump文件
- 進程外抓取Dump文件
- 使用進程外抓取Dump時,需要指定服務端和客戶端,在服務端中需要創建CrashGenerationServer的實例,而在客戶端中則只需要創建ExceptionHandler即可。此外,如果服務端自己需要抓進程內的Dump,請將pipe的參數置為NULL
總結
- 上一篇: HTTP请求的TCP瓶颈分析
- 下一篇: 央行根据LPR调整房贷利率,看看你的房贷