减少调试符号的占用空间(可执行文件膨胀到 4 GB)

问题描述 投票:0回答:3

所以基本问题是我构建的可执行文件大小为 4GB,并且打开了调试符号(在 75 MB 到 300 MB 之间,没有调试符号且优化级别不同)。我如何诊断/分析所有这些符号从何而来,以及哪些符号在占用空间方面是最大的罪魁祸首?我发现了一些关于减少非调试可执行文件大小的问题(尽管它们并没有非常有启发性),但在这里我主要关心的是减少调试符号的混乱。可执行文件太大,gdb 需要花费大量时间来加载所有符号,这阻碍了调试。也许减少代码膨胀是根本任务,但我首先想知道我的 4GB 都花在哪里了。

通过“size --format=SysV”运行可执行文件,我得到以下输出:

section                    size       addr
.interp                      28    4194872
.note.ABI-tag                32    4194900
.note.gnu.build-id           36    4194932
.gnu.hash                714296    4194968
.dynsym                 2728248    4909264
.dynstr                13214041    7637512
.gnu.version             227354   20851554
.gnu.version_r              528   21078912
.rela.dyn                 37680   21079440
.rela.plt                 15264   21117120
.init                        26   21132384
.plt                      10192   21132416
.text                  25749232   21142608
.fini                         9   46891840
.rodata                 3089441   46891872
.eh_frame_hdr            584228   49981316
.eh_frame               2574372   50565544
.gcc_except_table       1514577   53139916
.init_array                2152   56753888
.fini_array                   8   56756040
.jcr                          8   56756048
.data.rel.ro             332264   56756064
.dynamic                    992   57088328
.got                        704   57089320
.got.plt                   5112   57090048
.data                     22720   57095168
.bss                    1317872   57117888
.comment                     44          0
.debug_aranges          2978704          0
.debug_info           278337429          0
.debug_abbrev           1557345          0
.debug_line            13416850          0
.debug_str           3620467085          0
.debug_loc            236168202          0
.debug_ranges          37473728          0
Total                4242540803

我想我们可以从中看到“debug_str”占用了约 3.6 GB。我不 100% 知道“debug_str”是什么,但我猜它们可能确实是调试符号的字符串名称?那么这是否告诉我,经过整理后的符号名称实在是太大了?我怎样才能找出哪些并修复它们?

我想我可以用“nm”做一些事情,直接检查符号名称,但输出很大,我不确定如何最好地搜索它。有没有工具可以进行此类分析?

使用的编译器是“c++ (GCC) 4.9.2”。我想我应该提到我正在 Linux 环境中工作。

c++ compiler-optimization debug-symbols
3个回答
7
投票

因此,我主要根据John Zwinck 的回答,通过执行以下操作找到了罪魁祸首。本质上,我只是按照他的建议在可执行文件上运行“string”并分析输出。

strings my_executable > exec_strings.txt

然后,我主要按照 mindriot 的方法

对输出进行排序
cat exec_strings.txt | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- > exec_strings_sorted.txt

并查看了最长的字符串。事实上,这一切似乎都是来自某个特定库的一些疯狂的模板膨胀。然后我又做了一些计数,例如:

cat exec_strings.txt | wc -l
2928189
cat exec_strings.txt | grep <culprit_libname> | wc -l
1108426

查看提取的大约 300 万个字符串,其中大约 100 万个字符串似乎来自该库。最后,做

cat exec_strings.txt | wc -c
3659369876
cat exec_strings.txt | grep <culprit_libname> | wc -c
3601918899

很明显,这百万个字符串都非常长,并且构成了调试符号垃圾的大部分。所以至少现在我可以专注于这个库,同时尝试消除问题的根源。


4
投票

我使用的一个技巧是在可执行文件上运行

strings
,它将打印所有这些长(可能是由于模板)和大量(同上)调试符号名称。您可以将其通过管道传输到
sort | uniq -c | sort -n
并查看结果。在许多大型 C++ 可执行文件中,您会看到如下模式:

my_template<std::basic_string<char, traits, allocator>, std::unordered_map<std::basic_string<char, traits, allocator>, 1L>
my_template<std::basic_string<char, traits, allocator>, std::unordered_map<std::basic_string<char, traits, allocator>, 2L>
my_template<std::basic_string<char, traits, allocator>, std::unordered_map<std::basic_string<char, traits, allocator>, 3L>

你明白了。

在某些情况下,我决定简单地减少模板的数量。有时它会失控。其他时候,您可能会通过使用显式模板实例化,或者在不调试符号的情况下编译项目的特定部分,甚至在不依赖

dynamic_cast
typeid
的情况下禁用 RTTI 来赢得一些东西。


3
投票

我想我可以用“nm”做一些事情,直接检查符号名称,但输出很大,我不确定如何最好地搜索它。有没有工具可以进行此类分析?

您可以运行以下命令来按符号长度对所有

nm
的符号输出进行排序:

nm --no-demangle -a -P --size-sort myexecutable \
    | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-

(感谢按行长度对文本文件进行排序,包括第一个|

之后的所有内容的空格
。)这将在最后显示最长的名称。您可以进一步将输出通过管道传输到
c++filt -t
以获得分解后的名称,这可能会帮助您进行搜索。

根据您的情况,将可执行文件及其调试符号拆分为单独的文件可能会很有用,这将允许您将不太臃肿的可执行文件分发到目标环境/客户端/等,并将调试符号保留在单个文件中如果需要的话位置。请参阅如何在构建目标之外生成 gcc 调试符号?了解一些详细信息。

© www.soinside.com 2019 - 2024. All rights reserved.