使用traceview时,遇到一个这样的问题:
Exception in thread "main" java.lang.RuntimeException: Method exit (android/widget/AbsListView.startScrollIfNeeded (I)Z) does not match current method (java/lang/Math.abs (I)I)
这个是google自己bug,貌似出现在android4.0-4.2之间,主要是在mips架构里面发现的,不过现在的最新版本已修复此bug。
如果不幸拿到的android源码还存在这个bug,需要自己修复。
traceiew的工作原理是跟踪函数的调用路径,然后计算整条调用路径或者单个函数的执行时间,进而测试系统或者应用程序的性能。实际上,traceiew只是在pc端运行的一个工具,它用来解析目标设备上生成的.trace文件。所以,traceview本身没有问题,只是目标设备上生成的.trace文件有问题。目标设备上是使用profile生成.trace文件的。profile把函数的进入(entry)和退出(exit)都记录在.trace文件中,记录的存储方式是按照栈(stack)的方式,所以traceiew解析.trace文件时也需要用栈的方式。
现在出现错误的原因是函数的进入(entry)和退出(exit)不配对,也就是说有些函数的进入或者退出没有被记录下来。这就需要跟踪.trace文件是如何生成的。
详细的过程就不螯述了,这里只说一下跟踪的路径吧,有兴趣的朋友可以看下。经过的文件有:
frameworks/base/cmds/am/src/com/android/commands/am/Am.java
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/os/Debug.java
libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
dalvik/vm/native/dalvik_system_VMDebug.cpp
dalvik/vm/Profile.cpp
.trace最主要的生成过程是在dalvik/vm/Profile.cpp中,后来发现最可能出错的地方是,其中只有一个记录函数进入的地方,然而却有两个记录退出的地方。所以有可能是退出时使用了错误的函数。
记录进入的函数:
dvmFastMethodTraceEnter
记录退出的两个函数:
dvmFastNativeMethodTraceExit
dvmFastMethodTraceExit
这些函数是在汇编代码里边调用的,涉及到汇编代码就是架构相关了。android支持的架构有ARM,MIPS和X86。既然是在MIPS发现了这个问题,所以就看MIPS架构相关的代码,后来发现在:
dalvik/vm/mterp/mips/OP_EXECUTE_INLINE.S
dalvik/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S
dalvik/vm/mterp/out/InterpAsm-mips.S
这三个文件里面都调用了上面的函数。第三个文件是由很多.S文件(包括第一和第二个文件)生成的,可以忽视。所以调查前两个文件。
OP_EXECUTE_INLINE.S里调用的代码片段:
.L${opcode}_debugmode:
movea0, rBIX
JAL(dvmResolveInlineNative)
beqzv0, .L${opcode}_resume#did it resolve? no, just move on
moverOBJ, v0#remember method
movea0, v0
movea1, rSELF
JAL(dvmFastMethodTraceEnter)#(method, self)
addua1, rSELF, offThread_retval#a1<- &self->retval
GET_OPB(a0)#a0 <- B
# Stack should have 16/20 available
swa1, 16(sp)#push &self->retval
BAL(.L${opcode}_continue)#make call;
will return after
lwgp, STACK_OFFSET_GP(sp)#restore gp
moverINST, v0#save result of inline
movea0, rOBJ#a0<- method
movea1, rSELF#a1<- self
JAL(dvmFastMethodTraceExit)#(method, self)
beqzv0, common_exceptionThrown#returned false, handle exception
FETCH_ADVANCE_INST(3)#advance rPC, load rINST
GET_INST_OPCODE(t0)#extract opcode from rINST
GOTO_OPCODE(t0)#jump to next instruction
OP_EXECUTE_INLINE_RANGE.S里面的调用片段:
/*
* We're debugging or profiling.
* rBIX: opIndex
*/
.L${opcode}_debugmode:
movea0, rBIX
JAL(dvmResolveInlineNative)
beqzv0, .L${opcode}_resume#did it resolve? no, just move on
moverOBJ, v0#remember method
movea0, v0
movea1, rSELF
JAL(dvmFastMethodTraceEnter)#(method, self)
addua1, rSELF, offThread_retval#a1<- &self->retval
GET_OPA(a0)#a0 <- A
# Stack should have 16/20 available
swa1, 16(sp)#push &self->retval
moverINST, rOBJ#rINST<- method
BAL(.L${opcode}_continue)#make call;
will return after
lwgp, STACK_OFFSET_GP(sp)#restore gp
moverOBJ, v0#save result of inline
movea0, rINST#a0<- method
movea1, rSELF#a1<- self
JAL(dvmFastNativeMethodTraceExit)#(method, self)
beqzrOBJ, common_exceptionThrown #returned false, handle exception
FETCH_ADVANCE_INST(3)#advance rPC, load rINST
GET_INST_OPCODE(t0)#extract opcode from rINST
GOTO_OPCODE(t0)#jump to next instruction
其中,OP_EXECUTE_INLINE.S调用的组合是:
dvmFastMethodTraceEnter
dvmFastMethodTraceExit
OP_EXECUTE_INLINE_RANGE.S调用的组合是:
dvmFastMethodTraceEnter
dvmFastNativeMethodTraceExit
但是,我发现OP_EXECUTE_INLINE.S里面的调用是不符合逻辑的。
请看:
movea0, rOBJ#a0<- method
movea1, rSELF#a1<- self
JAL(dvmFastMethodTraceExit)#(method, self)
这段代码,程序准备了a0 和a1两个参数,但是回头看看dvmFastMethodTraceExit和dvmFastNativeMethodTraceExit的函数原型:
void dvmFastMethodTraceExit(Thread* self)
void dvmFastNativeMethodTraceExit(const Method* method, Thread* self)
【TraceView issue】很明显,dvmFastMethodTraceExit只有一个参数,所以没必要准备两个参数。所以,这段代码的编写者原意应该是想要调用dvmFastNativeMethodTraceExit的。把OP_EXECUTE_INLINE.S里面的dvmFastMethodTraceExit换成dvmFastNativeMethodTraceExit,执行dalvik/vm/mterp/rebuild.sh重新生成dalvik/vm/mterp/out/InterpAsm-mips.S,然后重编dalvik,启动运行,生成.trace文件,traceiew解析,成功了!