链接库文件体积优化工具篇:bloaty

笔者之前参与过一个嵌入式智能手表项目,曾经碰到过这样一个问题:手表的flash大小只有2M,这意味着只能在上面烧录2M大小的代码。随着开发不断进行,代码越写越多,编译出来的bin也越来越大。最后bin大小超过了2M, 就没法烧写了,很尴尬。最后只能想办法精简代码,当然这是在不影响功能的前提下精简代码。那如何精简代码呢?我们自然会想到先看看哪里的代码最多,比如使用的各个so的大小,so里边哪个源文件最大,源文件里边哪一个函数最耗空间等等,先做一个统计分析,然后再看一下怎么优化。那这个统计如何进行呢?这个就需要用到一些工具。

本文介绍的工具:bloaty就用来干这个活的,这是谷歌公司开源的一个项目,在GitHub上有源码,主要是用来查看可执行文件,链接库内存分布的。Bloaty对二进制文件进行深入分析,使用自定义的ELF、DWARF和Mach-O解析器,旨在将二进制文件的每个字节准确地定位到是属于哪个符号或编译单元。它甚至会反汇编二进制文件,寻找对匿名数据的引用。
下面是一个例子,用bloaty工具来分析bloaty二进制文件,看一下各个编译单元(源文件)所占的内存大小和占总大小的百分比:

./bloaty bloaty -d compileunits
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  34.8%  10.2Mi  43.4%  2.91Mi    [163 Others]
  17.2%  5.08Mi   4.3%   295Ki    third_party/protobuf/src/google/protobuf/descriptor.cc
   7.3%  2.14Mi   2.6%   179Ki    third_party/protobuf/src/google/protobuf/descriptor.pb.cc
   4.6%  1.36Mi   1.1%  78.4Ki    third_party/protobuf/src/google/protobuf/text_format.cc
   3.7%  1.10Mi   4.5%   311Ki    third_party/capstone/arch/ARM/ARMDisassembler.c
   1.3%   399Ki  15.9%  1.07Mi    third_party/capstone/arch/M68K/M68KDisassembler.c
   3.2%   980Ki   1.1%  75.3Ki    third_party/protobuf/src/google/protobuf/generated_message_reflection.cc
   3.2%   965Ki   0.6%  40.7Ki    third_party/protobuf/src/google/protobuf/descriptor_database.cc
   2.8%   854Ki  12.0%   819Ki    third_party/capstone/arch/X86/X86Mapping.c
   2.8%   846Ki   1.0%  66.4Ki    third_party/protobuf/src/google/protobuf/extension_set.cc
   2.7%   800Ki   0.6%  41.2Ki    third_party/protobuf/src/google/protobuf/generated_message_util.cc
   2.3%   709Ki   0.7%  50.7Ki    third_party/protobuf/src/google/protobuf/wire_format.cc
   2.1%   637Ki   1.7%   117Ki    third_party/demumble/third_party/libcxxabi/cxa_demangle.cpp
   1.8%   549Ki   1.7%   114Ki    src/bloaty.cc
   1.7%   503Ki   0.7%  48.1Ki    third_party/protobuf/src/google/protobuf/repeated_field.cc
   1.6%   469Ki   6.2%   427Ki    third_party/capstone/arch/X86/X86DisassemblerDecoder.c
   1.4%   434Ki   0.2%  15.9Ki    third_party/protobuf/src/google/protobuf/message.cc
   1.4%   422Ki   0.3%  23.4Ki    third_party/re2/re2/dfa.cc
   1.3%   407Ki   0.4%  24.9Ki    third_party/re2/re2/regexp.cc
   1.3%   407Ki   0.4%  29.9Ki    third_party/protobuf/src/google/protobuf/map_field.cc
   1.3%   397Ki   0.4%  24.8Ki    third_party/re2/re2/re2.cc
 100.0%  29.5Mi 100.0%  6.69Mi    TOTAL

