如何识别大型C项目中的"死c文件"?

K.M*_*ier 1 c linker gcc makefile gnu-make

注意:在将此标记为重复之前,请阅读整个问题,
尤其是第5章:-)

1.用例

假设你有一个大型的C-项目与众多.h.c文件.但大多数文件从未使用过.这通常是嵌入式软件项目的情况.微控制器的每个外围设备的驱动程序都存在于项目的某个位置,但您通常只使用其中的一些.如果 - 例如 - 我使用usb驱动程序和uart驱动程序,我将导入我的相应.h文件main.c:

#include "stm32f7xx_usb.h"
#include "stm32f7xx_uart.h"
Run Code Online (Sandbox Code Playgroud)

好的......我过度简化了用例,但你明白了.

 

2.死文件

让我们就死文件的以下快速和脏定义达成一致:

.c文件是永远不会到达函数或数据的文件,因为.h不导入相应的文件.

我知道,这是一个非常不洁的定义.文件.c.h文件之间并不总是存在一对一的关系.所以也许我应该说:

.c,如果没有它的功能/数据通过一个或多个进口暴露的文件已经死了.h的文件.

通常在嵌入式软件中,我们将这些import语句包含在一个#ifdef块中:

#ifdef USE_USB
    #include "stm32f7xx_usb.h"
#endif
Run Code Online (Sandbox Code Playgroud)

这意味着状态"dead"或"alive"可以在编译时更改,具体取决于编译时注入的预处理器宏.因此,让我们假设在编译时注入的所有预处理器宏都是已知的.它们可以简单地在makefile中找到.

 

3.最终目标:
    在编译之前区分死文件

有时其中一个死文件有错误.当错误冻结整个编译过程时非常令人沮丧 - 特别是因为无论如何都不会使用该文件.在编译之前知道哪些文件已经死了,可能非常有用.
但我真的不知道可以使用什么工具.如何.

PS:当然,无论如何都应该运行某种预编译器或解析器.但那没关系.

 

4.不太有利的目标:
    编译后区分死文件

我想我越来越接近这个目标了,但我还没有.我会告诉我现在的位置.

传递参数-ffunction-sections-fdata-sectionsgcc编译器会在生成的目标文件中产生干净分离的函数和数据块.
接下来,我传递参数-Wl,--gc-sections-Wl,--print-gc-sections海湾合作委员会链接.链接器启动所有不可缓存的数据和函数,从而产生更小的二进制文件.此外,链接器打印出他扔掉的东西:

...
ld.exe: Removing unused section '.text.HAL_UART_Receive_IT' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.HAL_UART_Transmit_DMA' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.HAL_UART_Receive_DMA' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.HAL_UART_DMAPause' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.HAL_UART_DMAResume' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.HAL_UART_DMAStop' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.HAL_UART_IRQHandler' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
ld.exe: Removing unused section '.text.UART_DMATransmitCplt' in file 'build\Drivers\..\stm32f7xx_hal_uart.o'
...
Run Code Online (Sandbox Code Playgroud)

这很好,但它并没有告诉我整个文件是否已经死了.也许文件的一小部分仍然在某处使用 - 可能是单个函数 - 这样文件应该被认为是"活着的".
出于这个原因,我不能同意@Mike Kinghan在这篇文章中的答案: gcc链接器获取未使用对象的列表

我听说可以在output.map文件中找到文件的状态(死或活).我打开了output.map文件,但很快就放弃了阅读.它是30.342行!如果你认为它可以在output.map文件中找到,请告诉我在哪里看:-).

 

5.为什么我需要知道死文件

确切的原因将导致我们走得太远.它只会分散注意力.只是假设它很重要;-)

 

6.为什么这不是一个重复的问题

我知道StackOverflow上有几个与C程序中"死代码"检测相关的问题.但是,我对死代码不感兴趣.我对死文件很感兴趣.当然这些都是相关的主题,但它们并不重复.
毕竟,活着的文件仍然可以包含大量死代码.只要通过导入的.h文件至少暴露了一个函数或变量,我就会认为该文件是"活着的".

即使是关于未使用的目标文件(gcc链接器获取未使用对象列表)的问题也不重复.我的最终目标是在编译之前检测文件的状态(死或活).  
 


非常感谢@cleblanc,@ zwol和@ user2162550帮助我!我更专注于@zwol的答案,因为它完全避免了编译.我将在下面展示我得到的东西.

>首先提出解决方案

这个解决方案来自@zwol.我在终端发出以下命令:

