头文件包含静态分析工具?

一位同事最近向我透露,我们的一个源文件在编译期间包含了超过3,400个标题。 我们有超过1,000个翻译单元可以在构建中进行编译,从而导致对标题的巨大性能损失,这些标题肯定不会全部使用。

是否有任何静态分析工具可以揭示这样一片森林中的树木,特别是让我们能够决定我们应该在哪些方面进行削减?

UPDATE

在这里找到了一些关于包含头文件(以及优化其包含的包含保护类型)的成本的一些有趣信息,源于这个问题 。

gcc -w -H 的输出可能很有用(如果你解析它并放入一些计数) -w就可以抑制所有警告,这可能很难处理。

来自gcc文档:

-H

除了其他正常活动之外,还打印所使用的每个头文件的名称。 每个名称都缩进以显示#include堆栈的深度。 即使发现它们无效,也会打印预编译的头文件; 一个无效的预编译头文件打印有...x和一个有效的...!

输出如下:

 . /usr/include/unistd.h .. /usr/include/features.h ... /usr/include/bits/predefs.h ... /usr/include/sys/cdefs.h .... /usr/include/bits/wordsize.h ... /usr/include/gnu/stubs.h .... /usr/include/bits/wordsize.h .... /usr/include/gnu/stubs-64.h .. /usr/include/bits/posix_opt.h .. /usr/include/bits/environments.h ... /usr/include/bits/wordsize.h .. /usr/include/bits/types.h ... /usr/include/bits/wordsize.h ... /usr/include/bits/typesizes.h .. /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h .. /usr/include/bits/confname.h .. /usr/include/getopt.h . /usr/include/stdio.h .. /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h .. /usr/include/libio.h ... /usr/include/_G_config.h .... /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h .... /usr/include/wchar.h ... /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stdarg.h .. /usr/include/bits/stdio_lim.h .. /usr/include/bits/sys_errlist.h Multiple include guards may be useful for: /usr/include/bits/confname.h /usr/include/bits/environments.h /usr/include/bits/predefs.h /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h /usr/include/bits/typesizes.h /usr/include/gnu/stubs-64.h /usr/include/gnu/stubs.h /usr/include/wchar.h 

如果您使用的是gcc / g ++,则-M-MM选项将输出包含您搜索的信息的行。 (前者将包括系统头,而后者则不包括。还有其他变体;请参阅手册。)

 $ gcc -M -c foo.c foo.o: foo.c /usr/include/stdint.h /usr/include/features.h \ /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \ /usr/include/bits/wchar.h 

您需要在开头删除foo.o: foo.c ,但其余部分是文件所依赖的所有头文件的列表,因此编写脚本来收集这些文件并对其进行汇总并不会太难。

当然,这个建议只适用于Unix,只有在没有其他人有更好的想法的情况下才有用。 🙂

一些东西-

  • 使用“仅预处理”来查看预处理器输出。 gcc -E选项,其他编译器也有这个function

  • 使用预编译的头文件。

  • gcc具有-verbose和–trace选项,它们也显示完整的包含树,MSVC具有在高级C ++属性页下找到的/ showIncludes选项

此外, 在Visual Studio中显示C ++文件的#include层次结构

GCC有一个-M标志,它将输出给定源文件的依赖项列表。 您可以使用该信息来确定哪些文件具有最多依赖性,哪些文件最依赖,等等。

有关更多信息,请查看手册页 。 -M有几种变体。

John Lakos的“大规模C ++软件设计”提供了一些工具,可以提取源文件之间的编译时依赖关系。

不幸的是,他们在Addison-Wesley网站上的存储库已经消失了(连同AW的网站本身),但我在这里发现了一个tarball: http : //prdownloads.sourceforge.net/introspector/LSC-rpkg-0.1.tgz?download

我发现它在几个工作前很有用,它具有自由的优点。

顺便说一句,如果你还没有读过Lakos的书,听起来你的项目会受益。 (目前的版本有点陈旧,但我听说Lakos还有另一本书在2012年出版。)

我个人不知道是否有一个工具会说“删除此文件”。 这真的是一个复杂的问题,取决于很多事情。 看一下包含声明的树肯定会让你疯狂……这会让我发疯,也会毁了我的眼睛。 有更好的方法来减少编译时间。

  1. 取消你的类方法。
  2. 在取消它们之后,重新检查您的include语句并尝试删除它们。 通常有助于删除它们,并重新开始。
  3. 希望尽可能使用前向声明。 如果您在头文件中取消内联方法,则可以执行此操作。
  4. 将大型头文件分解为较小的文件。 如果文件中的类比大多数使用的频率更高,则将其全部放入头文件中。
  5. 实际上,1000个翻译单位并不是很多。 我们有10-20万。 🙂
  6. 如果编译时间太长,请获取Incredibuild。

我听说有一些工具可以做,但我不使用它们。

我创建了一些工具https://sourceforge.net/p/headerfinder可能是有用的。 不幸的是它是“HOME MADE”工具,有以下问题,

  • 在Vb.Net开发
  • 源代码需要编译
  • 非常慢并消耗内存。
  • 没有帮助。

GCC有一个标志(-save-temps),您可以使用该标志保存中间文件。 这包括.ii文件,它们是预处理器的结果(因此在编译之前)。 您可以编写一个脚本来解析它,并确定所包含内容的权重/成本/大小,以及依赖关系树。

我写了一个Python脚本来做这件事(在这里公开: https : //gitlab.com/p_b_omta/gcc-include-analyzer )。