GDB调试程序的常用命令

1. GDB 简介:

GDB是GNU开源组织发布的一个强大的UNIX下的 程序调试工具
虽然没有IDE那些图形化调试工具直观,但是要比IDE具有更强大的功能。

2. GDB能做的事:(4个功能)

  1. 启动你的程序;(按照你的要求随你所有的运行程序)
  2. 在断点处停住;(断点、观察点、捕捉点,断点可以是条件判断)
  3. 停住时检查程序中发生的事情;(查看变量的值)
  4. 动态改变程序的执行环境。(修改程序中的变量、发送信号等)

3. GDB关于断点的操作:

3.1 维护断点:

(1)设置断点:

break 16        //在程序的某一行停住
break func      //在某个函数停住

(2)查看断点:

info break          //查看 所有的 断点信息
info break [num]    //查看 num 指定的断点信息

(3)禁用/启用 断点:

disable [breakpoint_num]  
enable [breakpoint_num]     

(4)删除断点:

clear
clear [location]
clear [func]       
//clear用于删除指定 函数名、或者指定行号 上的断点,如不指定则删除所有断点

delete
delete [breakpoint_num]     
//delete用于删除指定的 断点num(用info break可以查看到断点序号),如不指定则删除所有

3.2 条件断点:

普通的设置断点的方式,是 “break命令 + 行号/函数名”,这样当程序运行到指定的这一行 或者 这个函数时,就会自动停下来。

在普通的停止控制基础上,GDB支持一种高级的控制程序停止的方法:
在设置断点时,同时为这个断点设置一个 “停止条件”,当程序运行到断点处,只有GDB判断条件满足时,才会将程序停下来。
(三种停止点中只有 Break 和 Watch 命令 支持 if, Catch目前不支持if。)

例如:

break 10 if index == 3          
//在程序的第10行设置一个“条件断点”,当程序运行到第10行,只有变量a的值满足=10的条件,程序才停下来
//程序的第10行的表达式中可以不包含变量a

break func if index != 10       
//在程序的func函数处设置一个条件断点,当满足 index!=10 的条件时程序才会停下来

此外,一个 “条件断点” 的停止条件同样还支持 判断条件 的修改, 使用 condition 命令:

//1. 修改 断点条件:
//将断点号的停止条件修改为 expression
condition  <break_number>  <expresion>    

//2. 删除 断点条件:
//清除断点号上的判断条件,但是断点仍然存在,成为了一个“逢断必停”的普通断点
condition  <break_number>           

例如:

(gdb) break 10 if a == 5                //设置一个条件断点,停止条件是 “a==5”                               
Breakpoint 1 at 0x400838: file 1.cpp, line 10.

(gdb) info breakpoints                  //查看断点信息上的停止条件
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400838 in main() at 1.cpp:10
	stop only if a == 5

(gdb) condition 1 a == 9                //修改断点 1 的条件为 “a == 9(gdb) info breakpoints                  //查看发现条件已更改
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400838 in main() at 1.cpp:10
	stop only if a == 9

(gdb) condition 1                       //清除断点上的判断条件,但断点本身依然保留
Breakpoint 1 now unconditional.

(gdb) info breakpoints                  //查看断点信息,发现确实 “stop only if...” 条件已经没有了,断点还在
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400838 in main() at 1.cpp:10

3.3 为断点设置附加的运行命令:

给 断点 附加一些 “自动化” 执行的命令,当程序运行到断点并停住时,就会自动执行这些预先设定好的命令,
达到“自动化调试”的目的。

使用 commands 命令:

commands <break_number> 
> ...command_list...
> end

举例:

(gdb) commands 3                    //在 break_number = 3 的断点上设置附加的命令
Type commands for breakpoint(s) 3, one per line.    //gdb会提示让你输入具体的命令,一行一个,以另起一行的“end”作为结束标志
End with a line saying just "end".
>printf "b is %d\n",b
>end