arm-none-eabi-gcc -H -fsyntax-only main.c
    -IX:\source\Inc
    -IX:\source\Drivers\CMSIS\RTOS\Template
    -IX:\source\Drivers\STM32F7xx_HAL_Driver\Inc
    -IX:\source\Drivers\CMSIS\Include
    -IX:\source\Drivers\STM32F7xx_HAL_Driver\Inc\Legacy
    -IX:\source\Drivers\CMSIS\Device\ST\STM32F7xx\Include
Run Code Online (Sandbox Code Playgroud)

此命令应该在一行上,但为了清楚起见,我插入了一些换行符.

我还尝试将以下预处理器宏添加到命令中:

    -DSTM32F746xx
    -DARM_MATH_CM7
    -D__weak="__attribute__((weak))"
    -D__packed="__attribute__((__packed__))"
    -DUSE_HAL_DRIVER
Run Code Online (Sandbox Code Playgroud)

但它会导致命令失败.所以我别无选择,只能将这些宏直接插入到main.c顶部的文件中:

#define STM32F746xx
#define ARM_MATH_CM7
#define __weak __attribute__((weak))
#define __packed __attribute__((__packed__)
#define USE_HAL_DRIVER
Run Code Online (Sandbox Code Playgroud)

 
现在该命令有效.

我得到了很多输出.请注意,X:\实际上是我的硬盘上的C项目根文件夹的位置.X:\为清楚起见,我已经替换了根文件夹路径.

. X:\source\Inc/main.h
. X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal.h
.. X:\source\Inc/stm32f7xx_hal_conf.h
... X:\source\Inc/main.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_rcc.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_def.h
..... X:\source\Drivers\CMSIS\Device\ST\STM32F7xx\Include/stm32f7xx.h
...... X:\source\Drivers\CMSIS\Device\ST\STM32F7xx\Include/stm32f746xx.h
....... X:\source\Drivers\CMSIS\Include/core_cm7.h
........ c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stdint.h
......... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\stdint.h
.......... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\_default_types.h
........... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\features.h
............ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\_newlib_version.h
.......... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_intsup.h
.......... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_stdint.h
........ X:\source\Drivers\CMSIS\Include/core_cmInstr.h
......... X:\source\Drivers\CMSIS\Include/cmsis_gcc.h
........ X:\source\Drivers\CMSIS\Include/core_cmFunc.h
........ X:\source\Drivers\CMSIS\Include/core_cmSimd.h
....... X:\source\Drivers\CMSIS\Device\ST\STM32F7xx\Include/system_stm32f7xx.h
...... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal.h
..... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/Legacy/stm32_hal_legacy.h
..... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\stdio.h
...... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\_ansi.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\newlib.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\config.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\ieeefp.h
...... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\cdefs.h
....... c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stddef.h
...... c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stddef.h
...... c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stdarg.h
...... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\reent.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\_ansi.h
....... c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stddef.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_types.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\_types.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\lock.h
........ c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stddef.h
...... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\types.h
....... c:\gnu_arm_embedded_toolchain\lib\gcc\arm-none-eabi\6.3.1\include\stddef.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\endian.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\_endian.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\select.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_sigset.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_timeval.h
........ c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\timespec.h
......... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_timespec.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\_pthreadtypes.h
....... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\types.h
...... c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\sys\stdio.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_rcc_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_gpio.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_gpio_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_dma.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_dma_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_cortex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_eth.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_flash.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_flash_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_i2c.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_i2c_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_pwr.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_pwr_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_uart.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_uart_ex.h
... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_pcd.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_ll_usb.h
.... X:\source\Drivers\STM32F7xx_HAL_Driver\Inc/stm32f7xx_hal_pcd_ex.h

Multiple include guards may be useful for:
X:\source\Drivers\CMSIS\Include/core_cm7.h
X:\source\Drivers\CMSIS\Include/core_cmFunc.h
X:\source\Drivers\CMSIS\Include/core_cmInstr.h
X:\source\Drivers\CMSIS\Include/core_cmSimd.h
c:\gnu_arm_embedded_toolchain\arm-none-eabi\include\machine\_endian.h
Run Code Online (Sandbox Code Playgroud)

我需要更多的时间来围绕这个结果.但它肯定感觉更接近我最终需要的东西.谢谢@zwol!

PS:如果你知道为什么我不能将预处理器宏注入命令,请告诉我;-)

 
 
 

cle*_*anc 6

如果您尝试将参数-ffunction-sections和-fdata-sections传递给gcc编译器,则在使用任何源或数据时,将强制链接器链接整个对象.然后将参数-Wl, - gc-sections和-Wl, - print-gc-sections传递给gcc链接器,链接器应该告诉您哪些对象文件完全未使用.