【问题标题】:Header file inclusion static analysis tools?头文件包含静态分析工具?
【发布时间】:2011-10-04 20:30:39
【问题描述】:

一位同事最近向我透露,我们的一个源文件在编译期间包含超过 3,400 个头文件。我们在构建中编译了 1,000 多个翻译单元,这对肯定不会全部使用的标头造成巨大的性能损失。

是否有任何静态分析工具能够揭示这样一个森林中的树木,特别是让我们能够决定我们应该对哪些树木进行修剪?

更新

发现了一些关于包含头文件的成本的有趣信息(以及用于优化其包含的包含保护的类型)here,源自 this question

【问题讨论】:

  • 什么平台? gcc 有可以帮助解决这个问题的选项(如果没有人提出更好的主意)
  • @fbrereto:(几年后...)我正在研究 C 的类型推断,其预期用途之一是删除所有#include,让引擎仅推断实际使用的类型,仍然得到同样精度的分析结果。我正在寻找一篇论文的真实案例。如果您想讨论这个问题,请直接与我联系。引擎有一个在线界面:cuda.dcc.ufmg.br/psyche-c

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


【解决方案1】:

如果您使用 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】:

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

查看man page 了解更多信息。 -M 有几种变体。

【讨论】:

    【解决方案3】:

    gcc -w -H <file> 的输出可能有用(如果您解析它并输入一些计数)-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
    

    【讨论】:

    • devenv/msvs 是否存在类似的东西?
    • @user375251 我不知道,不要用MS工具链做任何事情,查看MSDN
    • @Spudd86 我可以在不使用 -H 选项手动键入每个文件名的情况下获得项目中每个文件的此类包含列表吗?
    • @Alecs 也许你可以用你的Makefile 搞砸,这样你就可以运行一个添加了那个标志的完整构建......或者在你的构建规则中添加第二个命令来运行带有那个标志的 gcc 和将其输出重定向到文件或其他东西。
    【解决方案4】:

    一些事情-

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

    • 使用预编译的头文件。

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

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

    【讨论】:

      【解决方案5】:

      John Lakos 的“Large Scale C++ Software Design”具有提取源文件之间编译时依赖关系的工具。

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

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

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

      【讨论】:

        【解决方案6】:

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

        1. 取消内联您的类方法。
        2. 取消内联后,重新检查您的包含语句并尝试删除它们。通常有助于删除它们并重新开始。
        3. 尽量使用前向声明。如果你在头文件中取消内联方法,你可以这样做。
        4. 将大的头文件分解成更小的文件。如果文件中的某个类比大多数类使用得更多,则将其单独放在头文件中。
        5. 1000 个翻译单位实际上并不多。我们有 10-2 万之间。 :)
        6. 如果您的编译时间仍然太长,请获取 Incredibuild。

        【讨论】:

          【解决方案7】:

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

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

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

          【讨论】:

            【解决方案8】:

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

            我为此编写了一个 Python 脚本(在此处公开:https://gitlab.com/p_b_omta/gcc-include-analyzer)。

            【讨论】:

              猜你喜欢
              • 2012-06-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-05-14
              • 1970-01-01
              • 2011-12-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多