【问题标题】:Test embedded code by replacing static symbols at compile time通过在编译时替换静态符号来测试嵌入式代码
【发布时间】:2019-10-23 10:47:49
【问题描述】:

背景

我正在为嵌入式 Cortex M4 TI-RTOS SYS/BIOS 目标构建 C 应用程序,但是这个问题应该适用于将单个二进制文件加载到某个微处理器上的所有嵌入式目标。

我想要什么

我想在目标上做一些原位回归测试,我只是用一些测试函数替换单个函数。例如。 GetAdcMeasurement() 函数将从只读数组返回预定义值,而不是进行实际测量并返回该值。

这当然可以用一堆#ifndefs 来完成,但我宁愿尽可能保持生产代码不变。

我的尝试

我认为实现这一点的一种方法是在链接器阶段有重复的符号定义,然后让链接器优先考虑来自测试套件的定义(给定一些#define)。

我研究过使用 LD_PRELOAD,但这似乎并不适用于这里(因为我只使用静态对象)。

详情

我正在使用 TI Code Composer,在 Sitara AM57xx 平台上使用 TI-RTOS 和 SYS/BIOS,为 M4 远程处理器(表示为 IPU1)进行编译。 这是编译器和链接器的路径

/opt/ti/ccsv7/tools/compiler/ti-cgt-arm_16.9.6.LTS/bin/armcl

【问题讨论】:

标签: c compilation linker arm


【解决方案1】:

一种解决方案可能是为每个模块创建多个 .c 文件,一个是生产代码,一个是测试代码,然后编译和链接这两个文件中的一个。两个 .c 文件中的全局变量和函数签名必须至少相同(至少:符号可能更多但不能更少)。

在前一个的基础上构建的另一种解决方案是拥有两个库,一个包含生产代码,一个包含测试代码,并与两者中的一个链接。你甚至可以链接这两个库,首先是测试版本,因为链接器通常按照符号遇到的顺序来解析它们。

而且,正如您所说,您可以使用一堆 #ifdefs,这样做的好处是只有一个 .c 文件,但会降低代码的可读性。

我不会在函数级别上使用#ifdefs,即只定义 .c 文件的一个函数进行测试,并保持其他函数不变;但是,如有必要,它可能会消失。如果有必要,您可以为每个函数创建一个 .c 文件(两个),但这会否定模块概念。

我认为第一种方法是最干净的。

【讨论】:

  • 解决方案 #1 实际上是在整个 HAL 层被存根时在代码中使用的,但现在我想让所有其他的东西都保持同步,这使得它有点不切实际。我会考虑其他想法,谢谢!
【解决方案2】:

另一种方法(除了 Paul Ogilvie 的方法)是让您的模拟标题也创建一个定义,该定义将在预处理阶段替换原始函数符号。

即如果您的模拟标题如下所示:

// mock.h

#ifdef MOCKING_ENABLED

  adcdata_t GetAdcMeasurement_mocked(void);
  stuff_t   GetSomeStuff_mocked(void);

  #define GetAdcMeasurement GetAdcMeasurement_mocked
  #define GetSomeStuff GetSomeStuff_mocked

#endif

然后,无论何时包含文件,预处理器都会在调用编译器之前替换调用:

#include "mock.h"

void SomeOtherFunc(void)
{
    // preprocessor will change this symbol into 'GetAdcMeasurement_mocked'
    adcdata_t data = GetAdcMeasurement();
}

这种方法可能会使不知情的代码读者感到困惑,因为他们不一定会意识到您完全调用了不同的函数。不过,我发现这种方法对生产代码的影响最小(显然,除了添加包含)。

【讨论】:

  • 我自己也有同样的想法,我认为这是#pragma weak(尚未尝试过)的次佳选择。感谢这两个想法!
  • @vindarmagnus:NP,虽然我认为如果你让弱指令起作用,最好发布你自己的答案并接受它以帮助未来的读者。
  • 当然,我会这样做的。
【解决方案3】:

(这是对cmets中讨论的快速总结,感谢您的回答)

如果函数具有弱属性,则可以重新定义,请参见 https://en.wikipedia.org/wiki/Weak_symbol

在 GCC 上,这将是弱属性,例如

int __attribute__((weak)) power2(int x);

在 armcl 上(如我的问题),这将是 pragma 指令

#pragma weak power2

int power2(int x);

让生产代码包含部分弱函数将允许测试框架替换单个函数。

【讨论】:

  • 仅供参考:这是公认的答案,但我必须等待 2 天。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2017-02-06
相关资源
最近更新 更多