• 正文
  • 相关推荐
申请入驻 产业图谱

RT1170 基于 MCUXpresso IDE 的 gcov 代码覆盖率测试全流程(含可视化实操)

09/29 17:26
1415
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

嵌入式开发中,代码覆盖率是验证测试完整性的核心指标 —— 确保if/switch等分支无遗漏、关键逻辑均被执行,尤其对工业控制汽车电子等高可靠性场景至关重要。MCUXpresso IDE 11.9.0 及以上版本内置 GNU gcov 工具,无需额外安装即可实现代码覆盖率数据收集与可视化。本文以 RT1170-EVK 开发板为例,从软硬件准备到结果解读,分步讲解实操流程,确保开发者可复现并落地覆盖率测试。

1. 核心背景与价值

  • 代码覆盖率的意义:嵌入式项目中,仅通过功能测试无法确认 “未执行代码” 是否存在隐患(如隐藏 bug、逻辑漏洞),覆盖率测试可量化 “代码被执行的比例”,目标是接近 100% 覆盖关键分支。
  • MCUXpresso IDE 的优势:内置 gcov 工具链,无需手动配置环境;支持 semihost 文件 IO(依赖 J-Link 等 debugger),可直接将覆盖率数据写入主机;提供可视化界面,直观区分 “已执行 / 未执行” 代码。
  • 适用场景:RT1170 系列 MCU 的中大型项目,如工业网关、车载控制单元,需通过覆盖率验证测试用例完整性。

2. 软硬件环境准备(必选配置)

gcov 依赖 “semihost 文件 IO” 与特定工具链版本,需严格匹配以下环境,避免流程卡顿:

类别 具体配置 说明
硬件 RT1170-EVK 开发板、J-Link Plus(外部 debugger)、USB-C 线 板载 debugger 可能不支持 semihost,优先用 J-Link Plus;需串联 debugger 与开发板 SWD 接口
软件 MCUXpresso IDE v11.9.0+(Build 2144 及以上) 低版本无内置 gcov 完整支持,需升级
SDK 与 Demo SDK v2.15.000、hello_world_demo_cm7 选择 hello_world 是因代码简单,便于快速验证覆盖率流程
辅助工具 xpack-arm-none-eabi-gcc-12.3.1+ 用于替换 IDE 默认的空libgcov.a库(关键坑点)
串口工具 PuTTY(可选) 用于查看 demo 运行日志,确认程序正常执行

3. 分步实操:从配置到覆盖率收集

3.1 步骤 1:导入 Demo 并验证基础运行

  1. 导入 Demo:打开 MCUXpresso IDE,通过「File → Import → MCUXpresso SDK Examples」,选择 RT1170-EVK → hello_world_demo_cm7,点击 “Finish” 导入项目。
    • 原因:hello_world代码仅含主函数与基础板级初始化,无复杂外设逻辑,适合首次验证 gcov 流程。
  2. 验证编译与调试:选择 “Debug” 配置,点击「Build Project」,编译通过后,连接 J-Link Plus 与开发板,点击「Debug」按钮,确认程序可正常运行(串口打印 “hello world”)。
    • 关键:确保 debugger 识别 RT1170 芯片,若提示 “no target connected”,检查 SWD 接线与 J-Link 驱动版本(需 V7.80+)。

3.2 步骤 2:添加 gcov 支持文件(gcov_support.c/h)

gcov 需底层支持文件实现数据收集与文件写入,文件源自 Erich 的开源项目:

  1. 获取文件:访问Erich 的 Github 仓库,下载gcov_support.c、gcov_support.h、readme.txt。
  2. 添加到项目:在 IDE 的 Project Explorer 中,右键项目 → 「New → Folder」,命名为 “gcov”;将下载的 3 个文件复制到该文件夹,右键文件 → 「Properties」,取消勾选 “Exclude resource from build”(确保编译时包含)。

3.3 步骤 3:配置库与 Debug Console(关键:启用 semihost)

gcov 通过semihost 文件 IO将覆盖率数据写入主机,需配置 IDE 使用支持 semihost 的库:

  1. 选择 Newlib (semihost):在项目上右键 → 「Quick Settings」,在 “Set library/header type” 下拉框中选择「Newlib (semihost)」,点击 “Apply”。
    • 原理:Newlib (semihost) 允许 MCU 通过 debugger 访问主机文件系统,而 “nohost” 模式无文件 IO 功能,无法保存*.gcda覆盖率文件。
  2. 设置 Debug Console 为 Semihost:同样在「Quick Settings」中,点击 “Set Debug Console”,选择「Semihost console」,替代默认的 UART console。

