www.digitalmars.com         C & C++   DMDScript  

D.gnu - Reducing debug info for stack traces, while preserving for gdb

reply Witold Baryluk <witold.baryluk gmail.com> writes:
Hi,

I am trying to split debug symbols out of final binary, to reduce 
its size. But I would like to preserve at least line numbers in 
the stacktraces from `Exception`s.

Linux, amd64. gdc 12.2.0 on Debian testing.

Binary is compiled using `-O3 -g -g3` (and possibly -gz).

I then use `objcopy` to split debug info from final ELF binary 
file into .exe and .debug files.

I use `objcopy --only-keep-debug --compress-debug-sections=zlib 
binary binary.debug` to produce .debug file.

Then I use normally `objcopy --strip-debug binary binary.exe` 
(possibly with --compress-debug-sections=zlib) to produce binary.

This makes it possibly to use in gdb without issues (using `gdb 
-s binary.debug -e binary` for example, or by utilizing 
`--add-gnu-debuglink=` option in `objcopy`).

But this causes stacktraces to miss line numbers (and columns). 
(function names are still there, as these are derived from symbol 
tables instead).

I tried selectively removing DWARF debug sections, but it looks 
that at least these are required:

`.debug_info`
`.debug_aranges`
`.debug_abbrev`
`.debug_line`
`.debug_str`
`.debug_line_str`
`.debug_rnglists`

So, I can only remove these:

`objcopy -R .debug_loc -R .debug_macro -R .debug_ranges -R 
.debug_loclists`

(`.debug_loc` and `.debug_ranges` are not even generated by gcc, 
so it does not matter probably).

The issue is, this saves me very little space. `.debug_macro` and 
`.debug_loclists` are rather small.

The bulk of information is in `.debug_info`. But I believe it 
contains way more information than is really needed to just 
produce line numbers.



I did inspect final binaries , and it is using DWARF version 5.

I also tried `-gas-loc-support` , but no change.

Using `-g1` makes stack traces work nicely, by making 
`.debug_info` smaller, but then debugging in `gdb` is very 
limited. One option would be to compile application twice, once 
with `-g1` and once with `-g3`. But I really do not think this is 
supported, or reliable, even if I enable deterministic builds.

In one article ( 
https://support.backtrace.io/hc/en-us/articles/360040105792-DWARF#Remo
ingDebugInformation )  I read that for C/C++ in GCC, it is enough to preserve
only `.debug_frame` and `.debug_line` to get function, source filenames and
line numbers. But that is not true for gdc and its stacktrace handler.


I did read about this gdb extension, which is interesting, 
https://sourceware.org/gdb/onlinedocs/gdb/MiniDebugInfo.html , 
but I did not try, and it is probably only supported in gdb (i.e. 
addr2libe, Phobos, libunwind do not  support it).


Any ideas?
Dec 14 2022
next sibling parent Witold Baryluk <witold.baryluk gmail.com> writes:
For example to give you some context.

This is an example small app, the actual app is significantly 
larger (binary with debug symbols is about 20MB, without debug 
symbols only 2-3MB).

```

     FILE SIZE        VM SIZE
  --------------  --------------
   19.8%   107Ki  39.3%   107Ki    .text
   17.0%  93.0Ki   0.0%       0    .strtab

72kB
   12.0%  65.3Ki  23.8%  65.3Ki    .dynstr
    5.2%  28.5Ki  10.4%  28.5Ki    .rodata
    5.1%  27.9Ki  10.2%  27.9Ki    .eh_frame

27kB
    4.5%  24.8Ki   0.0%       0    .symtab

20kB

18.5kB
    3.2%  17.6Ki   6.4%  17.6Ki    .dynsym
    1.3%  7.25Ki   0.0%       0    [Unmapped]
    1.2%  6.61Ki   2.1%  5.64Ki    [27 Others]
    1.1%  6.18Ki   2.3%  6.18Ki    .eh_frame_hdr
    0.9%  5.08Ki   1.9%  5.08Ki    .gnu.hash
    0.8%  4.31Ki   1.6%  4.31Ki    .data.rel.ro
    0.8%  4.12Ki   1.5%  4.12Ki    .rela.dyn
    0.5%  2.69Ki   0.0%       0    [ELF Section Headers]

small

small
    0.3%  1.52Ki   0.6%  1.52Ki    .rela.plt
  100.0%   545Ki 100.0%   273Ki    TOTAL
```


