【问题标题】:How to avoid that gcc generates extra code after compiling?如何避免 gcc 在编译后生成额外的代码?
【发布时间】:2019-05-24 09:22:05
【问题描述】:

我正在尝试使用“gcovr”获取代码覆盖率。我使用-O0 作为优化级别。对于线路覆盖,我得到了我需要的结果,但不幸的是分支覆盖没有。根据以下answer,gcc 在编译时会产生额外的代码,这会影响程序中的分支数。有没有办法告诉gcc不要生成额外的代码,或者至少生成尽可能少的代码?

根据提到的答案,使用-O1 有助于生成更少的额外代码,但它并没有给我带来任何好处。相反,我得到了更多的行覆盖(这是错误的)和一个分支更少覆盖。

【问题讨论】:

  • 切勿在生产代码中使用-O0-O1,它们仅用于调试。 -O2-O3 要好得多,并且会产生很多的不同。做一个基准测试,看看他们带来的优势

标签: gcc code-coverage gcov gcovr


【解决方案1】:

GCC 的分支覆盖率统计取决于机器代码级别的分支,而不仅仅是源代码中明确的分支(if/else、switch/case、for、while、||、&&、?:、...)。编译器通常必须插入额外的分支才能实现语言特性,尤其是在 C++ 中:

  • 静态初始化(C 和 C++)
  • 析构函数
  • 异常处理
  • 安全检查,例如绑定检查(不常用)

如果允许 GCC 进行优化,这也许可以消除其中一些分支。这就是为什么使用-O1 有时可以帮助处理覆盖数据。另一方面,这使得 gcov 更难以将覆盖率数据归因于正确的源代码行。

理论上可以采用所有机器码分支。如果发现其中一个分支,则表示未测试的状态转换。从编译器的角度来看,if 语句或调用可以抛出的函数之间没有太大区别。但对您而言,这些并不等同:您可能只对测试显式分支感兴趣。

根据您的质量要求,这是一个合理的位置。如果没有故障注入等先进技术,就不可能彻底测试所有机器代码分支。我个人建议不要忽略编译器插入的分支,因为它给人一种错误的安全感。

因此,我们必须接受 50% 的分支覆盖率仅来自良好的语句覆盖率是免费的,但 100% 的分支覆盖率是无法实现的。在 C++ 的上下文中,最好使用分支覆盖率来查看逐行覆盖率,而不是作为汇总统计数据。

GCC 确实允许您通过使用-fno-exceptions 标志编译软件而不进行异常处理。该进程不会抛出异常,而是直接中止。但这有效地将您切换到不兼容的 C++ 方言。 try/catch 之类的东西将不再起作用;标准库更改中的错误检查。因此,仅出于代码覆盖的目的,您不能毫无例外地编译您的软件。

幸运的是,GCC 标记了为异常处理而添加的分支。自 gcovr 4.2(尚未发布)以来,您可以使用 gcov --exclude-throw-branches 标志来忽略未发现的仅异常分支。这并不完美:它不仅排除了某些异常处理板的隐式分支,而且还分支到了显式的catch 子句。这可能会隐藏您确实想要测试的未覆盖的未覆盖。

Gcovrs 还提供--exclude-unreachable-branches 选项。如果它们归因于似乎不包含有用代码的行,这将删除分支覆盖数据。同样,这可能会排除重要的覆盖率数据,例如,如果多行语句的覆盖率归因于不包含有用代码的行。但是,这通常有助于排除由于静态变量而插入的分支。

【讨论】:

    猜你喜欢
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 1970-01-01
    • 2012-05-23
    相关资源
    最近更新 更多