外场反馈了一个终端设备异常高温的问题, 通过一系列分析发现是 HAL 层服务异常使CPU其中一个核心运算跑满, 导致功耗和发热异常, 进而影响温升.
本文主要记录了何如在 Android 系统中搭建一个基础的 gdb 调试环境, 以及一种跟踪线程状态的思路.
定位功耗来源
通过功率计检测到目标场景整机功耗比预计高 250ma, 硬件确认是软件问题导致.
软件层面引起高功耗一般是 CPU高占用 或是 硬件配置异常 导致高功耗, 从实际场景分析应该属于 CPU高占用.
定位异常进程
首先通过 top 命令确认当前 CPU 使用情况:
发现两个完全跑满单核的进程 camerahalserver 和 android.hardware.gnss@1.0-service-unicore, 其中前者是我们的正常业务消耗, 后者这个进程是 GNSS 的 HAL服务, 跑满一核是不正常的.
定位异常线程
上一步知道了异常的 pid, 现在来确认一下这个进程发生了什么, 一般 HAL 服务会有多个线程, 需要确认是什么线程异常, 使用 top -Hp pid 来观察线程工作情况:
定位异常代码块
上一步已经知道了异常的线程, 但是这个线程工作较复杂, 通过走读代码没有发现明显异常, 需要一种手段定位到运行时环境的异常堆栈信息, 这里引入一个神器 GDB.
GDB 是 GNU 开源组织发布的一个强大的 UNIX 下的程序调试工具.
部署 GDB 工具
我们的整机中正常是没有 gdb 相关工具的, 所以接下来使用 android-ndk 中附带的 gdb 套件进行环境搭建.
通常在我们 Android-SDK 下有许多不同版本的 ndk, 挑选一个不太新的版本(太新的版本发现已经没有 gdb 相关套件了), 我这使用的是 21.4.7075529 版本的 ndk.
cd {ANDROID_SDK}
adb push ndk\21.4.7075529\prebuilt\android-arm64\gdbserver\gdbserver /data/local/tmp/gdbserver
adb shell chmod +x /data/local/tmp/gdbserver
运行 gdbserver 工具
通过 gdbserver 工具可以实现对目标进程 attach, 并且对外暴露 gdb 远程调试端口:
adb shell /data/local/tmp/gdbserver :1234 --attach 637
其中 1234 是 gdbserver 的监听端口, 637 是 attach 的进程 ID.
这个操作需要
ROOT权限!
运行 gdb 工具
上一步 gdbserver 已经跑起来了, 但监听端口是在终端里面, 我使用的 gdb 是 Windows 下的, 需要把终端的端口映射过来:
adb forward tcp:1234 tcp:1234
然后使用 gdb 就能连接到机器里面 gdbserver, 先启动 gdb 工具:
ndk\21.4.7075529\prebuilt\windows-x86_64\bin\gdb.exe
启动之后使用以下指令连接到终端的 gdbserver
(gdb) target remote :1234
使用 gdb 打印线程信息
使用命令 info threads 可以打印进程所以线程:
(gdb) info threads
Id Target Id Frame
* 1 Thread 637.637 "gnss@1.0-servic" 0x000000724813c794 in __ioctl () from target:/apex/com.android.runtime/lib64/bionic/libc.so
2 Thread 637.2040 "um220_gps" 0x000000724813c658 in __epoll_pwait () from target:/apex/com.android.runtime/lib64/bionic/libc.so
3 Thread 637.2043 "um220_gps_tmr" 0x000000724813d3b4 in nanosleep () from target:/apex/com.android.runtime/lib64/bionic/libc.so
4 Thread 637.2044 "um220_gps" 0x0000007247255848 in gps_nmea_thread () from target:/vendor/lib64/hw/gps_unicore.default.so
使用 thread 4 切换到目标线程:
(gdb) thread 4
[Switching to thread 4 (Thread 637.2044)]
#0 0x0000007247255848 in gps_nmea_thread () from target:/vendor/lib64/hw/gps_unicore.default.so
这里的 4 对应的 2044 就是上面步骤跟踪到的占用异常的线程ID.
使用 bt 打印线程调用栈信息:
(gdb) bt
#0 0x0000007247255848 in gps_nmea_thread () from target:/vendor/lib64/hw/gps_unicore.default.so
#1 0x0000007247298a10 in threadFunc(void*) () from target:/vendor/lib64/hw/android.hardware.gnss@1.0-impl-unicore.so
#2 0x0000007248151074 in __pthread_start(void*) () from target:/apex/com.android.runtime/lib64/bionic/libc.so
#3 0x00000072480f36c8 in __start_thread () from target:/apex/com.android.runtime/lib64/bionic/libc.so
重复使用 bt 可以看到这个线程一直在 gps_nmea_thread 函数中, 可以基本判断此函数出现了异常, 接下来就需要具体分析这个函数的逻辑.
由于这套环境暂时还没法加载代码信息(正在研究中), 暂时不能借助
gdb调试变量等信息, 后续如果搞定了再补上.