嵌入式软件测试自动化:Rhapsody与CodeTEST集成配置实战
2026/6/21 19:51:33 网站建设 项目流程

1. 项目概述与核心价值

在嵌入式软件开发的深水区,尤其是那些关乎人身安全的实时系统——比如汽车电子控制单元、医疗设备或工业控制器,代码的可靠性和可预测性不是“加分项”,而是“生命线”。我们这些一线的开发者,每天都在与复杂的时序、有限的内存和严苛的实时性要求搏斗。传统的“写代码-编译-运行-肉眼观察”的调试模式,在动辄数十万行代码、状态机错综复杂的嵌入式世界里,早已力不从心。我们需要更强大的“眼睛”和“尺子”,来透视软件内部的运行逻辑,度量每一行代码的执行质量。

这就是为什么像CodeTESTRhapsody这样的工具组合,会成为我们工具箱里的“黄金搭档”。Rhapsody作为基于UML的可视化建模与代码生成环境,它解决的是“设计”和“实现”之间那道令人头疼的鸿沟。你可以用状态图、类图清晰地描述系统行为,然后让它自动生成框架代码,保证模型与代码的一致性。但生成的代码质量如何?运行效率怎样?有没有“死代码”?内存使用是否合理?这些问题,Rhapsody的模型动画可以给你一部分运行时行为的可视化,但它不是专业的测试工具。

CodeTEST,恰恰是嵌入式软件测试领域的“手术刀”。它通过代码插桩技术,能在不侵入业务逻辑的前提下,精准地收集程序执行时的覆盖率、性能剖面和内存分配数据。想象一下,你运行一遍测试用例,就能立刻看到哪段代码从未被执行(语句覆盖),哪个条件判断的分支没有走到(分支覆盖),甚至哪个函数的执行时间最长(性能分析)。这对于验证高安全完整性等级(如DO-178C、IEC 61508)要求的代码至关重要。

然而,将这两者手动结合起来是个苦差事:先在Rhapsody里生成代码,再手动调用CodeTEST的插桩工具(ctcxx等)处理源代码,修改构建脚本,最后再编译链接。流程繁琐,容易出错,更难以融入持续集成。本文要分享的,正是我们如何通过修改Rhapsody的构建配置,实现**“一键式”的自动化集成**——在Rhapsody生成代码的同时,无缝地完成CodeTEST的插桩,让测试分析成为构建流程的自然延伸。这套方法基于一份古老的Metrowerks应用笔记,但其中的核心思想与配置技巧,在今天追求DevOps和自动化测试的嵌入式开发中,依然极具价值。

2. 集成方案的设计思路与原理拆解

2.1 核心挑战:侵入构建流程

要让CodeTEST分析Rhapsody生成的代码,核心在于插桩。CodeTEST的插桩器(Instrumenter)需要在编译器处理源代码之前,先对代码进行“加工”,插入用于数据采集的探针函数。在常规开发中,我们通常需要修改Makefile或CMakeLists.txt,将编译器命令(如cl.exeg++)替换为CodeTEST的编译器驱动(如ctcxx.exe),由它来协调预处理、插桩和最终编译的流程。

Rhapsody的特殊性在于,它的Makefile是动态生成的。你无法直接修改一个固定的Makefile,因为每次“Generate Code”操作,Rhapsody都会根据当前的模型配置重新生成构建文件。因此,我们的集成策略必须作用于Rhapsody的代码生成模板本身。

2.2 解决方案:修改属性配置文件

Rhapsody通过一系列属性文件(.prp)来控制代码生成的方方面面。其中,factoryC++.prp是出厂默认设置,而siteC++.prp是用于站点级或用户级自定义的覆盖文件。我们的思路就是:在siteC++.prp中,为Rhapsody的“Microsoft”代码生成环境(对应Visual Studio工具链)添加自定义属性,并修改其Makefile模板。

具体来说,我们需要实现三个目标:

  1. 添加控制开关:创建新的属性(如CodeTESTUsage),让用户能在Rhapsody的图形界面中选择是否启用CodeTEST插桩。
  2. 重定向编译器:在Makefile模板中,加入条件判断。当开关打开时,将CPP宏(即C++编译器命令)从默认的cl.exe替换为ctcxx.exe,并附上必要的插桩参数。
  3. 链接运行时库:CodeTEST插桩后的代码需要链接其特定的运行时库(如NT_TargetLibMD.lib),以提供探针函数的实现。我们需要在项目的库文件列表中添加它们。

