1. 测试结果分析基础:嵌入式测试数据采集方法、日志规范与标准化、测试结果分类与优先级划分

各位好,我是老赵。做嵌入式测试十几年了,踩过的坑比写过的代码还多。今天咱们聊聊测试结果分析的基础——说白了,就是怎么把那些乱七八糟的测试数据,变成能帮你定位问题的线索。

很多人觉得测试就是跑个脚本、收个日志。其实不然。数据采集不规范,后面分析全是白费功夫。我见过太多团队,日志格式五花八门,结果出了问题谁也看不懂。嗯,咱们今天就把这事捋清楚。

1.1 嵌入式测试数据采集方法

嵌入式系统的测试数据采集,跟普通软件不一样。你想想看,嵌入式设备往往资源受限,没有硬盘,没有大内存,甚至没有标准输出。怎么把数据弄出来?

我个人习惯把采集方法分成三类:

采集方式 适用场景 典型工具/方法 注意事项
串口/控制台输出 MCU、RTOS系统 UART、printf重定向 波特率匹配、缓冲区溢出
文件系统日志 带存储介质的系统 FATFS、ext4日志文件 Flash磨损、掉电保护
远程网络采集 联网设备、分布式系统 TCP/UDP、MQTT、syslog 网络延迟、丢包重传

核心原则:采集过程不能影响被测系统的实时行为。我曾经在一个电机控制项目里,因为printf占用了太多CPU时间,导致PWM波形失真——测出来的数据全是假的。

具体到代码层面,我建议这样设计采集接口:

/* 轻量级日志采集接口示例 */
typedef enum {
    LOG_LEVEL_ERROR = 0,
    LOG_LEVEL_WARN  = 1,
    LOG_LEVEL_INFO  = 2,
    LOG_LEVEL_DEBUG = 3
} log_level_t;

/* 采集函数:支持等级过滤和时间戳 */
void log_output(log_level_t level, const char *module, 
                const char *fmt, ...) {
    if (level > g_current_log_level) return;
    
    /* 加时间戳,我习惯用系统滴答 */
    uint32_t tick = get_system_tick();
    char buffer[LOG_BUF_SIZE];
    
    va_list args;
    va_start(args, fmt);
    vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);
    
    /* 通过回调输出,方便切换后端 */
    if (g_log_callback) {
        g_log_callback(tick, module, level, buffer);
    }
}

小技巧:回调函数的设计很灵活。调试时指向串口,量产时指向环形缓冲区或Flash。我一般还会加一个"紧急模式"——当系统崩溃时,自动把最后128条日志写到保留区。

1.2 日志规范与标准化

日志不规范,队友两行泪。这不是开玩笑。我接手过一个项目,日志里全是"error occurred"、"something wrong"——你根本不知道哪里错了。

标准化日志,我总结了四个要素:

  1. 时间戳:统一用系统滴答或UTC,精度至少到毫秒
  2. 模块标识:明确是哪个子系统、哪个任务
  3. 日志等级:ERROR/WARN/INFO/DEBUG,别自己发明
  4. 上下文数据:关键变量值、状态码、堆栈指针

来看看一个规范的日志格式:

[2025-01-15 14:23:45.678] [CAN_DRV] [ERROR] Frame ID=0x123, 
    DLC=8, CRC=0xA5B6, expected=0xA5B7, retry=3/5
    Context: bus_load=72%, last_error=0x04

为什么这么写?你看,时间、模块、等级、关键数据全有了。出问题后,光看这一行就能知道:CAN总线在发ID为0x123的帧时CRC校验失败,已经重试了3次,总线负载72%。

避坑指南:我曾经在一个项目里,日志时间戳用了本地RTC,结果设备跨时区后时间全乱了。后来统一改成系统上电后的毫秒数,配合启动时的GPS对时,再也没出过问题。

标准化的另一个重点是日志分类。我习惯用模块前缀来区分:

前缀 模块 示例
CAN_ CAN总线驱动 CAN_TX_TIMEOUT
MEM_ 内存管理 MEM_HEAP_OVERFLOW
NET_ 网络协议栈 NET_TCP_RETRANSMIT
APP_ 应用层逻辑 APP_STATE_MACHINE_ERR

1.3 测试结果分类与优先级划分

数据采集回来了,日志也规范了。接下来怎么办?你得给它们分个类,排个优先级。不然几百条日志堆在一起,你根本不知道先看哪个。

我一般把测试结果分成四类:

  • 致命错误(Critical):系统崩溃、看门狗复位、内存越界。优先级最高,必须立即处理。
  • 功能异常(Major):功能不符合需求,但系统还能跑。比如通信超时、数据校验错。
  • 性能问题(Minor):响应慢、资源占用高。不影响功能,但影响体验。
  • 信息提示(Info):正常流程日志、状态切换。用于追溯,不视为问题。

我的经验:优先级划分不是死的。同一个"通信超时",在安全关键系统里可能是Critical,在消费电子里可能只是Minor。一定要结合业务场景来判断。

下面这张图,是我常用的测试结果分类与处理流程:

测试结果分类与优先级处理流程 测试数据采集 日志标准化处理 测试结果分类 Critical 致命 Major 功能异常 Minor 性能/信息 立即停止测试 定位修复 记录问题单 安排修复计划 归档记录 持续监控

你看,数据从采集到分类,再到不同优先级的处理路径,清清楚楚。实际项目中,我还会给每个优先级配一个响应时间要求:

优先级 响应要求 处理方式
Critical 2小时内 立即成立攻关小组,阻断问题扩散
Major 24小时内 录入缺陷管理系统,指定责任人
Minor 下一迭代 记录到技术债务清单,定期评审
Info 不要求 作为分析参考,不强制处理

实战建议:我习惯在测试报告中加一个"优先级矩阵"。横轴是影响范围(单设备/局部/全局),纵轴是严重程度(轻微/中等/致命)。这样分类更客观,不容易被主观情绪带偏。

好了,这一章的内容就这些。数据采集、日志规范、结果分类——这三件事做好了,后面的故障定位才能事半功倍。记住一句话:测试结果分析,七分在准备,三分在分析。准备工作做扎实了,问题自然就藏不住了。