```

compressed debug sections
     FILE SIZE        VM SIZE
  --------------  --------------
   24.4%   107Ki  39.3%   107Ki    .text
   21.1%  93.0Ki   0.0%       0    .strtab
   14.8%  65.3Ki  23.8%  65.3Ki    .dynstr
    6.5%  28.5Ki  10.4%  28.5Ki    .rodata
    6.3%  27.9Ki  10.2%  27.9Ki    .eh_frame
    5.6%  24.8Ki   0.0%       0    .symtab
    4.0%  17.6Ki   6.4%  17.6Ki    .dynsym

smaller

smaller

smaller
    1.6%  7.25Ki   0.0%       0    [Unmapped]
    1.4%  6.18Ki   2.3%  6.18Ki    .eh_frame_hdr
    1.2%  5.08Ki   1.9%  5.08Ki    .gnu.hash
    1.0%  4.31Ki   1.6%  4.31Ki    .data.rel.ro
    1.0%  4.20Ki   0.9%  2.43Ki    [25 Others]
    0.9%  4.12Ki   1.5%  4.12Ki    .rela.dyn
    0.6%  2.56Ki   0.0%       0    [ELF Section Headers]
    0.3%  1.52Ki   0.6%  1.52Ki    .rela.plt
    0.3%  1.47Ki   0.5%  1.47Ki    .gnu.version
    0.2%  1.03Ki   0.4%  1.03Ki    .plt
    0.2%     728   0.3%     728    [ELF Program Headers]
  100.0%   441Ki 100.0%   273Ki    TOTAL
```

(`.debug_rnglists` and `.debug_abbrev` do not even register).





```

sections (broken lines in stack trace)
     FILE SIZE        VM SIZE
  --------------  --------------
   26.9%   107Ki  39.3%   107Ki    .text
   23.2%  92.9Ki   0.0%       0    .strtab
   16.3%  65.3Ki  23.8%  65.3Ki    .dynstr
    7.1%  28.5Ki  10.4%  28.5Ki    .rodata
    6.9%  27.9Ki  10.2%  27.9Ki    .eh_frame
    6.2%  24.7Ki   0.0%       0    .symtab
    4.4%  17.6Ki   6.4%  17.6Ki    .dynsym
    1.8%  7.25Ki   0.0%       0    [Unmapped]
    1.5%  6.18Ki   2.3%  6.18Ki    .eh_frame_hdr
    1.3%  5.08Ki   1.9%  5.08Ki    .gnu.hash
    1.1%  4.31Ki   1.6%  4.31Ki    .data.rel.ro
    1.0%  4.12Ki   1.5%  4.12Ki    .rela.dyn
    0.5%  2.12Ki   0.0%       0    [ELF Section Headers]
    0.4%  1.52Ki   0.6%  1.52Ki    .rela.plt
    0.4%  1.47Ki   0.5%  1.47Ki    .gnu.version
    0.3%  1.18Ki   0.3%     881    [18 Others]
    0.3%  1.03Ki   0.4%  1.03Ki    .plt
    0.2%     728   0.3%     728    [ELF Program Headers]
    0.1%     552   0.2%     552    .data
    0.1%     544   0.2%     544    .got.plt
    0.1%     512   0.2%     512    .dynamic
  100.0%   401Ki 100.0%   273Ki    TOTAL
```

```

sections
     FILE SIZE        VM SIZE
  --------------  --------------
   31.0%   178Ki   0.0%       0    .debug_info
    0.0%       0  39.3%   107Ki    .text
   18.5%   106Ki   0.0%       0    .debug_loclists
   16.8%  96.9Ki   0.0%       0    .debug_str
   16.1%  93.0Ki   0.0%       0    .strtab
    0.0%       0  23.8%  65.3Ki    .dynstr
   10.5%  60.7Ki   0.0%       0    .debug_line
    0.0%       0  10.4%  28.5Ki    .rodata
    0.0%       0  10.2%  27.9Ki    .eh_frame
    4.3%  24.8Ki   0.0%       0    .symtab
    0.0%       0   6.4%  17.6Ki    .dynsym
    0.0%       0   2.3%  6.18Ki    .eh_frame_hdr
    1.0%  5.86Ki   0.0%       0    .debug_rnglists
    0.0%       0   1.9%  5.08Ki    .gnu.hash
    0.8%  4.54Ki   0.0%       0    .debug_abbrev
    0.0%       0   1.6%  4.31Ki    .data.rel.ro
    0.5%  2.79Ki   1.5%  4.17Ki    [27 Others]
    0.0%       0   1.5%  4.12Ki    .rela.dyn
    0.5%  2.69Ki   0.0%       0    [ELF Section Headers]
    0.0%       0   0.6%  1.52Ki    .rela.plt
    0.0%       0   0.5%  1.47Ki    .gnu.version
  100.0%   576Ki 100.0%   273Ki    TOTAL
```

