c++ - 如何使用 gdb 找到全局变量的析构函数调用?

标签 c++ linux gdb global-variables destructor

我们有一个非常大的项目。(大约 200'000 个文件)

当项目退出时,它在全局变量的解构中崩溃。

但是我们找不到变量定义的地方。

有人知道如何找到全局变量在哪里吗?

最佳答案

But we can't find where the variable defined.

大概,您想找出哪个全局变量导致了问题。如果你知道它是哪一个,你可以使用 grep , 或 GDB info variable foobar找到它。

让我们考虑一个例子。

cat foo.cc
#include <stdio.h>
#include <string>

using std::string;

string foo("foo");
string bar("bar");
string baz("baz");

// It is not valid to call this function with a global string.
void invalid_with_global(string *str)
{
  printf("str: %s @%p)\n", str->c_str(), str);
  printf("before dtor\n");
  str->~string();
  printf("after dtor\n");
}

int main()
{
  invalid_with_global(&baz);
}

g++ -g foo.cc && gdb -q ./a.out

Reading symbols from /tmp/a.out...done.
(gdb) r
Starting program: /tmp/a.out
str: baz @0x601088)
before dtor
after dtor
*** glibc detected *** /tmp/a.out: double free or corruption (fasttop): 0x0000000000602070 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7ffff7583b96]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(_ZNSsD1Ev+0x23)[0x7ffff7b78c13]
/lib/x86_64-linux-gnu/libc.so.6(+0x3b901)[0x7ffff7540901]
/lib/x86_64-linux-gnu/libc.so.6(+0x3b985)[0x7ffff7540985]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf4)[0x7ffff7526774]
/tmp/a.out[0x4007f9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 ca:01 147024                             /tmp/a.out
00600000-00601000 r--p 00000000 ca:01 147024                             /tmp/a.out
00601000-00602000 rw-p 00001000 ca:01 147024                             /tmp/a.out
00602000-00623000 rw-p 00000000 00:00 0                                  [heap]
7ffff7209000-7ffff7304000 r-xp 00000000 ca:01 881564                     /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7304000-7ffff7503000 ---p 000fb000 ca:01 881564                     /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7503000-7ffff7504000 r--p 000fa000 ca:01 881564                     /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7504000-7ffff7505000 rw-p 000fb000 ca:01 881564                     /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7505000-7ffff76ba000 r-xp 00000000 ca:01 881572                     /lib/x86_64-linux-gnu/libc-2.15.so
7ffff76ba000-7ffff78b9000 ---p 001b5000 ca:01 881572                     /lib/x86_64-linux-gnu/libc-2.15.so
7ffff78b9000-7ffff78bd000 r--p 001b4000 ca:01 881572                     /lib/x86_64-linux-gnu/libc-2.15.so
7ffff78bd000-7ffff78bf000 rw-p 001b8000 ca:01 881572                     /lib/x86_64-linux-gnu/libc-2.15.so
7ffff78bf000-7ffff78c4000 rw-p 00000000 00:00 0
7ffff78c4000-7ffff78d9000 r-xp 00000000 ca:01 881501                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff78d9000-7ffff7ad8000 ---p 00015000 ca:01 881501                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7ad8000-7ffff7ad9000 r--p 00014000 ca:01 881501                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7ad9000-7ffff7ada000 rw-p 00015000 ca:01 881501                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff7ada000-7ffff7bbc000 r-xp 00000000 ca:01 1096828                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
7ffff7bbc000-7ffff7dbb000 ---p 000e2000 ca:01 1096828                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
7ffff7dbb000-7ffff7dc3000 r--p 000e1000 ca:01 1096828                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
7ffff7dc3000-7ffff7dc5000 rw-p 000e9000 ca:01 1096828                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16
7ffff7dc5000-7ffff7dda000 rw-p 00000000 00:00 0
7ffff7dda000-7ffff7dfc000 r-xp 00000000 ca:01 881673                     /lib/x86_64-linux-gnu/ld-2.15.so
7ffff7fd8000-7ffff7fdd000 rw-p 00000000 00:00 0
7ffff7ff7000-7ffff7ffb000 rw-p 00000000 00:00 0
7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00022000 ca:01 881673                     /lib/x86_64-linux-gnu/ld-2.15.so
7ffff7ffd000-7ffff7fff000 rw-p 00023000 ca:01 881673                     /lib/x86_64-linux-gnu/ld-2.15.so
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Program received signal SIGABRT, Aborted.
0x00007ffff753b425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64  ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff753b425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x00007ffff753eb8b in __GI_abort () at abort.c:91
#2  0x00007ffff757939e in __libc_message (do_abort=2, fmt=0x7ffff7683008 "*** glibc detected *** %s: %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:201
#3  0x00007ffff7583b96 in malloc_printerr (action=3, str=0x7ffff76831f8 "double free or corruption (fasttop)", ptr=<optimized out>) at malloc.c:5007
#4  0x00007ffff7b78c13 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7540901 in __run_exit_handlers (status=0, listp=0x7ffff78bd688 <__exit_funcs>, run_list_atexit=true) at exit.c:78
#6  0x00007ffff7540985 in __GI_exit (status=<optimized out>) at exit.c:100
#7  0x00007ffff7526774 in __libc_start_main (main=0x400904 <main()>, argc=1, ubp_av=0x7fffffffd828, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffd818) at libc-start.c:258
#8  0x00000000004007f9 in _start ()

此时,您知道您的全局“std::basic_string<...>”对象之一造成了麻烦,但您不知道是哪一个。

很容易发现:只需转到第 4 帧并打印 this ,对吧?

(gdb) fr 4
#4  0x00007ffff7b78c13 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) p this
No symbol "this" in current context.

嗯,效果不是很好,因为我没有 libstdc++.so.6 的调试符号安装(如果给您带来麻烦的对象是在您自己的源代码中定义的,那么您不应该有那个麻烦)。

现在,我可以为我的 libstdc++.so.6 安装调试符号。 ,或者我可以使用我对 ABI 的了解。在 Linux/x86_64 上,第一个 ( this ) 参数在寄存器 %rdi 中传递.我无法使用当前的 %rdi不过,因为它已经被用于其他用途。所以我在问题析构函数上设置了一个断点:std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() , 然后重启。

在实际程序中,析构函数会被调用很多次,但我只对来自 exit 的调用感兴趣.所以我在 exit 上设置了断点,并且仅当遇到该断点时,我才会在析构函数上设置一个新断点。

(gdb) b exit
(gdb) r
Starting program: /tmp/a.out
str: baz @0x601088)
before dtor
after dtor

