Android 除錯中 addr2line 工具的使用
我們在解bug的時候經常能碰到一些段錯誤。下面是我從一個bug的log中擷取的一個段錯誤:
////////////////////////////////////////////////////////////////////////////////////////////////////// 08-19 19:08:27.132 2105 2105 I DEBUG : pid: 134, tid: 2104, name: OMXCallbackDisp >>> /system/bin/mediaserver <<< 08-19 19:08:27.132 2105 2105 I DEBUG : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 44c86948 08-19 19:08:27.280 1803 1803 I SurfaceTextureClient: [STC::queueBuffer] (this:0x6162c9f8) fps:21.68, dur:1014.76, max:104.52, min:10.36 08-19 19:08:27.281 130 1018 I BufferQueue: [com.android.gallery3d/com.android.camera.Camera](this:0x41c1f008,api:1) [queue] fps:21.70, dur:1013.89, max:103.33, min:10.27 08-19 19:08:27.293 130 321 I SurfaceTextureClient: [STC::queueBuffer] (this:0x406df2e0) fps:34.70, dur:1037.44, max:75.25, min:14.19 08-19 19:08:27.294 130 321 I BufferQueue: [FramebufferSurface](this:0x406de810,api:1) [release] fps:34.70, dur:1037.44, max:75.23, min:14.19 08-19 19:08:27.294 130 321 I BufferQueue: [FramebufferSurface](this:0x406de810,api:1) [queue] fps:34.70, dur:1037.43, max:75.23, min:14.19 08-19 19:08:27.294 130 321 I SurfaceFlinger: [SurfaceFlinger] fps:34.701393,dur:1037.42,max:75.22,min:14.19 08-19 19:08:27.452 465 538 D MDLOGGER: pMDEngine->m_bTerminate is false. busy for modem = 0,m_nM2ABufCnt = 8 08-19 19:08:27.452 465 538 D MDLOGGER: thrDetectFilter: Detecting catcher_filter.bin exist or not every 5 seconds! 08-19 19:08:27.462 2105 2105 I DEBUG : r0 00000000 r1 00000081 r2 00000001 r3 ffffffe8 08-19 19:08:27.462 2105 2105 I DEBUG : r4 46d4de60 r5 42671490 r6 40185d35 r7 00100000 08-19 19:08:27.462 2105 2105 I DEBUG : r8 45661240 r9 00000000 sl 40dd0159 fp 46c4d930 08-19 19:08:27.462 2105 2105 I DEBUG : ip ffffffea sp 46d4de38 lr 4009915c pc 44c86948 cpsr 60000010 08-19 19:08:27.462 2105 2105 I DEBUG : 08-19 19:08:27.462 2105 2105 I DEBUG : backtrace: 08-19 19:08:27.462 2105 2105 I DEBUG : #00 pc 00042948 <unknown> 08-19 19:08:27.462 2105 2105 I DEBUG : #01 pc 0000d158 /system/lib/libc.so 08-19 19:08:27.462 2105 2105 I DEBUG : #02 pc 00032954 <unknown> 08-19 19:08:27.462 2105 2105 I DEBUG : 08-19 19:08:27.462 2105 2105 I DEBUG : stack: 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4ddf8 00000000 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4ddfc 00000020 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de00 00000000 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de04 00000000 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de08 00000000 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de0c 46d4de34 [stack:2104] 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de10 44c86a04 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de14 46d4de60 [stack:2104] 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de18 42671490 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de1c 40185d35 /system/lib/libutils.so 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de20 00100000 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de24 45661240 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de28 00000000 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de2c 40dd0159 /system/lib/libstagefright.so 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de30 df0027ad 08-19 19:08:27.462 2105 2105 I DEBUG : 46d4de34 00000000 08-19 19:08:27.462 2105 2105 I DEBUG : #00 46d4de38 46d4de60 [stack:2104] 08-19 19:08:27.463 2105 2105 I DEBUG : ........ ........ 08-19 19:08:27.463 2105 2105 I DEBUG : #01 46d4de38 46d4de60 [stack:2104] 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de3c 46c4d958 08-19 19:08:27.463 2105 2105 I DEBUG : #02 46d4de40 46d4de80 [stack:2104] 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de44 40f01d33 /system/lib/libstagefright_omx.so (android::OMXNodeInstance::onMessage(android::omx_message const&)+50) 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de48 46c4db48 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de4c 46c4db5c 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de50 46d4de80 [stack:2104] 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de54 40f00347 /system/lib/libstagefright_omx.so (android::OMX::CallbackDispatcher::loop()+110) 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de58 00000000 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de5c 46c4db50 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de60 00000000 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de64 00000019 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de68 00000000 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de6c 00000000 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de70 00000002 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de74 45def1ac /system/lib/libMtkOmxVdec.so 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de78 44c89f38 08-19 19:08:27.463 2105 2105 I DEBUG : 46d4de7c 45dedea1 /system/lib/libMtkOmxVdec.so (MtkOmxVdec::DeInitVideoDecodeHW()+84) //////////////////////////////////////////////////////////////////////////////////////////////////////
有沒有看起來很眼熟?
關於段錯誤的定義,我百科了一下:
所謂的段錯誤就是指訪問的記憶體超出了系統所給這個程式的記憶體空間,通常這個值是由gd tr來儲存的,他是一個48位的暫存器,其中的32位是儲存由它指向的 gdt表,後13位儲存相應於gdt的下標,最後3位包括了程式是否在記憶體中以及程式的在cpu中的執行級別,指向 的gdt是由以64位為一個單位的表,在這張表中就儲存著程式執行的程式碼段以及資料段的起始地址以及與此相應的段限和頁面交換還有程式執行級別還有記憶體粒度等等的資訊。【不甚理解】
在 Android 的工程當中,有一部分檔案會被編譯成 so 動態共享庫供系統執行時不同模組連結使用。若調用出錯的話,會報出如上面文字中貼出來的段錯誤。
出了這種錯誤,我們一般都會通過 add2line 工具解析具體出錯程式碼的位置,能精確到哪個檔案,哪一行!再去具體位置的程式碼檢視。
若你去找這個檔案的時候卻發現沒有,不必要大驚小怪,MTK沒有提供。在MTK 提供的 Android 的工程程式碼中,經常會出於一些保密方面的考慮,將一些核心檔案編譯成 so 庫提供給客戶。讓客戶看不到這一部分的程式碼,但是能夠正常使用。諸如此類問題,可以提給MTK客服讓其幫忙追查Bug!
1,在MTK的工程中,我們是這樣來解析段錯誤的:
我們將 backtrace 以下的段錯誤 copy 到 trace.txt中。(trace.txt放到工程根目錄下)
然後在linux 環境下面,進到工程根目錄,執行如下命令:
./adbs -sout/target/product/bbk82_wet_jb5/symbols/ -l trace.txt>a.txt
將會解析得到 a.txt 檔案,該檔案中就能追溯到具體哪一個檔案哪一行出錯了。
trace.txt內容:
08-19 19:08:27.462 2105 2105 I DEBUG : backtrace:
08-19 19:08:27.462 2105 2105 I DEBUG : #00 pc 00042948 <unknown>
08-19 19:08:27.462 2105 2105 I DEBUG : #01 pc 0000d158 /system/lib/libc.so
08-19 19:08:27.462 2105 2105 I DEBUG : #02 pc 00032954 <unknown>
adbs 是一個指令碼檔案,它的作用是呼叫 arm-linux-androideabi-addr2line這個工具來解析 trace.txt 中的內容,並將解析的結果存到一個具體的檔案中去,譬如 a.txt
-s 和 -l 是引數
解析出來的結果 a.txt內容:
08-19 19:08:27.462 2105 2105 I DEBUG : backtrace:
08-19 19:08:27.462 2105 2105 I DEBUG : backtrace:
08-19 19:08:27.462 2105 2105 I DEBUG : #00 pc 00042948 <unknown>
08-19 19:08:27.462 2105 2105 I DEBUG : #00 (unknown) (unknown)
08-19 19:08:27.462 2105 2105 I DEBUG : #01 pc 0000d158 /system/lib/libc.so
08-19 19:08:27.462 2105 2105 I DEBUG : #01 __pthread_cond_pulse /home/compiler/workspace/gphone/MT6582/PD1224CT/ALPS.JB5.MP.V1.6_WET_20130810_trunk_user/bionic/libc/bionic/pthread.c:1689
08-19 19:08:27.462 2105 2105 I DEBUG : #02 pc 00032954 <unknown>
08-19 19:08:27.462 2105 2105 I DEBUG : #02 (unknown) (unknown)
08-19 19:08:27.462 2105 2105 I DEBUG :
adbs 內容:
#!/usr/bin/env python
import os
import re
import string
import sys
import getopt
###############################################################################
# match "#00 pc 0003f52e /system/lib/libdvm.so" for example
###############################################################################
trace_line = re.compile("(.*)(\#[0-9]+) (..) ([0-9a-f]{8}) ([^\r\n \t]*)")
class Options(object):pass
OPTIONS = Options()
OPTIONS.symbols = ""
OPTIONS.log = ""
# returns a list containing the function name and the file/lineno
def CallAddr2Line(lib, addr):
global symbols_dir
global addr2line_cmd
global cppfilt_cmd
if lib != "":
cmd = addr2line_cmd + \
" -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 = cppfilt_cmd + " " + mangled_name
stream = os.popen(cmd)
list[0] = stream.readline()
stream.close()
list = map(string.strip, list)
else:
list = [ "(unknown)", "(unknown)" ]
return list
###############################################################################
# similar to CallAddr2Line, but using objdump to find out the name of the
# containing function of the specified address
###############################################################################
def CallObjdump(lib, addr):
global objdump_cmd
global symbols_dir
unknown = "(unknown)"
uname = os.uname()[0]
if uname == "Darwin":
proc = os.uname()[-1]
if proc == "i386":
uname = "darwin-x86"
else:
uname = "darwin-ppc"
elif uname == "Linux":
uname = "linux-x86"
if lib != "":
next_addr = string.atoi(addr, 16) + 1
cmd = objdump_cmd \
+ " -C -d --start-address=0x" + addr + " --stop-address=" \
+ str(next_addr) \
+ " " + symbols_dir + lib
stream = os.popen(cmd)
lines = stream.readlines()
map(string.strip, lines)
stream.close()
else:
return unknown
# output looks like
# file format elf32-littlearm
# Disassembly of section .text:
# 0000833c <func+0x4>:
# 833c: 701a strb r2, [r3, #0]
# we want to extract the "func" part
num_lines = len(lines)
if num_lines < 2:
return unknown
func_name = lines[num_lines-2]
func_regexp = re.compile("(^.*\<)(.*)(\+.*\>:$)")
components = func_regexp.match(func_name)
if components is None:
return unknown
return components.group(2)
###############################################################################
# determine the symbols directory in the local build
###############################################################################
def FindSymbolsDir():
global symbols_dir
try:
#path = "sourcecode/MT6575/ALPS.ICS.MP.V2_W_20120504/out/target/product/bbk75_cu_ics/symbols/"
if (len(OPTIONS.symbols) == 0):
path = os.environ['ANDROID_PRODUCT_OUT'] + "/symbols"
else:
path = OPTIONS.symbols
except:
cmd = "CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " \
+ "SRC_TARGET_DIR=build/target make -f build/core/config.mk " \
+ "dumpvar-abs-TARGET_OUT_UNSTRIPPED"
stream = os.popen(cmd)
str = stream.read()
stream.close()
path = str.strip()
if (not os.path.exists(path)):
print path + " not found!"
sys.exit(1)
symbols_dir = path
###############################################################################
# determine the path of binutils
###############################################################################
def SetupToolsPath():
global addr2line_cmd
global objdump_cmd
global cppfilt_cmd
global symbols_dir
uname = os.uname()[0]
if uname == "Darwin":
proc = os.uname()[-1]
if proc == "i386":
uname = "darwin-x86"
else:
uname = "darwin-ppc"
elif uname == "Linux":
uname = "linux-x86"
prefix = "./prebuilts/gcc/" + uname + "/arm/arm-linux-androideabi-4.6/bin/"
addr2line_cmd = prefix + "arm-linux-androideabi-addr2line"
if (not os.path.exists(addr2line_cmd)):
try:
prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilt/gcc/" + uname + \
"/arm/arm-linux-androideabi-4.6/bin/"
except:
prefix = "";
addr2line_cmd = prefix + "arm-linux-androideabi-addr2line"
if (not os.path.exists(addr2line_cmd)):
print addr2line_cmd + " not found!"
sys.exit(1)
objdump_cmd = prefix + "arm-linux-androideabi-objdump"
cppfilt_cmd = prefix + "arm-linux-androideabi-c++filt"
###############################################################################
# look up the function and file/line number for a raw stack trace line
# groups[0]: log tag
# groups[1]: stack level
# groups[2]: "pc"
# groups[3]: code address
# groups[4]: library name
###############################################################################
def SymbolTranslation(groups):
lib_name = groups[4]
code_addr = groups[3]
caller = CallObjdump(lib_name, code_addr)
func_line_pair = CallAddr2Line(lib_name, code_addr)
# If a callee is inlined to the caller, objdump will see the caller's
# address but addr2line will report the callee's address. So the printed
# format is desgined to be "caller<-callee file:line"
if (func_line_pair[0] != caller):
print groups[0] + groups[1] + " " + caller + "<-" + \
' '.join(func_line_pair[:]) + " "
else:
print groups[0] + groups[1] + " " + ' '.join(func_line_pair[:]) + " "
###############################################################################
COMMON_DOCSTRING = """
-s (--symbols) <symbols dir>
-l (--log) <logcat file>
eg:
adbs -s out/target/product/bbk75_cu_ics/symbols/ -l logcat.txt
or
adbs -s out/target/product/bbk75_cu_ics/symbols/ logcat
"""
def Usage():
print COMMON_DOCSTRING
if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "hs:l:",
["help", "symbols=", "log="])
except getopt.GetOptError:
Usage()
for o, a in opts:
if o in ("-h", "--help"):
Usage()
sys.exit()
elif o in ("-s", "--symbols"):
OPTIONS.symbols = a
elif o in ("-l", "--log="):
OPTIONS.log = a
# pass the options to adb
#adb_cmd = "adb " + ' '.join(sys.argv[1:])
if (len(OPTIONS.log) == 0):
adb_cmd = "adb " + ' '.join(args)
else:
adb_cmd = "cat " + OPTIONS.log
# setup addr2line_cmd and objdump_cmd
SetupToolsPath()
# setup the symbols directory
FindSymbolsDir()
# invoke the adb command and filter its output
stream = os.popen(adb_cmd)
while (True):
line = stream.readline()
print line
# EOF reached
if (line == ''):
break
# remove the trailing \n
line = line.strip()
# see if this is a stack trace line
match = trace_line.match(line)
if (match):
groups = match.groups()
# translate raw address into symbols
SymbolTranslation(groups)
else:
print line
sys.stdout.flush()
# adb itself aborts
stream.close()
通過分析,我們最終找到錯誤發生於 /home/compiler/workspace/gphone/MT6582/project_name/ALPS.JB5.MP.V1.6_WET_20130810_trunk_user/bionic/libc/bionic/pthread.c:1689
實際上libc.so 出錯對我們的幫助不大,需要找更上層的出錯的地方。或者通過log去分析app層的錯誤。
注意:
需要注意的是symbols目錄需要與 bug對應的版本號編譯出來的那個symbols。直接用本地目錄下的 symbols 解析出來的會與trace中的錯誤不一致!如果沒有相應的 symbols 目錄,那麼我們需要自己重新編譯一個軟體出來復現問題,再從頭來分析該段錯誤並解析。 symbols 下面的庫會多出來一些除錯資訊。
2,高通平臺下解析段錯誤就很簡單了。
同樣需要對應出問題版本下編譯出來的 symbols目錄解壓覆蓋原來的 out/product/project_name/symbols 目錄。通過如下方式來實現解析!
arm-eabi-addr2line-fe ./out-PD1225TMA/target/product/msm8960/symbols/system/lib/libc.so 0000e498
-fe 引數 libc.so 出問題的庫, 0000e498 函式地址。
參考資料: