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

时间:2011-07-13 20:48:45

标签: c++ c static-analysis header-files

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

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

更新

发现了一些有趣的信息,包括一个头文件(以及优化其包含的包含警卫的类型)here,来自this question

8 个答案:

答案 0 :(得分:24)

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

来自gcc docs:

  

-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

答案 1 :(得分:3)

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

$ 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,只有在没有其他人有更好的想法的情况下才有用。 : - )

答案 2 :(得分:3)

一些事情 -

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

  • 使用预编译的标题。

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

此外,Displaying the #include hierarchy for a C++ file in Visual Studio

答案 3 :(得分:1)

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

查看man page以获取更多信息。 -M有几种变体。

答案 4 :(得分:1)

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

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

我发现它在几个工作之前很有用,它具有免费的优点。

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:0)

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

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

  • 以Vb.Net开发
  • 源代码需要编译
  • 非常慢,消耗记忆。
  • 没有帮助。

答案 7 :(得分:0)

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

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