gdb
incomplete type
This happens generally with std::stringstream values. There is no way of printing the value other than to add code to convert it and recompile.
| example |
|---|
| #include <iostream>
#include <sstream>
int main(int argc, char *argv[]) {
std::stringstream ss;
ss << "Hello world\n";
std::string s = ss.str();
std::cout << ss.str();
return 0;
}
|
In gdb :
| stringstream |
|---|
| p ss
$1 = <incomplete type>
p s
$2 = "Hello world\n"
p ss.str()
Couldn't find method std::stringstream::str
|
Mostly you only use the .str() conversion at the very end of operations so in the middle you can never see it's value !?
The solution is to install the -dbg version of the stdlib package and of course make sure you link against it.
First find out what libraries your executable is using :
| install debug versions of libc etc |
|---|
| ldd ./a.out
linux-vdso.so.1 (0x00007fff888fc000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f2429688000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f2429668000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f24294a0000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2429358000)
/lib64/ld-linux-x86-64.so.2 (0x00007f24298a0000)
|
This clearly shows the non-dbg version is still used.
| check if we use the dbg versions |
|---|
| sudo su
apt-get install libstdc++6-10-dbg # match the version of libstdc++
dpkg-query -L libstdc++6-10-dbg
# last command will show amongst others :
/usr/lib/x86_64-linux-gnu/debug/libstdc++.a
/usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.28
/usr/lib/x86_64-linux-gnu/debug/libstdc++fs.a
|
Now don't make the mistake of thinking you have to link against these !! You should start gdb with the correct path to it :
| start gdb like this |
|---|
| export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/debug/
ldd ./a.out # see it now find the debug version
linux-vdso.so.1 (0x00007fff727dc000)
libstdc++.so.6 => //usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6 (0x00007febb1878000)
|
Without recompiling anything, so this would also work for deep debugging (slower)
| start like |
|---|
| LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/debug gdb ./a.out
|
You will see more messages when running as well ! Now be aware you get a LOT more info now, but now ss.str() will work so it is manageable :
| print stringstream content |
|---|
| p ss.str()
# you will be able to see the buffer somewhere.
|
Start it normally for basic (faster) debugging !
In normal mode print std::string just prints the content, which is normally what you want, use deep debugging only when needed.
register_libstdcxx_printers
If this message is printed during startup :
| error |
|---|
| from libstdcxx.v6 import register_libstdcxx_printers
ModuleNotFoundError: No module named 'libstdcxx'
|
It seems this is a bug, so without explanation, open up the python file :
| gdb python wrapper |
|---|
| vi /usr/share/gdb/auto-load/usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.28-gdb.py
|
And add this line after the definition of pythondir.
| alter |
|---|
| import sys
import gdb
import os
import os.path
pythondir = '/usr/share/gcc-10/python'
libdir = '/usr/lib/x86_64-linux-gnu'
sys.path.append(pythondir)
|
After this the message is gone and you will probably notice the MUCH more readable objects after this.
gdbinit
Again without explanation, some site recommended these settings for c++ debugging in you .gdbinit :
| .gdbinit |
|---|
| set confirm off
set print pretty on
set print object on
set print static-members on
set print vtbl on
set print demangle on
set demangle-style gnu-v3
|
Find out what each one does.
disassemble
Just a neat option inside gdb, you can print the disassembly of a function.
| dissambly |
|---|
| (gdb) disassable main
Dump of assembler code for function main(int, char**):
0x00000000000011de <+0>: push %rbp
0x00000000000011df <+1>: mov %rsp,%rbp
0x00000000000011e2 <+4>: push %rbx
0x00000000000011e3 <+5>: sub $0x1e8,%rsp
0x00000000000011ea <+12>: mov %edi,-0x1e4(%rbp)
0x00000000000011f0 <+18>: mov %rsi,-0x1f0(%rbp)
0x00000000000011f7 <+25>: lea -0x1c0(%rbp),%rax
0x00000000000011fe <+32>: mov %rax,%rdi
0x0000000000001201 <+35>: callq 0x1040 <_ZNSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEEC1Ev@plt>
0x0000000000001206 <+40>: lea -0x1c0(%rbp),%rax
0x000000000000120d <+47>: add $0x10,%rax
0x0000000000001211 <+51>: lea 0xded(%rip),%rsi # 0x2005
0x0000000000001218 <+58>: mov %rax,%rdi
0x000000000000121b <+61>: callq 0x1090 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000001220 <+66>: lea -0x1e0(%rbp),%rax
0x0000000000001227 <+73>: lea -0x1c0(%rbp),%rdx
0x000000000000122e <+80>: mov %rdx,%rsi
0x0000000000001231 <+83>: mov %rax,%rdi
0x0000000000001234 <+86>: callq 0x1080 <_ZNKSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEE3strEv@plt>
|
watchpoints
In all their wisdom the gdb maintainers have made watchpoints follow scope rules. This is of course a ridiculous decision. This simple example will not work anymore :
| example |
|---|
| type_t *new_type()
{
type_t *newobj = malloc(sizeof(type_t));
return newobj;
}
|
Setting watch newobj will say this when leaving scope at the closing }
| watchpoints |
|---|
| Watchpoint 2 deleted because the program has left the block in
which its expression is valid.
|
You cannot watch an address outside of the function scope in which it was created, actually the EXACT purpose of the watch function.
You now have to set a watch point on the address itself.
| watchpoint on the address |
|---|
| # break somewhere in you program
p newobj
# or if you need a member
p &newobj->member
(type) 0x555555772e78
# directly will not work, you need to prepend a *
watch *0x555555772e78
c
|
This works !