遗留的C/C++项目中死代码检测

68

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


2
这里有一个更活跃的类似问题:https://dev59.com/vm445IYBdhLWcg3wiayV - Mr Shark
8个回答

30
您可以使用代码覆盖率分析工具来查找代码中未使用的部分。对于gcc工具链,一个流行的工具是gcov,与图形化前端lcov(http://ltp.sourceforge.net/coverage/lcov.php)一起使用。如果您使用gcc,可以使用"--coverage"标志启用gcov支持进行编译。然后,使用此gcov启用的构建运行应用程序或运行测试套件。基本上,gcc将在编译过程中发出一些额外的文件,并且应用程序在运行时还会发出一些覆盖数据。您必须收集所有这些(.gcdo和.gcda文件)。我在这里不会详细介绍,但您可能需要设置两个环境变量以合理地收集覆盖数据:GCOV_PREFIX和GCOV_PREFIX_STRIP... 运行后,您可以将所有覆盖数据放在一起,并通过lcov工具套件运行它们。合并来自不同测试运行的所有覆盖文件也是可能的,尽管有点复杂。无论如何,最终您将获得一组漂亮的网页,显示一些覆盖信息,指出没有覆盖和因此未使用的代码片段。当然,您需要仔细检查代码部分是否在任何情况下都没有被使用,很多事情取决于您的测试如何练习代码库。但至少,这将为可能的死代码候选者提供一个想法...

我仍然被Sun C++编译器困住,但我们正在进行gcc迁移,所以我要尝试一下。谢谢。 - Nazgob
1
代码覆盖率分析(例如gcov)可以提供数据,指出特定运行软件的哪些代码未被覆盖——未被覆盖的代码不一定是死代码。软件的不同运行方式(例如不同的编译选项、不同的运行时选项或不同的输入数据)或不同的执行路径(例如错误处理)可能会触发之前未被调用的函数。 - Arun

17

使用gcc进行编译,加上-Wunreachable-code参数。

我认为版本越新,结果就越好,但我可能对他们一直在积极开发这个工具的印象有误。请注意,这个工具会进行流程分析,但我不认为它会告诉你关于预处理器离开时就已经无用的代码,因为编译器从未解析过它。它也不会检测例如未被调用的导出函数或只是恰好永远不会调用具有特殊情况处理的代码 - 您需要使用代码覆盖率(而非单元测试)来完成这些任务。单元测试应该有100%的代码覆盖率,并且执行应用程序所涉及的“死”代码路径。尽管如此,考虑到这些限制,这仍然是一个简单的方法来开始查找代码库中最完全混乱的例程。

此 CERT 咨询列出了其他静态死代码检测工具


3
由于-Wunreachable-code选项已从gcc中删除,因此此答案不再有效。http://gcc.gnu.org/ml/gcc-help/2011-05/msg00360.html - philippe lhardy
有些时候,即使是“不稳定”的死代码检测也比没有好。除此之外,总体上完美的死代码检测是不可能的(停机问题),所以每个人都知道他们使用的工具是不完美的。或许有人真的关心在-O0下它更不完美而-O3下则不带新警告,或者他们不想要优化器改进时出现新的警告。 - Steve Jessop
4
如果你的代码没有使用新功能,你仍然可以使用旧版本的gcc作为静态分析工具。所以我的答案并不是完全错误的。我知道这有点牵强。 - Steve Jessop

6
仅适用于C代码,且假定整个项目的源代码可用,在使用开源工具Frama-C进行分析。在GUI中显示为红色的程序语句是死代码。
如果您有“死代码”问题,您可能也会对删除“备用代码”感兴趣,即执行但不对最终结果产生贡献的代码。这需要您提供准确的I/O函数建模(您不想删除似乎是“备用”的计算,但它作为printf的参数使用)。Frama-C有一个指出备用代码的选项。

5
您的方法取决于可用的(自动化)测试。如果您有一个测试套件,可以信任其覆盖足够多的功能,您可以使用覆盖分析,就像之前的答案建议的那样。
如果您没有这么幸运,您可能需要查看源代码分析工具,例如SciTools的Understand,该工具可以帮助您使用许多内置的分析报告分析您的代码。我对该工具的经验可以追溯到2年前,因此我无法给您提供更多细节,但我记得他们拥有令人印象深刻的支持,非常快速地修复错误并回答问题。
我发现了一个静态源代码分析页面,列出了许多其他工具。
如果这些方法都不能满足您的需求,而您特别想找出与预处理器相关的死代码,我建议您发布有关代码的更多详细信息。例如,如果它主要涉及各种#ifdef设置的组合,您可以编写脚本来确定(组合)设置,并找出哪些组合实际上从未构建等。

5

现在这两个链接都无法访问。有人可以更新一下吗? - syam
2
我已将第一个链接从博客文章更改为(希望更持久的)文档页面。Open Office链接似乎有效。 - Max Lybbert

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并从那里调用;只有链接器才能确定。


4
这种死代码分析需要对整个项目进行全局分析。您无法通过单独分析翻译单元来获取此信息(好吧,如果它们完全位于单个翻译单元中,则可以检测到死实体,但我认为这不是您真正想要的)。
我们使用DMS软件重构工具包将其用于Java代码,通过一次解析所有涉及的编译单元,为所有内容构建符号表并跟踪所有引用。没有引用且没有声称为外部API项的顶级定义是死代码。该工具还会自动剥离死代码,并在最后让您选择:死实体报告或剥离这些实体的代码。
DMS还以多种方言解析C ++(编辑于2014年2月:包括C ++14的MS和GCC版本[编辑于2017年11月:现在是C ++17]),并构建所有必需的符号表。从那时起,跟踪死引用将变得简单。 DMS也可用于剥离它们。请参见http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html

1

Bullseye 覆盖率工具会很有帮助。不过它并不是免费的。


这个软件值得购买吗?有使用经验吗?他们提供试用版,我可能会试一下,如果好用的话,我们可以购买它 :) - Nazgob
是的,我在塞班平台上使用过它... 它绝对值得购买。 - Ashwin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接