【问题标题】:How do debug symbols affect performance of a Linux executable compiled by GCC?调试符号如何影响 GCC 编译的 Linux 可执行文件的性能?
【发布时间】:2011-12-30 05:31:35
【问题描述】:

在所有其他因素相同(例如优化级别)的情况下,ELF 或 SO 中的调试符号有何影响:

  1. 加载时间。
  2. 运行时内存占用。
  3. 运行时性能?

可以采取哪些措施来减轻负面影响?

编辑 我已经看到了这个问题,但我发现讨论没有帮助,因为代码优化因素已经混淆了那里的问题。 Why does my code run slower with multiple threads than with a single thread when it is compiled for profiling (-pg)?

【问题讨论】:

  • 请注意,可以将调试符号构建到 单独 文件中,因此您可以单独分发它们,就像 Debian / Ubuntu 对具有调试符号的 foo-dbg 软件包所做的那样对于foo 中的二进制文件。这甚至消除了激进的文件系统预读将调试符号拉入页面缓存的机会,即使它们在二进制文件执行时甚至没有映射到内存中。 (请注意,在内部,execve() 执行新二进制文件的工作方式更像是文本段的 mmap(MAP_PRIVATE, PROT_READ|PROT_EXEC),而不是 read()。)

标签: gcc debug-symbols


【解决方案1】:

调试符号位于与代码/数据部分完全不同的部分。可以通过objdump查看:

$ objdump -h a.out

a.out:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400200  0000000000400200  00000200  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  000000000040021c  000000000040021c  0000021c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  000000000040023c  000000000040023c  0000023c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .hash         00000018  0000000000400260  0000000000400260  00000260  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .gnu.hash     0000001c  0000000000400278  0000000000400278  00000278  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       00000048  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .dynstr       00000038  00000000004002e0  00000000004002e0  000002e0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version  00000006  0000000000400318  0000000000400318  00000318  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .gnu.version_r 00000020  0000000000400320  0000000000400320  00000320  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.dyn     00000018  0000000000400340  0000000000400340  00000340  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .rela.plt     00000018  0000000000400358  0000000000400358  00000358  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .init         00000018  0000000000400370  0000000000400370  00000370  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .plt          00000020  0000000000400388  0000000000400388  00000388  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .text         000001c8  00000000004003b0  00000000004003b0  000003b0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .fini         0000000e  0000000000400578  0000000000400578  00000578  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 15 .rodata       00000004  0000000000400588  0000000000400588  00000588  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame_hdr 00000024  000000000040058c  000000000040058c  0000058c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .eh_frame     0000007c  00000000004005b0  00000000004005b0  000005b0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 18 .ctors        00000010  0000000000600630  0000000000600630  00000630  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .dtors        00000010  0000000000600640  0000000000600640  00000640  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .jcr          00000008  0000000000600650  0000000000600650  00000650  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .dynamic      000001a0  0000000000600658  0000000000600658  00000658  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got          00000008  00000000006007f8  00000000006007f8  000007f8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .got.plt      00000020  0000000000600800  0000000000600800  00000800  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 24 .data         00000010  0000000000600820  0000000000600820  00000820  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 25 .bss          00000010  0000000000600830  0000000000600830  00000830  2**3
                  ALLOC
 26 .comment      00000039  0000000000000000  0000000000000000  00000830  2**0
                  CONTENTS, READONLY
 27 .debug_aranges 00000030  0000000000000000  0000000000000000  00000869  2**0
                  CONTENTS, READONLY, DEBUGGING
 28 .debug_pubnames 0000001b  0000000000000000  0000000000000000  00000899  2**0
                  CONTENTS, READONLY, DEBUGGING
 29 .debug_info   00000055  0000000000000000  0000000000000000  000008b4  2**0
                  CONTENTS, READONLY, DEBUGGING
 30 .debug_abbrev 00000034  0000000000000000  0000000000000000  00000909  2**0
                  CONTENTS, READONLY, DEBUGGING
 31 .debug_line   0000003b  0000000000000000  0000000000000000  0000093d  2**0
                  CONTENTS, READONLY, DEBUGGING
 32 .debug_str    00000026  0000000000000000  0000000000000000  00000978  2**0
                  CONTENTS, READONLY, DEBUGGING
 33 .debug_loc    0000004c  0000000000000000  0000000000000000  0000099e  2**0
                  CONTENTS, READONLY, DEBUGGING

您可以看到额外的部分(27 到 33)。这些部分不会在运行时加载,因此不会有任何性能损失。使用gdb,您还可以在运行时检查它们:

