针对Android平台的native崩溃问题,有很多工具可以跟踪检测,比如ndk-stack,腾讯bugly等,本文引入一种相对于前两者,更能清晰定位native崩溃问题的监测手段,及提供了一种封装好的JNI方法,方便开发者快速引入并使用的,加快native崩溃问题的分析和定位。
Google Breakpad是什么?
Google Breakpad 是一个开源的跨平台崩溃转储工具,由 Google 开发,旨在帮助开发者自动捕获应用程序崩溃时的关键信息,并生成便于分析的文件,从而提升软件的稳定性和用户体验。
GoogleBreakpad 支持 Windows、Linux、macOS、Android、iOS 等平台。Breakpad 具有三个主要组件:
- client 在崩溃系统中负责抓取当前线程和当前载入的库生成 minidump 文件。
- dump-syms 负责读取用户开发应用中的debug信息,并生成特定的符号文件。
- processor 通过 minidump_stackwalk 读取 minidump 文件 找到合适的符号文件产生一个可读的 c/c++ 调用栈。

在默认情况下,当程序崩溃时 breakpad 会生成一个 minidump 文件,它在不同平台上的实现机制不一样:
在 Windows 平台上,使用微软提供的 SetUnhandledExceptionFilter() 方法来实现。
在 OS X 平台上,通过创建一个线程来监听 Mach Exception port 来实现。
在 Linux 平台上,通过设置一个信号处理器来监听 SIGILL SIGSEGV 等异常信号。
Breakpad 在所有的平台上都使用 minidump 文件格式,minidump 文件格式是由微软开发的用于崩溃上传,它包括:
当 dump 生成时进程中一系列 executable 和 shared libraries, 包括这些文件的文件名和版本号。
进程中的线程列表,对于每个线程,minidump 包含它在寄存器中的状态,线程的 stack memory 内容。这些数据都是未解析的字节流,Breakpad client 通常没有调试信息能生成函数名,行号,甚至无法确定 stack frame 的边界。
其他收集关于系统的信息,如处理器,操作系统高版本,dump 的原因等等。
如何在Android平台上使用Breakpad?
Google github源码地址:https://github.com/google/breakpad
1. 下载源码,将其集成到android工程中,结构如下:

