Android NDK Crash 日志抓取及定位

Android NDK Crash 日志抓取及定位

有人说,如何在Android Studio 可以配置日志模式,过滤NDK的crash,那么就让我们来简单看看下如何配置的。

一、Android Studio 配置 NDK Crash 过滤日志

第一步:点击下图框中部分:
这里写图片描述

第二步:选择“Edit Filter Configuration”

第三步:编译如下图中的内容,其中“Filter Name” 取一个合适的就行,但是“Log Tag” 必须为**“DEBUG”**模式,Log Level 可以为DEBUG就行。

这里写图片描述

第四步:手动出发一个NDK的crash。

第五步:查看日志,如下图。

这里写图片描述

在上图中,我们看到了关键的日志如下:

#11 pc 00032493  /data/app/com.pic.livefilters-1/lib/arm/libhairUtils.so (detectHairByOpenCV+1930)

嗯,出错的是一个叫detectHairByOpenCV的方法,但是后面的1930是什么鬼?好像不是行号,我要怎么定位呢?(PS 我也不知道这是什么鬼,之前一直被这个困扰,没找到解决方案,只能很傻的通过打印关键日志,判断出错的位置)。

so, 接下来我将介绍两个能直接定位到具体行号的方法。

二、NDK-STACK 定位 NDK Crash 位置

操作复杂吗?
so, easy。
只要执行如下代码就行:

adb logcat | ndk-stack -sym /yourProjectPath/obj/local/armeabi-v7a

PS: 必须是带symbols的so,也就是在’<PROJECT_ROOT>\app\src\main\obj\local<ABI>\下面的so’, 否则行号打印出来降是??.??
得到的日志如下图:
这里写图片描述

如图得到就是具体的类的行号。

三、使用 arm-linux-androideabi-addr2line 定位 NDK Crash

在第一章中,使用Android Studio 得到了相应的日志,我们是否可以利用下,否则Android Stuido 配置完,感觉有点鸡肋。那么这一章就可以然后利用上第一章过滤的日志。

首先,我们得配置下arm-linux-androideabi-addr2line这个工具,这个工具藏在哪里呢?
如果之前我们已经配置过NDK的环境,可以通过 which ndk-build (MAC or Linux) 得到相应地址。
大致位置如下(可供参考):

/Users/yourUsrName/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line

为了以后更方便的使用,我们可以做一个连接:

sudo ln -s arm-linux-androideabi-addr2line /usr/local/bin/addr2line

接下来,让我们开始操作吧,还记得上面我们得到的关键日志吗?忘了?没事,我再写一遍,如下:

#11 pc 00032493  /data/app/com.pic.livefilters-1/lib/arm/libhairUtils.so (detectHairByOpenCV+1930)

我们只要执行如下命令:

addr2line -f -e /yourProjectPath/src/main/obj/local/armeabi-v7a/libhairUtils.so 00032493

00032493 就是上面的指针位置,得到如下结果:
这里写图片描述

这里和使用ndk-stack命令得到结果是一致的,行号都是332,这就是引起crash的位置。让我们来看看,到底是以为上面行为引起的呢?
这里写图片描述

哦? 是创建了Object一起的? 纳尼? 创建 Object会引起什么crash? 这个时候让我们回到第一章抓取的日志,其中的message有说明为(local reference table overflow (max=512)):
这里写图片描述

哦,原来如此,是因为创建了过多的Object,导致本地变量过多,溢出了。所以降注释掉的那行代码打开就行,用完就释放掉。

总结

本文讲述了使用Android Stuido过滤日志、NDK-Stack、arm-linux-androideabi-addr2line等三种方式,其实每种都有各自的特性,用好,用顺手为重要的。