(gdb) run                          //设置好附加命令后,run开始运行程序
Starting program: /home/10222385@zte.intra/桌面/test_1/1 
10
90

Breakpoint 1, print_vector (v=std::vector of length 10, capacity 10 = {...}, a=@0x7fffffffda84: 90) at 1.cpp:7
7	    a = 7;
b is 0                          //可以看到程序在 breakpint 1 位置停了下来,并打印了事先设定好的语句:“b is 0

注意: commands 中的printf 命令不需要加括号 “()”。

3.4 如何给C++中类的成员函数打断点:

如果想要给C++类的成员函数打上断点,直接break命令加函数名不行,GDB识别不到这个函数,需要使用 “break + 类名::函数名” 的方式。

举例:

/*
假设有如下的类:
class Solution {
public:
	char firstUniqChar(string s) {
        char result = ' ';
        map<char, int> m;
        for(int i = 0; i < s.length(); i++) {
            m[s[i]]++;
        }
        return result;
    }
};
*/


//给Solution类的成员函数"firstUniqChar()" 打上断点:
(gdb)break Solution::firstUniqChar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)

4. GDB中的停止点:

在GDB中,有以下几种方式可能会使程序停止下来:

  1. 断点; (BreakPoint)
  2. 观察点; (WatchPoint)
  3. 捕捉点; (CatchPoint)
  4. 信号;(Signals)
  5. 线程停止; (ThreadStops)

如果要恢复程序运行,使用命令:

(gdb) continue

程序将继续运行直至下一个断点位置。

除了BreakPoints打断点的方式外,还可以使用 观察点、捕捉点、信号 的方式让程序暂停住。

4.1 观察点:(WatchPoint)

观察点一般用来观察某个 “表达式(变量也是一种表达式)的值” 是否有变化,
如果有变化,马上停住程序。

关于观察点的操作:

(1)设置观察点:

watch <expr>        
//为变量/表达式 expr 设置一个观察点,当expr的值有变化时,停住程序

rwatch <expr>       
//“读观察点”,当expr被读时,停住程序

awatch <expr>       
//“读写观察点”,当expr被读或被写时,停住程序

(2)查看观察点:

info watchpoints    //列出当前所有的观察点

使用 info breakpoints 命令同样可以打印出程序中的观察点信息,使用 info watchpoints 的好处可能是当我们只想关注观察点的信息时,使用这个命令不必受断点打印的干扰。

4.2 捕捉点:(CatchPoint)

捕捉点用于设置捕捉 程序运行时的一些事件,
例如:发生了C++中的抛出异常、载入了共享库(动态链接库)等事件。

使用命令:

catch <event>

tcatch <event>  //只设置一次捕捉点,当程序停住后,这个点被自动删除

event可以是:

  1. throw: 抛出了一个事件
  2. catch: 捕捉到了一个事件
  3. exec: 调用了 系统调用 exec
  4. fork: 调用了 系统调用 fork
  5. vfork: 调用了 系统调用 vfork
  6. load: 载入共享库
  7. unload: 卸载共享库

5. 启动GDB的方式:

//1. 普通方式,用于调试一个程序:
gdb <program>


//2. 调试core文件:
//用于调试一个运行程序和core文件,core文件是程序非法执行后core dump产生的文件
gdb <program> core  


//3. 使用GDB “attach”到一个正在运行的服务程序上去,并调试它:
gdb <program> <PID>
或者:
gdb attach <PID>

6. 程序运行流程的控制:

r       		//run,开始运行
start			//start

n       		//next,下一行
next [count]                //可以控制前进的行数
//"next" 命令列出来的 “下一条命令”,是程序 准备执行 “但是还没有执行” 的下一条命令。


c       		//continue,继续运行程序,直到下一个断点处
continue [ignore-count]     //正常情况下continue遇到断点后会停下来,ignore-count 表示忽略其后的断点次数

s       		//step,进入函数
step [count]


fnish   		//结束这个函数。当不小心step进入了某个函数,使用finish快速退出这个函数
 				//finish命令用于结束当前函数,但是,只能用于非main函数,
	 			//如果想要在main函数中一直执行,可以用 continue 命令


set args 10         //指定运行时参数
show args           //查看设置好的运行参数

run 和 start 的区别:

start 会在main函数处自动停住,
run如果没有断点就会一直执行下去,
start就相当于是在main函数打了断点的run。

7. 控制程序的执行路线:

7.1 修改变量的值:(print/set var)

(gdb) print x = 4		//将变量x的值修改为4

(gdb) set var x = 5		//set command可能与某些GDB内置的命令冲突,所以当用于改变变量的值时,需要使用“set var”格式

7.2 查看变量的类型:(whatis)

(gdb)whatis width
type = double

(gdb)whatis a
type = int

7.3 跳转执行:(jump)

jump <linespec>		//跳转到指定的行上
jump <file:line>	//跳转到指定的文件的指定的行上
jump +num			//跳转从当前位置的num个偏移量

jump <address>		//跳转到指定代码行的内存地址

set $pc = 0x485		//直接修改PC指针

7.4 产生信号量:(signal)

使用signal命令,可以产生一个信号量给被调试的程序。

语法:

signal <signal>
//UNIX的系统信号通常从1到15,所以 <signal> 取值也在这个范围

signal命令 和 shell的kill命令不同,系统的 kill 命令发喜好给被调试程序时,是由GDB截获的,而signal命令所发出的信号是直接发给被调试程序的

7.5 强制函数返回:(return)

如果你的调试断点在某个函数中,并且还有语句没有执行完,可以选择使用return命令强制函数忽略还没有执行的语句并返回。

使用命令:

return					//返回空
return <expression>		//返回结果<expression>

7.6 强制调用函数:(call)

call <expr>

8. 查看栈信息:(bt、frame 命令)

bt				//打印出当前的调用栈信息
等价于:
backtrace


bt <n>		//n是一个正整数,表示只打印栈顶上的n层的栈信息

bt <-n>		//n是一个负整数,表示只打印栈底下的n层的栈信息


frame		//

frame <n>	//切换到 第n层 栈帧上

info frame	//查看当前栈帧的信息


info args		//打印出当前函数的 “参数名” 及其值

info locals		//打印出当前函数中所有 “局部变量” 及其值

info catch		//打印出当前函数中的 “异常处理” 信息

举例:

(关键点在于 info frame 命令)


(gdb) info threads		//先查看一下当前有多少个线程,以及每个线程的信息
  Id   Target Id                              Frame 
* 1    Thread 0x7ffff7a4d740 (LWP 307040) "1" 0x00005555555555d5 in main () at 1.cpp:82
  2    Thread 0x7ffff7a4c700 (LWP 307044) "1" futex_wait_cancelable (private=<optimized out>, expected=0, 
    futex_word=0x5555555591c8 <m_cond+40>) at ../sysdeps/nptl/futex-internal.h:183
  3    Thread 0x7ffff724b700 (LWP 307045) "1" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78


(gdb) thread 1			//切换到“线程1” 上面去
[Switching to thread 1 (Thread 0x7ffff7a4d740 (LWP 307040))]
#1  0x00005555555555d5 in main () at 1.cpp:82
82	    pthread_join(tid_1, NULL);


(gdb) bt				//查看一下“线程1” 的堆栈信息
#0  __pthread_join (threadid=140737348159232, thread_return=0x0) at pthread_join.c:23
#1  0x00005555555555d5 in main () at 1.cpp:82


