【问题标题】:Static vs Non Static functions - Debugging embedded systems context静态与非静态函数 - 调试嵌入式系统上下文
【发布时间】:2012-03-25 19:01:26
【问题描述】:

被下面的问题迷惑了:如何在保持“静态”标签的优势的同时还能现场调试生产代码?

一旦发生意外行为,就不会在客户现场发生,而且只会发生在客户现场。在许多情况下,选择执行调试可以节省大量精力并提供非常快速的响应。这样的调试通常涉及检查函数行为,这将我们带到“静态”定义中。

不能从调试 shell 调试静态函数,例如设置断点或执行它。 另一方面,将所有函数定义为 public 会导致代码结构和优化问题。

我知道诸如编译至少两种不同的构建,一种是静态的,另一种是没有的选项,但这很适合自动化测试,而不是最终发布的最终生产构建。

希望您能提供一些见解,主要是关于您如何解决(如果有的话)这个困境。或者将问题改写为:“什么更重要?

关于“静态”在 C here 中的一个很好的讨论。

【问题讨论】:

  • 你的意思是你不能在声明为static的函数中设置断点?在 C 中,static 函数只是普通函数,但不能从其他“翻译单元”调用。除非它们已经被优化掉,否则没有什么可以阻止它们的远程或本地调试。
  • 您可以定义一个指向静态函数的全局指针,并使用它的值在静态函数上设置断点。您甚至可以将此指针设为volatile,以防止编译器将其作为未使用的对象消除。
  • @JoachimPileborg 据我所知,静态函数不会进入符号表(即使使用编译器标记),因此无法调试(如断点)。
  • @Alex 有趣的方法,这是一个自我管理的符号表。如果我们要对所有源都这样做,我们需要模块(文件)将其功能注册到这样的实体。但据我所知,编译器可能会将这样的静态函数优化为内联,读取静态函数地址安全吗?
  • 它肯定在我刚刚制作的(我承认很简单)测试程序的符号表中。您的目标和主机平台是什么?你的调试器呢?

标签: c debugging static embedded


【解决方案1】:

一些调试器可以调试“静态”函数。但是,有时静态函数会在调用站点进行扩展,这使得调试器的工作变得困难,一些调试器就放弃了。

(调用站点的内联扩展实际上并不是特定于“静态”的属性,只是编译器更有可能这样做,因为他们对函数“了解更多”——具体来说,名称在外部不可见当前翻译单元,因此如果函数的所有调用都内联扩展,则可以完全省略该函数的代码。)

使用宏曾经很常见:

#ifndef STATIC
#  define STATIC static
#endif
...
STATIC void somefunc() { ... }

然后将宏变为“无”以进行调试构建。这工作得很好,但最好找到一个足够智能的调试器来处理静态函数,即使它们是内联扩展的。

【讨论】:

  • 宏解决了内部测试期间的问题,但当问题出现在客户现场且仅在客户现场时,该宏无法很好地解决。有时,放一个调试版本会解决这个问题。
【解决方案2】:

你能在调用静态函数的可公开访问的函数中设置断点吗?然后你至少可以检查输入的参数和返回值。如果问题仅发生在客户站点,并且您的单元测试/集成测试没有显示您期望这些函数接收的输入有问题,那么问题可能是这些函数正在获取输入(或输入序列,如果函数是有状态的)你没有预料到,这意味着实际的问题可能在你正在查看的静态函数之外。

如果您已经怀疑您知道哪个静态函数包含问题,并且您知道您怀疑导致问题的输入类型,您可以在发布版本中放置一个简单的单元测试函数来检查您怀疑的错误.当然,如果此函数直接控制,例如 6 吨起重机,这会变得复杂,在这种情况下,您可能必须编写该函数的两个版本,一个带有用于控制起重机的模型,并在其上运行测试。

最不可能但并非不可能,永远不要排除发布和调试版本之间的编译器不一致。我们都喜欢认为编译器是万无一失的,包括我在内,但事情总会发生。而且我在您对 torek 的回复中看到,为客户提供调试版本有时可以解决问题...

【讨论】:

  • 调试模块的 API 并不总是足够的,尤其是当您已经怀疑私有函数存在问题时。我完全同意您进行所需测试的观点,但是,这并不总是适用于嵌入式环境。很多时候,我们都在谈论单元测试可能无法涵盖的事件的排序和异步处理。
【解决方案3】:

我认为基本问题不是“您是否使用 'static' 发布?”,而是“您是否准确测试您发布的内容?”对于嵌入式代码,如果您在 Debug 版本上进行大部分测试,然后发布使用不同选项编译的 Release 版本,那么您实际上是在向您的客户交付未经测试的代码。当您在靠近硬件的地方运行时,时间或内存访问模式(优化器可以轻松引入)的微小变化可能会导致系统行为发生重大变化。

我目前的策略是发布 Debug 版本,在调试时配置为尽可能多地进行优化。没有静态函数,让调试器尽可能多地看到状态。

是的,我放弃了一些可能的编译器生成的效率,但只要我确保 Debug 版本足够快以满足其要求,这不是问题。回报是我发布的代码与我在整个发布周期中测试的代码完全相同 - 没有优化器产生的意外。

【讨论】:

  • 值得一提的是,“静态”的使用不仅与效率有关,它在很多方面都是面向对象的概念(比如声明和实现适当的分离,因此其他编码人员不会绕过定义的 API /s)。您提出的新问题值得在这里单独讨论,有几个优点和缺点需要考虑。
  • 在封装和可见性方面,将函数从相应的标头中删除几乎与将其声明为静态一样好。命名也有帮助。 “你为什么从 Module2.c 打电话给module1DoSomething_private()?”是在评论中提出的好问题...
猜你喜欢
  • 1970-01-01
  • 2021-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 2014-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多