上个季度我全程参与了一块国产 CPU 加国产操作系统的嵌入式主控板第三方测评,项目启动前我本来觉得这事不复杂,毕竟测过的嵌入式软件不算少:交叉编译、刷机、跑用例,流程熟得很。真正开始搭环境、定方案之后才发现,嵌入式信创软件测试要关注的东西比普通嵌入式测试多出好几个维度。CPU 架构指令集、操作系统内核裁剪情况、交叉编译链版本、第三方中间件是否适配、外设驱动有没有到位,再到上电掉电、长时间稳定性,任何一层没对上,测出来的结论都可能和用户现场的真实表现完全不一样。
这篇文章我就把自己在这类项目里踩过的坑、反复验证过的测试思路和能直接照用的实操方法整理出来,给正在做或者准备接手嵌入式信创软件测试的朋友一个比较完整的参考清单。不管是软件测试工程师、嵌入式研发,还是需要在国产化板卡上做验证的团队,应该都能从中找到一些能落地的做法。
1. 拿到板卡先别急着测功能,先把“环境定性”做扎实
嵌入式和信创两个词叠在一起,最容易让人忽略的一件事是:这里的软件运行环境根本不是“一个 Linux”那么简单。普通软件测试里说“我在 Linux 上跑没问题”,到了嵌入式信创场景几乎是无效描述,因为同样叫 Linux 的系统,可能跑在 x86_64 上,也可能跑在 ARM64、LoongArch、MIPS64、SW64 上;即便是同一个架构,内核版本、libc 版本、系统裁剪程度也可能完全不同。环境不对,后面所有测试结论都站不住。
所以我在这类项目的测试计划里永远把“环境定性”放在第一优先级,正式功能测试开始之前,必须先花时间把被测平台的底细摸清楚,并且把确认结果记录成一份可追溯的环境基线文档。这一步看着不起眼,实际能避免后面大量“同一个版本在我这里没问题,在你那里就复现不了”的扯皮。
1.1 三分钟确认 CPU 架构、操作系统版本和内核详情
拿到板卡后,我习惯先通过串口或者 SSH 登录目标系统,执行一组基础命令把环境信息抓出来。这个过程最好全程保留原始输出,不要只看最后一行结论,因为后续排查问题很可能需要回头翻这些细节。
# 查看内核版本和系统基本信息 uname -a # 查看操作系统发行版信息 cat /etc/os-release # 查看 CPU 架构型号信息 lscpu # 查看内存信息 cat /proc/meminfo | head -20 # 查看根文件系统挂载和磁盘占用 df -h # 查看关键动态库版本 ldd --version实际执行时,不同板卡输出差别很大。有的系统里/etc/os-release内容非常完整,有的裁剪得只剩下PRETTY_NAME。有的内核版本新到能直接支持容器,有的内核老到某些多线程同步原语都要绕路。这些细节直接影响测试范围的确定,比如内核里如果没有对应的网卡驱动模块,网络测试就得先变成驱动验证。
另外我建议把lscpu里的字节序、CPU 核数、CPU 最大最小频率都记下来。国产平台经常存在“标称频率很高,实际功耗策略限制得很死”的情况,跑性能测试时如果发现结果和预期差距明显,第一反应不是怀疑业务代码,而是先核对 CPU 当前是否工作在正常频率档位,是否被调度到了低功耗模式。
1.2 交叉编译链版本不一致的坑,测试还没开始就被卡住了
嵌入式软件测试和普通 PC 软件测试还有一个明显差异:测试包通常是研发在 X86 的构建机上交叉编译出来的,并不是直接在板卡上编译。这就带来了工具链一致性问题。我在项目里遇到过好几次类似情况:研发用的是 GCC 8 系列的交叉编译链,测试组拿到板卡后又用自己的 GCC 10 编译了一个测试辅助工具,结果放到板卡上一跑,报GLIBC_2.18 not found,或者某个符号链接不到。
出现这种问题,往往是宿主机上的 libc 版本高于目标板。嵌入式信创系统很多是裁剪过的,libc 版本不一定跟得上最新的交叉编译链默认目标版本。解决思路不是“把所有工具都静态编译”一刀切,而是先建立动态库依赖核对环节。每次拿到测试版本,执行两条命令:
# 检查可执行文件的架构是否正确 file app_binary # 检查动态库依赖是否能被目标系统满足 # 在开发机上先做一轮分析 ls -l app_binary objdump -p app_binary | grep NEEDED更严谨的做法是把二进制拷贝到目标板上后跑一遍ldd ./app_binary,看所有依赖是否能解析。如果目标板上没有 ldd,也可以用readelf -d ./app_binary或直接用LD_TRACE_LOADED_OBJECTS=1 ./app_binary代替。这个动作要固化到测试准备清单里,每次新版本进来都过一遍,能省掉后续很多莫名奇妙的“跑不起来”问题。
1.3 每个测试版本都该带一份环境锁定清单
被测系统在迭代,交叉编译链、依赖库、内核配置也可能在悄悄变。测试组如果只记录“本次测的是 v1.2 版本”,不去锁编译链和 OS 版本,等缺陷出现时会非常被动。所以我在项目里强制要求每个提交测试的版本都附带下面这张环境信息表,作为准出和准入门槛:
| 环境项 | 必须记录内容 | 记录方式 |
|---|---|---|
| CPU 架构 | 架构名、型号、核心数、是否小核大核 | lscpu 原始输出 |
| 操作系统 | 发行版名称、完整版本号 | /etc/os-release |
| 内核版本 | 内核 release 字符串、是否有实时补丁 | uname -a |
| libc | glibc 版本 | ldd --version |
| 交叉编译链 | 编译器路径、gcc 完整版本 | 构建脚本或 CI 记录 |
| 应用运行时 | JVM/node/go 等运行时版本 | 对应版本命令的输出 |
| 关键依赖库 | 库名、版本、编译时的 ABI | pkg-config 记录或构建产物清单 |
这张表其实是在给缺陷复现上保险。嵌入式信创场景的组合实在太多,CPU 架构、OS 发行版、内核补丁、运行时版本只要有一个变量没锁住,就可能复现不出问题,或者复现出假问题。我后来习惯在每条缺陷记录里都抄一份精简版环境三件套:架构 + OS 版本 + 内核版本,看起来啰嗦,但真的能救急。
2. 模拟器里跑通不等于能上板,嵌入式测试必须过“实板关”
很多团队做嵌入式软件测试时,会先用 QEMU 或者厂商自带的模拟器跑一轮功能冒烟。这个思路本身没有问题,毕竟板卡数量有限,模拟器能提升开发阶段的反馈速度。但信创板卡的模拟器生态成熟度和 X86 平台有明显差距,我见过有人在模拟器上测得好好的功能,一上真实板卡就崩,最后定位到模拟器对并发中断时序的模拟和真实硬件差别很大。
所以我的原则是:模拟器测试用来做逻辑功能冒烟和崩溃类问题的粗筛,真正作为发布依据的测试结论必须来自实板。尤其是涉及外设、中断、驱动、实时性、掉电恢复这几类场景,模拟器几乎不具备参考价值。
2.1 模拟器能帮你提前暴露的问题类型
逻辑层面的问题在模拟器上测是划算的。比如某个协议解析模块对输入报文的处理逻辑、某个状态机的迁移条件、纯计算类的算法结果,这些不依赖具体外设时序的功能,在模拟器里跑一遍能快速发现明显缺陷。做法是先交叉编译出一个能在模拟器环境下运行的版本,把单元测试和接口测试套件跑一遍,及时拦住低级错误。
不过我提醒一句,即便在模拟器上做逻辑测试,也要用和目标板一致的架构。模拟器架构不同,字节序、对齐访问方式都不一样,测出来的意义会打折扣。用 QEMU 做 ARM64 或 LoongArch 的用户态模拟时,还要确认模拟器的 CPU 特性配置,有些模拟器默认只模拟基础指令集,碰到用到新指令扩展的二进制会出现非法指令。
2.2 上实板才验得出的几类“硬骨头”测试点
模拟器再逼真,也无法覆盖真实硬件的中断优先级、总线竞争、DMA 行为、外设寄存器时序。我在实际项目里总结出几类必须上实板的测试点,缺了任何一类都不能说测试完整:
第一类是外设读写与中断。应用程序读写串口、网口、CAN、GPIO 时,真实板卡上存在中断风暴、数据溢出、FIFO 竞争等问题。这类问题模拟器很难构造出来,只有在实板高流量、高并发情况下才容易暴露。第二类是异常掉电和上电时序。模拟器里根本不存在“电”的概念,而真实板卡上掉电瞬间主控可能正处于 Flash 写入或文件系统提交过程中,再次上电后数据是否损坏、系统能否自恢复,只能实测。第三类是看门狗复位。看门狗期间外设是否恢复正常、复位后内存中残留数据是否影响新进程,都是硬伤高发点。第四类是性能真实性。CPU 频率调整、Cache 命中率、内存带宽、外设总线吞吐,只有实板性能数据才可作为发布参考。
我在一个主控板项目里就碰到过模拟器跑连续 5000 次数据采集全部正常,上了实板跑到 312 次直接卡死。最后查下来是串口驱动的中断处理函数在数据量上来后出现丢中断,这种问题模拟器不可能暴露出来,也是我坚持“最终结论必须实板验证”的原因。
2.3 实板上怎么搭一个高效的版本部署、执行和日志回收闭环
被测板卡大多资源受限,没有图形界面,甚至没有外网。如果想提高测试效率,我建议在实板上搭一个最小的“下发执行-结果回收”闭环。最简单的形式是利用 SSH 或串口通道,把待测二进制、测试脚本、测试数据通过 scp 传上去,在板卡上执行并输出日志,结束后再拉回开发机分析。
如果板卡不具备 SSH 服务,可以使用串口加文件传输协议,比如在 U-Boot 下用 tftp 下载内核和文件系统,或者在 Linux 下用串口或 U 盘拷贝测试包。测试过程中,我习惯让应用把日志写到板卡的一个专门目录,同时通过串口输出一部分关键日志,方便对端实时观察。
这里有个很实在的坑:板卡的系统时间经常不准,尤其没用 RTC 电池的板子,一上电就是 1970 年。日志时间戳不准会直接影响测试结论,特别是排查“哪个操作触发了崩溃”的时候,时间对不上非常痛苦。所以每次开始测试前,我都手动校准目标板时间:
# 手动设置系统时间,格式为 MMDDhhmmYYYY date 010112002025如果板卡有外网或者局域网内有时间服务器,也可以用ntpdate或chronyc同步,但很多测试环境没这个条件,手动校时最直接。日志回收之后,我会在分析前先把时间轴归一化,保证后续判断异常时序时有据可循。
3. 测试用例怎么拆,才不至于把“跑通”当成“测完”
嵌入式信创软件测试最容易犯的错,是测试脚本只覆盖“正常流程能跑通”,比如应用能启动、能收数据、能上报状态,就算通过。但嵌入式系统最终要长期 7×24 小时运行在无人值守现场,测试用例必须覆盖异常场景、资源耗尽场景、边界条件和并发场景,才可能尽量提前暴露线上才会出现的问题。
以前我带的测试团队里,新同事拿到一个嵌入式主控程序后,习惯先把业务主流程跑一遍,然后说“功能没问题”。我一般会反问三个问题:断网了怎么办?收到的报文比协议规定的长怎么办?连续跑一个月内存会不会涨?这三个问题想清楚,用例设计基本就入门了。
3.1 功能用例拆解法:把每个功能拆成“输入-处理-输出-状态”四要素
写功能用例时,我习惯不直接照着需求文档写“验证 XX 功能正常”,而是先把功能拆成输入、处理、输出、状态依赖四个要素。以“通过串口采集传感器数据并落盘存储”这个功能为例,拆开之后是这样:
| 要素 | 内容 | 对应测试关注点 |
|---|---|---|
| 输入 | 串口报文:帧头、长度、数据段、校验 | 短帧、长帧、超长帧、错校验、粘包半包 |
| 处理 | 协议解析、校验、格式转换、写盘 | 校验失败是否丢帧、转换是否丢精度、写盘是否加锁 |
| 输出 | 数据文件、日志、状态指示 | 文件是否可读、数据是否完整、异常时是否有日志 |
| 状态依赖 | 存储满、文件被删、串口被占用 | 磁盘满时的表现、文件重建、端口占用冲突 |
这样拆的好处是测试点不会漏。很多功能“看起来正常”但其实只是“处理”环节没错,输入端的异常输入根本没测过。嵌入式系统外部环境复杂,传感器干扰、电磁噪声、对端设备异常都可能让输入变得不规矩,输入端不做充分测试,系统到了现场很容易被一个异常帧打崩。
3.2 异常和边界场景,才是嵌入式系统测试的主战场
嵌入式软件的异常场景比普通应用软件更容易构造,也更值得测。我给每个功能模块设计用例时,会强制加入下面几类异常:网络断连与自动重连、对端设备异常下线、带外数据或广播风暴、文件系统只读或写满、系统时间跳变、关键配置缺失或损坏。
边界场景上,重点去看数值边界和缓冲区边界。比如协议字段长度是 1 字节,那就测 0、255、256 三种值;协议帧长定义是 256 字节,就测 255、256、257 三种长度;接收缓冲区设了 1024 字节,就设计 1023、1024、1025 的报文。这些场景在 X86 开发机上可能同样会出问题,但信创平台因为库实现不同,出问题的方式可能更隐蔽,比如结构体对齐方式变了,溢出后破坏的内存区域不同,崩溃位置也跟着变。
另外嵌入式通信特别容易出现“粘包”和“半包”问题。很多测试只发整帧数据,实际通信链路上应用层收到的却可能是半个帧、两个帧拼一起、甚至一帧拆成好几段。用例设计时要把数据链路层缓冲设为小于应用帧长度,或者用延迟发送模拟网络慢启动,专门触发粘包半包处理逻辑。
测并发和资源耗尽,建议用“压力场景用例”倒逼隐患
嵌入式主控程序往往同时处理采集、通信、存储、告警多个任务,资源共享问题非常常见。测试设计时我会专门留出“资源耗尽场景用例”,用工具把系统资源压到临界状态,再验证应用行为。常见做法包括:
- 通过循环创建文件的方式把磁盘剩余空间压到接近 0,然后触发数据落盘,观察应用是报错退出还是优雅处理;
- 通过反复启动退出子进程,观察系统是否有僵尸进程累积;
- 长时间跑多路并发通信,观察内存占用是否稳定,判断无内存泄漏。
例如以下这个简单脚本,可以用来对一个通信服务做连接数压力测试,同时观察内存变化:
#!/bin/bash for i in $(seq 1 200); do # 模拟第 i 路连接建立 echo "connect $i" >> /tmp/pressure.log # 每轮记录可用内存 free -m | grep Mem >> /tmp/pressure_mem.log sleep 1 done压测过程中一旦发现内存持续下降或进程崩溃,基本就说明有内存泄漏或者资源句柄没释放。这类问题在模拟器里很难出现,因为模拟器内存充足、句柄足够,只有实板压到临界值才容易暴露。
4. 兼容性和生态测试是一个专项,别随手散在功能用例里
嵌入式信创软件测试最深层的工作,其实是“兼容性验证”。普通 X86 平台开发编译的软件,拿到国产 CPU + 国产操作系统的环境里,能不能编译、能不能链接、能不能运行、运行后行为是否一致,每一步都可能有意外。
我把兼容性拆成了几个层次来看:处理器指令集和体系结构层、操作系统内核和系统库层、第三方库和中间件层、业务应用层。每一层都需要有对应的验证策略,测试计划里必须有专项章节,而不是随便加几个用例就算覆盖了。
4.1 不同 CPU 架构对软件行为的影响,比想象中更隐蔽
很多人觉得“都是 Linux,跨平台不就是重新编译一下吗”,但实际测试中,跨架构之后代码的“未定义行为”很容易现出原形。比如结构体对齐规则不同,同一个struct在不同架构下内存布局不同,如果代码里依赖结构体某个字段的偏移做直接访问,就可能读取到完全错误的数据。
另一个典型是隐式类型转换和char符号性。C/C++ 标准并没有规定char到底是 signed 还是 unsigned,具体由编译器和平台决定。X86 上大多数编译器把char当作 signed,某些 RISC 架构上却可能当作 unsigned。如果开发者在代码里默认char是 signed,拿它来和负数做比较,到了另一种架构上逻辑就悄悄变了。
这类问题用功能用例不一定测得出,必须靠更多层次的测试方法来捕捉,比如可移植性静态分析,比如在多个架构上都跑一遍相同的数值边界用例。测试团队至少要保证每轮测试不只在一个架构上跑,如果条件不允许,至少要把涉及字节序、结构体强转、位运算、浮点数处理的代码模块标注出来,重点设计针对性用例。
4.2 第三方库和中间件适配测试:越早启动越好
信创平台真正的痛点是生态。X86 平台上一句apt install或者pip install就装好的库,在国产化环境下不一定有现成包,有的需要从源码手动编译,有的则是版本残缺。这意味着应用依赖的第三方库,每一个都要单独做一次适配验证。
我经手的一个项目里,业务方用了一个比较新的 Java 序列化库,在 X86 的 JDK 上一切正常。移植到国产化服务器之后,服务能启动,但处理特定报文时数据出现乱码和错位。查了半天,发现这个库的新版本在部分 CPU 架构上的运行时行为有兼容性缺陷,必须降级到某个特定版本才正常。这种问题如果等到系统联调阶段才暴露,排查代价非常大。
所以我的经验是:兼容性测试要从项目早期就介入。开发机上的依赖锁文件里列出的每个库,都要在目标环境的早期原型板上验证过“能编译、能链接、能运行核心场景”。这在信创环境下尤其重要,因为国产 OS 的软件源和包管理器生态还在完善过程中,缺包、版本旧、依赖冲突都是常态。测试人员不能等到版本完整了才开始,而是要从最小可运行原型开始,跟着迭代持续验证。
4.3 性能指标不能平移 X86 基准,必须重新建立参考值
用户经常会拿着原来 X86 平台上的性能指标来要求信创平台达到同样水平。从测试角度讲,这个要求要拆开看。不同 CPU 的 IPC(每时钟周期指令数)、内存带宽、缓存大小都不一样,直接拿整机性能做硬性对标,有时并不合理。测试人员能做的,是把性能数据测准、测透,并给出工程层面的解释和建议。
性能测试建议在实板上做,而且要把运行环境条件记录完整,包括 CPU 频率档位、核心数、内存大小、存储介质、网络带宽、应用启动参数。给一组数据时可参考这种表格:
| 测试场景 | X86 参考平台 | 国产平台实测值 | 差异比例 | 备注 |
|---|---|---|---|---|
| 冷启动到服务就绪 | 2.0 秒 | 3.8 秒 | 90% | 国产平台主频低约 30%,建议调优初始化 |
| 10000 条数据入库 | 12 秒 | 21 秒 | 75% | 后续优化磁盘写入策略 |
| 并发 100 路请求响应 | 平均 80ms | 平均 150ms | 87% | 需确认是否命中 CPU 降频 |
测性能过程中还要特别关注误差来源。比如国产平台上如果开了大量调试日志,性能会明显劣化;CPU 降频策略导致测试结果不稳定,需要多次跑取中位数;文件系统如果放在 SD 卡或 eMMC 上,磨损均衡机制也会带来随机抖动。把这些影响因素记录清楚,性能报告才有参考价值。
另外,“性能不达标”的结论不能只丢给研发。我会建议测试团队在报告里尽量附带基础的性能剖析数据,比如用perf或者top找到热点函数,或者通过vmstat、iostat判断瓶颈在 CPU、内存还是磁盘 IO,帮研发缩小优化方向。国产平台上的调优手段和 X86 平台大同小异,但可用工具链不一定齐全,很多剖析工具也需要提前交叉编译好带到板子上,这个准备动作要早做。
4.4 字符编码问题的真实排查记录:一个典型的“信创乱码”案例
前面提到的一个 Java 服务乱码问题,我把排查过程展开说一下,因为它很有代表性。现象很简单:服务部署到国产化服务器后,接口返回的中文变成了乱码,而同一个服务在 X86 开发环境上完全正常。
初步怀疑是 HTTP 请求和响应的字符集配置不一致,但反复确认Content-Type里的charset=UTF-8配置没有变化。后来我对比了开发机和目标机的默认字符集输出,发现开发机的file.encoding是 UTF-8,目标机因为系统/etc/locale.conf没有配置,JVM 启动时默认走的是 POSIX 字符集,导致String.getBytes()这类依赖默认字符集的方法在两端行为不一致。
问题其实不是 JSON 库本身坏了,而是 JVM 启动环境和系统语言环境没有对齐。解决方式是显式给 JVM 加上-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8启动参数,并统一目标机的 locale 配置。这类问题在嵌入式信创环境很容易被误判成“中间件 bug”,实际上只要应用不依赖系统默认字符集,显式指定编码,就能规避绝大多数同类问题。
5. 稳定性、断电恢复和长稳测试,“土办法”反而最实用
嵌入式信创主控软件的可靠性要求,通常比普通应用软件高得多。设备部署到现场后可能几个月没人碰,期间要持续采集数据、维持通信、响应告警。所以稳定性测试和长稳测试不是可有可无的收尾动作,而是一项独立的核心测试活动。
长稳测试最怕的是“挂机几天看结果”的原始方式,没有指标记录,没有异常监控,跑完几天后才发现第三天就崩了,中间过程全凭猜测。所以即便测试环境再简陋,也要搭一个最简单的自动监控记录方案,哪怕是一个 shell 脚本 + 定时器都行。
5.1 长稳测试方案应该怎么定:时长、负载、监控指标
长稳测试的时长没有固定标准,要根据产品形态来定。我曾经参与过的一个工业主控板项目,要求至少连续运行 72 小时不重启、不泄漏、不丢数据。如果是涉及计费、医疗、轨道交通这类高可靠场景,建议做 7×24 小时甚至更久。测试方案的负载要尽量接近真实使用场景,比如被测系统现场一天处理 1 万条消息,测试时就要模拟一天 1 万条以上的负载,不能只跑空载。
监控指标至少要覆盖四类:进程状态类(是否存活、是否重启过)、资源使用类(内存占用、CPU占用、句柄数)、业务功能类(处理成功数、失败数、丢包数)、日志错误类(有无异常堆栈、关键错误码)。监控方式可以分两种,一种是在板卡内部起一个后台任务定时采样,另一种是外部测试机通过远程方式定期收集状态。前者适合板卡故障时留存现场信息也一起断了的情况,但至少能记录死前状态;后者稳定性更高,但如果板卡完全死机,外部监控也拿不到板内信息,所以我的做法是两者结合。
一个简单又可用的板卡内监控脚本可以长这样:
#!/bin/bash LOG_FILE="/tmp/longrun_monitor.log" while true; do echo "===== $(date '+%Y-%m-%d %H:%M:%S') =====" >> $LOG_FILE # 检查被测进程是否存活 if pgrep -f "target_app" > /dev/null; then echo "target_app: alive" >> $LOG_FILE else echo "target_app: DEAD" >> $LOG_FILE fi # 记录可用内存 free -m | head -2 >> $LOG_FILE # 记录 CPU 占用最高的前三个进程 top -bn1 | head -15 | tail -5 >> $LOG_FILE sleep 60 done脚本每分钟写一条记录,日志会越来越大,所以还需要配套做日志轮转,或者每 24 小时归档一次。跑完长稳后,我一般会写一个简单的统计命令,把监控日志里的异常次数、进程重启次数拉出来,再结合应用日志里的关键报错定位具体时间窗口。
5.2 断电、看门狗和反复上下电测试:故障注入别怕搞坏板子
嵌入式设备区别于普通软件的一个最大特点,就是它活在真实物理世界里,随时都可能断电。掉电测试是嵌入式信创软件测试里绝对不能省的项目。测试方法最简单的是人工反复断电上电,有条件的用可编程电源按设定周期执行自动断电上电。测试关注点不只是“能不能重新启动”,还要看启动后文件系统是否损坏、关键配置是否丢失、断电时正在写入的数据文件是否损坏。
我实际测过一块板卡,掉电后再次上电总会出现配置文件丢失。应用的非易失存储函数写文件时没有先写临时文件再原子 rename,只要写一半就断电,文件就损坏了。这类测试在 X86 虚拟机里很难模拟出来,因为虚拟磁盘有缓存机制,断电瞬间和真实物理掉电的差异很大。
看门狗测试也要专门设计。第一要验证正常运行时看门狗会不会误复位,可以连续长时间运行观察重启次数。第二要验证异常时看门狗能不能兜底,比如让应用暂停喂狗,预期系统在一定时间后自动重启。第三是验证复位后的状态恢复,看门狗复位和人工重启的区别在于复位后内核缓存没有干净清理,某些外设寄存器状态可能处于残留状态,应用必须在这种情况下可靠恢复。
5.3 长稳压测中的异常数据注入,比单纯“长时间正常运行”更有价值
纯空载长稳运行能发现的问题有限,我习惯在长稳周期的不同时间点有计划地注入异常数据,模拟真实现场的故障场景。比如第 6 小时模拟网络断开 5 分钟,第 12 小时模拟传感器输出异常报文,第 24 小时模拟存储空间写满。这样测的不是“系统在理想状态下能跑多久”,而是“系统在反复出状况的恶劣环境中是否还能自我恢复”。
异常注入后要重点观察三件事:恢复时间是否在指标范围内,系统是否产生僵尸进程或堆积的临时文件,以及后续业务是否连续受影响。曾经有一个被测系统,每次网络恢复后都要 30 多秒才能重新注册到服务器,而现场协议要求 10 秒内恢复,这类问题只做纯长稳不注入断网根本发现不了。
如果板卡有多路通信接口,我还会把故障做成轮换故障,每次只断一路,验证系统在其他链路正常时能否降级运行。分布式采集之类的场景,还要验证故障恢复后数据补传逻辑是否正确,补传会不会和实时数据冲突。这套做法虽然让长稳测试的工作量增加不少,但大大提升了测试结论的说服力。
6. 缺陷定位效率低,往往是环境信息和复现步骤没做到位
嵌入式信创软件测试推进到中后期,工作重心会从“发现缺陷”转向“推动缺陷修复和回归”。这时候最头痛的往往不是缺陷本身有多难修,而是缺陷信息不完整导致研发无法复现,或者研发在自己的 X86 环境上复现不了,两边来回拉扯浪费大量时间。
我个人的定位原则是:测试人员把缺陷的前半段工作做足,把环境差异缩小到可控范围,研发就有条件集中精力解决问题,双方合作效率会明显提升。
6.1 在缺工具的条件下,怎样定位问题更高效
信创环境下,很多熟悉的商业调试工具不一定可用,但并不意味着无从下手。我一直用的思路是:先应用日志、再系统日志、再系统调用跟踪、再内核级排查,一层层缩小范围。
应用日志是最便宜的线索来源。如果被测软件崩溃,先看最后输出的日志和崩溃转储,很多问题当场就能定位到具体模块。如果应用日志没有有效信息,就进系统层,看dmesg里有没有 segment fault、OOM、硬件错误记录。有时候磁盘满了导致文件系统只读也能从 dmesg 里看到大量 IO error。
再往下可以用strace跟踪系统调用,观察程序卡在哪个系统调用上、返回了什么错误码。如果目标板上没有 strace,可以从源码交叉编译一个静态版本带进去。strace -p PID可以实时观察一个卡住进程正在干什么。如果怀疑网络问题,可以抓包分析通信报文,板子上没有 tcpdump 就从外部测试机的端口镜像来抓。
如果怀疑是应用逻辑问题但实在没有头绪,我还会请求研发提供一个最小复现程序,把问题场景缩小到最简。这种情况经常发生在跨架构的底层差异上,最小复现程序能极大降低定位难度。
6.2 缺陷报告里的“环境三件套”必须写全
嵌入式信创场景写缺陷报告,除了普通缺陷要描述的步骤和预期结果,还一定要带上环境三件套:完整操作系统信息、CPU 架构、内核版本。如果涉及 JVM 或其他运行时,也要把运行时版本写清楚。不要嫌麻烦,因为这类缺陷很可能只在特定国产化平台出现,研发那边八成不是同一个环境,环境信息缺失就等于让他瞎猜。
我见过一个缺陷,现象是“解码结果偶尔出现乱码”。提交人只写了两行复现步骤,连架构都没注。研发在自己的 X86 环境上怎么跑都正常,双方来回试了三天,最后测试补了一句“平台是某某国产 CPU 的板卡”,研发拿去一跑,当场复现。一个问题定位花三天,中间全是沟通成本,但如果一开始信息完整,半小时就能进入修复。
缺陷报告里还应该尽量附上以下信息:
- 日志和截图的具体时间点,能对应到一个操作步骤;
- 被测版本号和编译环境关键信息;
- 该用例本次运行的完整前置条件,比如板卡运行时间、存储余量、网络状态;
- 如果过程涉及上电掉电,写明当时具体在做什么操作。
6.3 常见问题速查表,给团队当排查手册用
以下是嵌入式信创软件测试过程中比较常见的问题和排查方向,整理成速查表,日常遇到问题可以先对照思路走一遍,比直接求助研发高效得多。
| 现象 | 可能原因 | 建议排查方向 |
|---|---|---|
| 二进制在开发机正常,放到板卡无法执行 | 架构不匹配 | file查看二进制架构,核对板卡 CPU 架构 |
| 启动报找不到共享库 | libc 版本过高或依赖库缺失 | 目标板上执行ldd或readelf -d |
| 进程运行一段时间后崩溃 | 动态库版本漂移、内存踩踏 | 先看 dmesg 有无段错误,对比编译链版本 |
| 系统时间总是 1970 年 | RTC 没配置或电池没电 | date 手动设置,检查内核 RTC 驱动 |
| 中文或日志出现乱码 | 字符集/编码配置不一致 | 检查 locale、JVM file.encoding、应用默认编码 |
| 网络不通但配置正确 | 网卡驱动未加载或设备树错误 | 检查 dmesg、ifconfig -a、驱动模块是否加载 |
| CPU 占用极高但业务简单 | 轮询空转、日志刷屏、死循环 | top + 线程栈排查,留意应用日志频率 |
| 性能远低于预期 | CPU 降频、锁竞争、文件系统慢 | lscpu 看频率,vmstat/iostat 看瓶颈 |
| 掉电后配置丢失 | 写文件非原子、缓存未落盘 | 检查写盘逻辑,强制 fsync 或原子 rename |
| 看门狗生效后外设异常 | 复位后驱动未正确重初始化 | 检查外设初始化流程和复位处理钩子 |
这张表是我们团队在实际项目里不断沉淀出来的,每次遇到新问题解决后,我都会把结论补充回去。做嵌入式信创软件测试,本质上是在跟一个还没完全成熟的生态打交道,经验积累比什么都重要,把踩过的坑固化成团队的速查手册,会让后续项目少走很多弯路。
以我个人经验来看,这类项目中最重要的一件事,不是某个具体工具或某条命令,而是从第一天起就建立起严格的环境记录规范和用例分级习惯。嵌入式信创环境的组合变量太多,好记性远远不如一张结构化的环境基线表,认真记录的每一条信息,都可能在未来某个棘手缺陷的定位过程中成为突破口。测试策略上,宁可多花时间设计异常和边界场景,也别把大部分精力放在“跑通主流程”上,真正体现测试价值的,往往正是那些别人没想到要测的角落。