GDB学习笔记[wip]

1. 基础用法

1.1 启动

1.1.1 gdb program

使用gdb 启动应用程序

1.1.1.1 --symbol=symbol_file(-s symbol_file)指定读取symbol文件

可参考objcopy命令生成symbol文件, 参考https://www.jianshu.com/p/7050a8f8841c.

1.1.1.2 --exec=release_exe(-e)

不读取符号信息, 指定release版本的可执行程序
发布程序的流程:

// 先生成带符号表的可执行程序test
// 从可执行程序中生成符号表文件
objcopy --only-keep-debug test test.symbol
// 生成发布程序,不带符号信息
objcopy --strip-debug test test-release
// 使用符号表文件debug不带符号的发布程序
gdb -q --symbol=test.symbol --exec=test-release
1.1.1.3 --core=core_file(-c core_file)

如果程序core dump, 可gdb program [-c] core_file调试core文件

1.1.1.4 attach操作: gdb program ${PID}

如果是在运行的服务程序, 可指定pid进行attach

1.1.2 list / l命令

进入gdb后, 通过list/l命令列出main函数所在文件前10行

1.1.3 退出gdb: quit(q)

1.1.4 设置程序参数: set args ${arg_list}

1.1.5 在gdb中进入shell: shell

1.2 断点操作

1.2.1 打断点

1.2.1.1 指定行号: break(b) 行号

break(b) linenum 在指定行号打断点
break(b) +/- offset 在当前行的后/前offset行打断点

1.2.1.2 指定函数名: break(b) 函数名

对于C++来说, 由于有函数重载, 因此这种情况需要函数签名, 即填写参数信息, 例如:

int func(int a) {
	...
}

int func(int a, double b) {
	...
}

// 在gdb中需要:
b func(int) // 或 b func(int, double)

如果有namespace可写可不写namespace

1.2.1.3 指定文件

break(b) filename:linenum
break(b) filename:function

1.2.1.4 条件断点

break(b) [上述参数] if cond
例如cond为 i5: b main.cpp:func(int) if i5

condition breaknum cond: 修改条件断点的条件(注意没有if了)

1.2.2 断点信息: info break( info b)

在这里插入图片描述
前面一行是断点的编号, 如果针对单个断点操作就可以根据编号指定

1.2.3 观察点(watchpoint)与捕捉点(catchpoint)

观察点: 观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序
捕捉点: 补捉程序运行时的一些事件, 例如载入共享库(动态链接库)或是C++的异常

1.2.3.1 观察点设置

watch expr: 为表达式/变量设置观察点, 一旦值发生变化就会停住
rwatch expr: expr被读时停住程序
awatch expr: expr被读或被写时停住程序
info watchpoints: 查看观察点信息

1.2.3.2 捕捉点设置

catch event, event可以是
throw: 一个C++抛出的异常
catch: 一个C++捕获的异常
exec/fork/vfork/

1.2.4维护停止点

1.2.4.1 clear

clear [file:]function: 清除function的断点
clear [file:]linenum: 清除指定行的断点

1.2.4.2 delete(d)

delete [breakpoints] [range…]: breakpoints关键字可有可无, range为 1-3这样的范围, 也可以为单个断点的编号; 若什么都不指定, 则删除所有的断点

1.2.4.3 disable/enable

disable [breakpoints] [range…]: 用法和delete相同

1.3 流程控制

1.3.1 启动运行: run ( r )

gdb启动程序后, 可以run来让程序开始运行

1.3.2 单步执行: next(n) [count]

单条语句执行, 不进入; count表示执行后面的count条指令再停住

1.3.3 单步进入执行: step(s) [count]

跳入函数中执行. 默认情况下, 如果函数没有调试信息, 则step(s)也不会进入, 可以通过设置set step-mode on, 之后s会跳入无调试信息的函数里继续执行, 然后可通过调试汇编程序的方法继续调试(后续有时间学习汇编相关知识)

1.3.4 跳出当前函数: finish

相对于step, finish会跳出当前所在函数, 并停在函数返回行

1.3.5 继续执行: continue( c ) [ignore-count]

直到断点或退出, ignore-count表示忽略其后的断点次数

1.4 变量操作

1.4.1 打印变量值: print§ 变量名

1.5 堆栈及线程

1.5.1 给特定线程打断点