这种方法的巧妙之处在于非侵入性。我们不需要修改Rhapsody的核心或生成的业务代码,只是通过配置“欺骗”了它的构建系统。一旦配置完成,开发者在Rhapsody中点击“Generate/Make/Run”,后续的插桩、编译、链接、运行乃至数据收集,全部自动完成。

2.3 环境与版本考量

原文档基于一个比较古老的环境(Windows NT/2000, CodeTEST 2.2/3.0, Rhapsody in C++ 3.0.1, Visual Studio 6.0)。虽然具体路径和库文件名可能随版本变化,但集成原理是通用的。在现代环境中(如Windows 10/11, Visual Studio 2019/2022, 新版本Rhapsody),你需要关注以下几点:

  • 工具路径:CodeTEST和Rhapsody的安装路径会变,对应的AMC_HOMEOMROOT需要调整。
  • 库文件:CodeTEST的运行时库名称和位置可能不同。你需要在新版本的CodeTEST安装目录下的lib文件夹中寻找对应的库文件(例如,针对你的编译器和运行时库类型,如MD/MDd)。
  • 环境变量AMC_TARGET的值用于指定目标平台和编译器。你需要根据CodeTEST版本和你的VS工具集查找正确的值。
  • 属性文件语法:Rhapsody属性文件的语法基本稳定,但最好在修改前备份原文件。

核心提示:本文的步骤是一个“配方”。当你使用的工具版本不同时,配料(路径、文件名)可能需要微调,但烹饪的方法(修改prp文件、添加库、设置环境变量)是相同的。关键在于理解每一步的目的,而不是死记硬背命令。

3. 详细配置步骤与实操要点

下面,我将以原文档的流程为骨架,结合现代环境的理解,详细拆解每一步操作,并补充大量原文档未提及的细节和避坑指南。

3.1 第一步:修改站点属性文件(siteC++.prp)

这是整个集成的基石。我们将在siteC++.prp中为Microsoft构建环境注入CodeTEST的支持。

1. 定位与备份文件首先,找到你的Rhapsody安装目录。假设为C:\I-Logix\Rhapsody。属性文件通常位于<Rhapsody安装目录>\Share\Properties下。

  • 找到factoryC++.prpsiteC++.prp
  • 务必先备份原始的siteC++.prp文件。如果它不存在,通常里面只有一个end语句,新建一个即可。

2. 提取并修改Makefile模板

  • 用文本编辑器(如VS Code、Notepad++)打开factoryC++.prp
  • 搜索关键词Metaclass Microsoft。在这个元类(Metaclass)下,找到名为MakeFileContent的属性。它的值是一个巨大的、多行的Makefile模板字符串。
  • 将整个Metaclass Microsoft部分(从Metaclass Microsoft开始,到对应的end结束)复制出来。
  • 打开(或创建)siteC++.prp,在文件开头添加Subject CPP_CG,然后将复制的内容粘贴进去,最后加上end。现在,siteC++.prp的结构如下:
    Subject CPP_CG Metaclass Microsoft ... (所有从factoryC++.prp复制过来的属性,包括MakeFileContent) end end

3. 添加CodeTEST控制属性Metaclass Microsoft的内部,MakeFileContent属性之前,我们需要插入三个新的属性来控制CodeTEST。

Subject CPP_CG Metaclass Microsoft Property CodeTESTUsage Enum "No,Yes" "No" Property CodeTESTCoverageLevel Enum "None,Statement Coverage,Decision Coverage,MC/DC" "Statement Coverage" Property CodeTESTCompileSwitches MultiLine "-CTv -CTkeep -CTtag-allocator" Property MakeFileContent MultiLine " ... (原有的Makefile模板内容) " end end
  • CodeTESTUsage: 枚举类型,Yes/No。这是总开关,将在Rhapsody GUI中显示为一个下拉框。
  • CodeTESTCoverageLevel: 枚举类型,定义覆盖率等级。Statement Coverage(语句覆盖)、Decision Coverage(分支覆盖)、MC/DC(修正条件/判定覆盖)是常见的安全关键标准要求。None用于仅做内存或性能分析。
  • CodeTESTCompileSwitches: 多行字符串,用于传递额外的CodeTEST编译驱动开关。这里预设了-CTv(详细输出)、-CTkeep(保留中间文件,便于调试)、-CTtag-allocator(启用内存分配插桩)。

4. 修改Makefile模板以重定向编译器这是最关键的一步。我们需要在Makefile模板中,根据CodeTESTUsage属性的值,动态地改变CPP宏(编译器变量)的定义。 在Makefile模板中,找到定义或设置CPP变量的地方。通常,在模板靠前的位置,会有关于调试/发布模式的判断。我们在这些判断之后、编译器宏被最终使用之前,插入我们的条件块。

