68

您将如何在 C/C++ 代码中进行死代码检测?我有一个相当大的代码库可供使用,至少 10-15% 是死代码。是否有任何基于 Unix 的工具来识别这些区域?有些代码仍然使用大量的预处理器,自动化过程可以处理吗?

4

8 回答 8

30

您可以为此使用代码覆盖率分析工具,并在代码中查找未使用的点。

gcc 工具链的一个流行工具是 gcov,以及图形前端 lcov ( http://ltp.sourceforge.net/coverage/lcov.php )。

如果您使用 gcc,则可以使用 gcov 支持进行编译,该支持由“--coverage”标志启用。接下来,运行您的应用程序或使用此启用 gcov 的构建运行您的测试套件。

基本上 gcc 会在编译期间发出一些额外的文件,并且应用程序也会在运行时发出一些覆盖率数据。您必须收集所有这些(.gcdo 和 .gcda 文件)。我不会在这里详细介绍,但您可能需要设置两个环境变量以理智的方式收集覆盖率数据:GCOV_PREFIX 和 GCOV_PREFIX_STRIP...

运行后,您可以将所有覆盖率数据放在一起,并通过 lcov 工具包运行。合并来自不同测试运行的所有覆盖文件也是可能的,尽管有点涉及。

无论如何,你最终会得到一组漂亮的网页,显示一些覆盖信息,指出没有覆盖的代码片段,因此没有被使用。

当然,您需要仔细检查代码部分是否在任何情况下都没有使用,这在很大程度上取决于您的测试对代码库的执行情况。但至少,这将给出一个关于可能的死代码候选者的想法......

于 2008-10-23T09:38:59.103 回答
17

在 gcc 下使用 -Wunreachable-code 编译它。

我认为版本越新,你会得到更好的结果,但我的印象可能是错误的,因为这是他们一直在积极努力的事情。请注意,这会进行流分析,但我不相信它会告诉您在离开预处理器时已经死掉的“代码”,因为编译器从未解析过它。它也不会检测到例如从未调用过的导出函数,或者恰好是不可能的特殊情况处理代码,因为没有任何东西使用该参数调用函数 - 你需要代​​码覆盖(并运行功能测试,而不是单元测试。单元测试应该是具有 100% 的代码覆盖率,因此就应用程序而言执行“死”的代码路径)。尽管如此,考虑到这些限制,这是一种开始在代码库中查找最完整的 bollixed 例程的简单方法。

此 CERT 公告列出了一些其他用于静态死代码检测的工具

于 2008-10-23T14:50:23.797 回答
5

您的方法取决于可用性(自动化)测试。如果您有一个您信任的测试套件可以涵盖足够数量的功能,则可以使用覆盖率分析,正如之前的答案已经建议的那样。

如果你不是那么幸运,你可能想研究一下源代码分析工具,比如SciTools的“理解”,它可以帮助你使用大量内置的分析报告来分析你的代码。我使用该工具的经验可以追溯到 2 年前,因此我无法为您提供太多详细信息,但我确实记得他们提供了令人印象深刻的支持,以及非常快的错误修复和问题答案的周转时间。

我找到了一个关于静态源代码分析的页面,其中还列出了许多其他工具。

如果这也对您没有足够的帮助,并且您对找出与预处理器相关的死代码特别感兴趣,我建议您发布有关该代码的更多详细信息。例如,如果它主要与 #ifdef 设置的各种组合相关,您可以编写脚本来确定设置(的组合)并找出哪些组合从未真正构建,等等。

于 2008-10-23T14:30:40.450 回答
5

MozillaOpen Office都有自己开发的解决方案。

于 2008-10-23T18:32:30.617 回答
5

仅针对 C 代码并假设整个项目的源代码可用,使用开源工具Frama-C启动分析。任何在 GUI 中显示为红色的程序语句都是死代码。

如果您有“死代码”问题,您可能也有兴趣删除“备用代码”,即执行但对最终结果没有贡献的代码。这要求您提供 I/O 函数的准确建模(您不希望删除看似“备用”但用作 的参数的计算printf)。Frama-C 有一个用于指出多余代码的选项。

于 2009-07-16T20:55:02.220 回答
4

g++ 4.01 -Wunreachable-code 对函数内无法访问的代码发出警告,但不对未使用的函数发出警告。

int foo() { 
    return 21; // point a
}

int bar() {
  int a = 7;
  return a;
  a += 9;  // point b
  return a;
}

int main(int, char **) {
    return bar();
}

g++ 4.01 将发出关于点 b 的警告,但对 foo() (点 a) 只字不提,即使它在此文件中不可访问。尽管令人失望,但这种行为是正确的,因为编译器无法知道函数 foo() 没有在其他编译单元中声明为 extern 并从那里调用;只有链接器可以确定。

于 2009-02-18T21:41:23.343 回答
3

像这样的死代码分析需要对整个项目进行全局分析。您无法通过单独分析翻译单元来获取此信息(好吧,如果它们完全位于单个翻译单元中,您可以检测到死实体,但我认为这不是您真正想要的)。

我们已经使用我们的 DMS Software Reengineering Toolkit 为 Java 代码准确地实现了这一点,通过一次解析所有涉及的编译单元,为所有内容构建符号表并追踪所有引用。没有引用且没有声称是外部 API 项的顶级定义已死。这个工具还会自动去除死代码,最后你可以选择你想要的:死实体的报告,或者那些实体的代码剥离。

DMS 还解析各种方言中的 C++(2014 年 2 月编辑:包括 C++14 的 MS 和 GCC 版本 [2017 年 11 月编辑:现为 C++17])并构建所有必要的符号表。从那时起,追踪死引用就很简单了。DMS 也可用于剥离它们。请参阅http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html

于 2009-07-03T09:54:31.710 回答
1

Bullseye覆盖工具会有所帮助。虽然它不是免费的。

于 2008-10-23T09:23:23.520 回答