Breakpoint 1, __GI_exit (status=0) at exit.c:100
100 exit.c: No such file or directory.
(gdb) b std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
Breakpoint 2 at 0x7ffff7b78bf0
(gdb) c
Continuing.

Breakpoint 2, 0x00007ffff7b78bf0 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) bt
#0  0x00007ffff7b78bf0 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x00007ffff7540901 in __run_exit_handlers (status=0, listp=0x7ffff78bd688 <__exit_funcs>, run_list_atexit=true) at exit.c:78
#2  0x00007ffff7540985 in __GI_exit (status=<optimized out>) at exit.c:100
#3  0x00007ffff7526774 in __libc_start_main (main=0x400904 <main()>, argc=1, ubp_av=0x7fffffffd828, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffd818) at libc-start.c:258
#4  0x00000000004007f9 in _start ()
(gdb) p/x $rdi
$1 = 0x601088
(gdb) c
Continuing.
*** glibc detected *** /tmp/a.out: double free or corruption (fasttop): 0x0000000000602070 ***
... as before, omitted ...


Program received signal SIGABRT, Aborted.
0x00007ffff753b425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64  ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.

现在我们有了全局“坏”的地址:0x601088 ,您会注意到它与我打印的值匹配。

最后我们准备好回答“它是哪个变量?”问题:

(gdb) info sym 0x601088
baz in section .bss of /tmp/a.out

瞧:问题变量是 baz .最后:

(gdb) info var baz
All variables matching regular expression "baz":

File foo.cc:
std::string baz;

关于c++ - 如何使用 gdb 找到全局变量的析构函数调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15727823/

相关文章:

python - 未定义对 "gsl_rng_unform"、 "gsl_rng_mt19937"、 "gsl_rng_alloc"、 "gsl_rng_set"的引用

python - 如何在 Linux 上的 Python3.x 中检查窗口是否存在

使用最大调试符号和最低优化进行编译仍然会跳过代码片段

c - 如何向 Audacious 添加调试符号?

C++ 基本类布局

c++ - 如何跟踪给定主题的较新的 C++ 标准文档?

c++ - 在 Windows CE 上安装 CAB 时安装 DLL 不工作

linux - Linux 中的并发

linux - 桌面环境变量与终端不同?

c - GDB : Why can't I execute single statements when I compile without -g?