Bloaty支持许多功能:

  1. 文件格式:ELF、Mach-O、PE/COFF(实验)、WebAssembly(实验)
  2. 数据来源:compilenit(如上所示)、符号、节、段等。
  3. 分层解析:将多个数据源合并为一个报告
  4. size diffs:查看二进制文件的增长位置,非常适合CI测试
  5. 单独的调试文件:剥离测试中的二进制文件,同时使调试数据可用于分析
  6. 灵活的解映射:解映射C++符号,可选择丢弃函数/模板参数
  7. 自定义数据源:regex重写内置数据源,用于自定义munging/bucketing
  8. 正则表达式过滤:过滤掉二进制文件中与给定正则表达式匹配或不匹配的部分

使用说明

$ ./bloaty bloaty
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  30.0%  8.85Mi   0.0%       0    .debug_info
  24.7%  7.29Mi   0.0%       0    .debug_loc
  12.8%  3.79Mi   0.0%       0    .debug_str
   9.7%  2.86Mi  42.8%  2.86Mi    .rodata
   6.9%  2.03Mi  30.3%  2.03Mi    .text
   6.3%  1.85Mi   0.0%       0    .debug_line
   4.0%  1.19Mi   0.0%       0    .debug_ranges
   0.0%       0  15.0%  1.01Mi    .bss
   1.6%   473Ki   0.0%       0    .strtab
   1.4%   435Ki   6.3%   435Ki    .data
   0.8%   254Ki   3.7%   254Ki    .eh_frame
   0.8%   231Ki   0.0%       0    .symtab
   0.5%   142Ki   0.0%       0    .debug_abbrev
   0.2%  56.8Ki   0.8%  56.8Ki    .gcc_except_table
   0.1%  41.4Ki   0.6%  41.4Ki    .eh_frame_hdr
   0.0%  11.4Ki   0.1%  9.45Ki    [26 Others]
   0.0%  7.20Ki   0.1%  7.14Ki    .dynstr
   0.0%  6.09Ki   0.1%  6.02Ki    .dynsym
   0.0%  4.89Ki   0.1%  4.83Ki    .rela.plt
   0.0%  4.59Ki   0.0%       0    [Unmapped]
   0.0%  3.30Ki   0.0%  3.23Ki    .plt
 100.0%  29.5Mi 100.0%  6.69Mi    TOTAL

“VM SIZE”列告诉二进制文件加载到内存时将占用多少空间。“文件大小”列告诉二进制文件在磁盘上占用的空间。这两者可能彼此非常不同:

  • 有些数据存在于文件中,但没有加载到内存中,例如调试信息。
  • 某些数据已映射到内存中,但文件中不存在。这主要适用于.bss部分(零初始化数据)。

Bloaty中的默认细分是分段的,但支持许多其他对二进制文件进行切片的方式,如符号和分段。如果使用调试信息进行编译,甚至可以按编译单元和内联进行分解!效果见第一个例子。

Size Diffs

可以使用Bloaty来查看二进制文件的大小是如何变化的。
例如,这里有几个不同版本的Bloaty之间的大小差异,显示了当我添加一些功能时它是如何增长的。

$ ./bloaty bloaty -- oldbloaty
     VM SIZE                     FILE SIZE
 --------------               --------------
  [ = ]       0 .debug_loc     +688Ki  +9.9%
   +19%  +349Ki .text          +349Ki   +19%
  [ = ]       0 .debug_ranges  +180Ki   +11%
  [ = ]       0 .debug_info    +120Ki  +0.9%
   +23% +73.5Ki .rela.dyn     +73.5Ki   +23%
  +3.5% +57.1Ki .rodata       +57.1Ki  +3.5%
 +28e3% +53.9Ki .data         +53.9Ki +28e3%
  [ = ]       0 .debug_line   +40.2Ki  +4.8%
  +2.3% +5.35Ki .eh_frame     +5.35Ki  +2.3%
  -6.0%      -5 [Unmapped]    +2.65Ki  +215%
  +0.5% +1.70Ki .dynstr       +1.70Ki  +0.5%
  [ = ]       0 .symtab       +1.59Ki  +0.9%
  [ = ]       0 .debug_abbrev +1.29Ki  +0.5%
  [ = ]       0 .strtab       +1.26Ki  +0.3%
   +16%    +992 .bss                0  [ = ]
  +0.2%    +642 [13 Others]      +849  +0.2%
  +0.6%    +792 .dynsym          +792  +0.6%
   +16%    +696 .rela.plt        +696   +16%
   +16%    +464 .plt             +464   +16%
  +0.8%    +312 .eh_frame_hdr    +312  +0.8%
  [ = ]       0 .debug_str    -19.6Ki  -0.4%
   +11%  +544Ki TOTAL         +1.52Mi  +4.6%

