Android平台抓取native crash log


Android開發中,在Java層可以方便的捕獲crashlog,但對於 Native 層的 crashlog 通常無法直接獲取,只能通過系統的logcat來分析crash日志。

做過 Linux 和 Win32 開發的都知道,在pc上程序crash時可以生成 core dump 文件通過相關的工具分析函數調用堆棧及崩潰時的內存信息。

那么作為軟件開發者有沒有方法自己獲取native層的crashlog呢?Android 系統是 Linux 內核,既然在Linux中crash時可以生成dump文件,那么在Android中也是有辦法的。

Linux系統的Crash dump

Linux 棧調用回溯

對 Linux 應用程序而言, 因為有 glibc 庫的支持, 所以構造程序的函數調用鏈相對容易。在 glibc 庫提供的關於堆棧回朔的一系列庫函數中,其核心函數是 backtrace()。它負責遍歷從程序入口點到當前調用點的所有堆棧幀,然后生成函數調用的地址序列。為了完成函數地址和函數名稱的轉換,函數backtrace_symbols() 負責將 backtrace()生成的地址序列轉換成一系列字符串列表,在每個字符串列表中包括了函數名稱,當前指令在函數中的偏移量和函數的返回地址。由於 backtrace_symbols() 需要動態申請空間以保存字符串列表,如果應用程序 crash 時破壞了系統內存,可能導致 backtrace_symbols()結果錯誤。為此,glibc庫還提供了一個更安全的地址轉換函數:backtrace_symbols_fd() 。該函數將生成的字符串直接輸出到外部文件,而不再需要申請新的內存空間。對於 backtrace() 的詳細使用方法可以通過man backtrace 查看。

在Andrid中,由於谷歌沒有使用glibc庫,而是使用了精簡版本的bionic庫,其中並沒有 backtrace() 可用。獲取調用堆棧還需要采用其他方法。

Linux 信號機制

信號機制是 Linux 進程間通信的一種重要方式,Linux 信號一方面用於正常的進程間通信和同步,如任務控制(SIGINT, SIGTSTP,SIGKILL, SIGCONT,……);另一方面,它還負責監控系統異常及中斷。 當應用程序運行異常時, Linux 內核將產生錯誤信號並通知當前進程。 當前進程在接收到該錯誤信號后,可以有三種不同的處理方式。 1. 忽略該信號。
2. 捕捉該信號並執行對應的信號處理函數(signal handler)。
3. 執行該信號的缺省操作(如 SIGTERM, 其缺省操作是終止進程)。

當 Linux 應用程序在執行時發生嚴重錯誤,一般會導致程序 crash。其中,Linux 專門提供了一類 crash 信號,在程序接收到此類信號時,缺省操作是將 crash 的現場信息記錄到 core 文件,然后終止進程。

Crash信號列表

Signal Description
SIGSEGV Invalid memory reference.
SIGBUS Access to an undefined portion of a memory object.
SIGFPE Arithmetic operation error, like divide by zero.
SIGILL Illegal instruction, like execute garbage or a privileged instruction
SIGSYS Bad system call.
SIGXCPU CPU time limit exceeded.
SIGXFSZ File size limit exceeded.

Linux 信號處理 sigaction

#include<signal.h>  
int sigaction(int sig, struct sigaction *act , struct sigaction *oact) ;  

struct sigaction{  
   void     (*sa_handler)(int);  
   void     (*sa_sigaction)(int, siginfo_t *, void *);  
   sigset_t   sa_mask;  
   int        sa_flags;  
   void     (*sa_restorer)(void);  
}