```

debug sections
     FILE SIZE        VM SIZE
  --------------  --------------
    0.0%       0  39.3%   107Ki    .text
   35.0%  93.0Ki   0.0%       0    .strtab
   27.3%  72.4Ki   0.0%       0    .debug_info
    0.0%       0  23.8%  65.3Ki    .dynstr
    0.0%       0  10.4%  28.5Ki    .rodata
    0.0%       0  10.2%  27.9Ki    .eh_frame
   10.4%  27.5Ki   0.0%       0    .debug_loclists
    9.4%  24.8Ki   0.0%       0    .symtab
    7.7%  20.4Ki   0.0%       0    .debug_str
    7.0%  18.5Ki   0.0%       0    .debug_line
    0.0%       0   6.4%  17.6Ki    .dynsym
    0.0%       0   2.3%  6.18Ki    .eh_frame_hdr
    0.0%       0   1.9%  5.08Ki    .gnu.hash
    0.0%       0   1.6%  4.31Ki    .data.rel.ro
    0.7%  1.89Ki   1.5%  4.17Ki    [27 Others]
    0.0%       0   1.5%  4.12Ki    .rela.dyn
    1.0%  2.69Ki   0.0%       0    [ELF Section Headers]
    0.9%  2.38Ki   0.0%       0    .debug_rnglists
    0.7%  1.74Ki   0.0%       0    .debug_abbrev
    0.0%       0   0.6%  1.52Ki    .rela.plt
    0.0%       0   0.5%  1.47Ki    .gnu.version
  100.0%   265Ki 100.0%   273Ki    TOTAL
```
Dec 14 2022
prev sibling parent Iain Buclaw <ibuclaw gdcproject.org> writes:
Hi,

On Wednesday, 14 December 2022 at 22:23:49 UTC, Witold Baryluk 
wrote:>
 The bulk of information is in `.debug_info`. But I believe it 
 contains way more information than is really needed to just 
 produce line numbers.
You're observation is correct. GDC uses libbacktrace to produce stack traces, and this indeed does traverse the `.debug_info` section in order to achieve its job. https://github.com/gcc-mirror/gcc/blob/8ec139af5ea9657c7517c1483c7a577815bea48e/libbacktrace/dwarf.c#L2116-L2120
 I did inspect final binaries , and it is using DWARF version 5.

 I also tried `-gas-loc-support` , but no change.

 Using `-g1` makes stack traces work nicely, by making 
 `.debug_info` smaller, but then debugging in `gdb` is very 
 limited. One option would be to compile application twice, once 
 with `-g1` and once with `-g3`. But I really do not think this 
 is supported, or reliable, even if I enable deterministic 
 builds.
I'm not sure on this, I'd have to give it some thought.
 In one article ( 
 https://support.backtrace.io/hc/en-us/articles/360040105792-DWARF#Remo
ingDebugInformation )  I read that for C/C++ in GCC, it is enough to preserve
only `.debug_frame` and `.debug_line` to get function, source filenames and
line numbers. But that is not true for gdc and its stacktrace handler.
C++ stdlib doesn't give stack traces AFAIU, and if it does, it would be using libbacktrace too. I guess then the article must be referring to the implementation of `execinfo.h` and/or `dladdr`. Perhaps a run-time fallback could be added to gdc's stack trace support to use the `execinfo.h` backtrace functions if the `.debug_info` (or whatever platform equivalent) section does not exist. From what I recall, execinfo is not quite as accurate as libbacktrace though. Iain.
Dec 24 2022