(gdb) info frame		//查看当前所在“栈帧”的信息
Stack level 0, frame at 0x7fffffffe390:
 rip = 0x7ffff7f9a9e0 in __pthread_join (pthread_join.c:23); saved rip = 0x5555555555d5
 called by frame at 0x7fffffffe3c0
 source language c.
 Arglist at 0x7fffffffe380, args: threadid=140737348159232, thread_return=0x0
 Locals at 0x7fffffffe380, Previous frame`s sp is 0x7fffffffe390
 Saved registers:
  rip at 0x7fffffffe388
/*
可以看到当前所在栈帧为“Level 0”(这是默认的栈帧),能到对应的RIP值、调用函数(pthread_join)等详细信息
*/


(gdb) frame 1		//从上面的“bt”调用栈中可以看到,当前线程的调用栈中有两个栈帧,这次切换到 栈帧1 上面去
#1  0x00005555555555d5 in main () at 1.cpp:82
82	    pthread_join(tid_1, NULL);


(gdb) info frame	//再执行“info frame”,看到的就是“栈帧1”上的信息了
Stack level 1, frame at 0x7fffffffe3c0:
 rip = 0x5555555555d5 in main (1.cpp:82); saved rip = 0x7ffff7be30b3
 caller of frame at 0x7fffffffe390
 source language c++.
 Arglist at 0x7fffffffe388, args: 
 Locals at 0x7fffffffe388, Previous frame's sp is 0x7fffffffe3c0
 Saved registers:
  rbp at 0x7fffffffe3b0, rip at 0x7fffffffe3b8



(gdb) info args
No arguments.

(gdb) info locals 
tid_1 = 140737348159232
tid_2 = 140737339766528

(gdb) info catch
Undefined info command: "catch".  Try "help info".

9. 查看源程序:(list 命令)

9.1 显示源代码:(list命令)


list <linenum>		//显示程序第linenum行的【周围的】源程序
list <function>		//显示函数名为function的函数的【周围的】源程序


list				//显示当前行 后面的源程序
lislt -				//显示当前行 前面的源程序



set listsize <count>	//设置一次显示源代码的行数
show listsize			//查看当前listsize的设置



list <first>, <last>	//显示从first行到last行之间的源代码
						//注意中间要加 “,” 逗号
list , <last>			//显示从当前行到last行之间的源代码
list +					//往后显示源代码

9.2 搜索源代码:(search命令)

forward-search <regexp>		//向前搜索
search <regexp>				//全文搜索
reverse-search <regexp>		//反向搜索

其中,<regexp> 是正则表达式。

但是我在实际中使用 search * main * 并没有 搜索到。

9.3 源代码的内存:(info line、disassemble func)

(gdb)info line				//查看当前位置的源代码在内存中地址
(gdb)info line test.c:func	//查看指定文件中的函数的在内存中的地址


(gdb)disassemble func		//可以查看函数的汇编指令!!
Dump of assembler code for function func:
0x8048450 <func>:       push   %ebp
        0x8048451 <func+1>:     mov    %esp,%ebp
        0x8048453 <func+3>:     sub    $0x18,%esp
        0x8048456 <func+6>:     movl   $0x0,0xfffffffc(%ebp)
        0x804845d <func+13>:    movl   $0x1,0xfffffff8(%ebp)
        0x8048464 <func+20>:    mov    0xfffffff8(%ebp),%eax
        0x8048467 <func+23>:    cmp    0x8(%ebp),%eax
        0x804846a <func+26>:    jle    0x8048470 <func+32>
        0x804846c <func+28>:    jmp    0x8048480 <func+48>
        0x804846e <func+30>:    mov    %esi,%esi
        0x8048470 <func+32>:    mov    0xfffffff8(%ebp),%eax
        0x8048473 <func+35>:    add    %eax,0xfffffffc(%ebp)
        0x8048476 <func+38>:    incl   0xfffffff8(%ebp)
        0x8048479 <func+41>:    jmp    0x8048464 <func+20>
        0x804847b <func+43>:    nop
        0x804847c <func+44>:    lea    0x0(%esi,1),%esi
        0x8048480 <func+48>:    mov    0xfffffffc(%ebp),%edx
        0x8048483 <func+51>:    mov    %edx,%eax
        0x8048485 <func+53>:    jmp    0x8048487 <func+55>
        0x8048487 <func+55>:    mov    %ebp,%esp
        0x8048489 <func+57>:    pop    %ebp
        0x804848a <func+58>:    ret
        End of assembler dump.

10. 打印变量:

10.1 如何打印全局变量和数组:

有两个特殊的打印场景需要注意:

  1. 当全局变量与局部变量重名,在局部变量所在的作用域内,如何打印出全局变量? “::”
  2. 动态数组(malloc)和静态数据如何打印?“*array@len”
//假设有全局变量和局部变量:int global_int = 4; 在func函数中:

(gdb)print global_int		//打印作用域内的局部变量
(gdb)print ::global_int		//打印全局变量
(gdb)print file::global_int	//打印指定文件的全局变量



//假设有数组:
//int array[] = {100,200,300};	//静态数组
//int *ptr = (int*)malloc(10*sizeof(int));

(gdb)p array		//对于静态数组,直接“p+数组名”就可以打印出所有的数组元素
$1 = {100, 200, 300}

(gdb)p ptr			//打印指针

(gdb)p *ptr			//打印动态数组ptr的首元素的值
$2 = (int *)0x7fff0008c0

(gdb)p *(ptr+1)		//打印动态数组ptr的第二个元素的值
$3 = 0

(gdb)p *ptr@7		//打印动态数组ptr的前7个元素的值
$4 = {0,0,0,0,0,0,0}

10.2 设置显示选项:

GDB中关于显示的选项比较多,列举几个:

//打印的最大长度:(默认200,超过200的部分显示“...”)
(gdb)show print elements
Limit on string chars or array elements to print is 200

(gdb)set print elements 300	//当你要打印的数组或者字符串比较长时,入如果想要显示完整的内容,可以修改elements的长度




//美化 结构体 打印显示效果:(默认off)
(gdb)show print pretty 		//查看结构体美化打印功能是否开启
Pretty formatting of structures i off.	//默认关

//假设有结构体:struct CTask { int val; };
(gdb)p m_task
$1 = {val = 32768}

(gdb)set print pretty on	//打开

(gdb)p m_task
$2 = {
	val = 32768
}


//打印C++中string的内容:(string是类,没法直接打印)
//假设有string类型对象:string m_str = "hello world";
(gdb)p m_str.size()
$3 = 11

(gdb)p m_str.c_str()
$4 = 0x7ffff6f49ea0 "hello world"

10.3 打印历史记录:

当使用 print 命令打印程序中的数据时,每一个print都会被GDB记录下来,GDB 会以 $1, $2, $3, ... 这样的方式为每一个print结果编号。
于是,就可以使用这个编号方位以前的表达式,例如:

(gdb)print a
$1 = 10
(gdb)print $1		//打印编号为“$1”的历史值
$2 = 10

这个功能所带来的好处是:
如果你先前输入了一个比较长的表达式,如果你还想查看这个表达式的值时,就可以使用“历史记录”来访问,省去了重复输入。

11. GDB对 SIGNAL 的处理:

信号是一种“软中断”,是一种处理异步事件的方法。

理解“软中断”:
所谓“软中断”,就是“软件中断”,由软件向目标程序发出一个信号,将运行中的程序中断,使其转而去按照事先注册好的方式处理信号,例如中止程序、忽略信号、调用信号回调函数等。

GDB有能力在你调试程序的时候处理任何一种信号,
使用 handle 命令,告诉 GDB要处理哪一种信号,以及处理方式:

handle <signal> <keywords>

举例:

(gdb) handle SIGINT print       //当程序在运行中收到 SIGINT信号时,print打印出一条信息

handle支持的处理方式:

stop            //当程序收到指定信号时,GDB会停住程序
nostop          

print           //当程序收到指定信号时,GDB会显示出一条信息
noprint

pass            //
nopass

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

查看当前有哪些信号在被GDB检测:

使用命令: infor signals 或者 info handle

(gdb) info signals
Signal        Stop	Print	Pass to program	Description

SIGHUP        Yes	Yes	Yes		Hangup
SIGINT        Yes	Yes	No		Interrupt
SIGQUIT       Yes	Yes	Yes		Quit
SIGILL        Yes	Yes	Yes		Illegal instruction
SIGTRAP       Yes	Yes	No		Trace/breakpoint trap
SIGABRT       Yes	Yes	Yes		Aborted
SIGEMT        Yes	Yes	Yes		Emulation trap
SIGFPE        Yes	Yes	Yes		Arithmetic exception
SIGKILL       Yes	Yes	Yes		Killed
SIGBUS        Yes	Yes	Yes		Bus error
SIGSEGV       Yes	Yes	Yes		Segmentation fault
SIGSYS        Yes	Yes	Yes		Bad system call
SIGPIPE       Yes	Yes	Yes		Broken pipe
SIGALRM       No	No	Yes		Alarm clock
SIGTERM       Yes	Yes	Yes		Terminated
SIGURG        No	No	Yes		Urgent I/O condition
SIGSTOP       Yes	Yes	Yes		Stopped (signal)
SIGTSTP       Yes	Yes	Yes		Stopped (user)
SIGCONT       Yes	Yes	Yes		Continued
SIGCHLD       No	No	Yes		Child status changed
SIGTTIN       Yes	Yes	Yes		Stopped (tty input)
---Type <return> to continue, or q <return> to quit---q

12. GDB命令分类:

GDB的命令很多,GDB把它们分成了很多类,使用 help 命令可以列出命令种类。
help只是列出命令种类,如果要看种类中的命令,使用:
help
例如:

help breakpoints

help 命令查看具体里命令
例如:

help break

13. info命令可以查看哪些信息:

info breakpoints / info break / info b      : 查看断点信息,
                    //包括断点类型(BreakPoint、WatchPoint、CatchPoint)、断点编号(Num)、断点状态(Eanble/Disable)、断点被击中的次数(hit times) 等信息
info watchpoints            : 查看观察点信息,显示的信息与 info b 类似,个人认为完全可以用info b替代,好处可能就是当断点数量比较多时,只关注观察点而屏蔽断点信息

info program        :      查看程序状态
                    //包括程序是否已停住,如果停住了是 《停在哪个断点上》

info stack          :  可以查看到程序的调用栈信息
info register       : 查看程序的各个寄存器的值


info threads        : 查看 线程 信息



info frame
info args
info locals
info catch


info file			:	查看当前GDB正在调试程序所在的文件


info line			:	查看(当前位置的)源代码在内存中的地址
info line test.c:func	: 查看(指定文件名:函数名)的源代码在(运行时)的内存地址

info threads :
以 redis_server 多线程为例:

(可以看到 线程ID(LWP)、线程的起始函数、线程的当前执行函数 等信息)

(gdb) info threads 
  Id   Target Id         Frame 
* 1    Thread 0x7f0c1b5f50c0 (LWP 25938) "redis-server" 0x00007f0c1a89d403 in epoll_wait () from /lib64/libc.so.6
  2    Thread 0x7f0c11e60700 (LWP 25939) "bio_close_file" 0x00007f0c1ab68460 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
  3    Thread 0x7f0c1165f700 (LWP 25940) "bio_aof_fsync" 0x00007f0c1ab68460 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
  4    Thread 0x7f0c10e5e700 (LWP 25941) "bio_lazy_free" 0x00007f0c1ab68460 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
  5    Thread 0x7f0c1065d700 (LWP 25942) "jemalloc_bg_thd" 0x00007f0c1ab68460 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

14. (小程序)一个用于调试的生产者、消费者多线程小程序:

#include <iostream>
#include <stdio.h>
#include <pthread.h>
#include <list>

using namespace std;

pthread_mutex_t  m_mutex;
pthread_cond_t   m_cond;

typedef struct CTask {
    CTask(int a) : val(a) {}
    int val;
} CTask;

list<CTask> free_task_list;



void* pthread_consumer(void *arg) {
    int a;

    while(1) {
        if(a == 65535) {
            break;
        }

        a++;

        CTask m_task(a);

        pthread_mutex_lock(&m_mutex);

        free_task_list.push_back(m_task);
        pthread_cond_signal(&m_cond);
        pthread_t pid = pthread_self();

        cout << "consumer: " << pid << ": --> Push A Task Into List Succ: val = " << a << endl;
        pthread_mutex_unlock(&m_mutex);    
    }
  
    return nullptr;
}

void* pthread_producer(void *arg) {
    while(1) {
        pthread_mutex_lock(&m_mutex);

        while(free_task_list.empty()) {
            pthread_cond_wait(&m_cond, &m_mutex);
        }

        CTask m_task = free_task_list.front();
        free_task_list.pop_front();

        pthread_mutex_unlock(&m_mutex);
        pthread_t pid = pthread_self();

        cout << "producer, tid = " << pid << ": --> Get A Task Succ, task->val: " << m_task.val << endl;
    }

    return nullptr;
}


int main() {
    pthread_t tid_1;
    pthread_t tid_2;

    pthread_mutex_init(&m_mutex, NULL);
    pthread_cond_init(&m_cond, NULL);

	//创建三个线程,加上main,程序中一共4个线程:
    pthread_create(&tid_1, NULL, pthread_consumer, (void*)&free_task_list);
    pthread_create(&tid_2, NULL, pthread_producer, (void*)&free_task_list);
    pthread_create(&tid_2, NULL, pthread_producer, (void*)&free_task_list);

    pthread_join(tid_1, NULL);
    pthread_join(tid_2, NULL);
	pthread_join(tid_3, NULL);
	
    pthread_mutex_destroy(&m_mutex);
    pthread_cond_destroy(&m_cond);

    return 0;
}

15. GDB调试多线程:

15.1 调试多线程时可能用到的命令:

//1. 查看所有的线程:
info threads		//查看当前程序的所有线程信息


//2. 切换到指定的线程上:
thread 2
thread <thread_ID>  //切换到ID指定的线程上去,ID是线程编号
		//"Use this command to switch between threads"


//3. 向指定的线程施加命令:
thread apply ... ...	//Apply a command to a list of threads
thread apply all bt		//查看所有线程的调用栈
thread apply 2 bt		//查看线程编号为2的调用栈
thread apply <thread_ID> <something>	


//4. 只给某个指定的线程施加断点:
break 18 thread 2
break <linespec> thread <threadno>
break <linespec> thread <threadno> if ...

举例:


(gdb) info threads		//查看所有线程信息:
  Id   Target Id                              Frame 
  1    Thread 0x7ffff7a4d740 (LWP 306767) "1" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78
* 2    Thread 0x7ffff7a4c700 (LWP 306772) "1" pthread_consumer (arg=0x0) at 1.cpp:52
  3    Thread 0x7ffff724b700 (LWP 306773) "1" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78



(gdb)break func thread 3	//只给某个指定的线程施加断点:
(gdb)info breakpoints
3       breakpoint     keep y   0x0000555555555712 in CTask::CTask(int) at 1.cpp:18 thread 2
	stop only in thread 2


对 “thread apply” 命令的理解:

thread apply 这个命令只是用来给线程实施命令用的,
正常情况下,例如我们输入 “bt” 命令,那么就是要查看当前线程的调用栈信息,
当我们需要查看某个指定的线程ID的调用栈,或者想要查看所有线程的调用栈信息,就需要使用“thread apply”命令去指定要查看的线程ID。

15.2 多线程下禁止线程切换:

//将程序流锁定在当前线程,禁止线程切换:
(gdb)set scheduler-locking on

//可以切换:
(gdb)set scheduler-locking off

//不常用
(gdb)set scheduler-locking step		

16. GDB调试core dump:

core进行分类:

机器、资源、程序bug。

待补充。。。

参考内容:
https://blog.csdn.net/haoel/article/details/2879

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值