我们在解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
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
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
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
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
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
08-19 19:08:27.462 2105 2105 I DEBUG : #02 (unknown) (unknown)
08-19 19:08:27.462 2105 2105 I DEBUG :
#!/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" 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 :
# 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)
-l (--log)
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 函数地址。
参考资料:
1,【百科段错误】http://baike.baidu.com/view/3994532.htm
2,【linux 下动态链接库、静态链接库】http://www.metsky.com/archives/604.html