gdb 进阶使用记录(主要是关于 optimized out)

文章探讨了在GDB调试中遇到optimizedout现象的原因,即代码被编译器优化导致的部分信息丢失。作者提供了通过调整编译选项、动态分析参数和利用gdb技巧(如强制转换和局部变量检查)来获取优化后隐藏信息的方法。
摘要由CSDN通过智能技术生成

如果 gdb 调试发现 optimized out,这个表示被编译器优化了,这个优化跟编译选项有关,gcc 有个 -O0 -O1 -O2 等编译选项,越高等级能看到的信息越少

一些发现

根据我的实验,这个编译器优化是一个动态优化,就是我同样的代码,但是我输入的参数不一样,优化就不一样(注意堆栈的 main 函数参数)

#0  0x00007ffff70255f7 in raise () from /lib64/libc.so.6
#1  0x00007ffff7026ce8 in abort () from /lib64/libc.so.6
#2  0x00007ffff7928a55 in __gnu_cxx::__verbose_terminate_handler ()
    at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/vterminate.cc:95
#3  0x00007ffff7926bf6 in __cxxabiv1::__terminate (handler=<optimized out>)
    at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/eh_terminate.cc:38
#4  0x00007ffff7926c23 in std::terminate () at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/eh_terminate.cc:48
#5  0x00007ffff7926e42 in __cxxabiv1::__cxa_throw (obj=0x6030c0, tinfo=0x601de0 <_ZTIPKc@@CXXABI_1.3>, dest=0x0)
    at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/eh_throw.cc:87
#6  0x0000000000400eaf in test2 (i=-1) at a.cpp:9
#7  test1 (i=0) at a.cpp:26
#8  test (i=1) at a.cpp:29
#9  0x0000000000400aa9 in main (arg=<optimized out>, args=<optimized out>) at a.cpp:37

对照(代码里有给 arg 重新赋值,没有传 999 个参数)

#0  0x00007ffff70255f7 in raise () from /lib64/libc.so.6
#1  0x00007ffff7026ce8 in abort () from /lib64/libc.so.6
#2  0x00007ffff7928a55 in __gnu_cxx::__verbose_terminate_handler ()
    at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/vterminate.cc:95
#3  0x00007ffff7926bf6 in __cxxabiv1::__terminate (handler=<optimized out>)
    at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/eh_terminate.cc:38
#4  0x00007ffff7926c23 in std::terminate () at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/eh_terminate.cc:48
#5  0x00007ffff7926e42 in __cxxabiv1::__cxa_throw (obj=0x6030c0, tinfo=0x601de0 <_ZTIPKc@@CXXABI_1.3>, dest=0x0)
    at ../../../../gcc-4.8.5/libstdc++-v3/libsupc++/eh_throw.cc:87
#6  0x0000000000400ad8 in main (arg=999, args=<optimized out>) at a.cpp:41

<optimized out> 解决办法

1、如果有条件,或者能复现,可以换 gcc 编译选项为 -O0

2、如果我们知道 optimized out 对应的地址,在 gdb 中,我们可以强制转换打印该参数

比如我们知道某个变量的地址是 0x12345678,类型是 classA,就可以使用 p (classA*)0x12345678,打印该参数,有时候打印出来还是 0x12345678,可以试试加 * 号,比如p *(classA *)0x12345678

我们如果有代码的上下文,如果能根据我们自己的推断,找到对应的地址,举个例子

#13 0x0000000000efe233 in abc() (last_msg=<optimized out>) at b.cpp:142
#14 0x0000000000efe7ab in test() (m=0x7f940ae69640) at c.cpp:184

比如我想打印 #13 的 last_msg,我根据代码推断,last_msg 传入的是 #14 的 m 的成员变量 msg,那我就能直接 p (classA*)(m->msg)

3、另外一个获取信息的渠道,还是上图,就是比如我切到 #13 使用 info locals 打印当前 abc 函数里面的所有局部变量,有时候 info locals 打印不全,就需要手动 print 每个变量,看看有没有可用信息,获取信息越多对于调试越有帮助

比如

(gdb) f 8
#8  test (i=1) at a.cpp:30
30          test1(i-1);
(gdb) info locals
a = <optimized out>
(gdb) p b
$1 = 1

在 test 函数我定义了两个局部变量 a 和 b, b 定义在 core 发生位置的前面,a 定义在 core 发生位置的后面(可能未执行定义),这个时候 a 也显示 <optimized out>,而 b 其实应该要在 info locals 里打印,很奇怪就没打印,所以手动 print 还是靠谱的,未来得及赋值的变量也会显示成 <optimized out>

一些其他的 gdb 调试技巧

1、有时候我 core 到一些标准库里了,比如 vector 的 clear,可以想想是不是线程读写冲突所致,一般我们认为标准库是可靠的

2、有时候会每次 core 到一些随机的位置(非导致 core 根因的第一现场),可能是其他地方代码错误把内存写坏了,这样可能 core 在奇奇怪怪的位置,这种就没办法,从概率学角度来说,它总有一天会第一次就写入到一个非法位置,我们看到的 core 文件就是第一现场

3、有时候 core 文件大小生成被截断了,这种情况下,运气好也能 gdb 调试

4、如果你不清楚某个类的成员变量有哪些,可以 pt 接类名,或者想打印某个变量的类型,可以用 pt 接变量名

(gdb) pt MyClass
type = class MyClass {
  private:
    int mData;

  public:
    MyClass(int);
    ~MyClass(int);
    void setData(int);
    int getData(void) const;
}

5、关于获取智能指针的原始指针

首先直接打印智能指针,然后尝试强制转换里面的指针(一般不超过两个),示例中的类是 MyClass

# shared_ptr
(gdb) p temp
$1 = {<std::__shared_ptr<MyClass, (__gnu_cxx::_Lock_policy)2>> = {_M_ptr = 0x604058, _M_refcount = {
      _M_pi = 0x604040}}, <No data fields>}
(gdb) pt MyClass
type = class MyClass {
  private:
    int mData;

  public:
    MyClass(int);
    ~MyClass(int);
    void setData(int);
    int getData(void) const;
}
(gdb) p ((MyClass*)0x604058)->mData
$2 = 666

# unique_ptr
(gdb) p temp2
$2 = {
  _M_t = {<std::_Tuple_impl<0ul, MyClass*, std::default_delete<MyClass> >> = {<std::_Tuple_impl<1ul, std::default_delete<MyClass> >> = {<std::_Tuple_impl<2ul>> = {<No data fields>}, <std::_Head_base<1ul, std::default_delete<MyClass>, true>> = {<std::default_delete<MyClass>> = {<No data fields>}, <No data fields>}, <No data fields>}, <std::_Head_base<0ul, MyClass*, false>> = {_M_head_impl = 0x605070}, <No data fields>}, <No data fields>}}
(gdb) p ((MyClass*)0x605070)->mData
$3 = 888

在上面的示例里,实际的 share_ptr 的原始指针被存在了 _M_ptr 里,而  unique_ptr 的原始指针藏在了 _M_t 的 _M_head_impl 里,这个不同的系统库实现不一样,按照我的方法是能找到对应原始指针的

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值