在原文档的模板中,找到类似############# Target type (Debug/Release) ##################的注释部分之后。插入如下代码:

!IF \"$(CodeTESTUsage)\" == \"Yes\" !IF \"$(CodeTESTCoverageLevel)\" == \"Statement Coverage\" CPP=ctcxx.exe $(CodeTESTCompileSwitches) -CTtag-level=SC !ELSEIF \"$(CodeTESTCoverageLevel)\" == \"Decision Coverage\" CPP=ctcxx.exe $(CodeTESTCompileSwitches) -CTtag-level=DC !ELSEIF \"$(CodeTESTCoverageLevel)\" == \"MC/DC\" CPP=ctcxx.exe $(CodeTESTCompileSwitches) -CTtag-level=MCDC !ELSE CPP=ctcxx.exe $(CodeTESTCompileSwitches) !ENDIF !ENDIF

这段NMAKE语法的意思是:如果用户选择了启用CodeTEST,那么CPP变量就不再是cl.exe,而是ctcxx.exe,并且会根据选择的覆盖率等级附加-CTtag-level参数。ctcxx.exe是CodeTEST的C++编译器驱动,它会内部调用微软的cl.exe,但在此之前会先进行插桩。

重要提示ctcxx.exe必须在系统的PATH环境变量中,或者你需要使用绝对路径(如$(AMC_HOME)\bin\ctcxx.exe)。建议将CodeTEST的bin目录添加到系统PATH。

5. 保存并验证保存siteC++.prp文件。此时,Rhapsody的Microsoft构建环境就已经具备了CodeTEST的配置能力。重启Rhapsody以使新的站点属性生效。

3.2 第二步:在Rhapsody项目中应用配置

我们以Rhapsody自带的Dishwasher示例项目来演示。

1. 创建专用的构建配置

  • 打开Rhapsody和Dishwasher项目。
  • 在“Components”标签页,找到“Configurations”文件夹。右键点击,选择“Add Configuration”,命名为CodeTESTHost为什么创建新配置?这是最佳实践。你可以保留一个干净的“Debug”配置用于日常开发,而CodeTESTHost配置专门用于插桩测试,互不干扰。
  • 在右侧“Settings”标签页,确保新配置的“Environment”设置为“Microsoft”。

2. 启用CodeTEST插桩

  • 在“Components”标签页,右键点击CodeTESTHost配置,选择“Properties”。
  • 这会打开属性编辑器。导航到CPP_CG::Microsoft属性组。你会发现我们刚才在siteC++.prp中添加的三个新属性(CodeTESTUsage,CodeTESTCoverageLevel,CodeTESTCompileSwitches)已经出现在这里了!
  • CodeTESTUsage设置为Yes
  • 根据需要设置CodeTESTCoverageLevel(例如Statement Coverage)。
  • CodeTESTCompileSwitches可以保留默认值,或根据需求调整(例如,如果你不需要内存分析,可以移除-CTtag-allocator)。

3. 添加CodeTEST运行时库插桩后的代码需要链接CodeTEST的运行时库才能正确运行。

  • CodeTESTHost配置的属性窗口中,找到“Libraries”或“Linker”相关的设置(通常在“Compile”或“Link”标签页下)。在Rhapsody中,可能是一个名为“Libraries”的文本框。
  • 你需要添加两个库:
    1. 主运行时库:提供覆盖率、跟踪等基础功能。路径类似$(AMC_HOME)\lib\swinckt\lib\NT_TargetLibMD.lib。注意MD表示链接到多线程DLL运行时库(/MD),如果你的项目使用/MT(静态链接),则需要对应的MT版本库。
    2. 内存分析库(如果启用了-CTtag-allocator):路径类似$(AMC_HOME)\lib\rtos\winnt4\mvc_Release\ct_MemMD.obj。这是一个目标文件,直接参与链接。
  • 将这两个库的完整路径添加到“Libraries”列表中,用分号分隔。例如:
    C:\AMC\CodeTEST\lib\swinckt\lib\NT_TargetLibMD.lib;C:\AMC\CodeTEST\lib\rtos\winnt4\mvc_Release\ct_MemMD.obj

    避坑指南:库的路径和名称必须完全正确,且与你的编译选项(Debug/Release, /MD/MT)匹配。链接错误大多源于此。如果不确定,去CodeTEST安装目录的lib子目录下仔细查找。