$ gdb ./a.out
(gdb) break main
(gdb) run
(gdb) info files
// blah blah ....
Local exec file:
        `/home/kghost/a.out', file type elf64-x86-64.
        Entry point: 0x4003b0
        0x0000000000400200 - 0x000000000040021c is .interp
        0x000000000040021c - 0x000000000040023c is .note.ABI-tag
        0x000000000040023c - 0x0000000000400260 is .note.gnu.build-id
        0x0000000000400260 - 0x0000000000400278 is .hash
        0x0000000000400278 - 0x0000000000400294 is .gnu.hash
        0x0000000000400298 - 0x00000000004002e0 is .dynsym
        0x00000000004002e0 - 0x0000000000400318 is .dynstr
        0x0000000000400318 - 0x000000000040031e is .gnu.version
        0x0000000000400320 - 0x0000000000400340 is .gnu.version_r
        0x0000000000400340 - 0x0000000000400358 is .rela.dyn
        0x0000000000400358 - 0x0000000000400370 is .rela.plt
        0x0000000000400370 - 0x0000000000400388 is .init
        0x0000000000400388 - 0x00000000004003a8 is .plt
        0x00000000004003b0 - 0x0000000000400578 is .text
        0x0000000000400578 - 0x0000000000400586 is .fini
        0x0000000000400588 - 0x000000000040058c is .rodata
        0x000000000040058c - 0x00000000004005b0 is .eh_frame_hdr
        0x00000000004005b0 - 0x000000000040062c is .eh_frame
        0x0000000000600630 - 0x0000000000600640 is .ctors
        0x0000000000600640 - 0x0000000000600650 is .dtors
        0x0000000000600650 - 0x0000000000600658 is .jcr
        0x0000000000600658 - 0x00000000006007f8 is .dynamic
        0x00000000006007f8 - 0x0000000000600800 is .got
        0x0000000000600800 - 0x0000000000600820 is .got.plt
        0x0000000000600820 - 0x0000000000600830 is .data
        0x0000000000600830 - 0x0000000000600840 is .bss
// blah blah ....

因此,唯一的损失是您需要额外的磁盘空间来存储这些信息。也可以使用strip去除调试信息:

$ strip a.out

使用objdump再次检查,你会发现不同。

编辑:

实际上加载程序不是查看部分,而是根据其Program Header 加载elf 文件,objdump -p 可以看到(以下示例使用不同的elf 二进制文件):

$ objdump -p /bin/cat

/bin/cat:     file format elf64-x86-64

Program Header:
    PHDR off    0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
  INTERP off    0x0000000000000238 vaddr 0x0000000000000238 paddr 0x0000000000000238 align 2**0
         filesz 0x000000000000001c memsz 0x000000000000001c flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
         filesz 0x00000000000078bc memsz 0x00000000000078bc flags r-x
    LOAD off    0x0000000000007c28 vaddr 0x0000000000207c28 paddr 0x0000000000207c28 align 2**21
         filesz 0x0000000000000678 memsz 0x0000000000000818 flags rw-
 DYNAMIC off    0x0000000000007dd8 vaddr 0x0000000000207dd8 paddr 0x0000000000207dd8 align 2**3
         filesz 0x00000000000001e0 memsz 0x00000000000001e0 flags rw-
    NOTE off    0x0000000000000254 vaddr 0x0000000000000254 paddr 0x0000000000000254 align 2**2
         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off    0x0000000000006980 vaddr 0x0000000000006980 paddr 0x0000000000006980 align 2**2
         filesz 0x0000000000000274 memsz 0x0000000000000274 flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x0000000000007c28 vaddr 0x0000000000207c28 paddr 0x0000000000207c28 align 2**0
         filesz 0x00000000000003d8 memsz 0x00000000000003d8 flags r--

程序头告诉哪个段将被什么rwx标志加载;具有相同标志的多个部分将合并为一个段。

顺便说一句:

加载器在加载elf文件时并不关心节,但它会在需要时查看几个与符号相关的节来解析符号。

【讨论】:

【解决方案2】:

您可能想查看Why does my code run slower with multiple threads than with a single thread when it is compiled for profiling (-pg)? 以快速了解调试符号如何影响优化。

回答您的 3 个问题:

  1. 当调试符号存在时加载时间会比不存在时增加
  2. 磁盘占用空间会更大
  3. 如果您使用零优化进行编译,那么您真的没有任何损失。如果您设置了优化,那么优化后的代码将因为调试符号而优化得更少。

【讨论】:

  • 我很确定#3 不是真的;调试符号的存在与否不会影响代码生成。 (调试符号在优化的二进制文件中可能不准确或部分不可用。)
  • 对您链接到的问题的公认答案指出提问者已打开 profiling - 这会导致运行时损失。调试符号不会。
  • 谢谢 - 我没有发现这个特定的问题很有用,因为讨论与优化混为一谈。请注意,这个答案直接与您的所有观点相矛盾stackoverflow.com/a/8676610/94239
  • @Keith,看来你没有。 3 不正确。请考虑删除此答案。
  • 只有 #2 是真的。 #1 不是真的,因为只映射了可执行文件的使用部分。不过,预读可能过于激进,并且会花一些时间将文件的无用部分拉入页面缓存。 #3绝对是错误的。我查看了编译器 asm 输出很多,gcc/clang/icc 肯定是错误的,我假设使用 MSVC。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-19
相关资源
最近更新 更多