版权申明:未经允许请勿转载。转载前请先联系作者([email protected])
背景是上一篇博客遇到了问题。crashlytics的解析符号没解析出来,./gradlew crashlyticsUploadSymbolsRelease
执行了也没起什么作用。
嗯,这个问题先从 so
库的一些背景知识说起。
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
so 就是share-library。先看怎么build出so吧。
Makefile
all : test.o main.o out libtest oout
test.o: test.cc test.h
g++ -c test.cc
main.o : main.cc test.h
g++ -c main.cc
libtest : test.o
g++ -shared -o libtest.so test.o
out: main.o libtest.so
g++ -o out main.o libtest.so
clean:
rm -f *.o *.so out oout
main.cc
#include
#include "test.h"
int main(int argc, const char *argv[])
{
test t(22);
t.increase();
printf("Received: %i", t.getFlag());
}
test.cc
#include
#include "test.h"
test::test(unsigned int flag)
: _flag(flag)
{
}
test::~test()
{
}
unsigned int test::getFlag() const
{
return _flag;
}
void test::increase()
{
_flag += 1;
}
test.h
#ifndef TEST_H
#define TEST_H
#include
class test
{
private:
unsigned int _flag;
public:
test(unsigned int flag);
~test();
void increase();
unsigned int getFlag() const;
};
#endif
关键的代码编译代码就只有一行
g++ -shared -o libtest.so test.o
也就是它把多个o文件合并成一个。
crash日志中,最重要的是符号,是代码的行数和对应的记录。
可以怎么看呢?
make
nm -C out
执行之后,就可以看到具体的函数信息了。
0000000100000f68 s GCC_except_table0
U __Unwind_Resume
0000000100000f00 T test::increase()
0000000100000e90 T test::test(unsigned int)
0000000100000e70 T test::test(unsigned int)
0000000100000ed0 T test::~test()
0000000100000ec0 T test::~test()
0000000100000ef0 T test::getFlag() const
U std::terminate()
0000000100000e60 t ___clang_call_terminate
U ___cxa_begin_catch
U ___gxx_personality_v0
0000000100000000 T __mh_execute_header
0000000100000db0 T _main
U _printf
U dyld_stub_binder
当然我们对外的时候,不能把symbols都打进包里,所以这个时候,会
strip out
在看一下
U __Unwind_Resume
U std::terminate()
U ___cxa_begin_catch
U ___gxx_personality_v0
0000000100000000 T __mh_execute_header
U _printf
U dyld_stub_binder
可以看到,很多信息都被strip
掉了。
在android平台上,不是直接用上面的 g++、strip。因为具体手机平台和PC的平台的环境是不同的,要用交叉编译。也就是用对应平台的gcc去编译。android的话,在 android/sdk/ndk-bundle/toolchains
下面。
参考: https://developer.android.com/ndk/guides/ndk-stack?hl=zh-CN
借助 ndk-stack
工具,您可以使用符号来表示来自 adb logcat
的堆栈轨迹或 /data/tombstones/
中的 tombstone
。该工具会将共享库内的任何地址替换为源代码中对应的
,从而简化调试流程。
例如,它可将下面的代码:
I/DEBUG ( 31): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG ( 31): Build fingerprint: 'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys'
I/DEBUG ( 31): pid: 351, tid: 351 >>> /data/local/ndk-tests/crasher <<<
I/DEBUG ( 31): signal 11 (SIGSEGV), fault addr 0d9f00d8
I/DEBUG ( 31): r0 0000af88 r1 0000a008 r2 baadf00d r3 0d9f00d8
I/DEBUG ( 31): r4 00000004 r5 0000a008 r6 0000af88 r7 00013c44
I/DEBUG ( 31): r8 00000000 r9 00000000 10 00000000 fp 00000000
I/DEBUG ( 31): ip 0000959c sp be956cc8 lr 00008403 pc 0000841e cpsr 60000030
I/DEBUG ( 31): #00 pc 0000841e /data/local/ndk-tests/crasher
I/DEBUG ( 31): #01 pc 000083fe /data/local/ndk-tests/crasher
I/DEBUG ( 31): #02 pc 000083f6 /data/local/ndk-tests/crasher
I/DEBUG ( 31): #03 pc 000191ac /system/lib/libc.so
I/DEBUG ( 31): #04 pc 000083ea /data/local/ndk-tests/crasher
I/DEBUG ( 31): #05 pc 00008458 /data/local/ndk-tests/crasher
I/DEBUG ( 31): #06 pc 0000d362 /system/lib/libc.so
I/DEBUG ( 31):
转换为更具可读性的代码:
********** Crash dump: **********
Build fingerprint: 'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys'
pid: 351, tid: 351 >>> /data/local/ndk-tests/crasher <<<
signal 11 (SIGSEGV), fault addr 0d9f00d8
Stack frame #00 pc 0000841e /data/local/ndk-tests/crasher : Routine zoo in /tmp/foo/crasher/jni/zoo.c:13
Stack frame #01 pc 000083fe /data/local/ndk-tests/crasher : Routine bar in /tmp/foo/crasher/jni/bar.c:5
Stack frame #02 pc 000083f6 /data/local/ndk-tests/crasher : Routine my_comparison in /tmp/foo/crasher/jni/foo.c:9
Stack frame #03 pc 000191ac /system/lib/libc.so
Stack frame #04 pc 000083ea /data/local/ndk-tests/crasher : Routine foo in /tmp/foo/crasher/jni/foo.c:14
Stack frame #05 pc 00008458 /data/local/ndk-tests/crasher : Routine main in /tmp/foo/crasher/jni/main.c:19
Stack frame #06 pc 0000d362 /system/lib/libc.so
要使用 ndk-stack,您首先要有一个包含未剥离版应用共享库的目录。如果您使用 ndk-build,则可在 $PROJECT_PATH/obj/local/ 中找到这些未剥离版共享库,其中 是您设备的 ABI。
使用此工具的方式有两种。您可以将 logcat 文本作为直接输入馈送到程序。例如:
adb logcat | $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi-v7a
您也可以使用 -dump 选项将 logcat 指定为输入文件。例如:
adb logcat > /tmp/foo.txt
$NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi-v7a -dump foo.txt
该工具会在开始解析 logcat 输出时查找第一行星号。例如:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***