這個函數可以: 1. 給一個signal安裝一個handler,並且在使用sigaction修改該handler之前,不用reinstall。 2. 使用sigaction結構,該結構包含handler,其中可以指定2個handler,一個是使用sigiinfo_t等參數的handler,即支持給handler更多的參數,使其可以知道自己是被什么進程,那個用戶,發來的什么信號,發來該信號的具體的原因是什么,當然要像這樣,得給sigaction的sa_flags設置SA_SIGINFO標記。 3.使用sigaction的sa_flags標記還可以指定系統調用被這個信號打斷后,是直接返回,還是自動restart. 一個典型就是,一般我們不讓SIGALRM信號將被打斷的系統調用restart,因為SIGALARM一般本來就是用來打斷一個block的調用的。 4. 為了模仿老的signal函數的作用,實現unreliable 的類似signal的操作,可以通過給sa_flags設置SA_RESETHAND使handler不會自動reinstall,以及SA_NODEFER標記來使在本信號的handler內部,本信號不被自動block,當然如果你手動在sa_mask中指定要block本信號的話就可以將其block了。 5. 通過使用sigaction結構中的sa_mask,可以在該handler執行的過程中,block一些信號,注意,這個mask是與我們使用sigprocmask設置的mask不同的mask,這個mask的作用范圍僅限於本handler函數,而且他不會將我們用sigprocmask設置的mask取消,而僅僅是在其基礎上再次將一些信號block掉,當handler結束時,系統會自動將mask恢復成以前的樣子,所以這個sigaction中的sa_mask只作用本信號的handler的執行時間。

一個使用 sigaction 進行信號處理的示例:

#include <signal.h>

void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}

int main(int argc,char **argv)
{
    struct sigaction sa;  
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sig_handler_with_arg;
    sa.sa_flags = SA_RESETHAND;

sigaction(SIGSEGV, &sa, NULL);
...
 }

Android tombstones 分析

Android系統中應用出現nativecrash時,會在 /data/tombstones 目錄下生成 tombstone_xx 的日志文件,記錄了應用crash發生時的內存、寄存器、堆棧信息等。並且通過logcat將其內容輸出。

Android 4.0中tombstones處理部分的源碼位於 /system/core/debuggerd 和 bonic/linker/debugger.c 中。

在 bonic/linker/debugger.c 中的 debugger_init() 中對7個Signal進行了注冊處理,debugger_signal_handler作為信號處理函數。

void debugger_init()
{
  struct sigaction act;
  memset(&act, 0, sizeof(act));
  act.sa_sigaction = debugger_signal_handler;
  act.sa_flags = SA_RESTART | SA_SIGINFO;
  sigemptyset(&act.sa_mask);

  sigaction(SIGILL, &act, NULL);
  sigaction(SIGABRT, &act, NULL);
  sigaction(SIGBUS, &act, NULL);
  sigaction(SIGFPE, &act, NULL);
  sigaction(SIGSEGV, &act, NULL);
  sigaction(SIGSTKFLT, &act, NULL);
  sigaction(SIGPIPE, &act, NULL);
}

debugger_signal_handler 中,通過socket client 與 /system/core/debuggerd 中的socket server進行通信,在/system/core/debuggerd中進行crash進程的分析( handle_crashing_process 函數中),生成tombstones文件(dump_crash_report 函數)。

unwind_backtrace_with_ptrace 函數獲取backtrae,通過 ptrace 讀取寄存器和相關內存地址。

Google Breakpad 項目

Google Breakpad 是Google開源的跨平台崩潰轉儲和分析模塊,他支持Windows,Linux和Mac和Solaris系統,並可以編譯到Android工程中。Google-breakpad的好處在於可以屏蔽了不同平台的差異,使用統一的文件格式記錄和分析符號文件格式和崩潰棧信息。

在Linux系統上,google-breakpad也是通過信號機制來捕獲crash,大致過程可以通過源碼中的注釋了解:

//    The signal flow looks like this:

//  SignalHandler (uses a global stack of ExceptionHandler objects to find
//        |         one to handle the signal. If the first rejects it, try
//        |         the second etc...)
//        V
//   HandleSignal ----------------------------| (clones a new process which
//        |                                   |  shares an address space with
//   (wait for cloned                         |  the crashed process. This
//     process)                               |  allows us to ptrace the crashed
//        |                                   |  process)
//        V                                   V
//   (set signal handler to             ThreadEntry (static function to bounce
//    SIG_DFL and rethrow,                    |      back into the object)
//    killing the crashed                     |
//    process)                                V
//                                          DoDump  (writes minidump)
//                                            |
//                                            V
//                                         sys_exit

客戶端中google-breakpad的使用也很簡單,可以參照官方wiki的教程文檔:How To Add Breakpad To Your Linux Application 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM