天天看點

Android TombStone 分析1.什麼是tombstone2.tombstone檔案長什麼樣3.怎麼分析tombstone檔案4.一些分析工具

轉載自:https://www.cnblogs.com/CoderTian/p/5980426.html

1.什麼是tombstone

當一個動态庫(native 程式)開始執行時,系統會注冊一些連接配接到 debuggerd 的 signal handlers,當系統 crash 的時候,會儲存一個 tombstone 檔案到/data/tombstones目錄下(Logcat中也會有相應的資訊),檔案的确就像墓碑一樣記錄了死亡了的程序的基本資訊(例如程序的程序号,線程号),死亡的位址(在哪個位址上發生了 Crash),死亡時的現場是什麼樣的(記錄了一系列的堆棧調用資訊)等等。 

2.tombstone檔案長什麼樣

一個tombstone檔案大概包含以下資訊:

--------- beginning of crash
F/libc    (  244): invalid address or address of corrupt block 0xb82f54a0 passed to dlfree
I/libc    (  244): debuggerd_signal_handler called: signal=11, fn=0xb6fbdaa1
F/libc    (  244): Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadbaad in tid 244 (mediaserver)
I/libc    (  244): exit from debuggerd_signal_handler
W/NativeCrashListener(  916): Couldn't find ProcessRecord for pid 244
I/DEBUG   (  241): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/DEBUG   (  241): AM write failure (32 / Broken pipe)
I/DEBUG   (  241): Build fingerprint: XXXXXXXXX
I/DEBUG   (  241): Revision: '0'
I/DEBUG   (  241): ABI: 'arm'
I/DEBUG   (  241): pid: 244, tid: 244, name: mediaserver  >>> /system/bin/mediaserver <<<
I/DEBUG   (  241): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdeadbaad
I/art     ( 3078): now dumpable=1
I/DEBUG   (  241): Abort message: 'invalid address or address of corrupt block 0xb82f54a0 passed to dlfree'
I/DEBUG   (  241):     r0 00000000  r1 b6f20dec  r2 deadbaad  r3 00000000
I/DEBUG   (  241):     r4 b82f54a0  r5 b6f220f8  r6 00000000  r7 42424242
I/DEBUG   (  241):     r8 ffffffff  r9 b82f5460  sl 00000030  fp 00000000
I/DEBUG   (  241):     ip 00000000  sp beb2c020  lr b6ef1fa7  pc b6ef1fa8  cpsr 600e0030
I/DEBUG   (  241):     d0  0000000000000000  d1  6f2073736572646c
I/DEBUG   (  241):     d2  707572726f632066  d3  206b636f6c622072
I/DEBUG   (  241):     d4  4242424242424242  d5  4242424242424242
I/DEBUG   (  241):     d6  4242424242424242  d7  3ecccccd42424242
I/DEBUG   (  241):     d8  0000000000000000  d9  0000000000000000
I/DEBUG   (  241):     d10 0000000000000000  d11 0000000000000000
I/DEBUG   (  241):     d12 0000000000000000  d13 0000000000000000
I/DEBUG   (  241):     d14 0000000000000000  d15 0000000000000000
I/DEBUG   (  241):     d16 0000000000000000  d17 3ff0000000000000
I/DEBUG   (  241):     d18 7e37e43c8800759c  d19 bfd5f3f082400000
I/DEBUG   (  241):     d20 3e66376972bea4d0  d21 bf66b12699b6468f
I/DEBUG   (  241):     d22 3fc54aa75950670f  d23 bfd73498f0a5ef3a
I/DEBUG   (  241):     d24 3fe0000000000000  d25 bfaaf3ec933c988f
I/DEBUG   (  241):     d26 0000000000000000  d27 4000000000000000
I/DEBUG   (  241):     d28 4002e6931e14bde7  d29 3faaf3ec9198f99c
I/DEBUG   (  241):     d30 3ff0000000000000  d31 3fd29572efd86cee
I/DEBUG   (  241):     scr 20000010
I/DEBUG   (  241): 
I/DEBUG   (  241): backtrace:
I/DEBUG   (  241):     #00 pc 00028fa8  /system/lib/libc.so (dlfree+1239)
I/DEBUG   (  241):     #01 pc 0000f2cb  /system/lib/libc.so (free+10)
I/DEBUG   (  241):     #02 pc 0000a1cb  /system/lib/libstagefright_foundation.so (_ZN7android7ABufferD2Ev+42)
I/DEBUG   (  241):     #03 pc 0000a211  /system/lib/libstagefright_foundation.so (_ZN7android7ABufferD0Ev+4)
I/DEBUG   (  241):     #04 pc 0000d68d  /system/lib/libutils.so (_ZNK7android7RefBase9decStrongEPKv+40)
I/DEBUG   (  241):     #05 pc 0005adfd  /system/lib/libstagefright.so (_ZN7android2spINS_13GraphicBufferEED2Ev+10)
I/DEBUG   (  241):     #06 pc 0007cd0f  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor10parseChunkEPxi+634)
I/DEBUG   (  241):     #07 pc 0007d43d  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor10parseChunkEPxi+2472)
I/DEBUG   (  241):     #08 pc 0007e873  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor12readMetaDataEv+58)
I/DEBUG   (  241):     #09 pc 0007eaa1  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor11countTracksEv+4)
I/DEBUG   (  241):     #10 pc 000acf9d  /system/lib/libstagefright.so (_ZN7android13ExtendedUtils29MediaExtractor_CreateIfNeededENS_2spINS_14MediaExtractorEEERKNS1_INS_10DataSourceEEEPKc+60)
I/DEBUG   (  241):     #11 pc 0008e3f5  /system/lib/libstagefright.so (_ZN7android14MediaExtractor6CreateERKNS_2spINS_10DataSourceEEEPKc+624)
I/DEBUG   (  241):     #12 pc 0006ace9  /system/lib/libstagefright.so (_ZN7android13AwesomePlayer15setDataSource_lERKNS_2spINS_10DataSourceEEE+12)
I/DEBUG   (  241):     #13 pc 0006c0dd  /system/lib/libstagefright.so (_ZN7android13AwesomePlayer13setDataSourceEixx+228)
I/DEBUG   (  241):     #14 pc 0003d647  /system/lib/libmediaplayerservice.so (_ZN7android18MediaPlayerService6Client13setDataSourceEixx+362)
I/DEBUG   (  241):     #15 pc 0005ea03  /system/lib/libmedia.so (_ZN7android13BnMediaPlayer10onTransactEjRKNS_6ParcelEPS1_j+478)
I/DEBUG   (  241):     #16 pc 00017fad  /system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+60)
I/DEBUG   (  241):     #17 pc 0001cfdb  /system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+562)
I/DEBUG   (  241):     #18 pc 0001d12f  /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+38)
I/DEBUG   (  241):     #19 pc 0001d171  /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+48)
I/DEBUG   (  241):     #20 pc 00001721  /system/bin/mediaserver
I/DEBUG   (  241):     #21 pc 0000f411  /system/lib/libc.so (__libc_init+44)
I/DEBUG   (  241):     #22 pc 00001998  /system/bin/mediaserver
I/DEBUG   (  241): 
I/DEBUG   (  241): stack:
I/DEBUG   (  241):          beb2bfe0  00000000  
I/DEBUG   (  241):          beb2bfe4  29ec038f  
I/DEBUG   (  241):          beb2bfe8  0009eb34  
I/DEBUG   (  241):          beb2bfec  b82f54a0  [heap]
I/DEBUG   (  241):          beb2bff0  b6f220f8  
I/DEBUG   (  241):          beb2bff4  00000000  
I/DEBUG   (  241):          beb2bff8  42424242  
I/DEBUG   (  241):          beb2bffc  b6edb3d1  /system/lib/libc.so (__libc_fatal_no_abort+16)
I/DEBUG   (  241):          beb2c000  b6f12f97  /system/lib/libc.so
I/DEBUG   (  241):          beb2c004  beb2c014  [stack]
I/DEBUG   (  241):          beb2c008  b6f167be  /system/lib/libc.so
I/DEBUG   (  241):          beb2c00c  b6ef1fa7  /system/lib/libc.so (dlfree+1238)
I/DEBUG   (  241):          beb2c010  b6f12f97  /system/lib/libc.so
I/DEBUG   (  241):          beb2c014  b82f54a0  [heap]
I/DEBUG   (  241):          beb2c018  b6f167be  /system/lib/libc.so
I/DEBUG   (  241):          beb2c01c  b82f54b0  [heap]
I/DEBUG   (  241):     #00  beb2c020  b82f5460  [heap]
......
           

它包含了發生問題的程序ID資訊

I/DEBUG   (  241): pid: 244, tid: 244, name: mediaserver  >>> /system/bin/mediaserver <<<
           

當 tid == pid 時,問題發生在父程序,反之問題發生在子程序,從上面的日志資訊可以看出發生問題的程序是mediaserver的子程序。

Terminated signal 和 fault address 資訊

F/libc    (  244): Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadbaad in tid 244 (mediaserver)
           

這裡的資訊說明出現程序 Crash 的原因是因為程式産生了段錯誤的信号,通路了非法的記憶體空間,而通路的非法位址是 0xdeadbaad。

信号機制是 Linux 程序間通信的一種重要方式,Linux 信号一方面用于正常的程序間通信和同步,如任務控制(SIGINT, SIGTSTP,SIGKILL, SIGCONT,……);另一方面,它還負責監控系統異常及中斷。 當應用程式運作異常時, Linux 核心将産生錯誤信号并通知目前程序。 目前程序在接收到該錯誤信号後,可以有三種不同的處理方式。

(1)忽略該信号。

(2)捕捉該信号并執行對應的信号處理函數(signal handler)。

(3)執行該信号的預設操作(如 SIGSEGV, 其預設操作是終止程序)。

當 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.

定義在prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/signum.h

