[英]Reducing the footprint of debug symbols (executable is bloated to 4 GB)
所以基本的问题是我构建的可执行文件大小为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环境中工作。
所以我通过以下方式追踪了主要罪魁祸首,主要基于John Zwinck的回答 。 基本上我只是按照他的建议在可执行文件上运行“string”并分析输出。
string 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
很明显,这些百万字符串都超长,构成了调试符号垃圾的大部分。 所以至少现在我可以专注于这个库,同时尝试删除问题的根源。
我想我可以用某种方式做某事'nm',直接检查符号名称,但输出是巨大的,我不知道如何最好地搜索它。 有没有工具可以进行这种分析?
您可以运行以下命令按符号长度排序所有nm
的符号输出:
nm --no-demangle -a -P --size-sort myexecutable \
| awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
(感谢按行长度对文本文件进行排序,包括第一个|
之后的所有内容的空格 。)这将显示最后的最长名称。 您可以进一步将输出传递到c++filt -t
以获取解码名称,这可以帮助您进行搜索。
根据您的具体情况,将可执行文件及其调试符号拆分为单独的文件可能很有用,这样可以将较少膨胀的可执行文件分发到目标环境/客户端等,并将调试符号保存在一个位置如果需要的话。 请参阅如何在构建目标之外生成gcc调试符号? 一些细节。
我使用的一个技巧是在可执行文件上运行strings
,这将打印所有那些长(可能是由于模板)和许多(同上)调试符号名称。 你可以管它来sort | uniq -c | sort -n
sort | uniq -c | sort -n
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。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.