外场反馈了一个终端设备异常高温的问题, 通过一系列分析发现是 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
调试变量等信息, 后续如果搞定了再补上.