4. 配置项目初始化(针对示例项目)对于Dishwasher这样的示例,为了能直接运行一个可执行程序进行测试,需要确保Rhapsody不为主角(Actor)生成代码(因为它们通常只是测试驱动),并创建一个根对象实例。

  • CodeTESTHost配置的属性中,找到“Initialization”或“Execution”标签页。
  • 取消勾选“Generate Code for Actors”。
  • 在“Initial Instances”列表中,确保Dishwasher类被选中,这样Rhapsody会在main函数中自动创建它的一个实例。

5. 设置活动配置并生成代码

  • 右键点击CodeTESTHost配置,选择“Set as Active Configuration”。
  • 现在,当你点击“Code -> Generate/Make/Run”时,Rhapsody将使用我们修改后的Makefile模板。在输出窗口中,你应该能看到编译器命令不再是单纯的cl,而是ctcxx,并且后面跟着一堆-CT开头的参数。这表明插桩正在工作。

3.3 第三步:设置环境与执行测试

1. 设置AMC_TARGET环境变量CodeTEST编译器驱动需要知道目标平台和编译器的详细信息,这是通过AMC_TARGET环境变量实现的。

  • 打开Windows系统属性 -> 高级 -> 环境变量。
  • 在“系统变量”中,新建或编辑AMC_TARGET变量。
  • 其值是一个字符串,标识目标。对于使用Visual Studio编译器的Windows Native目标,典型值可能是mvcMD-x86-winnt4-native(针对VS6/2003)或类似vc14-x86-win10-native(针对VS2015+)。你必须查阅你所使用的CodeTEST版本的文档,找到正确的AMC_TARGET值。
  • 关键一步:设置或修改环境变量后,必须重启Rhapsody(以及任何已经打开的Command Prompt),新的环境变量才会生效。

2. 编译、运行与数据收集

  • 在Rhapsody中执行“Generate/Make/Run”。如果一切配置正确,项目将成功编译、链接并运行。
  • 程序运行期间,CodeTEST的插桩代码已经在后台收集数据(覆盖率、内存等)。
  • 启动CodeTEST Host Analysis Tool(对应你的版本,如CodeTEST 3.0的独立GUI或集成环境)。
  • 在CodeTEST工具中:
    • 配置数据源:选择“Native”数据源类型。
    • 设置IDB文件:指向Rhapsody生成目录下的codetest.idb文件。这个文件由插桩过程生成,包含了源代码与插桩点的映射信息,是CodeTEST进行分析的“地图”。
    • 添加源码目录:添加你的项目生成代码的目录(如Dishwasher\EXE\CodeTESTHost),以便CodeTEST能关联源码显示。
    • 配置许可证和采集选项:选择你已授权的分析类型(覆盖率、内存等)。
  • 点击“开始”或“连接”按钮,CodeTEST会从正在运行(或已运行结束)的程序中读取数据,并在GUI中展示详细的报告视图,如函数覆盖率列表、源代码着色覆盖视图、内存分配图表等。

4. 高级主题、疑难排查与经验分享

4.1 与Rhapsody动画(Animation)的协同与干扰

Rhapsody的动画功能本身也会向生成的代码中插入宏(如NOTIFY_REACTIVE_CONSTRUCTOR),用于在Rhapsody环境中可视化对象和状态机的活动。这带来了一个重要的技术细节,也是原文档特别指出的:

当同时启用CodeTEST插桩和Rhapsody动画时,CodeTEST看到的代码是预处理之后、宏展开之前的源代码。而动画宏在预处理阶段会被展开成包含条件判断的多个语句。因此,CodeTEST的覆盖率报告(尤其是函数覆盖率视图)可能会出现“偏差”。例如,一个简单的构造函数在源文件中只有3行,但CodeTEST报告其覆盖了11行中的75%,这是因为它将展开后的动画宏代码也计入了统计。

如何应对?

  1. 理解现象,不困惑:知道这是正常现象,并非工具错误。CodeTEST的“Coverage Report”(覆盖率报告)功能通常会提供更准确、基于原始源码的覆盖率数据。
  2. 测试目的分离
    • 功能与交互调试:使用启用了动画的配置。此时CodeTEST数据可作为辅助参考,但不要过分纠结于覆盖率的绝对数值。
    • 纯粹的覆盖率/性能测试使用“None”或“Tracing”级别的Rhapsody Instrumentation。这会生成“干净”的、没有动画宏的代码,此时CodeTEST收集的数据最能真实反映你的测试用例对业务逻辑的覆盖情况。
  3. 配置管理:为“带动画测试”和“纯插桩测试”创建不同的Rhapsody构建配置,并在属性中明确设置不同的Instrumentation级别。