<think>我们正在解决如何在Android定位backtrace地址的问题。根据引用内容,我们知道在AndroidNDK程序中,当发生崩溃时,会生成一个tombstone文件(或通过logcat抓取)提供backtrace信息。但是,由于发布版本通常是release版(去除了调试信息),所以我们需要使用工具将backtrace中的地址转换为源代码文件和行号。主要步骤包括:1.获取崩溃时的backtrace(通常包含在tombstone文件或logcat输出中)。2.使用addr2line工具(或其他类似工具)将地址转换为源代码位置。根据引用[2]和引用[3],我们可以使用交叉编译工具链中的`arm-linux-androideabi-addr2line`(或对应架构的addr2line)来定位地址。具体步骤:1.从backtrace中提取崩溃的地址。例如,在tombstone文件中,你可能会看到类似这样的行:```#00pc00001474/data/local/tmp/myapp(my_function+42)```这里`00001474`就是我们需要转换的地址(注意,实际地址可能是一个完整的16进制数,如0x00001474)。2.使用addr2line工具,并指定未strip的共享库(或可执行文件)路径。未strip的共享库通常是在编译时带有调试信息的版本(在NDK中,通常位于`obj/local/<abi>/`目录下)。命令格式:```arm-linux-androideabi-addr2line-e<带调试信息的库文件><地址>```例如:```arm-linux-androideabi-addr2line-e./obj/local/armeabi-v7a/libmyapp.so00001474```这将输出对应的源文件位置和行号。3.如果使用ndk-stack工具,可以更方便地解析整个backtrace。将logcat输出保存到文件,然后使用:```ndk-stack-sym<带符号表的目录>-dump<logcat文件>```其中,`<带符号表的目录>`是包含未strip库的目录(例如`obj/local/armeabi-v7a/`)。注意事项:-确保使用的addr2line工具与目标设备的架构匹配(如armeabi-v7a、arm64-v8a等)。-使用的库文件必须与崩溃设备上的库文件一致(即编译时生成的未strip版本),否则转换的地址可能不准确。引用[1]提到,发布版本通常去除了调试信息,因此我们必须保留编译时生成的未strip库文件,以便后续调试。引用[4]提到,Android系统可能不支持backtrace函数(但通常支持unwind),我们可以通过dlsym来检查是否支持backtrace。引用[5]展示了一个函数地址对应的源文件位置(functional:569),说明通过工具成功定位到了源代码行。因此,总结步骤:1.保留编译生成的未strip库文件(用于后续解析)。2.当崩溃发生时,获取backtrace(通过tombstone文件或logcat)。3.使用addr2line或ndk-stack工具解析地址。下面给出一个完整的示例:假设在logcat中捕获到以下native崩溃backtrace:```backtrace:#00pc00001474/data/app/com.example.myapp/lib/arm/libmyapp.so(my_function+42)#01pc00001234/data/app/com.example.myapp/lib/arm/libmyapp.so(another_function+100)```我们想要定位#00的地址00001474(注意,实际地址是0x00001474,但addr2line可以接受不带0x前缀的地址)。步骤:1.找到编译时生成的未strip库文件,例如:`/path/to/project/obj/local/armeabi-v7a/libmyapp.so`2.使用addr2line(位于NDK目录中,如`$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-addr2line.exe`,具体路径根据平台和NDK版本可能不同):```arm-linux-androideabi-addr2line-e/path/to/project/obj/local/armeabi-v7a/libmyapp.so00001474```输出可能是:```/home/user/project/jni/myfile.c:30```这样我们就知道崩溃发生在myfile.c的第30行。另外,也可以使用ndk-stack工具:1.将logcat中崩溃的整个日志保存到文件(比如crash.log)。2.运行:```ndk-stack-sym/path/to/project/obj/local/armeabi-v7a-dumpcrash.log```ndk-stack会自动解析整个backtrace,并输出包含源文件位置和行号的信息。注意:如果崩溃发生在系统库(如libc.so),则需要下载对应Android版本的符号表才能解析。相关问题:1.如何在没有保留未strip库的情况下解析backtrace地址?2.如何获取Android系统库的符号表?3.如何通过崩溃地址定位到具体的代码行?4.如何避免发布版本无法定位backtrace地址的问题?5.在AndroidStudio中如何自动解析nativebacktrace?</think>### 在 Android定位 Backtrace 地址的方法 #### 一、核心工具:addr2line 使用 NDK 提供的 **addr2line** 工具将内存地址转换为源码位置: ```bash # 基本语法 arm-linux-androideabi-addr2line -e <未strip的.so文件> <内存地址> # 实际示例(参考引用[2][3]) arm-linux-androideabi-addr2line -e ./build/libnative.so 00001474 ``` **关键参数**: - `-e`:指定带调试符号的未strip库文件 - `-f`:显示函数名(可选) - 地址格式:直接使用 backtrace 中的16进制地址(如00001474) #### 二、操作步骤详解 1. **获取崩溃地址** - 从 tombstone 文件或 logcat 中提取: ``` backtrace: #00 pc 00001474 /data/app/libnative.so (crash_function+42) ``` 目标地址:**00001474** 2. **准备未strip库文件** - 编译时保留调试符号(CMake配置): ```cmake set(CMAKE_BUILD_TYPE RelWithDebInfo) # 保留调试符号的发布版本 ``` 3. **执行地址转换** ```bash # 使用NDK路径中的工具(参考引用[3]) $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-addr2line \ -e path/to/obj/local/armeabi-v7a/libnative.so \ 00001474 ``` 4. **解析结果** - 成功输出格式: ``` crash_function /project/src/native_code.c:30 ``` - 失败情况:显示`??:0`(需检查符号文件匹配性) #### 三、替代方案:ndk-stack(批量解析) ```bash # 解析整个logcat输出(参考引用[1]) adb logcat | $NDK/ndk-stack -sym obj/local/armeabi-v7a/ # 解析tombstone文件 $NDK/ndk-stack -sym obj/local/armeabi-v7a/ -dump tombstone_01.txt ``` **优势**:自动识别所有地址,无需手动提取 #### 四、关键注意事项 1. **符号文件必须匹配**: - 使用编译时生成的**未strip .so文件** - 确保CPU架构一致(armeabi-v7a/arm64-v8a)[^1] 2. **地址有效性检查**: - 有效地址范围:`0x0000XXXX` - `0xFFFFXXXX` - 无效地址示例:`00000000`(空指针) 3. **动态加载库处理**: ```cpp // 在代码中打印加载地址(参考引用[4]) void* base_addr = dlopen("libnative.so", RTLD_LAZY); LOGD("Base address: %p", base_addr); ``` 实际地址 = 基地址 + backtrace地址 #### 五、完整工作流程 ```mermaid graph TD A[获取崩溃backtrace] --> B{提取内存地址} B --> C[准备未strip的.so] C --> D[使用addr2line转换] D --> E{成功?} E -->|是| F[定位源码行] E -->|否| G[检查符号文件匹配性] G --> C ``` #### 六、常见问题解决 **问题**:输出`??:0` **解决方案**: 1. 确认使用的.so文件包含调试符号(`file libnative.so` 应显示`not stripped`) 2. 检查架构匹配性: ```bash # 查看设备架构 adb shell getprop ro.product.cpu.abi ``` 3. 使用编译时生成的精确版本符号文件 **问题**:地址超出范围 **解决方案**:确认是否需计算基地址偏移(动态加载库场景) --- ### 相关问题 1. 如何为 Android NDK 程序保留发布版本的调试符号? 2. 当 backtrace 中包含多个动态库地址时,如何批量解析? 3. 如何通过崩溃地址快速定位到 Git 提交记录? 4. 在 Android Studio 中如何自动化 backtrace 解析流程? 5. 如何解析系统库(如 libc.so)的 backtrace 地址? [^1]: 使用未 strip 的库文件是解析成功的关键前提 [^2]: 地址转换需精确匹配库文件和架构 [^3]: NDK 工具链路径可能随版本变化
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值