因此,基本问题是,我构建的可执行文件大小为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环境中工作的。
3条答案
按热度按时间2fjabf4q1#
因此,我通过以下操作追踪到了罪魁祸首,主要基于John Zwinck's answer。本质上,我只是遵循了他的建议,在可执行文件上运行“string”并分析输出。
字符串
然后我将输出排序为mindriot's method:
型
看了看最长的字符串。事实上,这一切似乎都是一些疯狂的模板膨胀,从一个特定的库。然后我做了更多的计数,如:
型
在提取的大约300万个字符串中,似乎有100万个来自这个库。最后,
型
很明显,这一百万个字符串都是超长的,构成了调试符号垃圾的大部分。所以至少现在我可以专注于这个库,同时试图消除问题的根源。
zzwlnbp82#
我使用的一个技巧是在可执行文件上运行
strings
,它将打印所有那些长的(可能是由于模板的原因)和大量的(同上)调试符号名称。您可以将其传输到sort | uniq -c | sort -n
并查看结果。在许多大型C++可执行文件中,您会看到这样的模式:字符串
你懂的。
在某些情况下,我决定简单地减少模板的数量。有时它会失控。其他时候,你可能会通过使用显式模板示例化,或者编译项目的特定部分而不调试符号,或者如果你不依赖
dynamic_cast
或typeid
,甚至禁用RTTI来赢得一些东西。d4so4syb3#
我想我可以用'nm'做些什么,直接检查符号名称,但输出量很大,我不知道如何最好地搜索它。有没有工具可以做这种分析?
您可以运行以下命令,按符号长度对
nm
的所有符号输出进行排序:字符串
(
|
之后的所有内容都是Sort a text file by line length including spaces的功劳。)这将最后显示最长的名称。您可以进一步将输出导入c++filt -t
以获得非重命名的名称,这可能有助于您的搜索。根据您的情况,将可执行文件及其调试符号拆分到单独的文件中可能会很有用,这将允许您将较不臃肿的可执行文件分发到目标环境/客户端/等,并在需要时将调试符号保留在单个位置。有关详细信息,请参阅How to generate gcc debug symbol outside the build target?。