3.4 步骤 4:编写测试文件(test.c/h)

为演示覆盖率,创建含分支逻辑的测试代码(验证if-else是否被完全覆盖):

  1. 新建test.h:在 “source” 文件夹下新建头文件,内容如下:
    #ifndef TEST_H_
    #define TEST_H_
    void TEST_Test(void); // 测试函数声明
    #endif /* TEST_H_ */
    
  2. 新建test.c:新建源文件,含简单分支逻辑,模拟需覆盖的代码:
    #include "test.h"
    int var = 5; // 全局变量,控制分支
    
    void TEST_Test(void) {
        if (var < 10) {  // 分支1:var<10时执行
            var++;
        } else {          // 分支2:var≥10时执行(初始值5,默认不执行)
            var = 20;
        }
    }
    

3.5 步骤 5:添加链接脚本(main_text.ldt)

gcov 初始化依赖__init_array_start符号,需手动添加链接脚本:

  1. 新建 linkscripts 文件夹:右键项目 → 「New → Folder」,命名为 “linkscripts”。
  2. 创建main_text.ldt:在该文件夹下新建文件,内容仅需确保符号存在(无需复杂逻辑):
    PROVIDE (__init_array_start = .);
    
  3. 关联链接脚本:右键项目 → 「Properties → C/C++ Build → Tool Settings → MCU Linker → General」,在 “Additional linker script input sections” 中添加${ProjDirPath}/linkscripts/main_text.ldt,点击 “Apply”。

3.6 步骤 6:配置编译与链接选项(gcov 核心依赖)