2. 编写cmakeList.txt
cmake_minimum_required(VERSION 3.4.1)
set(BREAKPAD_ROOT include)
include_directories(${BREAKPAD_ROOT} ${BREAKPAD_ROOT}/common/android/include)
# 传递版本号
if(DEFINED VERSION_NAME)
add_definitions(-DVERSION_NAME="${VERSION_NAME}")
endif()
file(GLOB BREAKPAD_SOURCES_COMMON
${BREAKPAD_ROOT}/../cpp/breakpad.cpp
${BREAKPAD_ROOT}/client/linux/crash_generation/crash_generation_client.cc
${BREAKPAD_ROOT}/client/linux/dump_writer_common/thread_info.cc
${BREAKPAD_ROOT}/client/linux/dump_writer_common/ucontext_reader.cc
${BREAKPAD_ROOT}/client/linux/handler/exception_handler.cc
${BREAKPAD_ROOT}/client/linux/handler/minidump_descriptor.cc
${BREAKPAD_ROOT}/client/linux/log/log.cc
${BREAKPAD_ROOT}/client/linux/microdump_writer/microdump_writer.cc
${BREAKPAD_ROOT}/client/linux/minidump_writer/linux_dumper.cc
${BREAKPAD_ROOT}/client/linux/minidump_writer/linux_ptrace_dumper.cc
${BREAKPAD_ROOT}/client/linux/minidump_writer/minidump_writer.cc
${BREAKPAD_ROOT}/client/minidump_file_writer.cc
${BREAKPAD_ROOT}/common/convert_UTF.c
${BREAKPAD_ROOT}/common/md5.cc
${BREAKPAD_ROOT}/common/string_conversion.cc
${BREAKPAD_ROOT}/common/linux/elfutils.cc
${BREAKPAD_ROOT}/common/linux/file_id.cc
${BREAKPAD_ROOT}/common/linux/guid_creator.cc
${BREAKPAD_ROOT}/common/linux/linux_libc_support.cc
${BREAKPAD_ROOT}/common/linux/memory_mapped_file.cc
${BREAKPAD_ROOT}/common/linux/safe_readlink.cc
)
file(GLOB BREAKPAD_ASM_SOURCE
${BREAKPAD_ROOT}/common/android/breakpad_getcontext.S )
set_source_files_properties(${BREAKPAD_ASM_SOURCE} PROPERTIES LANGUAGE C)
add_library( # Sets the name of the library.
breakpad
SHARED
${BREAKPAD_SOURCES_COMMON} ${BREAKPAD_ASM_SOURCE})
find_library( # Sets the name of the path variable.
log-lib
log )
target_link_libraries( # Specifies the target library.
breakpad
${log-lib} )
3. 修改build.gradle 文件
android {
defaultConfig {
externalNativeBuild {
cmake {
arguments '-DVERSION_NAME=' + defaultConfig.versionName
cppFlags ""
abiFilters 'armeabi-v7a', 'arm64-v8a' // 指定目标ABI
}
}
}
externalNativeBuild {
cmake {
path "src/main/jni/CMakeLists.txt"
}
}
}
4. 开发JNI 接口的breakpad.cpp源文件
extern "C"
JNIEXPORT void JNICALL
Java_com_cc_breakpad_Breakpad_init(
JNIEnv *env,
jclass type,
jstring log_dir,jstring log_name) {
const char *path = env->GetStringUTFChars(log_dir, 0);
const char *env_log_name = env->GetStringUTFChars(log_name, 0);
google_breakpad::MinidumpDescriptor descriptor(path);
static google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback, NULL, true, -1);
strcpy(logPath,path);
strcpy(logNamePrefix,env_log_name);
env->ReleaseStringUTFChars(log_dir, path);
env->ReleaseStringUTFChars(log_name, path);
#if defined(VERSION_NAME)
ALOGD("breakpad init success, version:%s, compile time:%s %s\n",VERSION_NAME, __DATE__, __TIME__);
#else
ALOGD("breakpad init success, compile time:%s %s\n", __DATE__, __TIME__);
#endif
}
extern "C"
JNIEXPORT void JNICALL
Java_com_cc_breakpad_Breakpad_crashTest(
JNIEnv *env,
jclass type) {
volatile int *a=(int*)(NULL);
*a=1;
}
5. 开发JNI 接口的Kotlin源文件Breakpad.kt
public class Breakpad {
static {
System.loadLibrary("breakpad");
}
/**
* 初始化
* @param logPath 日志目录
* @param logNamePrefix 日志文件名前缀,前缀+时间.dmp
*/
public static native void init(String logPath,String logNamePrefix);
public static native void crashTest();
}
6. 应用集成使用
至此,开发集成工作开发完成,可在应用中通过Breakpad.init方法进行初始化,即可实现native崩溃监测。
class MainActivity : AppCompatActivity() {
private var TAG = "MainActivity"
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Breakpad.init(Environment.getExternalStorageDirectory().absolutePath,"Blib")
binding.button.setOnClickListener {
Breakpad.crashTest()
}
}
}
运行程序,调用Breakpad.crashTest()可捕获native崩溃异常,具体信息如下:
2025-12-11 11:09:41.040 3037-3062/com.android.cc.nlib D/breakpad:
breakpad dump path: /storage/emulated/0/463d081f-9d7b-4029-b137e5bc-1822af04.dmp
7. 崩溃堆栈dmp信息解析
将dmp文件导出到PC,通过dump_syms和minidump_stackwalk 对dmp文件进行解析,libbreakpad.so 不能用apk里集成的,需要用未strip的so,具体路径为:
dump_syms 在 jni/include/tools/linux/dump_syms/dump_syms,
minidump_stackwalk 在 jni/include/processor/minidump_stackwalk
libbreakpad.so 在 build/intermediates/cmake/release/obj/(release的版本),
build/intermediates/cmake/debug/obj/(debug的版本)
具体操作如下:
cc@linux ~/b/breakpaddemo> ls
463d081f-9d7b-4029-b137e5bc-1822af04.dmp libbreakpad.so
cc@linux ~/b/breakpaddemo> dump_syms libbreakpad.so > libbreakpad.so.sym
cc@linux ~/b/breakpaddemo> head -n1 libbreakpad.so.sym
MODULE Linux arm64 BE286F546A6E957F7A240604B230BE7B0 libbreakpad.so
cc@linux ~/b/breakpaddemo> mkdir -p symbols/libbreakpad.so/BE286F546A6E957F7A240604B230BE7B0
cc@linux ~/b/breakpaddemo> mv libbreakpad.so.sym symbols/libbreakpad.so/BE286F546A6E957F7A240604B230BE7B0/
cc@linux ~/b/breakpaddemo> minidump_stackwalk 463d081f-9d7b-4029-b137e5bc-1822af04.dmp symbols > 463d081f-9d7b-4029-b137e5bc-1822af04.txt
至此,dump 监测的native 崩溃的详细堆栈信息被解析到了463d081f-9d7b-4029-b137e5bc-1822af04.txt文件中,具体堆栈信息如下:
Operating system: Android 0.0.0 Linux 4.4.138
#187 SMP PREEMPT Tue Aug 26 09:27:58 CST 2025 aarch64
CPU: arm64
4 CPUs
GPU: UNKNOWN
Crash reason: SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available
Thread 0 (crashed)
0 libbreakpad.so!Java_com_cc_breakpad_BreakPad_crashTest [breakpad.cpp : 48 + 0x0]
x0 = 0x00000074494711c0 x1 = 0x0000007fc84e6974
x2 = 0x0000000000000000 x3 = 0x000000744945aa00
对照breakpad.cpp Breakpad.crashTest()代码查看:
extern "C"
JNIEXPORT void JNICALL Java_com_cc_breakpad_Breakpad_crashTest(
JNIEnv *env,
jclass type) {
volatile int *a=(int*)(NULL);
*a=1;
}
崩溃点:
libbreakpad.so!Java_com_cc_breakpad_BreakPad_crashTest(breakpad.cpp第48行)
错误类型:SEGV_MAPERR(访问无效内存地址)
关键线索:Crash address: 0x0(尝试访问地址0x0,即空指针)
寄存器状态:x8 = 0x0(可能表示传递的指针参数为NULL)
备注:
如果用apk中的libbreakpad.so,由于经过strip去除调试符号信息,只能解析到崩溃的函数,无法定位到具体行数。
breakpad的Android项目JNI开发及使用基本完成了,如果需要集成到实际项目中,可以直接集成libbreakpad.so的JNI接口,这样就不用每次都编译breakpad源码了。
8. 附录
breakpad JNI项目源码地址:https://gitee.com/carmanshaw/blib