24、稳定性与压力测试:Monkey测试、CTS/VTS认证测试、长时间稳定性测试、Crash分析与解决
各位同学,今天我们来聊聊一个让所有嵌入式工程师又爱又恨的话题——稳定性测试。
说实话,我在做i.MX平台移植的头两年,最怕听到的就是测试同事喊“又Crash了”。那时候我就在想,为什么明明代码逻辑没问题,跑着跑着就挂了?后来我才明白,稳定性不是测出来的,是设计出来的。但话说回来,没有系统性的测试手段,你连问题在哪都找不到。
24.1 稳定性测试的整体框架
先给大家画个图,看看我们今天要聊的这几个测试维度之间的关系。
你看,这四个维度其实是层层递进的。Monkey测试帮你快速暴露问题,CTS/VTS保证兼容性,长时间测试验证耐力,最后Crash分析是收尾的硬功夫。我个人的习惯是,先跑Monkey,再跑CTS,最后挂机跑长时间。
24.2 Monkey测试:暴力但有效
Monkey测试,说白了就是让一只猴子在手机上乱点。Android自带的adb monkey工具会生成随机事件流——触摸、滑动、按键、音量调节,什么都来。
我在项目中遇到过最夸张的一次,Monkey跑了不到5分钟,系统直接黑屏重启。后来一查,是某个GPU驱动在特定频率下触发了死锁。
24.2.1 基本用法
# 最简单的Monkey测试,发送500个随机事件
adb shell monkey -p com.android.car 500
# 带详细日志,方便定位
adb shell monkey -p com.android.car -v -v -v 10000
# 指定事件间隔(毫秒),模拟真实操作节奏
adb shell monkey -p com.android.car --throttle 200 5000
24.2.2 关键参数详解
| 参数 | 作用 | 推荐值 |
|---|---|---|
| -p | 指定包名,只测试目标应用 | com.android.car / com.android.systemui |
| --throttle | 事件间隔(毫秒) | 200-500 |
| -s | 随机种子,复现相同序列 | 固定值如12345 |
| --pct-touch | 触摸事件百分比 | 50-70% |
| --pct-motion | 手势事件百分比 | 10-20% |
| --pct-appswitch | 应用切换百分比 | 5-10% |
--pct-syskeys设太高。Home键、Back键在车载场景下行为特殊,按多了可能导致SystemUI崩溃。我曾经吃过这个亏,一晚上跑了8次,每次都在30分钟左右SystemUI挂掉。
24.2.3 车载场景的Monkey定制
车载系统和手机不一样。你不能让Monkey乱切应用,因为中控屏上同时显示导航、音乐、空调三个窗口。我建议这样配置:
# 针对车载Launcher的Monkey测试
adb shell monkey -p com.android.car.launcher \
--pct-touch 60 \
--pct-motion 15 \
--pct-appswitch 5 \
--pct-nav 5 \
--pct-majornav 5 \
--pct-syskeys 2 \
--pct-anyevent 8 \
--throttle 300 \
-s 20240101 \
50000
为什么这么配?你想想看,用户在车上主要就是点按和滑动,很少会疯狂按音量键。所以触摸事件占比要高,系统按键要压低。
24.3 CTS/VTS认证测试
CTS(Compatibility Test Suite)和VTS(Vendor Test Suite)是Google的官方认证测试。说白了,就是Google给你发的一张“合格证”。没过CTS,你的系统就不能叫Android。
我记得第一次跑CTS的时候,整整跑了3天3夜。中间断了一次电,全部重来。那种感觉,嗯,不想再体验第二次。
24.3.1 CTS测试要点
- 测试范围: 涵盖所有Android API的兼容性,包括多媒体、传感器、网络、存储等
- 运行方式: 通过
cts-tradefed命令行工具执行 - 结果判定: 所有测试项必须为PASS,FAIL项必须提供合理解释
# 启动CTS测试
./cts-tradefed
run cts --plan CTS
# 只测试某个模块,比如车载相关
run cts -m CtsCarTestCases
# 查看结果
l r # 列出所有测试结果
24.3.2 VTS测试要点
VTS主要测HAL层和内核接口。对于i.MX平台来说,VTS比CTS更容易出问题。为什么?因为HAL层直接跟硬件打交道,而i.MX的某些外设驱动可能没完全适配。
# 运行VTS
./vts-tradefed
run vts -m VtsHalCameraProviderV2_4TargetTest
# 查看详细失败日志
run vts -m VtsHalCameraProviderV2_4TargetTest --log-level VERBOSE
| 常见VTS失败项 | 可能原因 | 解决方案 |
|---|---|---|
| Camera HAL测试失败 | ISP驱动未正确初始化 | 检查dmesg中camera相关错误,确认v4l2节点存在 |
| Audio HAL测试失败 | ALSA配置与HAL期望不匹配 | 对比audio_policy_configuration.xml和实际声卡设备 |
| Sensor HAL测试失败 | 传感器数据上报频率不达标 | 检查i2c通信速率,可能需要调整设备树中的时钟配置 |
24.4 长时间稳定性测试
Monkey测试跑几个小时,CTS跑几天,但长时间稳定性测试——我建议至少跑72小时。为什么是72小时?因为很多内存泄漏问题,48小时内才会暴露出来。
我曾经遇到过一个问题:系统运行到第50个小时左右,蓝牙开始断连。查了三天,最后发现是蓝牙协议栈里有个定时器没释放,每连接一次就泄漏4字节。50小时刚好把内存池耗尽。
24.4.1 测试方案设计
# 长时间稳定性测试脚本(简化版)
#!/bin/bash
# 循环执行测试场景
for i in {1..1000}; do
echo "=== Round $i ==="
# 1. 启动导航
am start -n com.android.car.navigation/.MainActivity
sleep 30
# 2. 播放音乐
am start -n com.android.car.media/.MainActivity
sleep 60
# 3. 调节空调
am broadcast -a com.android.car.action.SET_TEMP --ei temp 24
sleep 10
# 4. 检查系统状态
dumpsys meminfo | grep "Total RAM"
cat /proc/meminfo | grep MemAvailable
# 5. 抓取关键日志
logcat -d | grep -E "Error|Fatal|ANR|CRASH" >> /data/stable_test.log
sleep 120
done
- 每30分钟记录一次可用内存,看是否有持续下降趋势
- 每1小时检查一次dmesg,看是否有驱动报错
- 每2小时执行一次
top,看哪个进程CPU占用异常 - 每6小时重启一次蓝牙/WiFi,模拟用户日常使用
24.4.2 自动化监控
手动盯着跑72小时不现实。我写了个简单的Python脚本,配合adb做自动化监控:
import subprocess
import time
import datetime
def check_system_health():
# 检查是否ANR
result = subprocess.run(
['adb', 'shell', 'logcat', '-d', '-b', 'events', '|', 'grep', 'ANR'],
capture_output=True, text=True
)
if result.stdout:
print(f"[{datetime.now()}] ANR detected!")
# 触发dump
subprocess.run(['adb', 'shell', 'dumpsys', 'dropbox', '--print', 'data_app_anr'])
# 检查内存
result = subprocess.run(
['adb', 'shell', 'cat', '/proc/meminfo'],
capture_output=True, text=True
)
for line in result.stdout.split('\n'):
if 'MemAvailable' in line:
print(f"[{datetime.now()}] {line.strip()}")
while True:
check_system_health()
time.sleep(300) # 每5分钟检查一次
24.5 Crash分析与解决
好了,前面跑了那么多测试,现在该处理问题了。Crash分析是嵌入式工程师的必修课,也是最能体现功力的地方。
24.5.1 抓取Crash信息
当系统Crash时,Android会生成tombstone文件。这是我们的第一手资料。
# 查看tombstone文件
adb shell ls /data/tombstones/
adb shell cat /data/tombstones/tombstone_00
# 抓取完整logcat
adb logcat -b crash -d > crash_log.txt
adb logcat -b system -d > system_log.txt
# 抓取kernel日志
adb shell dmesg > dmesg_log.txt
24.5.2 常见Crash类型与定位
| Crash类型 | 典型特征 | 定位方法 |
|---|---|---|
| NullPointerException | Java层空指针,常见于UI线程 | 查看logcat中的stack trace,定位到具体行号 |
| Native Crash (SIGSEGV) | tombstone中显示signal 11 | 使用addr2line将PC地址转换为源码位置 |
| ANR (Application Not Responding) | logcat中显示"ANR in ..." | 查看/data/anr/traces.txt中的线程堆栈 |
| Kernel Panic | 系统完全死机,串口输出panic信息 | 分析dmesg最后几行,通常有Call Trace |
24.5.3 实战:分析一个Native Crash
假设我们拿到一个tombstone,关键信息如下:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'android/i.mx8qm/i.mx8qm:12/SP1A.210812.001/123456:userdebug/test-keys'
Revision: '0'
ABI: 'arm64'
Timestamp: 2024-01-15 14:23:45.123456789+0800
pid: 1234, tid: 1235, name: HwBinder:1234_1
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
x0 0000000000000000 x1 0000007f8c123456 x2 0000000000000001
x3 0000000000000000 x4 0000000000000000 x5 0000000000000000
...
pc 0000007f8b123abc /vendor/lib64/hw/android.hardware.camera.provider@2.4-impl.so
lr 0000007f8b123a98 /vendor/lib64/hw/android.hardware.camera.provider@2.4-impl.so
看到fault addr 0x0了吗?这是典型的空指针解引用。PC指针指向了camera.provider的so库。接下来我们用addr2line定位:
# 将PC地址转换为源码位置
arm-linux-androideabi-addr2line -e android.hardware.camera.provider@2.4-impl.so 0x7f8b123abc
# 输出结果
# /home/user/android/vendor/nxp/camera/impl/CameraProvider.cpp:256
打开CameraProvider.cpp第256行,发现是一个指针在调用前没有判空。嗯,典型的HAL层bug。
24.5.4 系统性Crash预防
光会分析Crash还不够,更重要的是预防。我总结了几个原则:
- 所有HAL接口必须判空: 硬件可能在任何时候断开连接
- Binder调用必须加try-catch: 远程服务可能挂掉
- 异步回调必须检查生命周期: Activity可能已经销毁
- 日志要打全: 关键路径上加上
Log.d,方便事后追溯
最后说一句,稳定性测试不是一锤子买卖。每次合入新代码,都应该跑一轮Monkey和CTS。我团队里有个规矩:谁合入的代码导致CTS失败,谁负责修复并请全组喝奶茶。嗯,效果还不错。
公众号:蓝海资料掘金营,微信deep3321