有没有一种方法可以提醒未使用的函数?

52
我想在代码库中找到未使用的函数 - 包括跨编译单元。 我正在使用gcc作为我的编译器。
这是一个例子: foo.c(假设适当的foo.h):
void foo() {
   ....
}

void bar() {
   ....
}

main.c:

#include <stdio.h>
#include "foo.h"  

int main(void)  {
    bar();
    return 0;
}
在这个例子中,我想被警告未使用foo()
-Wunused-function的gcc选项:

-Wunused-function

每当声明一个静态函数但未定义或未使用非内联静态函数时发出警告。此警告由-Wall启用。

但是它仅适用于静态函数-它不会在上面的示例中产生警告。
我也接受工具/脚本/其他编译器的建议可以为我执行此操作 - 尽管如果可能的话,我更愿意坚持使用gcc

9
默认情况下不警告未使用的非静态函数是有很好的理由的。非静态函数是公共接口的一部分,因为你可以很容易地将gcc生成的.o文件通过链接器生成.a或.so库,在该库中所有这些非静态函数都可以供链接该库的人使用。尽管如此,这仍然是一个很好的问题,我希望能得到一个有用的答案。 - Adam Mihalcin
4
当然,它不应该默认开启。不过,如果存在该功能,我希望能在最终链接时将其打开 :) - Timothy Jones
1
请问 http://gcc.gnu.org/ml/gcc-help/2003-08/msg00072.html 与编程有关吗? - shadyabhi
2
你可以考虑使用代码覆盖率工具 :) - Billy ONeal
1
你可以尝试使用“-fwhole-program”一次编译所有文件,这样或许会给你额外的警告信息。 - Kerrek SB
显示剩余4条评论
6个回答

52

Caolan Mc Namara是一位LibreOffice开发者,他开发了一个小工具来检测LibreOffice源代码中未使用的数千个函数和方法。他的工具是删除它们的关键元素。

这个工具叫做callcatcher,它可以:

收集定义的函数/方法并减去调用/引用的函数/方法

它直接在汇编器输出上工作,因此仅适用于x86和x86_64架构。它可以生成类似的输出。您可以将其与传统的gcc编译和链接调用集成。

Caolan同意将其变成一个gcc插件。


这正好符合我的要求,开箱即用。谢谢! - Timothy Jones
5
太棒了。为了让你更好地理解,我正在工作的项目中有1257个未使用的函数。我一直觉得这个代码库中可能有不少未使用的函数... - Greg Hewgill
1
原始链接已失效,这似乎是参考工具。https://github.com/caolanm/callcatcher - Taekahn
这篇文章中的第一个链接已经失效了,可以在https://web.archive.org/web/20120127161255/http://blogs.linux.ie/caolan/找到存档版本。 - AJM
1
我使用他博客的新网址 http://caolanm.blogspot.com/ 修复了链接。 - Coren

7

我知道你要求警告并且不喜欢使用gcc选项,但这真的很容易。

您可以使用链接器优化(--gc-sections)来从应用程序中删除死代码。

来自ld的手册页:

--gc-sections --no-gc-sections 启用未使用输入段的垃圾回收。在不支持此选项的目标上忽略它。通过在命令行上指定--no-gc-sections可以恢复默认行为(不执行此垃圾回收)。

--print-gc-sections --no-print-gc-sections 列出垃圾收集删除的所有部分。列表打印在stderr上。仅当通过--gc-sections选项启用了垃圾收集时,此选项才有效。通过在命令行上指定--no-print-gc-sections可以恢复默认行为(不列出已删除的部分)。

解释:

--gc-sections 通过检查符号和重定位来决定使用哪些输入节。将保留包含入口符号的节以及包含在命令行上未定义的符号的所有节,以及包含动态对象引用的符号的所有节。请注意,当构建共享库时,链接器必须假定任何可见符号都被引用。确定了此初始节集之后,链接器会递归地标记其重定位引用的任何节为已使用。请参阅--entry和--undefined。

在进行部分链接(使用-r选项启用)时,可以设置此选项。在这种情况下,必须通过--entry或--undefined选项或链接器脚本中的“ENTRY”命令显式指定要保留的符号根。


8
很遗憾,我不打算从二进制文件中删除代码,而是想找出哪些源代码部分可以进行清理。 - Timothy Jones
10
如果我没记错的话,有一个冗长选项可以打印出gc-sections所做的优化列表。我认为它是"--print-gc-sections"。 - eyalm
@eyalm,我认为如果您稍微修改一下并添加print-gc-sections选项及示例,您的答案会获得更多赞。 - Hi-Angel
引用的信息在ld的手册页面,而不是gcc的。 - AJM

6

在你的编译器中添加gcc选项-ffunction-sections,并将--gc-sections--print-gc-sections添加到链接命令中。如果通过gcc调用链接器,请使用-Wl,--gc-sections,--print-gc-sections将它们传递给ld。

这将剥离未使用的对象代码,这是您不需要的。在生成所需输出后,可以删除--gc-sections

虽然您没有询问如何识别未使用的数据,但要执行此操作,请在编译时添加-fdata-sections选项。


4
首先,如果你想让这样的警告在整个程序中生效,你肯定需要使用-flto标志,因为它应该在链接时解决,而不是在编译每个单独单元时解决。但我认为甚至以这种方式,GCC也不会提供这样的警告。

另外,在一般情况下,我认为提供这个功能可能并不明智(例如,链接的libc可能有很多函数你的应用程序不需要)。此外,一个应用程序可能使用dlsym技巧来访问一个看似未被调用的函数...

然而,这是一个潜在的使用GCC插件或MELT扩展的好例子,它将在某个地方注册每个调用发生的位置,并由后续工具找到所有未被调用的函数。(但编写GCC插件或MELT扩展将至少需要几天的时间,因为你需要了解GCC内部)。

你还可以使用分析技术来获取动态未使用(未被调用)的函数。

欢迎通过电子邮件向我提问。


1
+1 谢谢。如果我最终选择使用GCC插件路线,我肯定会给您发送电子邮件 :) - Timothy Jones
我在MELT扩展方面比在C语言的GCC插件方面更有资格帮助你。 - Basile Starynkevitch
@BasileStarynkevitch:也许你会对Caolan Mc Namara在这方面所做的工作感兴趣。请查看我的回答以获取更多详细信息。 - Coren
但是你与MELT有关联,这一点并没有透露出来。"Basile Starynkevitch ... 我的主要工作是在GCC上,特别是它的MELT分支" - Peter Mortensen
gcc-melt.org域名在2018年4月已经丢失。 以及 *GCC MELT文档*。 - Peter Mortensen

4

gprof 是我想到的最简单的解决方案。我使用 -pg 选项编译了您提供的示例程序,这样当我们运行 a.out 时就会得到 gmon.out(稍后可以由 gprof 使用),然后我最终运行了 gprof -z a.out gmon.out | tee output.txt。 在未使用的列表中找到了您的函数 foo!即调用了 0 次。 -z 是应与 gprof 一起使用以跟踪未使用的例程的选项。

感谢线程提供的适当指针!

附注:gprof 还列出了许多其他未使用的库函数,包括您的未使用的函数 foo。 我真的不知道如何过滤它们 :)


1

Eclipse CDT确实具有代码分析功能,您可以设置它来标记未使用的静态函数和未使用的函数声明(以及其他有用的内容)。正如已经提到的,只有链接器才能确定某些(非静态)函数在某个二进制文件中是否被使用...


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