开发过程中,最担心的问题就是程序崩溃,而且还不知道崩溃的原因,现在使用Google Breakpad来跟踪崩溃的位置,非常方便;由于目前使用Mac系统开发,Google Breadpad处理Android崩溃日志时需要Linux环境,借助 vagrant 可以非常方便地在Mac使用Ubuntu环境。
有了vagrant以后就方便了~
$ varant ssh $ cd /vagrant $ sudo apt-get update $ sudo apt-get install build-essential $ svn checkout http://google-breakpad.googlecode.com/svn/trunk/ google-breakpad-read-only $ cd google-breakpad-read-only $ ./configure $ make
执行完上面的命令将会在
- 如果编译遇到 “cc1plus: warnings being treated as errors “错误,请将google-breakpad-read-only/Makefile文件中”* -Werror* “删除,重新编译。
- 如果make的时候遇到 “/lib64/libc.so.6: version `GLIBC_2.14’ not found “错误,说明 glibc 没有安装,安装方法请移步:http://my.oschina.net/zhangxu0512/blog/262275
方法一:
include $(CLEAR_VARS) LOCAL_MODULE := breakpad_client LOCAL_MODULE_FILENAME := breakpad_client LOCAL_SRC_FILES := libbreakpad_client.a include $(PREBUILT_STATIC_LIBRARY)
$(call import-module,google-breakpad-read-only/android/google_breakpad)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := breakpad_client
LOCAL_CPP_EXTENSION := .cc .cpp
LOCAL_ARM_MODE := arm
LOCAL_SRC_FILES := \
main.cpp \
src/client/linux/crash_generation/crash_generation_client.cc \
src/client/linux/handler/exception_handler.cc \
src/client/linux/handler/minidump_descriptor.cc \
src/client/linux/log/log.cc \
src/client/linux/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
src/client/linux/microdump_writer/microdump_writer.cc \
src/client/linux/dump_writer_common/ucontext_reader.cc \
src/client/linux/dump_writer_common/seccomp_unwinder.cc \
src/client/linux/dump_writer_common/thread_info.cc \
src/client/minidump_file_writer.cc \
src/common/android/breakpad_getcontext.S \
src/common/convert_UTF.c \
src/common/md5.cc \
src/common/string_conversion.cc \
src/common/linux/elfutils.cc \
src/common/linux/file_id.cc \
src/common/linux/guid_creator.cc \
src/common/linux/linux_libc_support.cc \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \
$(LOCAL_PATH)/src
LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/cxx-stl/stlport/stlport
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_EXPORT_LDLIBS := -llog
LOCAL_LDLIBS := -llog
LOCAL_LDLIBS += $(NDK_ROOT)/sources/cxx-stl/stlport/libs/armeabi/libstlport_static.a
include $(BUILD_SHARED_LIBRARY)
LOCAL_WHOLE_STATIC_LIBRARIES += breakpad_client
$GOOGLE_BREAKPAD_PATH/src $GOOGLE_BREAKPAD_PATH/src/common/android/include
本文使用的是第三种集成方法。
#include <jni.h>
#include <time.h>
#include <stdlib.h>
#include "com_test_crash_TestCrash.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler/minidump_descriptor.h"
#include "log.h"
google_breakpad::ExceptionHandler* exceptionHandler;
JavaVM *jvm = NULL;
void onNativeCrash(const char* info) {
JNIEnv *env = 0;
int result = jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
if (result != JNI_OK) {
LOGE("%s", "jvm->GetEnv null");
return;
}
jclass crashPinClass = env->FindClass(
"com/test/crash/TestCrash");
if (crashPinClass == NULL) {
LOGE("%s", "FindClass com/test/crash/TestCrash null");
return;
}
jmethodID crashReportMethod = env->GetStaticMethodID(crashPinClass,
"onNativeCrash", "(Ljava/lang/String;)V");
if (crashReportMethod == NULL) {
LOGE("%s", "GetMethod onNativeCrash null");
return;
}
jstring crashInfo = env->NewStringUTF(info);
env->CallStaticVoidMethod(crashPinClass, crashReportMethod, crashInfo);
}
// 崩溃回调
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context, bool succeeded) {
LOGE("Dump path: %s\n", descriptor.path());
onNativeCrash("");
return succeeded;
}
extern "C" jint JNI_OnLoad(JavaVM *vm, void* /*reserved*/) {
LOGE("JNI_OnLoad");
jvm = vm;
return JNI_VERSION_1_6;
}
#ifdef __cplusplus
extern "C" {
#endif
// 通过java代码调用initial()方法传人dmp文件保存的路径
JNIEXPORT void JNICALL Java_com_test_crash_TestCrash_initial(JNIEnv* env, jobject obj, jstring filepath) {
LOGE("The Momo native crash initial.");
const char *path = env->GetStringUTFChars(filepath, 0);
google_breakpad::MinidumpDescriptor descriptor(path);
exceptionHandler = new google_breakpad::ExceptionHandler(descriptor, NULL, DumpCallback, NULL,
true, -1);
}
// native crash 测试代码
JNIEXPORT void JNICALL Java_com_test_crash_TestCrash_crash(JNIEnv *,
jobject) {
LOGE("The test native crash.");
volatile int* a = (int*)(NULL);
*a = 1;
}
#ifdef __cplusplus
}
#endif
public class TestCrash {
// 加载动态链接库(加载.so最好放在application中)
static {
try {
System.loadLibrary("breakpad_client");
} catch (Error e) {
e.printStackTrace();
}
}
// 初始化
public native void initial(String filePath);
// native crash测试代码方法
public native void crash();
}
$ mkdir Dump
分别将生成的.dmp文件、minidump_stackwalk、dump_syms、android项目下的obj/local/armeabi/libxxx.so库拷贝到Dump目录。( 特别说明:libxxx.so文件必须是obj/local/armeabi/ 文件夹下的,这个里面so文件包含了debug信息,相对lib下的so文件来说体积很大,有可能上百兆。)
创建好的Dump文件夹路径在“个人 —> ubuntu —> Dump”;
执行生成符号文件命令:
$ cd /Dump $ dump_syms libxxx.so > libxxx.so.sym
$ head -n1 libxxx.so.sym MODULE Linux arm BB0351B14DDA42A6D36FA6EA358B49D50 libxxx.so
$ mkdir -p symbols/libxxx.so/BB0351B14DDA42A6D36FA6EA358B49D50/ $ mv libxxx.so.sym symbols/libxxx.so/BB0351B14DDA42A6D36FA6EA358B49D50/
$ ./minidump_stackwalk filename.dmp symbols > crashed.log
Thread 0 (crashed) 0 libxxx.so!Java_com_test_crash_TestCrash [TestCrash.cpp : 68 + 0x8] r0 = 0x00000027 r1 = 0xc3dab5eb r2 = 0x00000001 r3 = 0x00000000 r4 = 0x0000026a r5 = 0x12f99f20 r6 = 0x12d63e00 r7 = 0x75c4a1b0 r8 = 0x1302b9e0 r9 = 0xb4f07800 r10 = 0x0000003a r12 = 0xb6e98929 fp = 0x0000003a sp = 0xbebb8f58 lr = 0xa289aafc pc = 0xa289ab04
Thread 0 (crashed)
0 libxxx.so + 0xa289ab04
r0 = 0x00000027 r1 = 0xc3dab5eb r2 = 0x00000001 r3 = 0x00000000
r4 = 0x0000026a r5 = 0x12f99f20 r6 = 0x12d63e00 r7 = 0x75c4a1b0
r8 = 0x1302b9e0 r9 = 0xb4f07800 r10 = 0x0000003a r12 = 0xb6e98929
fp = 0x0000003a sp = 0xbebb8f58 lr = 0xa289aafc pc = 0xa289ab04
arm-linux-androideabi-addr2line 路径为:$NDK_ROOT/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86/bin/arm-linux-androideabi-addr2line
usage: arm-linux-androideabi-addr2line -C -f -e <库路径> <内存地址>
arm-linux-androideabi-addr2line -C -f -e project/test/obj/local/armeabi/libxxx.so 0xa289ab04
这时候就可以在终端中看到是 libxxx.so 中的哪个文件的那一行发生异常了,接下来可以使用日志追踪有问题的代码了。
参考:http://blog.linguofeng.com/archive/2014/04/02/google-breakpad-android.html