break(b) breakpoint_pos thread threadno if …
threadno是gdb分配的, 通过info thread查看(非以0开始), 若不指定thread threadno, 则断点在所有线程上.

1.5.2 堆栈查看和跳转

1.5.2.1 查看栈信息

The call stack is divided up into contiguous pieces called stack frames, or frames for short; each frame is the data associated with one call to one function. The frame contains the arguments given to the function, the function’s local variables, and the address at which the function is executing.
Inside your program, stack frames are identified by their addresses.
命令: Usually this address is kept in a register called the frame pointer register while execution is going on in that frame.
命令: backtrace(bt)

(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6

每个帧frame有对应的编号, 栈顶frame为0号
backtrace(bt) n: n为正整数, 表示只打印栈顶下n层的frame信息
backtrace(bt) -n: 打印栈底上n层frame信息

1.5.2.2 栈帧跳转

想要查看堆栈某一层(帧)的信息, 需要切换当前栈位置
命令: frame n
frame addr: This is useful mainly if the chaining of stack frames has been damaged by a bug, making it impossible for gdb to assign numbers properly to all frames.
up n/down n: 向上/下移动n层, 省略n则表示1层
上述命令都会打印出移动到的帧的信息, 若不想打印信息, 则使用select-frame替代frame, up-silently/down-silently替代up/down.

1.5.2.3 查看当前栈帧信息

命令: frame/f, 显示栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。

(gdb) frame
#0  a::func (n=250) at test.cpp:6
6           int sum = 0, I;

命令: info frame/info f, 信息更全面, 主要是内存地址

(gdb) info frame
Stack level 0, frame at 0x7fffffffded0:
 rip = 0x4011dd in a::func (test.cpp:6); saved rip = 0x4012be
 called by frame at 0x7fffffffdf10
 source language c++.
 Arglist at 0x7fffffffdec0, args: n=250
 Locals at 0x7fffffffdec0, Previous frame's sp is 0x7fffffffded0
 Saved registers:
  rbp at 0x7fffffffdec0, rip at 0x7fffffffdec8

命令*: info args, 打印当前调用的函数参数

(gdb) info args 
n = 250

命令: info locals, 打印当前函数所有局部变量和值

(gdb) info locals 
sum = 0
i = 1

1.6 信号

命令: handle signal [keywords…], handle为gdb命令

1.6.1 signal

可以为单个信号如SIGINT, 也可以是一个信号的范围, 如1-5;
可以用all特殊词来表示除了SIGTRAP和SIGINT debugger使用的信号外的其它信号

1.6.2 keywords

Recognized actions include “stop”, “nostop”, “print”, “noprint”,
“pass”, “nopass”, “ignore”, or “noignore”.
Stop means reenter debugger if this signal happens (implies print).
Print means print a message if this signal happens.
Pass means let program see this signal; otherwise program doesn’t know.
Ignore is a synonym for nopass and noignore is a synonym for pass.
Pass and Stop may be combined.

nostop

当被调试的程序收到信号时,GDB不会停住程序的运行,但会打出消息告诉你收到这种信号。

stop

当被调试的程序收到信号时,GDB会停住你的程序。This implies the print keyword as well.

print

当被调试的程序收到信号时,GDB会显示出一条信息。

noprint

当被调试的程序收到信号时,GDB不会告诉你收到信号的信息。This implies the nostop keyword as well.

pass/noignore

当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程序处理 or else it may terminate if the signal is fatal and not handled.

nopass/ignore

当被调试的程序收到信号时,GDB不会让被调试程序来处理这个信号。

1.6.3 查看信号

info signals, info handle

1.6.4 给被调试程序发信号

signal 信号, 例如signal SIGINT

2. 进阶用法

2.1 查看源代码的内存

2.1.1 info line命令

info line [文件名:]行号/[文件名:]函数名
打印所指定的源码在运行时的内存地址

(gdb) info line test.cpp:func(int)
Line 5 of "test.cpp" starts at address 0x4011d6 <a::func(int)>
   and ends at 0x4011dd <a::func(int)+7>.

2.1.2 disassemble

disassemble [modifiers] [start, end], 显示汇编代码, modifiers可以是/m, /s, /r

(gdb) disassemble 
Dump of assembler code for function a::func(int):
   0x00000000004011d6 <+0>:     push   %rbp
   0x00000000004011d7 <+1>:     mov    %rsp,%rbp
   0x00000000004011da <+4>:     mov    %edi,-0x14(%rbp)
   0x00000000004011dd <+7>:     movl   $0x0,-0x4(%rbp)
=> 0x00000000004011e4 <+14>:    movl   $0x1,-0x8(%rbp)
   0x00000000004011eb <+21>:    cmpl   $0x64,-0x8(%rbp)
   0x00000000004011ef <+25>:    jg     0x4011fd <a::func(int)+39>
   0x00000000004011f1 <+27>:    mov    -0x8(%rbp),%eax
   0x00000000004011f4 <+30>:    add    %eax,-0x4(%rbp)
   0x00000000004011f7 <+33>:    addl   $0x1,-0x8(%rbp)
   0x00000000004011fb <+37>:    jmp    0x4011eb <a::func(int)+21>
   0x00000000004011fd <+39>:    mov    -0x4(%rbp),%eax
   0x0000000000401200 <+42>:    pop    %rbp
   0x0000000000401201 <+43>:    retq   
End of assembler dump.

/m: With a /m modifier, source lines are included (if available). This view is “source centric”: the output is in source line order, regardless of any optimization that is present. Only the main source file is displayed, not those of, e.g., any inlined functions. This modifier hasn’t proved useful in practice and is deprecated in favor of /s.

(gdb) disassemble /m
Dump of assembler code for function a::func(int):
5       int func(int n) {
   0x00000000004011d6 <+0>:     push   %rbp
   0x00000000004011d7 <+1>:     mov    %rsp,%rbp
   0x00000000004011da <+4>:     mov    %edi,-0x14(%rbp)

6           int sum = 0, i;
   0x00000000004011dd <+7>:     movl   $0x0,-0x4(%rbp)

7           for (i = 1; i <= 100; ++i) {
=> 0x00000000004011e4 <+14>:    movl   $0x1,-0x8(%rbp)
   0x00000000004011eb <+21>:    cmpl   $0x64,-0x8(%rbp)
   0x00000000004011ef <+25>:    jg     0x4011fd <a::func(int)+39>
   0x00000000004011f7 <+33>:    addl   $0x1,-0x8(%rbp)
   0x00000000004011fb <+37>:    jmp    0x4011eb <a::func(int)+21>

8               sum += i;
   0x00000000004011f1 <+27>:    mov    -0x8(%rbp),%eax
   0x00000000004011f4 <+30>:    add    %eax,-0x4(%rbp)

9           }
10          return sum;
   0x00000000004011fd <+39>:    mov    -0x4(%rbp),%eax

11      }
   0x0000000000401200 <+42>:    pop    %rbp
   0x0000000000401201 <+43>:    retq   

End of assembler dump.

/s: With a /s modifier, source lines are included (if available). This differs from /m in two important respects:

  • the output is still in pc address order, and
  • file names and contents for all relevant source files are displayed.
(gdb) disassemble /s
Dump of assembler code for function a::func(int):
test.cpp:
5       int func(int n) {
   0x00000000004011d6 <+0>:     push   %rbp
   0x00000000004011d7 <+1>:     mov    %rsp,%rbp
   0x00000000004011da <+4>:     mov    %edi,-0x14(%rbp)

6           int sum = 0, i;
   0x00000000004011dd <+7>:     movl   $0x0,-0x4(%rbp)

7           for (i = 1; i <= 100; ++i) {
=> 0x00000000004011e4 <+14>:    movl   $0x1,-0x8(%rbp)
   0x00000000004011eb <+21>:    cmpl   $0x64,-0x8(%rbp)
   0x00000000004011ef <+25>:    jg     0x4011fd <a::func(int)+39>

8               sum += i;
   0x00000000004011f1 <+27>:    mov    -0x8(%rbp),%eax
   0x00000000004011f4 <+30>:    add    %eax,-0x4(%rbp)

7           for (i = 1; i <= 100; ++i) {
   0x00000000004011f7 <+33>:    addl   $0x1,-0x8(%rbp)
   0x00000000004011fb <+37>:    jmp    0x4011eb <a::func(int)+21>

9           }
10          return sum;
   0x00000000004011fd <+39>:    mov    -0x4(%rbp),%eax

11      }
   0x0000000000401200 <+42>:    pop    %rbp
   0x0000000000401201 <+43>:    retq   
End of assembler dump.

指定start和end, 注意start和end是内存地址

(gdb) disassemble /s 0x00000000004011e4, 0x00000000004011fb
Dump of assembler code from 0x4011e4 to 0x4011fb:
test.cpp:
7           for (i = 1; i <= 100; ++i) {
=> 0x00000000004011e4 <a::func(int)+14>:        movl   $0x1,-0x8(%rbp)
   0x00000000004011eb <a::func(int)+21>:        cmpl   $0x64,-0x8(%rbp)
   0x00000000004011ef <a::func(int)+25>:        jg     0x4011fd <a::func(int)+39>

8               sum += i;
   0x00000000004011f1 <a::func(int)+27>:        mov    -0x8(%rbp),%eax
   0x00000000004011f4 <a::func(int)+30>:        add    %eax,-0x4(%rbp)

7           for (i = 1; i <= 100; ++i) {
   0x00000000004011f7 <a::func(int)+33>:        addl   $0x1,-0x8(%rbp)
End of assembler dump.

2.2 查看运行时数据

程序停住时可以使用pring§或inspect命令, 格式: print expr, print /f expr, print, print /f
/f指输出格式; expr如果省略, 则打印上次的值, 这样方便检查同一个变量/表达式的值

2.2.1 表达式expr

可接受一个表达式, gdb会根据当前程序上下文来计算此表达式的值
例如想以某类型的形式查看某个内存的值:

(gdb) p &i
$5 = (int *) 0x7fffffffdeec

p *((int*)0x7fffffffdeec) // 以int的方式查看0x7fffffffdeec的值
$7 = 101

p *((float*)0x7fffffffdeec) // 以float方式查看
$8 = 1.41531145e-43

2.2.2 查看数组

命令: p *array@len, array即为数组首地址, len为要打印的数据长度
栈上定长数组和动态分配的数组略有不同, 对于栈上定长数组, p arr即可打印出整个数组元素, 当然也可以指定[i]查看某个元素; 对于动态分配的数组, 则需要像上面的命令那样使用.
例如对于栈上数组:

int arr[4] { 1, 2, 3, 4 };

gdb查看数组数据:

(gdb) p arr
$1 = {1, 2, 3, 4}
(gdb) p arr[0]
$2 = 1
(gdb) p *arr
$3 = 1
(gdb) p *arr@3
$4 = {1, 2, 3}

对于动态分配的数组:

auto arr = new int[4];
for (int i = 0; i < 4; ++i) {
        arr[i] = i;
}

gdb查看数组数据:

(gdb) p arr
$1 = (int *) 0x417000
(gdb) p *arr
$2 = 0
(gdb) p *arr@4
$3 = {0, 1, 2, 3}
(gdb) q

2.2.3 输出格式/f

f支持的格式:
d:有符号十进制数
u:无符号十进制数
x 或 p:十六进制数
o:八进制数
f :浮点数
c:字符
s:字符串
x: 按十六进制格式显示变量
a: 按十六进制格式显示变量
t: 按二进制格式显示变量

2.3 查看内存

使用examine(简写为x)来查看某个内存地址的值. x命令的语法:
x[/nfu] addr, nfu是可选参数

  • n: 正整数, 表示显示内存的长度, 即从当前地址向后显示几个地址单元的内容
  • f: 显示格式, 参考上面👆🏻, 如果是字符串, 格式可以是s; 如果是指定地址, f可以为i
  • u: 以多少字节作为一个内存单元, 默认为4字节, 可以以字符表示: b=1 byte, h=2 bytes, w=4 bytes, g=8 bytes

例如对于下面的int 数组

    auto arr = new int[4];
    for (int i = 0; i < 4; ++i) {
        arr[i] = i;
    }

x的使用(小端字节序):

(gdb) p arr
$11 = (int *) 0x417000
(gdb) x/1d 0x417000
0x417000:       0
(gdb) x/2d 0x417000
0x417000:       0       0
(gdb) x/3a 0x417000
0x417000:       0x100000000     0x300000002
0x417010:       0x0
(gdb) x/4ab 0x417000
0x417000:       0x0     0x0     0x0     0x0
(gdb) x/4ah 0x417000
0x417000:       0x0     0x0     0x1     0x0
(gdb) x 0x417000
0x417000:       0x0
(gdb) x/2 0x417000
0x417000:       0x0     0x
(gdb) x/1d 0x417004
0x417004:       1
(gdb) x/4ab 0x417004
0x417004:       0x1     0x0     0x0     0x0
(gdb) x/1ab 0x417004
0x417004:       0x1
(gdb) x/1ab 0x417003
0x417003:       0x0
(gdb) x/1ab 0x417004
0x417004:       0x1
(gdb) x/8ab 0x417000
0x417000:       0x0     0x0     0x0     0x0     0x1     0x0     0x0     0x0
(gdb) x/4aw 0x417000
0x417000:       0x0     0x1     0x2     0x3
(gdb) x/i $pc
=> 0x401328 <main()+122>:       movl   $0x1,-0x60(%rbp)

2.4 自动显示

display/fmt expr: 当程序停止时, 自动显示expr的值; 格式s和I同样被display支持, 一个很常用的命令:
display/i $pc, 显示pc寄存器的指令
info display: 查看display设置
undisplay dnums…/delete display dnums: 删除编号对应的display, 可用空格或范围表示, 如2-5
disable/enable display dnums: 不删除

2.5 改变程序的执行

2.5.1 修改变量值

p x=4, x=4是C/C++的语法
set var x=4, 推荐这种使用方法

2.5.2 强制函数返回

return
return expr: 如果当前在某个断点中,还没有语句没执行完, 使用return会忽略后续还未执行的语句并返回

3 gdb实战记录

3.1 一次死锁问题使用gdb定位

死锁后进程卡住,此时可以先用gdb attach到进程,一般需要sudo

3.1.1 使用gdb看堆栈结果

(gdb) bt
#0  __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
#1  0x00007f0988950e42 in __GI___pthread_mutex_lock (mutex=0x116b908) at ../nptl/pthread_mutex_lock.c:115
#2  0x00007f09898bcfda in __gthread_mutex_lock (__mutex=0x116b908) at /usr/include/x86_64-linux-gnu/c++/5/bits/gthr-default.h:748
#3  0x00007f09898bd064 in __gthread_recursive_mutex_lock (__mutex=0x116b908) at /usr/include/x86_64-linux-gnu/c++/5/bits/gthr-default.h:810
#4  0x00007f09898c0b4a in std::recursive_timed_mutex::lock (this=0x116b908) at /usr/include/c++/5/mutex:315
#5  0x00007f09898c51b4 in std::lock_guard<std::recursive_timed_mutex>::lock_guard (this=0x7ffcfa2506d0, __m=...) at /usr/include/c++/5/mutex:386
#6  0x00007f098998b42d in eprosima::fastrtps::rtps::StatelessReader::matched_writer_remove (this=0x116b7e0, writer_guid=..., removed_by_lease=false)
    at /home/sensetime/ws/Fastdds/Fast-DDS/src/cpp/rtps/reader/StatelessReader.cpp:178
#7  0x00007f0989c8dd34 in eprosima::fastrtps::rtps::EDP::unpairWriterProxy (this=0xf68990, participant_guid=..., writer_guid=..., removed_by_lease=false)
    at /home/sensetime/ws/Fastdds/Fast-DDS/src/cpp/rtps/builtin/discovery/endpoint/EDP.cpp:540
#8  0x00007f0989c76453 in eprosima::fastrtps::rtps::PDP::removeWriterProxyData (this=0xf67520, writer_guid=...)
    at /home/sensetime/ws/Fastdds/Fast-DDS/src/cpp/rtps/builtin/discovery/participant/PDP.cpp:688
#9  0x00007f0989c9ad66 in eprosima::fastrtps::rtps::EDPSimple::removeLocalWriter (this=0xf68990, W=0x1166640)
    at /home/sensetime/ws/Fastdds/Fast-DDS/src/cpp/rtps/builtin/discovery/endpoint/EDPSimple.cpp:718
#10 0x00007f0989c6f134 in eprosima::fastrtps::rtps::BuiltinProtocols::removeLocalWriter (this=0xf660c0, W=0x1166640)
    at /home/sensetime/ws/Fastdds/Fast-DDS/src/cpp/rtps/builtin/BuiltinProtocols.cpp:273
#11 0x00007f09899c5691 in eprosima::fastrtps::rtps::RTPSParticipantImpl::deleteUserEndpoint (this=0xf41220, p_endpoint=0x1166640)
    at /home/sensetime/ws/Fastdds/Fast-DDS/src/cpp/rtps/participant/RTPSParticipantImpl.cpp:1711

// 看到f 5,看下__m的内容
(gdb) f 5
#5  0x00007f09898c51b4 in std::lock_guard<std::recursive_timed_mutex>::lock_guard (this=0x7ffcfa2506d0, __m=...) at /usr/include/c++/5/mutex:386
386           { _M_device.lock(); }
(gdb) info args 
this = 0x7ffcfa2506d0
__m = @0x116b908: {<std::__recursive_mutex_base> = {_M_mutex = {__data = {__lock = 2, __count = 1, __owner = 653792, __nusers = 1, __kind = 1, __spins = 0, __elision = 0, 
        __list = {__prev = 0x0, __next = 0x0}}, __size = "\002\000\000\000\001\000\000\000\340\371\t\000\001\000\000\000\001", '\000' <repeats 22 times>, 
      __align = 4294967298}}, <std::__timed_mutex_impl<std::recursive_timed_mutex>> = {<No data fields>}, <No data fields>}

接下来找owner对应的lwp是哪个线程,即和哪个线程死锁了,使用info threads即可:

(gdb) info threads 
  Id   Target Id         Frame 
* 1    Thread 0x7f098b80f780 (LWP 653759) "test_method_dds" 0x00007f09898c51b4 in std::lock_guard<std::recursive_timed_mutex>::lock_guard (this=0x7ffcfa2506d0, __m=...)
    at /usr/include/c++/5/mutex:386
  2    Thread 0x7f09853b1700 (LWP 653782) "test_method_dds" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:225
  3    Thread 0x7f0984bb0700 (LWP 653783) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  4    Thread 0x7f097ffff700 (LWP 653784) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  5    Thread 0x7f097f7fe700 (LWP 653785) "test_method_dds" __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
  6    Thread 0x7f097effd700 (LWP 653786) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  7    Thread 0x7f097e7fc700 (LWP 653789) "test_method_dds" pthread_cond_wait@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
  8    Thread 0x7f097dffb700 (LWP 653792) "test_method_dds" __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
  9    Thread 0x7f097d7fa700 (LWP 653797) "test_method_dds" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:225
  10   Thread 0x7f097cff9700 (LWP 653798) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  11   Thread 0x7f097c7f8700 (LWP 653799) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  12   Thread 0x7f097bff7700 (LWP 653800) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  13   Thread 0x7f097b7f6700 (LWP 653801) "test_method_dds" 0x00007f09889578f3 in recvfrom () at ../sysdeps/unix/syscall-template.S:84
  14   Thread 0x7f097aff5700 (LWP 653802) "test_method_dds" pthread_cond_wait@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
  15   Thread 0x7f097a7f4700 (LWP 653826) "test_method_dds" 0x00007f0988956827 in futex_abstimed_wait_cancelable (private=128, abstime=0x0, expected=0, 
    futex_word=0x7f098b82d0d8) at ../sysdeps/unix/sysv/linux/futex-internal.h:205
  16   Thread 0x7f0979ff3700 (LWP 653827) "test_method_dds" 0x00007f0988956827 in futex_abstimed_wait_cancelable (private=128, abstime=0x0, expected=0, 
    futex_word=0x7f098b8230d8) at ../sysdeps/unix/sysv/linux/futex-internal.h:205
  17   Thread 0x7f09797f2700 (LWP 653828) "test_method_dds" 0x00007f0988956827 in futex_abstimed_wait_cancelable (private=128, abstime=0x0, expected=0, 
    futex_word=0x7f098b6c70d8) at ../sysdeps/unix/sysv/linux/futex-internal.h:205
  18   Thread 0x7f0978ff1700 (LWP 653845) "test_method_dds" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:225
  19   Thread 0x7f096bfff700 (LWP 653850) "test_method_dds" pthread_cond_wait@@GLIBC_2.3.2 () at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
  20   Thread 0x7f096b7fe700 (LWP 653851) "test_method_dds" 0x00007f0988956827 in futex_abstimed_wait_cancelable (private=128, abstime=0x0, expected=0, 
    futex_word=0x7f098b6bf0d8) at ../sysdeps/unix/sysv/linux/futex-internal.h:205

可以看到是和thread 8死锁了,找到和哪个线程死锁了接下来就好分析了

3.1.2 mutex结构

注意看到__m的内容中,可以看到**__owner**, 是当前持有该mutex的线程lwp号,关于pthread_mutex的结果:

typedef union
{
  struct __pthread_mutex_s
  {
    int __lock;
    unsigned int __count;
    int __owner;
#ifdef __x86_64__
    unsigned int __nusers;
#endif
    /* KIND must stay at this position in the structure to maintain
       binary compatibility.  */
    int __kind;
#ifdef __x86_64__
    short __spins;
    short __elision;
    __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV	1
/* Mutex __spins initializer used by PTHREAD_MUTEX_INITIALIZER.  */
# define __PTHREAD_SPINS             0, 0
#else
    unsigned int __nusers;
    __extension__ union
    {
      struct
      {
	short __espins;
	short __elision;
# define __spins __elision_data.__espins
# define __elision __elision_data.__elision
# define __PTHREAD_SPINS         { 0, 0 }
      } __elision_data;
      __pthread_slist_t __list;
    };
#endif
  } __data;
  char __size[__SIZEOF_PTHREAD_MUTEX_T];
  long int __align;
} pthread_mutex_t;
  • __lock: 表示mutex状态,0表示未占用,1或2表示占用,其中2表示除了已占用锁的地方外,此处也在尝试拿锁
  • __count: 用于可重入锁,记录owner线程持有锁的次数
  • __owner: 持有该锁的线程lwp号
    **如果我们只看到mutex的地址,就需要结合mutex的结构源码使用x命令去查看了, 或者转换指针类型后print:
// 此时fastdds库非debug,看不到刚才的f 5
(gdb) bt
#0  __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
#1  0x00007fe8d2e70e42 in __GI___pthread_mutex_lock (mutex=0x15bc268) at ../nptl/pthread_mutex_lock.c:115
#2  0x00007fe8d357e436 in eprosima::fastrtps::rtps::StatelessReader::matched_writer_remove(eprosima::fastrtps::rtps::GUID_t const&, bool) ()
   from /home/sensetime/ws/autosar/senselotu_cm/3rdparty/fastrtps/lib/x86_64_ub16/libfastrtps.so.2.6
#3  0x00007fe8d378b0c5 in eprosima::fastrtps::rtps::EDP::unpairWriterProxy(eprosima::fastrtps::rtps::GUID_t const&, eprosima::fastrtps::rtps::GUID_t const&, bool) ()
   from /home/sensetime/ws/autosar/senselotu_cm/3rdparty/fastrtps/lib/x86_64_ub16/libfastrtps.so.2.6
#4  0x00007fe8d3778262 in eprosima::fastrtps::rtps::PDP::removeWriterProxyData(eprosima::fastrtps::rtps::GUID_t const&) ()
   from /home/sensetime/ws/autosar/senselotu_cm/3rdparty/fastrtps/lib/x86_64_ub16/libfastrtps.so.2.6


(gdb) x /16xw 0x15bc268
0x15bc268:      0x00000002      0x00000001      0x000c9bc4      0x00000001
0x15bc278:      0x00000001      0x00000000      0x00000000      0x00000000
0x15bc288:      0x00000000      0x00000000      0x015bb0b0      0x00000000
0x15bc298:      0x015bb0a0      0x00000000      0x015bc3b0      0x00000000

3.2 gdb查看STL容器内容

在一些低版本的gdb中,可能无法直观的打印STL容器的内容,需要对gdb做一些设置,参考这篇文章:
https://blog.csdn.net/witton/article/details/103188120
总的来说就是Clang的libcxx可以从https://github.com/llvm/llvm-project/tree/main/libcxx/utils/gdb下载整个libcxx目录
GCC的libstdc++可以从https://github.com/gcc-mirror/gcc/tree/master/libstdc%2B%2B-v3/python下载整个libstdcxx目录
然后在home目录创建一个.gdbinit文件,内容为:

python
import sys
sys.path.insert(0, '/home/admin/gdbscripts')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)

from libcxx.printers import register_libcxx_printer_loader
register_libcxx_printer_loader()
end

需要注意的是,需要定义宏:

_GLIBCXX_DEBUG

如果是clang,还需要额外定义

_LIBCPP_DEBUG=1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值