需添加 gcov 专用编译 / 链接选项,确保生成覆盖率数据文件(*.gcno/*.gcda):

  1. Linker 选项添加-fprofile-arcs:进入「MCU Linker → Miscellaneous」,在 “Other options” 中添加-fprofile-arcs,作用是链接 gcov 库,启用覆盖率数据收集。
  2. 测试文件添加 Debug Flags:右键test.c → 「Properties → C/C++ Build → Tool Settings → MCU C Compiler → Debugging」,在 “Other debugging flags” 中添加:-fprofile-arcs -ftest-coverage
    • 原理:-ftest-coverage生成覆盖率统计信息(*.gcno),-fprofile-arcs记录代码执行路径(运行后生成*.gcda)。

3.7 步骤 7:调整堆与栈大小(避免 semihost 溢出)

semihost 文件 IO 需较大堆(Heap)与栈(Stack),默认配置可能导致内存溢出

  1. 进入堆栈配置:右键项目 → 「Properties → C/C++ Build → Tool Settings → MCU Linker → Heap and Stack placement」。
  2. 设置参数:
    • Heap(堆):Location 设为 “Default Post Data”,Size 设为0x8000(32KB);
    • Stack(栈):Location 设为 “Default End”,Size 设为0x2000(8KB);
    • 原因:文件写入操作需临时缓存数据,堆栈过小会导致程序 HardFault。

3.8 步骤 8:修改main.c(初始化 gcov 与执行测试)

在主函数中添加 gcov 初始化、测试函数调用与覆盖率数据写入:

  1. 添加头文件:在main.c顶部引入 gcov 与测试头文件:
    #include "../gcov/gcov_support.h" // gcov支持头文件
    #include "test.h"               // 测试函数头文件
    
  2. 修改主函数逻辑:在板级初始化后添加 gcov 操作,完整代码如下:
    int main(void) {
        char ch;
        gcov_init(); // 1. 初始化gcov(必须在测试前)
    
        // 板级初始化(默认代码,无需修改)
        BOARD_ConfigMPU();
        BOARD_InitPins();
        BOARD_BootClockRUN();
        BOARD_InitDebugConsole();
    
        PRINTF("hello world.rn");
        TEST_Test(); // 2. 执行测试函数(触发分支逻辑)
    
        // 3. 写入覆盖率数据到主机(GCC版本兼容处理)
    #if __GNUC__ < 11
        __gcov_flush(); // GCC11前用此函数
    #else
        __gcov_dump();  // GCC11及以上用此函数(IDE 11.9.x用此)
    #endif
        gcov_write_files(); // 确保数据写入完成
    
        while (1) { // 主循环(保持程序运行)
            ch = GETCHAR();
            PUTCHAR(ch);
        }
    }
    

3.9 步骤 9:替换libgcov.a(解决 IDE 默认库空实现问题)

MCUXpresso IDE 11.9.x 自带的libgcov.a为空函数(无实际覆盖率功能),需替换为 xpack 工具链的完整版本:

  1. 下载 xpack 工具链:访问xpack-arm-none-eabi-gcc Releases,下载对应版本(如xpack-arm-none-eabi-gcc-12.3.1-1.1-win32-x64)。
  2. 找到完整libgcov.a:解压后进入路径:xpack-arm-none-eabi-gcc-12.3.1-1.1libgccarm-none-eabi12.3.1thumbv7e-m+dphardlibgcov.a
    注意:路径需匹配 RT1170 的架构(v7e-m+dphard,对应 Cortex-M7 的浮点与硬件浮点模式)。
  3. 替换 IDE 的库文件:找到 MCUXpresso IDE 的工具链目录(默认路径):C:NXPMCUXpressoIDE_11.9.1_2170idepluginscom.nxp.mcuxpresso.tools.arm.eabi12.3.1thumbv7e-m+dphard 将原libgcov.a重命名为libgcov_orig.a(备份),将 xpack 的libgcov.a复制到该目录。

3.10 步骤 10:运行调试与收集覆盖率数据

  1. 重新编译项目:右键项目 → 「Clean Project」,再「Build Project」,确保无编译错误(若提示 “undefined reference to __gcov_dump”,检查libgcov.a是否替换正确)。
  2. 启动调试:点击「Debug」按钮,程序运行后,串口打印 “hello world”,此时 gcov 已收集test.c的覆盖率数据,生成test.gcno(编译时)与test.gcda(运行后)。
  3. 中断调试:点击 IDE 的「Terminate」按钮停止调试,覆盖率文件已保存到项目的 “Debug/source” 目录下。

4.覆盖率结果可视化与解读

4.1 打开可视化界面

  1. 找到覆盖率文件:在 Project Explorer 中,展开 “Debug → source”,可看到test.gcda(数据文件)与test.gcno(元信息文件)。
  2. 启动 gcov 视图:右键test.gcda → 「Open With → gcov Coverage Viewer」,在弹出的对话框中选择对应二进制文件(test.o),点击 “OK”。

4.2 结果解读(以test.c为例)

  1. 颜色含义:
    • 绿色:代码已执行(如if (var < 10)、var++);
    • 红色:代码未执行(如else分支的var = 20);
    • 灰色:非执行代码(如注释、声明)。
  2. 覆盖率计算:IDE 会自动统计 “执行行数 / 总有效行数”,附件中test.c的结果为:
    • 总有效行数:5 行(if判断、var++、else、var=20、函数括号);
    • 执行行数:4 行(else分支未执行);
    • 覆盖率:4/5 = 80%。
  3. 优化方向:若需达到 100% 覆盖率,可修改test.c的var初始值为 15,重新运行后else分支会被执行,覆盖率提升至 100%。

5.关键问题与避坑指南

常见问题 根本原因 解决方案
无*.gcda文件生成 1. 未启用 semihost;2. libgcov.a未替换 1. 确认选择 Newlib (semihost);2. 按步骤 9 替换 xpack 的libgcov.a
程序 HardFault(硬 fault) 堆栈过小,semihost 文件 IO 溢出 按步骤 7 将堆设 0x8000、栈设 0x2000
编译报错 “undefined reference to __gcov_dump” libgcov.a未正确链接 检查 Linker 选项是否加-fprofile-arcs,libgcov.a是否替换
可视化无颜色标注 未为test.c添加-fprofile-arcs -ftest-coverage 按步骤 6 为测试文件配置 Debug Flags

通过 MCUXpresso IDE 的内置 gcov 工具,可快速实现 RT1170 项目的代码覆盖率测试,核心是 “正确配置 semihost 环境 + 替换完整 gcov 库 + 添加必要编译选项”。该流程不仅适用于hello_world demo,还可推广到复杂项目 —— 只需为关键文件添加 gcov 编译选项,即可验证测试用例的完整性,最终提升嵌入式软件的可靠性。

《RT1170 使用MCUXpressoIDE内置的gcov实现代码覆盖率测试和可视化》资料获取:https://www.nxpic.org.cn/document/id-18073

相关推荐