/* Signal number definitions.  Linux version.
   Copyright (C) 1995,1996,1997,1998,1999,2003 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#ifdef	_SIGNAL_H

/* Fake signal functions.  */
#define SIG_ERR	((__sighandler_t) -1)		/* Error return.  */
#define SIG_DFL	((__sighandler_t) 0)		/* Default action.  */
#define SIG_IGN	((__sighandler_t) 1)		/* Ignore signal.  */

#ifdef __USE_UNIX98
# define SIG_HOLD	((__sighandler_t) 2)	/* Add signal to hold mask.  */
#endif


/* Signals.  */
#define	SIGHUP		1	/* Hangup (POSIX).  */
#define	SIGINT		2	/* Interrupt (ANSI).  */
#define	SIGQUIT		3	/* Quit (POSIX).  */
#define	SIGILL		4	/* Illegal instruction (ANSI).  */
#define	SIGTRAP		5	/* Trace trap (POSIX).  */
#define	SIGABRT		6	/* Abort (ANSI).  */
#define	SIGIOT		6	/* IOT trap (4.2 BSD).  */
#define	SIGBUS		7	/* BUS error (4.2 BSD).  */
#define	SIGFPE		8	/* Floating-point exception (ANSI).  */
#define	SIGKILL		9	/* Kill, unblockable (POSIX).  */
#define	SIGUSR1		10	/* User-defined signal 1 (POSIX).  */
#define	SIGSEGV		11	/* Segmentation violation (ANSI).  */
#define	SIGUSR2		12	/* User-defined signal 2 (POSIX).  */
#define	SIGPIPE		13	/* Broken pipe (POSIX).  */
#define	SIGALRM		14	/* Alarm clock (POSIX).  */
#define	SIGTERM		15	/* Termination (ANSI).  */
#define	SIGSTKFLT	16	/* Stack fault.  */
#define	SIGCLD		SIGCHLD	/* Same as SIGCHLD (System V).  */
#define	SIGCHLD		17	/* Child status has changed (POSIX).  */
#define	SIGCONT		18	/* Continue (POSIX).  */
#define	SIGSTOP		19	/* Stop, unblockable (POSIX).  */
#define	SIGTSTP		20	/* Keyboard stop (POSIX).  */
#define	SIGTTIN		21	/* Background read from tty (POSIX).  */
#define	SIGTTOU		22	/* Background write to tty (POSIX).  */
#define	SIGURG		23	/* Urgent condition on socket (4.2 BSD).  */
#define	SIGXCPU		24	/* CPU limit exceeded (4.2 BSD).  */
#define	SIGXFSZ		25	/* File size limit exceeded (4.2 BSD).  */
#define	SIGVTALRM	26	/* Virtual alarm clock (4.2 BSD).  */
#define	SIGPROF		27	/* Profiling alarm clock (4.2 BSD).  */
#define	SIGWINCH	28	/* Window size change (4.3 BSD, Sun).  */
#define	SIGPOLL		SIGIO	/* Pollable event occurred (System V).  */
#define	SIGIO		29	/* I/O now possible (4.2 BSD).  */
#define	SIGPWR		30	/* Power failure restart (System V).  */
#define SIGSYS		31	/* Bad system call.  */
#define SIGUNUSED	31

#define	_NSIG		65	/* Biggest signal number + 1
				   (including real-time signals).  */

#define SIGRTMIN        (__libc_current_sigrtmin ())
#define SIGRTMAX        (__libc_current_sigrtmax ())

/* These are the hard limits of the kernel.  These values should not be
   used directly at user level.  */
#define __SIGRTMIN	32
#define __SIGRTMAX	(_NSIG - 1)

#endif	/* <signal.h> included.  */
           

3.怎麼分析tombstone檔案

我們主要關注 backtrace 下面的内容,它儲存了發生 crash 時候的函數調用關系,但是需要注意的是它的調用順序是從下向上執行的(#XX pc -->#00 pc),通過這些函數調用關系,我們就可以大概定位出問題發生的地方,在本次 tombstone 日志中,我們通過

I/DEBUG   (  241):     #00 pc 00028fa8  /system/lib/libc.so (dlfree+1239)
I/DEBUG   (  241):     #01 pc 0000f2cb  /system/lib/libc.so (free+10)
I/DEBUG   (  241):     #02 pc 0000a1cb  /system/lib/libstagefright_foundation.so (_ZN7android7ABufferD2Ev+42)
I/DEBUG   (  241):     #03 pc 0000a211  /system/lib/libstagefright_foundation.so (_ZN7android7ABufferD0Ev+4)
I/DEBUG   (  241):     #04 pc 0000d68d  /system/lib/libutils.so (_ZNK7android7RefBase9decStrongEPKv+40)
           

可以分析出問題是在調用free函數時發生了指針錯誤,還可以看出問題發生的原因是libstagefright_foundation.so中釋放了兩次ABuffer引用,接着就去分析是誰誰釋放的AUbffer強指針。

I/DEBUG   (  241):     #05 pc 0005adfd  /system/lib/libstagefright.so (_ZN7android2spINS_13GraphicBufferEED2Ev+10)
I/DEBUG   (  241):     #06 pc 0007cd0f  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor10parseChunkEPxi+634)
I/DEBUG   (  241):     #07 pc 0007d43d  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor10parseChunkEPxi+2472)
I/DEBUG   (  241):     #08 pc 0007e873  /system/lib/libstagefright.so (_ZN7android14MPEG4Extractor12readMetaDataEv+58)
           

可以看出來在 libstagefright 動态庫中的MPEG4Extractor.cpp 的 parseChunk函數出現的錯誤。

4.一些分析工具

雖然通過 tombstone 的日志檔案我們就可以大緻定位出引發 crash 的代碼的位置,但是通過借助一些分析工具,可以大大的提高工作效率和準确性,下面就來介紹以下這些工具。

(1)addr2line

addr2line 是 用來獲得指定動态連結庫檔案或者可執行檔案中指定位址對應的源代碼資訊的工具

工具路徑:

prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin/x86_64-linux-android-addr2line
           

它的各種參數如下所示(這個是google aosp android M 中帶的):

Usage: ./x86_64-linux-android-addr2line [option(s)] [addr(s)]
 Convert addresses into line number/file name pairs.
 If no addresses are specified on the command line, they will be read from stdin
 The options are:
  @<file>                Read options from <file>
  -a --addresses         Show addresses
  -b --target=<bfdname>  Set the binary file format
  -e --exe=<executable>  Set the input file name (default is a.out)
  -i --inlines           Unwind inlined functions
  -j --section=<name>    Read section-relative offsets instead of addresses
  -p --pretty-print      Make the output easier to read for humans
  -s --basenames         Strip directory names
  -f --functions         Show function names
  -C --demangle[=style]  Demangle function names
  -h --help              Display this information
  -v --version           Display the program's version

./x86_64-linux-android-addr2line: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 a.out-i386-linux pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex
Report bugs to <http://source.android.com/source/report-bugs.html>
           

addr2line 的基本用法如下所示:

./prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8/bin/arm-linux-androideabi-addr2line -f -e out/debug/target/product/XXXX/symbols/system/lib/libstagefright.so  0007cd0f 
_ZN7android14MPEG4Extractor10parseChunkEPxi
/home/XXX/source/XXX/LINUX/android/frameworks/av/media/libstagefright/MPEG4Extractor.cpp:2180 (discriminator 1)
           

這裡需要注意的是不能直接使用out/debug/target/product/XXX/system/lib/libstagefright.so,會出現運作上面指令之後顯示

??
??:0
           

因為這個動态庫是最後要打包到最後生成的system.ing中的,是以它不包含調試符号資訊。

(2)ndk-stack

Android NDK 自從版本 r6開始, 提供了一個工具 ndk-stack。這個工具能自動分析 tombstone 檔案, 能将崩潰時的調用記憶體位址和 c++ 代碼一行一行對應起來.

它的使用方法為:

Usage:
   ndk-stack -sym <path> [-dump <path>]
      -sym  Contains full path to the root directory for symbols.
      -dump Contains full path to the file containing the crash dump.
            This is an optional parameter. If ommited, ndk-stack will
            read input data from stdin
   See docs/NDK-STACK.html in your NDK installation tree for more details.
           

①dump 參數很容易了解, 即 dump 下來的 log 文本檔案. ndk-stack會分析此檔案。

②sym 參數就是你android項目下,編譯成功之後,obj目錄下的檔案(android系統源碼o 中帶有符号資訊的檔案)。

我們可以使用它來分析我們的log檔案

ndk-stack -sym xxx.so -dump logfile
           

是以我們在調試android系統源碼的時候也可以直接分析log中的crash資訊。

adb shell logcat | ndk-stack -sym out/debug/target/product/XXXX/symbols/system/lib/xxx.so
           

(3)stack.py

stack.py工具就是要把backtrace通過addr2line工具一次性把addr對應到代碼

#!/usr/bin/python2.4 -E
import getopt
import os
import re
import string
import sys
import getpass
import urllib
import subprocess
def PrintUsage():
  print
  print "  usage: " + sys.argv[0] + " [options] [FILE]"
  print
  print "  --symbols-dir=path"
  print "       the path to a symbols dir, such as =/tmp/out/target/product/dream/symbols"
  print
  print "  --symbols-zip=path"
  print "       the path to a symbols zip file, such as =dream-symbols-12345.zip"
  print
  print "  --auto"
  print "       attempt to:"
  print "         1) automatically find the build number in the crash"
  print "         2) if it's an official build, download the symbols "
  print "            from the build server, and use them"
  print
  print "  FILE should contain a stack trace in it somewhere"
  print "       the tool will find that and re-print it with"
  print "       source files and line numbers.  If you don't"
  print "       pass FILE, or if file is -, it reads from"
  print "       stdin."
  print
  sys.exit(1)
def FindSymbolsDir():
  cmd = "CALLED_FROM_SETUP=true make -f build/core/envsetup.mk " \
      + "dumpvar-abs-TARGET_OUT_UNSTRIPPED"
  stream = os.popen(cmd)
  str = stream.read()
  stream.close()
  return str.strip()
# returns a list containing the function name and the file/lineno
def CallAddr2Line(lib, addr):
  uname = os.uname()[0]
  if uname == "Darwin":
    proc = os.uname()[-1]
    if proc == "i386":
      uname = "darwin-x86"
    else:
      uname = "darwin-ppc"
  if lib != "":
    #cmd = "./prebuilt/" + uname + "/toolchain-eabi-4.2.1/bin/arm-eabi-addr2line" \
    #cmd = "./prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-addr2line" \
    cmd = " arm-eabi-addr2line" \
        + " -f -e " + SYMBOLS_DIR + lib \
        + " 0x" + addr
    stream = os.popen(cmd)
    lines = stream.readlines()
    list = map(string.strip, lines)
  else:
    list = []
  if list != []:
    # Name like "move_forward_type<JavaVMOption>" causes troubles
    mangled_name = re.sub('<', '\<', list[0]);
    mangled_name = re.sub('>', '\>', mangled_name);
    #cmd = "./prebuilt/" + uname + "/toolchain-eabi-4.2.1/bin/arm-eabi-c++filt "\
    cmd = "./prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-c++filt "\
          + mangled_name
    stream = os.popen(cmd)
    list[0] = stream.readline()
    stream.close()
    list = map(string.strip, list)
  else:
    list = [ "(unknown)", "(unknown)" ]
  return list
class SSOCookie(object):
  """
  creates a cookie file so we can download files from the build server
  """
  def __init__(self, cookiename=".sso.cookie", keep=False):
    self.sso_server = "login.corp.google.com"
    self.name = cookiename
    self.keeper = keep
    self.tmp_opts = ".curl.options"
    if not os.path.exists(self.name):
      user = os.environ['USER']
      print "\n%s, to access the symbols, please enter your LDAP " % user,
      password = getpass.getpass()
      params = urllib.urlencode({"u": user, "pw": password})
      fd = os.open(self.tmp_opts, os.O_RDWR | os.O_CREAT, 0600)
      os.write(fd, '-b "%s"\n' % self.name)
      os.write(fd, '-c "%s"\n' % self.name)
      os.write(fd, '-s"\n-L\n-d "%s"\n' % params)
      os.write(fd, 'url = "https://%s/login?ssoformat=CORP_SSO"\n' % 
               self.sso_server)
      # login to SSO
      response = os.popen("/usr/bin/curl -K %s" % self.tmp_opts)
      response.close()
      if os.path.exists(self.tmp_opts):
        os.remove(self.tmp_opts)
      if os.path.exists(self.name):
        os.chmod(self.name, 0600)
      else:
        print "Could not log in to SSO"
        sys.exit(1)
  def __del__(self):
      """clean up"""
      if not self.keeper:
        os.remove(self.name)
class NoBuildIDException(Exception):
  pass
def FindBuildFingerprint(lines):
  """
  Searches the given file (array of lines) for the build fingerprint information
  """
  fingerprint_regex = re.compile("^.*Build fingerprint:\s'(?P<fingerprint>.*)'")
  for line in lines:
    fingerprint_search = fingerprint_regex.match(line.strip())
    if fingerprint_search:
      return fingerprint_search.group('fingerprint')
      
  return None  # didn't find the fingerprint string, so return none
  
class SymbolDownloadException(Exception):
  pass
DEFAULT_SYMROOT = "/tmp/symbols"
def DownloadSymbols(fingerprint, cookie):
  """
  Attempts to download the symbols from the build server, extracts them,
  and returns the path.  Takes the fingerprint from the pasted stack trace
  and the SSOCookie
  """
  if fingerprint is None:
    return (None, None)
  symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(fingerprint))
  if not os.path.exists(symdir):
    os.makedirs(symdir)
  # build server figures out the branch based on the CL
  params = {
              'op': "GET-SYMBOLS-LINK",
              'fingerprint': fingerprint,
           }
  url = urllib.urlopen("http://android-build/buildbot-update?",
                            urllib.urlencode(params)).readlines()[0]
  if url == "":
    raise SymbolDownloadException, "Build server down? Failed to find syms..."
  regex_str = (r'(?P<baseURL>http\:\/\/android-build\/builds\/.*\/[0-9]+' + 
           r'\/)(?P<img>.*)')
  url_regex = re.compile(regex_str)
  url_match = url_regex.match(url)
  if url_match is None:
    raise SymbolDownloadException, "Unexpected results from build server URL..."
  
  baseURL = url_match.group('baseURL')
  img =  url_match.group('img')
  symbolfile = img.replace("-img-", "-symbols-")
  symurl = baseURL + symbolfile
  localsyms = symdir + symbolfile
  if not os.path.exists(localsyms):
    print "downloading %s ..." % symurl
    curlcmd = ("""/usr/bin/curl -b %s -sL -w %%{http_code} -o %s %s""" % 
                          (cookie.name, localsyms, symurl))
    (fi,fo,fe) = os.popen3(curlcmd)
    fi.close()
    code = fo.read()
    err = fe.read()
    if err != "":
      raise SymbolDownloadException, "stderr from curl download: %s" % err
    if code != "200":
      raise SymbolDownloadException, "Faied to download %s" % symurl
  else:
    print "using existing cache for symbols"
  print "extracting %s..." % symbolfile
  saveddir = os.getcwd()
  os.chdir(symdir)
  unzipcode = subprocess.call(["unzip", "-qq", "-o", localsyms])
  if unzipcode > 0:
    raise SymbolDownloadException, ("failed to extract symbol files (%s)." 
                             % localsyms)
  os.chdir(saveddir)
  
  return (symdir, "%s/out/target/product/dream/symbols" % symdir)
def UnzipSymbols(symbolfile):
  """Unzips a file to DEFAULT_SYMROOT and returns the unzipped location.
  Args:
    symbolfile: The .zip file to unzip
  Returns:
    A tuple containing (the directory into which the zip file was unzipped,
    the path to the "symbols" directory in the unzipped file).  To clean
    up, the caller can delete the first element of the tuple.
  Raises:
    SymbolDownloadException: When the unzip fails.
  """
  symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(symbolfile))
  if not os.path.exists(symdir):
    os.makedirs(symdir)
  print "extracting %s..." % symbolfile
  saveddir = os.getcwd()
  os.chdir(symdir)
  unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile])
  if unzipcode > 0:
    raise SymbolDownloadException, ("failed to extract symbol files (%s)." 
                             % symbolfile)
  os.chdir(saveddir)
  
  return (symdir, "%s/out/target/product/dream/symbols" % symdir)
def PrintTraceLines(traceLines):
    maxlen = max(map(lambda tl: len(tl[1]), traceLines))
    print
    print "Stack Trace:"
    print "  ADDR      " + "FUNCTION".ljust(maxlen) + "  FILE:LINE"
    for tl in traceLines:
      print "  " + tl[0] + "  " + tl[1].ljust(maxlen) + "  " + tl[2]
    return
def PrintValueLines(valueLines):
    print
    print "Stack Data:"
    print "  ADDR      VALUE     " + "FILE:LINE/FUNCTION"
    for vl in valueLines:
      print "  " + vl[1] + "  " + vl[2] + "  " + vl[4]
      if vl[4] != "":
        print "                      " + vl[3]
    return
def ConvertTrace(lines):
  PROCESS_INFO_LINE = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
  SIGNAL_LINE = re.compile("(signal [0-9]+ \(.*\).*)")
  REGISTER_LINE = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})")
  TRACE_LINE = re.compile("(.*)\#([0-9]+)  (..) ([0-9a-f]{3})([0-9a-f]{5})  ([^\r\n \t]*)")
  VALUE_LINE = re.compile("(.*)([0-9a-f]{2})([0-9a-f]{6})  ([0-9a-f]{3})([0-9a-f]{5})  ([^\r\n \t]*)")
  THREAD_LINE = re.compile("(.*)(\-\-\- ){15}\-\-\-")
  traceLines = []
  valueLines = []
  for line in lines:
    header = PROCESS_INFO_LINE.search(line)
    if header:
      print header.group(1)
      continue
    header = SIGNAL_LINE.search(line)
    if header:
      print header.group(1)
      continue
    header = REGISTER_LINE.search(line)
    if header:
      print header.group(1)
      continue
    if TRACE_LINE.match(line):
      match = TRACE_LINE.match(line)
      groups = match.groups()
      if groups[5] == "<unknown>" or groups[5] == "[heap]" or groups[5] == "[stack]":
        traceLines.append((groups[3]+groups[4], groups[5], groups[5]))
      else:
        info = CallAddr2Line(groups[5], groups[4])
        traceLines.append((groups[3]+groups[4], info[0], info[1]))
    if VALUE_LINE.match(line):
      match = VALUE_LINE.match(line)
      groups = match.groups()
      if groups[5] == "<unknown>" or groups[5] == "[heap]" or groups[5] == "[stack]" or groups[5] == "":
        valueLines.append((groups[0], groups[1]+groups[2], groups[3]+groups[4], groups[5], ""))
      else:
        info = CallAddr2Line(groups[5], groups[4])
        valueLines.append((groups[0], groups[1]+groups[2], groups[3]+groups[4], info[0], info[1]))
    header = THREAD_LINE.search(line)
    if header:
      if len(traceLines) > 0:
        PrintTraceLines(traceLines)
      if len(valueLines) > 0:
        PrintValueLines(valueLines)
      traceLines = []
      valueLines = []
      print
      print "-----------------------------------------------------\n"
  if len(traceLines) > 0:
    PrintTraceLines(traceLines)
  if len(valueLines) > 0:
    PrintValueLines(valueLines)
