堆栈错误信息:
I/DEBUG ( 30): r0 afd4d4a4 r1 ffffffff r2 7fffffff r3 afd4d4a4
I/DEBUG ( 30): r4 00000000 r5 ffffffff r6 00000001 r7 be9f6ccc
I/DEBUG ( 30): r8 00000000 r9 00000000 10 00000000 fp 00000000
I/DEBUG ( 30): ip afd11fcd sp be9f6c38 lr afd107f4 pc afd107fc cpsr 80000010
I/DEBUG ( 30): #00 pc 000107fc /system/lib/libc.so
I/DEBUG ( 30): #01 pc 0001ccfe /system/lib/libdtvservice.so
I/DEBUG ( 30): #02 pc 0001c646 /system/lib/libdtvservice.so
I/DEBUG ( 30): #03 pc 0001937e /system/lib/libdtvservice.so
I/DEBUG ( 30): #04 pc 000110f8 /system/lib/libdtvservice.so
I/DEBUG ( 30): #05 pc 000115a8 /system/lib/libdtvservice.so
I/DEBUG ( 30): #06 pc 00008852 /system/bin/dtvserver
I/DEBUG ( 30): #07 pc 00014dc8 /system/lib/libc.so
……
方法一:
1.将上面这段信息复制到一个新建的文件中,并保存。例如:test-dtvserver.txt.
2.用panic.py工具解析这段错误信息,panic.py工具的位置为~/android/
// 工具在附件中
运行:
[maw@localhost android]$ panic.py test-stack.txt
read file ok
stop search
pr-support.c:258 __gnu_unwind_execute
posix_thread.c:97 destroy_posix_thread
thread.c:293 thread_destroy
workpool.c:215 work_pool_uninitialize
dtvserver.c:203 dtvs_finalize
dtvserver.c:179 dtvs_initialize
DtvServer.cpp:19 main
dlmalloc.c:3890 tmalloc_small
这个就是结果。
使用panic.py的环境问题
1.要有arm-eabi-addr2line工具 ~/android/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/
2.在system/lib/中要有相应的库文件
3.panic.py的存放位置要正确~/android/,也可以自己修改panic.py工具的代码,放在其他地方.
方法二:
如果没有arm-eabi-addr2line工具,但是有arm-eabi-objdump工具
就用第二种方法。
I/DEBUG ( 30): #00 pc 000107fc /system/lib/libc.so
I/DEBUG ( 30): #01 pc 0001ccfe /system/lib/libdtvservice.so
I/DEBUG ( 30): #02 pc 0001c646 /system/lib/libdtvservice.so
I/DEBUG ( 30): #03 pc 0001937e /system/lib/libdtvservice.so
I/DEBUG ( 30): #04 pc 000110f8 /system/lib/libdtvservice.so
I/DEBUG ( 30): #05 pc 000115a8 /system/lib/libdtvservice.so
I/DEBUG ( 30): #06 pc 00008852 /system/bin/dtvserver
I/DEBUG ( 30): #07 pc 00014dc8 /system/lib/libc.so
1.用objdump工具将上面这些对应的库文件反汇编:
运行:arm-eabi-objdump –dS libc.so > libc.dump
//这些库文件路径在out/target/product/generic/obj/SHARED_LIBRARIES/
2.打开反汇编后的库文件。例如:libc.dump。
找到上面错误码提示的地址。例如000107fc。
在这里我们可以找到这行指令所在的函数和文件
以此类推,依次找出这些错误码对应的函数和文件,就可以知道是哪一个函数调用路线出问题了。
panic.py代码
#!/usr/bin/python
# stack symbol parser
import os
import string
import sys
#define android product name 这个视情况而定在out/target/product文件夹下
ANDROID_PRODUCT_NAME = 'generic'
#ANDROID_PRODUCT_NAME = 'smdk6410'
#这里可以修改panic.py工具的存放位置
ANDROID_WORKSPACE = os.getcwd()+"/"
# addr2line tool path and symbol path
addr2line_tool = ANDROID_WORKSPACE +'prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-addr2line'
symbol_dir = ANDROID_WORKSPACE + 'out/target/product/'+ ANDROID_PRODUCT_NAME +'/symbols'
symbol_bin = symbol_dir + '/system/bin/'
symbol_lib = symbol_dir + '/system/lib/'
class ReadLog:
def__init__(self,filename):
self.logname = filename
defparse(self):
f =file(self.logname,'r')
lines =f.readlines()
if lines!= []:
print 'read file ok'
else:
print 'read file failed'
result=[]
for linein lines:
ifline.find('stack') != -1:
print 'stop search'
break
elifline.find('system') != -1:
#print 'find one item' + line
result.append(line)
returnresult
class ParseContent:
def__init__(self,addr,lib):
self.address = addr # pc address
self.exename = lib # executableor shared library
defaddr2line(self):
cmd =addr2line_tool + " -C -f -s -e " + symbol_dir + self.exename + "" + self.address
#printcmd
stream =os.popen(cmd)
lines =stream.readlines();
list =map(string.strip,lines)
returnlist
inputarg = sys.argv
if len(inputarg) < 2:
print'Please input panic log'
exit()
filename = inputarg[1]
readlog = ReadLog(filename)
inputlist = readlog.parse()
for item in inputlist:
itemsplit =item.split()
test =ParseContent(itemsplit[-2],itemsplit[-1])
list =test.addr2line()
print"%-30s%s" % (list[1],list[0])
#usage panic.py crash Log > result.txt
如果提示 /usr/bin/python ^M:损坏的解释器:没有该文件或者目录的话,执行一下:dos2unix panic.py 转换一下格式即可。
反汇编pc指针附近代码
有时我们在log中看不到这样明显的调用栈信息,比如出现了dalvik-jit-code-cache,如下:
10-15 01:32:56.759 I/DEBUG ( 93): #00 pc 47aac03c /dev/ashmem/dalvik-jit-code-cache (deleted) 10-15 01:32:56.769 I/DEBUG ( 93): #01 lr 47ab7817 /dev/ashmem/dalvik-jit-code-cache (deleted)
这样,就没有办法用前面的方法来分析调用栈了。还好google提供了另一个方法来分析问题。虽然有点麻烦。
它的原理,就是将pc附件的指令放到某个程序的内存中,然后进行gdb调试来进行反汇编。
要使用gdbjithelper(位于dalvik/tools/gdbjithelper)将pr和lr附近的cache code反汇编。下面来介绍一下。
首先,打开gdbjithelper.c,可以看到如下代码
int codePC[] = {
// Sample content
0x4300e119, 0x4284aa7a, 0xf927f7b7, 0x40112268, 0x419da7f8, 0x00002000, 0x01000100, 0x00080000, 0x4191debc, 0x01010000, 0x4284aa74, 0x68b00054, 0x045cf205, 0xcc016468, 0x0718f2a5, 0xd0102800, 0x4c13c701, 0xa20aa108, 0xefb0f775, 0xe008e010, };
将log信息中对应部分
10-15 01:32:56.769 I/DEBUG ( 93): code around pc:
10-15 01:32:56.769 I/DEBUG ( 93): 47aac01c e12fff1e e3a00001 e12fff1e e2450014
10-15 01:32:56.769 I/DEBUG ( 93): 47aac02c e590a000 e5968024 e5904004 e5909010
10-15 01:32:56.769 I/DEBUG ( 93): 47aac03c e51a200c e5963018 e3520000 0a00000e
10-15 01:32:56.769 I/DEBUG ( 93): 47aac04c e59f1738 e1a0500a 1592a000 e5988000
10-15 01:32:56.769 I/DEBUG ( 93): 47aac05c e5862010 e59a0028 e5835028 e2844006
编辑为类似代码,替换掉,然后修改
#define START_PC_PAGE_OFFSET 0x1e4
改为
#define START_PC_PAGE_OFFSET 0x01c
修改的原理是code around pc的开始地址(0x47aac01c & 0xfff)
同理,修改codeLR和START_LR_PAGE_OFFSET
修改完成后,使用模块编译 mmm dalvik/tools/gdbjithelper,将编译好的gdbjithelper push到一台手机上/system/bin 进行调试。
连接设备后,执行步骤如下:
shell> adb shell gdbserver :5039 /path/to/gdbjithelper #设备路径
shell> adb forward tcp:5039 tcp:5039
shell> cd ${环境根目录}/
shell> prebuilt/Linux/toolchain-eabi-4.2.1/bin/arm-eabi-gdb /path/to/gdbjithelper #PC路径
(gdb) set solib-absolute-prefix out/target/product/${product-name}/symbols #告诉gdb查找共享库符号等路径
(gdb) set solib-search-path out/target/product/${product-name}/symbols/system/lib #告诉gdb查找共享库符号等路径 (gdb) target remote :5039
(gdb) run 会出现类似下面的输出信息: 0xb01c codePC[0]: 0xe12fff1e 0xb020 codePC[1]: 0xe3a00001 0xb024 codePC[2]: 0xe12fff1e 0xb028 codePC[3]: 0xe2450014 0xb02c codePC[4]: 0xe590a000 0xb030 codePC[5]: 0xe5968024 0xb034 codePC[6]: 0xe5904004 0xb038 codePC[7]: 0xe5909010 0xb03c codePC[8]: 0xe51a200c 0xb040 codePC[9]: 0xe5963018 0xb044 codePC[10]: 0xe3520000 0xb048 codePC[11]: 0x0a00000e 0xb04c codePC[12]: 0xe59f1738 0xb050 codePC[13]: 0xe1a0500a 0xb054 codePC[14]: 0x1592a000 0xb058 codePC[15]: 0xe5988000 0xb05c codePC[16]: 0xe5862010 0xb060 codePC[17]: 0xe59a0028 0xb064 codePC[18]: 0xe5835028 0xb068 codePC[19]: 0xe2844006
(gdb) x /20i 0xb01c+1 //输出反汇编结果 x/20i (char *)&CodePC+1 ,查看LR 结果 x/20i (char*)&codeLR+1
0xb01d: vrhadd.u16 d14, d14, d31
0xb021: lsls r1, r0, #0
0xb023: b.n 0xb766
0xb025: vrhadd.u16 d14, d14, d31
0xb029: lsls r4, r2, #0
0xb02b: b.n 0xb4b8
0xb02d: add r0, pc, #0 (adr r0,0xb030)
0xb02f: b.n 0xab52
0xb031: strh r4, [r4, #0]
0xb033: b.n 0xab62
0xb035: ands r4, r0
0xb037: b.n 0xab5a
0xb039: str r0, [sp, #64]
0xb03b: b.n 0xab5e //pc指向,实际跳转地址为0x47aabb5e (0x47aac03c-0xb03c+0xab5e) 0xb03d: movs r0, #12
0xb03f: b.n 0xaa76
0xb041: adds r0, #24
0xb043: b.n 0xab72
0xb045: lsls r0, r0, #0
0xb047: b.n 0xb6ee
类似,codeLR也是这样反汇编的 之后就可以对结果进行分析了。