1 现象
当使用线刷刷入版本的时候,发现oobe第一次必定出现crash的情况。但是以后就再也不会出现crash的情况。
截取关键日志如下:
11-18 15:50:59.130 D/ActionGuard( 656): tag = MainActivity, on = false kkkkkk
11-18 15:50:59.160 F/libc ( 656): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 656 (tws.oobeprocess)
11-18 15:50:59.270 I/DEBUG ( 74): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
11-18 15:50:59.270 I/DEBUG ( 74): Build fingerprint: 'Ingenic/x3_iwop/watch:4.3/JLS36G/66:userdebug/test-keys'
11-18 15:50:59.270 I/DEBUG ( 74): Revision: '0'
11-18 15:50:59.270 I/DEBUG ( 74): pid: 656, tid: 656, name: tws.oobeprocess >>> com.tencent.tws.oobeprocess <<<
11-18 15:50:59.270 I/DEBUG ( 74): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
11-18 15:50:59.390 I/DEBUG ( 74): zr 00000000 at 00000001 v0 58390f70 v1 2b8134f0
11-18 15:50:59.390 I/DEBUG ( 74): a0 00000000 a1 7fa9cab0 a2 00000000 a3 583a85d8
11-18 15:50:59.390 I/DEBUG ( 74): t0 2c046ba0 t1 000005e4 t2 00000000 t3 00000000
11-18 15:50:59.390 I/DEBUG ( 74): t4 00000004 t5 ffffffff t6 00000003 t7 7fa9cab8
11-18 15:50:59.400 I/DEBUG ( 74): s0 55d043f0 s1 57feda98 s2 55d043f0 s3 00000001
11-18 15:50:59.400 I/DEBUG ( 74): s4 2cdee4a0 s5 2ce115c8 s6 7fa9cc68 s7 00000001
11-18 15:50:59.400 I/DEBUG ( 74): t8 00000001 t9 2cdc5038 k0 8af00025 k1 00000000
11-18 15:50:59.400 I/DEBUG ( 74): gp 2ce1a270 sp 7fa9ca68 s8 7fa9cb24 ra 2cdca954
11-18 15:50:59.400 I/DEBUG ( 74): hi 00000000 lo 00000030 bva 00000000 epc 2cdc507c
11-18 15:50:59.400 I/DEBUG ( 74):
11-18 15:50:59.400 I/DEBUG ( 74): backtrace:
11-18 15:50:59.400 I/DEBUG ( 74): #00 pc 0002d07c /system/lib/libhwui.so
11-18 15:50:59.400 I/DEBUG ( 74): #01 pc 0003294c /system/lib/libhwui.so
11-18 15:50:59.400 I/DEBUG ( 74): #02 pc 00032b18 /system/lib/libhwui.so
11-18 15:50:59.400 I/DEBUG ( 74): #03 pc 000925b8 /system/lib/libandroid_runtime.so
11-18 15:50:59.400 I/DEBUG ( 74): #04 pc 00022c24 /system/lib/libdvm.so (dvmPlatformInvoke+212)
11-18 15:50:59.400 I/DEBUG ( 74): #05 pc 00000001 <unknown>
从log来看,很有可能存在空指针的情况。
在mips架构中,如果是c代码,a0 ~ a3对应前4个实参和arm架构类似。而在c++代码中,a0为对象指针,如果这为空,基本上可以判断对象为空。
2 分析过程
那么是哪个函数的哪个对象为空呢?
从backtrace来看,为空的对象应该来自libhwui.so
因为是源码编译,所以我们可以取到带symbol的so库。路径为:out/target/product/***/symbols/system/lib$
可以使用linux 的file命令来确认下这个so库是否真的是带有symbol的。
junsioncai@qrom-03:~$ file libhwui.so
libhwui.so: ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, with unknown capability 0xf41 = 0x756e6700, with unknown capability 0x70100 = 0x1040000, not stripped
junsioncai@qrom-03:~$
接下来,我们就可以使用gcc工具来进行分析咯。
首先,关键是要找到gcc工具的版本及路径。
一般android源码都会在prebuild目录下面提供toolchain,那么版本是哪个呢,就要从build脚本中去寻找咯。
我一般会使用grep -r TOOLCHAIN .在build目录下面进行搜索。
./core/combo/TARGET_linux-mips.mk:TARGET_TOOLCHAIN_ROOT := prebuilts/gcc/$(HOST_PREBUILT_TAG)/mips/mipsel-linux-android-$(TARGET_GCC_VERSION)
在源代码中,是通过smk.sh中指定了toolchain的路径的。
prebuilts/gcc/linux-x86/mips/mipsel-linux-android-4.7/bin
进入到这个目录下面,就可以看到很多gcc的工具了。
mipsel-linux-android-addr2line mipsel-linux-android-cpp mipsel-linux-android-gcc-ar mipsel-linux-android-gprof mipsel-linux-android-objdump mipsel-linux-android-strip
mipsel-linux-android-ar mipsel-linux-android-elfedit mipsel-linux-android-gcc-nm mipsel-linux-android-ld mipsel-linux-android-ranlib
mipsel-linux-android-as mipsel-linux-android-g++ mipsel-linux-android-gcc-ranlib mipsel-linux-android-ld.bfd mipsel-linux-android-readelf
mipsel-linux-android-c++ mipsel-linux-android-gcc mipsel-linux-android-gcov mipsel-linux-android-nm mipsel-linux-android-size
mipsel-linux-android-c++filt mipsel-linux-android-gcc-4.7 mipsel-linux-android-gdb mipsel-linux-android-objcopy mipsel-linux-android-strings
这里面,我会用到2个工具:objdump 和 C++filter
首先通过objdump将libhwui.so中的信息dump出来,然后找到出错pc指针的位置区域:
mipsel-linux-android-objdump -S libhwui.so >libhwui_objdump.txt
2d07c在函数_ZN7android10uirenderer5Layer5flushEv范围内。
_ZN7android10uirenderer5Layer5flushEv这个是个什么东西呢。接下来就可以有c++filter出场咯。
mipsel-linux-android-4.7/bin$ ./mipsel-linux-android-c++filt _ZN7android10uirenderer5Layer5flushEv
android::uirenderer::Layer::flush()
看到了吧,原来是在函数中。
接下来就可以来查看源码了。
在framework/base/libs/hwui/Layer.cpp中。
void Layer::flush() {
if (deferredList ) {
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
deferredList->flush(*renderer, dirtyRect);
renderer->finish();
dirtyRect.setEmpty();
renderNode = NULL;
}
}
我们大概可以猜测到,应该是renderer可能为空。
于是我们加上一个空指针保护。
void Layer::flush() {
if (deferredList && renderer) {
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
deferredList->flush(*renderer, dirtyRect);
renderer->finish();
dirtyRect.setEmpty();
renderNode = NULL;
}
}
3 延伸
上面的renderer为什么会为空,什么情况下会为空呢?
“renderer is checked as layer may be destroyed/put in layer cache with flush scheduled”
具体场景还需要继续研究。