4.2 常见问题与排查清单

在实际操作中,你可能会遇到以下问题。这里提供一个排查思路:

问题现象可能原因排查步骤
编译错误:找不到ctcxx.exe1. CodeTESTbin目录未加入系统PATH。
2. 在siteC++.prp中使用了相对路径或错误路径。
1. 在命令行中直接输入ctcxx,看是否能识别。
2. 在siteC++.prp的Makefile模板中使用$(AMC_HOME)\bin\ctcxx.exe的绝对路径格式,并确保AMC_HOME环境变量已定义。
链接错误:无法解析的外部符号_CT_...1. 未添加或路径错误地添加了CodeTEST运行时库。
2. 库的版本(MD/MT, Debug/Release)与项目设置不匹配。
1. 仔细检查“Libraries”属性中的路径,确保文件存在。
2. 核对项目属性中的“Runtime Library”设置(/MD, /MT等),去CodeTEST的lib目录下寻找对应版本的库文件。
程序运行崩溃或CodeTEST无法连接1.AMC_TARGET环境变量设置错误或未生效。
2. 程序链接了错误的运行时库(Debug链接了Release库)。
3. 代码插桩与某些特定的编译器优化或代码结构冲突。
1. 重启所有相关软件(Rhapsody, IDE, 命令行),检查AMC_TARGET值。
2. 确保项目配置(Debug/Release)与链接的CodeTEST库版本一致。
3. 尝试在CodeTESTCompileSwitches中添加-CTno-opt禁用某些优化相关的插桩,或查阅CodeTEST手册关于兼容性的章节。
覆盖率数据为0或明显不正确1. CodeTEST Host工具未正确配置IDB文件或源码路径。
2. 程序未实际执行到插桩代码(例如,测试用例未触发相关逻辑)。
3. 数据未成功上传(Native模式通常是内存共享,确保工具在程序运行后连接)。
1. 在CodeTEST工具中,确认IDB文件路径正确,并且源码目录包含了你正在查看的源文件。
2. 设计更充分的测试用例。
3. 对于Native分析,确保程序启动后,再在CodeTEST工具中点击“开始收集”或“连接”。
构建速度显著变慢CodeTEST插桩会增加额外的编译步骤,并生成更大的中间文件和最终可执行文件。这是正常代价。仅在需要收集数据的测试构建中使用CodeTEST配置。日常开发使用不插桩的配置。

4.3 性能与内存开销的考量

插桩技术不可避免地会引入开销:

  • 空间开销:插桩会增加代码体积(通常增加20%-50%),因为插入了大量的函数调用和数据记录代码。
  • 时间开销:每个插桩点都是一个函数调用,会降低程序执行速度。对于性能敏感的实时部分,需要评估影响。
  • 内存开销:覆盖率数据和动态内存跟踪需要额外的内存来存储。

应对策略

  • 选择性插桩:CodeTEST通常支持模块级或文件级的插桩控制。对于性能关键的模块,可以考虑不插桩或降低覆盖率等级。
  • 测试专用构建:明确区分“发布构建”(无插桩,全优化)和“测试构建”(带插桩,调试信息)。永远不要将插桩后的版本用于最终发布。
  • 分析数据解读:理解插桩本身带来的额外函数调用(尤其是内存分配钩子)对性能分析结果的影响,在分析时将其剥离。

4.4 集成到自动化流程

对于追求工程效率的团队,可以进一步将此集成自动化:

  1. 命令行构建:Rhapsody支持命令行代码生成和构建(通过rhapsody命令)。可以编写脚本,自动切换到CodeTESTHost配置并执行构建。
  2. 与CI/CD集成:在Jenkins、GitLab CI等平台上,设置一个专用的测试流水线。该流水线拉取代码后,调用Rhapsody命令行生成插桩版本,运行自动化测试套件,然后调用CodeTEST命令行工具生成覆盖率报告(如XML格式),并与SonarQube等质量平台集成。
  3. 基线管理:将配置好的siteC++.prp文件纳入版本控制,作为团队共享的开发环境配置的一部分。

通过将CodeTEST与Rhapsody的集成从手动操作变为自动化流程的一部分,你不仅为单个开发者提供了强大的测试能力,更是为整个团队构建了一道持续的质量防护网。每一次代码提交,都能自动获得一份客观的覆盖率与性能报告,让软件质量的提升变得可衡量、可追踪。这,正是现代嵌入式工程实践所追求的目标。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询