0%

使用 gdb 定位一个 HAL CPU 高占用过程小记

  1. 定位功耗来源
    1. 定位异常进程
    2. 定位异常线程
  2. 定位异常代码块
    1. 部署 GDB 工具
    2. 运行 gdbserver 工具
    3. 运行 gdb 工具
    4. 使用 gdb 打印线程信息

外场反馈了一个终端设备异常高温的问题, 通过一系列分析发现是 HAL 层服务异常使CPU其中一个核心运算跑满, 导致功耗和发热异常, 进而影响温升.

本文主要记录了何如在 Android 系统中搭建一个基础的 gdb 调试环境, 以及一种跟踪线程状态的思路.

定位功耗来源

通过功率计检测到目标场景整机功耗比预计高 250ma, 硬件确认是软件问题导致.

软件层面引起高功耗一般是 CPU高占用 或是 硬件配置异常 导致高功耗, 从实际场景分析应该属于 CPU高占用.

定位异常进程

首先通过 top 命令确认当前 CPU 使用情况:
定位高占用进程.PNG
发现两个完全跑满单核的进程 camerahalserverandroid.hardware.gnss@1.0-service-unicore, 其中前者是我们的正常业务消耗, 后者这个进程是 GNSSHAL服务, 跑满一核是不正常的.

定位异常线程

上一步知道了异常的 pid, 现在来确认一下这个进程发生了什么, 一般 HAL 服务会有多个线程, 需要确认是什么线程异常, 使用 top -Hp pid 来观察线程工作情况:
定位高占用线程.PNG

定位异常代码块

上一步已经知道了异常的线程, 但是这个线程工作较复杂, 通过走读代码没有发现明显异常, 需要一种手段定位到运行时环境的异常堆栈信息, 这里引入一个神器 GDB.

GDBGNU 开源组织发布的一个强大的 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

其中 1234gdbserver 的监听端口, 637attach 的进程 ID.

这个操作需要 ROOT 权限!

运行 gdb 工具

上一步 gdbserver 已经跑起来了, 但监听端口是在终端里面, 我使用的 gdbWindows 下的, 需要把终端的端口映射过来:

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 调试变量等信息, 后续如果搞定了再补上.

  • 本文作者: 6x
  • 本文链接: https://6xyun.cn/article/176
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-ND 许可协议。转载请注明出处!