SYMBOLS_DIR = FindSymbolsDir()
if __name__ == '__main__':
  try:
    options, arguments = getopt.getopt(sys.argv[1:], "",
                             ["auto", "symbols-dir=", "symbols-zip=", "help"])
  except getopt.GetoptError, error:
    PrintUsage()
  
  AUTO = False
  zipArg = None
  for option, value in options:
    if option == "--help":
      PrintUsage()
    elif option == "--symbols-dir":
      SYMBOLS_DIR = value
    elif option == "--symbols-zip":
      zipArg = value
    elif option == "--auto":
      AUTO = True
  
  if len(arguments) > 1:
    PrintUsage()
  if AUTO:
    cookie = SSOCookie(".symbols.cookie")
  
  if len(arguments) == 0 or arguments[0] == "-":
    print "Reading native crash info from stdin"
    f = sys.stdin
  else:
    print "Searching for native crashes in %s" % arguments[0]
    f = open(arguments[0], "r")
  lines = f.readlines()
  rootdir = None
  if AUTO:
    fingerprint = FindBuildFingerprint(lines)
    print "fingerprint:", fingerprint
    rootdir, SYMBOLS_DIR = DownloadSymbols(fingerprint, cookie)
  elif zipArg is not None:
    rootdir, SYMBOLS_DIR = UnzipSymbols(zipArg)
  
  print "Reading symbols from", SYMBOLS_DIR
  lines = ConvertTrace(lines)
  
  if rootdir is not None:
    # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
    cmd = "rm -rf \"%s\"" % rootdir
    print "\ncleaning up (%s)" % cmd
    os.system(cmd)
  
  # vi: ts=2 sw=2
           

使用方法:

python stack.py --symbols-dir=out/target/profuct/XXX/sysbols/  tombstone-00(tombstone檔案)