分层解析

Bloaty支持以多种不同的方式分解二进制文件。您可以将多个数据源组合到一个层次配置文件中。例如,我们可以在单个报告中使用分段和分段数据源:

$ ./bloaty -d segments,sections bloaty
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  80.7%  23.8Mi   0.0%       0    [Unmapped]
    37.2%  8.85Mi   NAN%       0    .debug_info
    30.6%  7.29Mi   NAN%       0    .debug_loc
    15.9%  3.79Mi   NAN%       0    .debug_str
     7.8%  1.85Mi   NAN%       0    .debug_line
     5.0%  1.19Mi   NAN%       0    .debug_ranges
     1.9%   473Ki   NAN%       0    .strtab
     1.0%   231Ki   NAN%       0    .symtab
     0.6%   142Ki   NAN%       0    .debug_abbrev
     0.0%  4.59Ki   NAN%       0    [Unmapped]
     0.0%     392   NAN%       0    .shstrtab
     0.0%     139   NAN%       0    .debug_macinfo
     0.0%      68   NAN%       0    .comment
  10.9%  3.21Mi  47.9%  3.21Mi    LOAD #4 [R]
    89.3%  2.86Mi  89.3%  2.86Mi    .rodata
     7.7%   254Ki   7.7%   254Ki    .eh_frame
     1.7%  56.8Ki   1.7%  56.8Ki    .gcc_except_table
     1.3%  41.4Ki   1.3%  41.4Ki    .eh_frame_hdr
     0.0%       1   0.0%       1    [LOAD #4 [R]]
   6.9%  2.03Mi  30.3%  2.03Mi    LOAD #3 [RX]
    99.8%  2.03Mi  99.8%  2.03Mi    .text
     0.2%  3.23Ki   0.2%  3.23Ki    .plt
     0.0%      28   0.0%      28    [LOAD #3 [RX]]
     0.0%      23   0.0%      23    .init
     0.0%       9   0.0%       9    .fini
   1.5%   439Ki  21.4%  1.44Mi    LOAD #5 [RW]
     0.0%       0  70.1%  1.01Mi    .bss
    99.1%   435Ki  29.6%   435Ki    .data
     0.4%  1.63Ki   0.1%  1.63Ki    .got.plt
     0.3%  1.46Ki   0.1%  1.46Ki    .data.rel.ro
     0.1%     560   0.0%     560    .dynamic
     0.1%     384   0.0%     376    .init_array
     0.0%      32   0.0%      56    [LOAD #5 [RW]]
     0.0%      32   0.0%      32    .got
     0.0%      16   0.0%      16    .tdata
     0.0%       8   0.0%       8    .fini_array
     0.0%       0   0.0%       8    .tbss
   0.1%  23.3Ki   0.3%  23.3Ki    LOAD #2 [R]
    30.7%  7.14Ki  30.7%  7.14Ki    .dynstr
    25.9%  6.02Ki  25.9%  6.02Ki    .dynsym
    20.8%  4.83Ki  20.8%  4.83Ki    .rela.plt
     7.7%  1.78Ki   7.7%  1.78Ki    .hash
     5.0%  1.17Ki   5.0%  1.17Ki    .rela.dyn
     3.1%     741   3.1%     741    [LOAD #2 [R]]
     2.7%     632   2.7%     632    .gnu.hash
     2.2%     514   2.2%     514    .gnu.version
     1.6%     384   1.6%     384    .gnu.version_r
     0.2%      36   0.2%      36    .note.gnu.build-id
     0.1%      32   0.1%      32    .note.ABI-tag
     0.1%      28   0.1%      28    .interp
   0.0%  2.56Ki   0.0%       0    [ELF Headers]
    46.3%  1.19Ki   NAN%       0    [19 Others]
     7.3%     192   NAN%       0    [ELF Headers]
     2.4%      64   NAN%       0    .comment
     2.4%      64   NAN%       0    .data
     2.4%      64   NAN%       0    .data.rel.ro
     2.4%      64   NAN%       0    .debug_abbrev
     2.4%      64   NAN%       0    .debug_info
     2.4%      64   NAN%       0    .debug_line
     2.4%      64   NAN%       0    .debug_loc
     2.4%      64   NAN%       0    .debug_macinfo
     2.4%      64   NAN%       0    .debug_ranges
     2.4%      64   NAN%       0    .debug_str
     2.4%      64   NAN%       0    .dynamic
     2.4%      64   NAN%       0    .dynstr
     2.4%      64   NAN%       0    .dynsym
     2.4%      64   NAN%       0    .eh_frame
     2.4%      64   NAN%       0    .eh_frame_hdr
     2.4%      64   NAN%       0    .fini
     2.4%      64   NAN%       0    .fini_array
     2.4%      64   NAN%       0    .gcc_except_table
     2.4%      64   NAN%       0    .gnu.hash
 100.0%  29.5Mi 100.0%  6.69Mi    TOTAL

Bloaty为每个级别显示最多20行;其他值被分组到[other]bin中。使用-n<num>可覆盖此设置。如果传递-n 0,所有数据都将被输出,而不会将任何内容折叠到[Other]中

调试剥离的二进制文件

Bloaty支持从单独的二进制文件中读取调试信息/符号。这使您可以对剥离的二进制文件进行配置,即使是对于像“compilenits”或“symbol”这样需要这些额外信息的数据源也是如此。
Bloaty使用构建ID来验证二进制文件和调试文件是否匹配。否则,结果将是无稽之谈(这种不匹配听起来可能不太可能,但这是一个很容易犯的错误)。
如果您的二进制文件有一个生成ID,那么使用单独的调试文件非常简单,如下所示:

$ cp bloaty bloaty.stripped
$ strip bloaty.stripped
$ ./bloaty -d symbols --debug-file=bloaty bloaty.stripped

数据源

Bloaty有许多内置的数据源。这些都提供了不同的方法来查看二进制文件。您还可以通过将正则表达式应用于内置数据源来创建自己的数据源(请参阅下面的“自定义数据源”)。
虽然Bloaty处理二进制文件、共享对象、对象文件和静态库(.a文件),但有些数据源不处理对象文件。这尤其适用于读取调试信息的数据源。

Segments段

段是运行时加载程序用来确定二进制文件的哪些部分需要加载/映射到内存中的内容。通常只有几个部分:每组mmap()权限需要一个:

$ ./bloaty -d segments bloaty
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  80.7%  23.8Mi   0.0%       0    [Unmapped]
  10.9%  3.21Mi  47.9%  3.21Mi    LOAD #4 [R]
   6.9%  2.03Mi  30.3%  2.03Mi    LOAD #3 [RX]
   1.5%   439Ki  21.4%  1.44Mi    LOAD #5 [RW]
   0.1%  23.3Ki   0.3%  23.3Ki    LOAD #2 [R]
   0.0%  2.56Ki   0.0%       0    [ELF Headers]
 100.0%  29.5Mi 100.0%  6.69Mi    TOTAL

在这里,我们看到一个段被映射[RX](读/执行)和一个段映射[RW](读取/写入)。二进制文件的很大一部分没有加载到内存中,我们将其视为[未映射]。
对象文件和静态库没有段。然而,我们通过将部分按其标志分组来伪造它。这给了我们一个分解,有点像真实的片段。

$ ./bloaty -d segments CMakeFiles/libbloaty.dir/src/bloaty.cc.o
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  87.5%   972Ki   0.0%       0    Section []
   8.2%  90.9Ki  78.3%  90.9Ki    Section [AX]
   2.3%  25.2Ki  21.7%  25.2Ki    Section [A]
   2.0%  22.6Ki   0.0%       0    [ELF Headers]
   0.1%     844   0.0%       0    [Unmapped]
   0.0%      24   0.1%      72    Section [AW]
 100.0%  1.09Mi 100.0%   116Ki    TOTAL

未完待续

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值