Linux初体验之gdb调试

使用gdb调试

使用gdb调试的方法有三中情况

  1. 程序崩溃了生成了coredump文件,调试coredump文件。

  2. 使用gdb启动程序。

  3. 程序在运行过程中切入。

参数列表

命令 命令缩写 命令说明
list l 显示多行源代码
break b 设置断点,程序运行到断点的位置会停下来
info i 描述程序的状态
run r 开始运行程序
display disp 跟踪查看某个变量,每次停下来都显示它的值
step s 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句
next n 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)
print p 打印内部变量值
continue c 继续程序的运行,直到遇到下一个断点
set var name=v   设置变量的值
start st 开始执行程序,在main函数的第一条语句前面停下来
file   装入需要调试的程序
kill k 终止正在调试的程序
watch   监视变量值的变化
backtrace bt 查看函数调用信息(堆栈)
frame f 查看栈帧 f n 切换到编号为n的栈
quit q 退出GDB环境

gdb 基本命令

  • 启动gdb $gdb 这样可以和gdb进行交互了。

  • 启动gdb,并且分屏显示源代码: $gdb -tui 这样,使用了'-tui'选项,启动可以直接将屏幕分成两个部分,上面显示源代码,比用list方便多了。这时候使用上下方向键可以查看源代码,想要命令行使用上下键就用[Ctrl]n和[Ctrl]p.

  • 启动gdb调试指定程序app: $gdb app 这样就在启动gdb之后直接载入了app可执行程序,需要注意的是,载入的app程序必须在编译的时候有gdb调试选项,例如'gcc -g app app.c',注意,如果修改了程序的源代码,但是没有编译,那么在gdb中显示的会是改动后的源代码,但是运行的是改动前的程序,这样会导致跟踪错乱的。

  • 启动程序之后,再用gdb调试: $gdb <program> <PID> 这里,<program>是程序的可执行文件名,<PID>是要调试程序的PID.如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。

  • 启动程序之后,再启动gdb调试: $gdb <PID> 这里,程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID,<PID>是要调试程序的PID.这样gdb就附加到程序上了,但是现在还没法查看源代码,用file命令指明可执行文件就可以显示源代码了。

  • 启动gdb之后的交互命令: 交互命令支持[Tab]补全。

  • 显示帮助信息: (gdb) help

  • 载入指定的程序: (gdb) file app 这样在gdb中载入想要调试的可执行程序app。如果刚开始运行gdb而不是用gdb app启动的话可以这样载入app程序,当然编译app的时候要加入-g调试选项。

  • 重新运行调试的程序: (gdb) run 要想运行准备调试的程序,可使用run命令,在它后面可以跟随发给该程序的任何参数,包括标准输入和标准输出说明符(<和> )和shell通配符(*、?、[、])在内。

  • 修改发送给程序的参数: (gdb) set args no 这里,假设我使用"r yes"设置程序启动参数为yes,那么这里的set args会设置参数argv[1]为no。

  • 显示缺省的参数列表: (gdb) show args

  • 列出指定区域(n1到n2之间)的代码: (gdb) list n1 n2 这样,list可以简写为l,将会显示n1行和n2行之间的代码,如果使用-tui启动gdb,将会在相应的位置显示。如果没有n1和n2参数,那么就会默认显示当前行和之后的10行,再执行又下滚10行。另外,list还可以接函数名。 一般来说在list后面可以跟以下这们的参数: <linenum> 行号。 <+offset> 当前行号的正偏移量。 <-offset> 当前行号的负偏移量。 filename:linenum 哪个文件的哪一行。 <function> 函数名。 filename:function 哪个文件中的哪个函数。 <*address> 程序运行时的语句在内存中的地址。

  • 执行下一步: (gdb) next 这样,执行一行代码,如果是函数也会跳过函数。这个命令可以简化为n.

  • 执行N次下一步: (gdb) next N

  • 执行上次执行的命令: (gdb) [Enter] 这里,直接输入回车就会执行上次的命令了。

  • 单步进入: (gdb) step 这样,也会执行一行代码,不过如果遇到函数的话就会进入函数的内部,再一行一行的执行。

  • 执行完当前函数返回到调用它的函数: (gdb) finish 这里,运行程序,直到当前函数运行完毕返回再停止。例如进入的单步执行如果已经进入了某函数,而想退出该函数返回到它的调用函数中,可使用命令finish.

  • 指定程序直到退出当前循环体: (gdb) until 或(gdb) u 这里,发现需要把光标停止在循环的头部,然后输入u这样就自动执行全部的循环了。

  • 跳转执行程序到第5行: (gdb) jump 5 这里,可以简写为"j 5"需要注意的是,跳转到第5行执行完毕之后,如果后面没有断点则继续执行,而并不是停在那里了。 另外,跳转不会改变当前的堆栈内容,所以跳到别的函数中就会有奇怪的现象,因此最好跳转在一个函数内部进行,跳转的参数也可以是程序代码行的地址,函数名等等类似list。

  • 强制返回当前函数: (gdb) return 这样,将会忽略当前函数还没有执行完毕的语句,强制返回。return后面可以接一个表达式,表达式的返回值就是函数的返回值。

  • 强制调用函数: (gdb) call <expr> 这里,<expr>可以是一个函数,这样就会返回函数的返回值,如果函数的返回类型是void那么就不会打印函数的返回值,但是实践发现,函数运行过程中的打印语句还是没有被打印出来。

  • 强制调用函数2: (gdb) print <expr> 这里,print和call的功能类似,不同的是,如果函数的返回值是void那么call不会打印返回值,但是print还是会打印出函数的返回值并且存放到历史记录中。

  • 在当前的文件中某一行(假设为6)设定断点: (gdb) break 6

  • 设置条件断点: (gdb) break 46 if testsize==100 这里,如果testsize==100就在46行处断点。

  • 检测表达式变化则停住: (gdb) watch i != 10 这里,i != 10这个表达式一旦变化,则停住。watch <expr> 为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序(也是一种断点)。

  • 在当前的文件中为某一函数(假设为func)处设定断点: (gdb) break func

  • 给指定文件(fileName)的某个行(N)处设置断点: (gdb) break fileName:N 这里,给某文件中的函数设置断点是同理的。

  • 显示当前gdb断点信息: (gdb) info breakpoints 这里,可以简写为info break.会显示当前所有的断点,断点号,断点位置等等。

  • 删除N号断点: (gdb) delete N

  • 删除所有断点: (gdb) delete

  • 清除行N上面的所有断点: (gdb) clear N

 

  • 继续运行程序直接运行到下一个断点: (gdb) continue 这里,如果没有断点就一直运行。

  • 显示当前调用函数堆栈中的函数: (gdb) backtrace 命令产生一张列表,包含着从最近的过程开始的所有有效过程和调用这些过程的参数。当然,这里也会显示出当前运行到了哪里(文件,行)。

  • 查看当前调试程序的语言环境: (gdb) show language 这里,如果gdb不能识别你所调试的程序,那么默认是c语言。

  • 查看当前函数的程序语言: (gdb) info frame

  • 显示当前的调试源文件: (gdb) info source 这样会显示当前所在的源代码文件信息,例如文件名称,程序语言等。

  • 手动设置当前的程序语言为c++: (gdb) set language c++ 这里,如果gdb没有检测出你的程序语言,你可以这样设置。

  • 查看可以设置的程序语言: (gdb) set language 这里,使用没有参数的set language可以查看gdb中可以设置的程序语言。

  • 终止一个正在调试的程序: (gdb) kill 这里,输入kill就会终止正在调试的程序了。

  • print显示变量(var)值: (gdb) print var 这里,print可以简写为p,print 是gdb的一个功能很强的命令,利用它可以显示被调试的语言中任何有效的表达式。表达式除了包含你程序中的变量外,还可以包含函数调用,复杂数据结构和历史等等。

  • 用16进制显示(var)值: (gdb) print /x var 这里可以知道,print可以指定显示的格式,这里用'/x'表示16进制的格式。 可以支持的变量显示格式有: x 按十六进制格式显示变量。 d 按十进制格式显示变量。 u 按十六进制格式显示无符号整型。 o 按八进制格式显示变量。 t 按二进制格式显示变量。 a 按十六进制格式显示变量。 c 按字符格式显示变量。 f 按浮点数格式显示变量。

 

  • 如果a是一个数组,10个元素,如果要显示则: (gdb) print *[email protected] 这样,会显示10个元素,无论a是double或者是int的都会正确地显示10个元素。

  • 修改运行时候的变量值: (gdb) print x=4 这里,x=4是C/C++的语法,意为把变量x值改为4,如果你当前调试的语言是Pascal,那么你可以使用Pascal的语法:x:=4。

  • 显示一个变量var的类型: (gdb) whatis var

  • 以更详细的方式显示变量var的类型: (gdb) ptype var 这里,会打印出var的结构定义。

[其他]

 

  • 在Qt4.x环境中打印QString msg;的msg变量: 步骤如下: 1)定义一个宏printqstring define printqstring printf "(QString)0x%x (length=%i): "",&$arg0,$arg0.d->size set $i=0 while $i < $arg0.d->size set $c=$arg0.d->data[$i++] if $c < 32 || $c > 127 printf "\u0x%04x", $c else printf "%c", (char)$c end end printf ""\n" end 2)(gdb) printqstring msg 这里,这个宏可以在gdb中直接定义,据说也可以写到$HOME/.gdbinit,这样每次启动自动加载。

  • 调试同时指明生成core文件: $gdb <program> core 用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。当程序非法崩溃的时候会产生一个core文件,然后使用这个命令,会直接定位到发生程序崩溃的位置。注意:有时需要设置系统命令“ulimit -c unlimited”才能产生core文件。

  • print显示存储块,如显示h后面的10个整数: print [email protected]

调试案例

 

今天在实现http客户端编程的时候,需要使用命令行参数来输入host的ip地址。但是程序运行的过程中一直报段错误。没办法,只能用gdb进行调试。那么怎样在调试的时候输入我们需要的命令行参数呢。一般有两种方法。

进入gdb调试:

gcc -g -Wall httpTest.c -o test 生成了可调试文件

gdb test 进入调试

下面可以使用两种方法输入命令行参数

1)run 命令行参数

2)set args 命令行参数

如:我的程序中需要输入的时服务器端ip地址,可以通过以下两种方法输入

1)如果直接运行程序,run www.baidu.com

2)set args www.baidu.com,后面再继续进行调试

#include <stdio.h>
void debug(char *str)
{    
    printf("debug info :%s\n",str );
}
​
main(int argc,char *argv[]){ 
   int i,j;    
   j=0;    
   for(i=0;i<10;i++){ 
        j+=5;        
        printf("now a=%d\n", j);   
    }
 }
gcc -g -o e e.c
#gdb e
#或者输入gdb
#然后 file e

list 命令用法

list命令显示多行源代码,从上次的位置开始显示,默认情况下,一次显示10行,第一次使用时,从代码起始位置显示

gdb) list1    
#include <stdio.h>2    
void debug(char *str)3    
{4        
printf("debug info :%s\n",str );5
}6    
main(int argc,char *argv[]){7
int i,j;8
j=0;9
for(i=0;i<10;i++){10
j+=5;
(gdb) 

list n显示已第n行未中心的10行代码

(gdb) list 83
{4
printf("debug info :%s\n",str );5
}6
main(int argc,char *argv[]){7
int i,j;8
j=0;9
for(i=0;i<10;i++){10
j+=5;11
printf("now a=%d\n", j);12
}
(gdb) 

list functionname显示以functionname的函数为中心的10行代码

(gdb) list main1
#include <stdio.h>2
void debug(char *str)3
{4
printf("debug info :%s\n",str );5
}6
main(int argc,char *argv[]){7
int i,j;8
j=0;9
for(i=0;i<10;i++){10
j+=5;
(gdb) 

list - 显示刚才打印过的源代码之前的代码

(gdb) list 105
}6
main(int argc,char *argv[]){7
int i,j;8
j=0;9
for(i=0;i<10;i++){10
j+=5;11
printf("now a=%d\n", j);12
}13
}
(gdb) list -1
#include <stdio.h>2
void debug(char *str)3
{4
printf("debug info :%s\n",str );
(gdb) 

断点命令break

break location:在location位置设置断点,该位置可以为某一行,某函数名或者其它结构的地址

GDB会在执行该位置的代码之前停下来

gdb) list 1
#include <stdio.h>2
void debug(char *str)3
{4
printf("debug info :%s\n",str );5
}6
main(int argc,char *argv[]){7
int i,j;8
j=0;9
for(i=0;i<10;i++){10
j+=5;(gdb) 11
printf("now a=%d\n", j);12
}13
}
(gdb) break 10
Breakpoint 1 at 0x40050a: file e.c, line 10.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e Breakpoint 1, main (argc=1, argv=0x7fffffffe548) at e.c:1010            j+=5;
(gdb) c
Continuing.now a=5Breakpoint 1, main (argc=1, argv=0x7fffffffe548) at e.c:1010
j+=5;
(gdb)c
Continuing.now a=10Breakpoint 1, main (argc=1, argv=0x7fffffffe548) at e.c:1010
j+=5;
(gdb) 

使用delete breakpoints 断点号 删除断点

这里的断点号表示的是第几个断点,刚才执行break 10返回 reakpoint 1 at 0x40050a: file e.c, line 10.

中的1表示该断点的标号,因此使用 delete breakpoints 1表示删除第10行所定义的断点

clear n表示清除第n行的断点,因此clear 10等同于delete breakpoints 1

disable/enable n表示使得编号为n的断点暂时失效或有效

可使用info查看断点相关的信息

info breakpoints

gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break 10
Breakpoint 2 at 0x40050a: file e.c, line 10.
(gdb) break 9
Breakpoint 3 at 0x400501: file e.c, line 9.
(gdb) info breakpoints
Num     Type           Disp Enb Address            What2       
breakpoint     keep y   0x000000000040050a in main at e.c:103       
breakpoint     keep y   0x0000000000400501 in main at e.c:9

display命令

查看参数的值

(gdb) break 10
Breakpoint 1 at 0x40050a: file e.c, line 10.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e Breakpoint 1, main (argc=1, argv=0x7fffffffe548) at e.c:1010
j+=5;
(gdb) display j
1: j = 0
(gdb) c
Continuing.now a=5Breakpoint 1, main (argc=1, argv=0x7fffffffe548) at e.c:1010 
j+=5;1: j = 5
(gdb) display
1: j = 5
(gdb) display i
2: i = 1
(gdb) display j
3: j = 5
(gdb) display j*2
4: j*2 = 10
(gdb) info display
Auto-display expressions now in effect:Num Enb Expression4:   y  j*23:   y  j2:   y  i1:   y  j

也可以使用disable,enable,delete,info命令修改及查看其状态,用法与对断点的一样

step及next命令

step可使得程序逐条执行,即执行完一条语句然后在吓一跳语句前停下来,等待用户的命令

一般使用step命令是,可使用display或者watch命令查看变量的变化,从而判断程序行为是否符合要求

当下一条指令为函数时,s进入函数内部,在其第一条语句前停下来

step n,next n 表示连续但不执行n条指令,如果期间遇到断点,则停下来

 

(gdb) list1    
#include <stdio.h>2    
void debug(char *str)3    
{4        
printf("debug info :%s\n",str );5    
}6    
7    
main(int argc,char *argv[]){8        
int i,j;9        
j=0;10        
for(i=0;i<10;i++){(gdb) 11            
j+=5;12            
printf("now j=%d\n", j);13            
debug("x=======x");14        
}15    
}
(gdb) Line number 16 out of range; e.c has 15 lines.
(gdb) break 11
Breakpoint 1 at 0x40050a: file e.c, line 11.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e1 Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:1111            j+=5;(gdb) s 12            
printf("now j=%d\n", j);(gdb) s__printf (format=0x400648 "now j=%d\n") at printf.c:3030    {
(gdb) bt
#0  __printf (format=0x400648 "now j=%d\n") at printf.c:30
#1  0x0000000000400525 in main (argc=1, argv=0x7fffffffe538) at e.c:12(gdb) n34      va_start (arg, format);(gdb) n35      done = vfprintf (stdout, format, arg);
(gdb) n
now j=539    }
(gdb) bt
#0  __printf (format=<value optimized out>) at printf.c:39
#1  0x0000000000400525 in main (argc=1, argv=0x7fffffffe538) at e.c:12(gdb) nmain (argc=1, argv=0x7fffffffe538) at e.c:1313            debug("x=======x");
(gdb) n
debug info :x=======x10        
for(i=0;i<10;i++){
(gdb) s
Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:1111            j+=5;
(gdb) s12            
printf("now j=%d\n", j);
(gdb) n
now j=1013            debug("x=======x");
(gdb) n
debug info :x=======x10        
for(i=0;i<10;i++){
(gdb) 

 

watch

watch可设置观察点(watchpoint)。使用观察点可以使得当某表达式的值发生变化时,程序暂停执行。

执行该命令前,必须保证程序已经运行

 

(gdb) list 1    
#include <stdio.h>2    
void debug(char *str)3    
{4        
printf("debug info :%s\n",str );5    
}6    
7    
main(int argc,char *argv[]){8       
int i,j;9        
j=0;10        
for(i=0;i<10;i++){
(gdb) 11            
j+=5;12            
printf("now j=%d\n", j);13            
debug("x=======x");14        }15    }(gdb) Line number 16 out of range; e.c has 15 lines.
(gdb) b 
mainBreakpoint 1 at 0x4004fa: file e.c, line 9.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e1 Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:99        
j=0;
(gdb) watch j
Hardware watchpoint 2: j
(gdb) c
Continuing.Hardware watchpoint 2: j
Old value = 0
New value = 5
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now 
j=5
debug info :x=======x
Hardware watchpoint 2: j
Old value = 5
New value = 10
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);

View Code

print命令

 

(gdb) list1    
#include <stdio.h>2    
void debug(char *str)3    
{4        
printf("debug info :%s\n",str );5    
}6    
7    
main(int argc,char *argv[]){8        
int i,j;9        
j=0;10        
for(i=0;i<10;i++){
(gdb) 11            
j+=5;12            
printf("now j=%d\n", j);13            
debug("x=======x");14        
}15    
}
(gdb) Line number 16 out of range; e.c has 15 lines.
(gdb) break 12Breakpoint 1 at 0x40050e: file e.c, line 12.(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e1 Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) p j
$1 = 5
(gdb) c
Continuing.now j=5debug info :x=======xBreakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) p i,j
$2 = 10
(gdb) p j
$3 = 10
(gdb) 

 

set var name=value

set args 可指定运行时参数(当main函数需要参数时 如:set args 10 20 30 40 50)

show args 命令可以查看设置好的运行参数

在程序运行中动态改变变量的值

 

(gdb) list1    
#include <stdio.h>2    
void debug(char *str)3    
{4        
printf("debug info :%s\n",str );5    
}6    
7    
main(int argc,char *argv[]){8        
int i,j;9        
j=0;10        
for(i=0;i<10;i++){
(gdb) 11            
j+=5;12            
printf("now j=%d\n", j);13            
debug("x=======x");14        
}15    
}
(gdb) Line number 16 out of range; e.c has 15 lines.
(gdb) break 
mainBreakpoint 1 at 0x4004fa: file e.c, line 9.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e1 Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:99        
j=0;
(gdb) watch i
Hardware watchpoint 2: i
(gdb) watch j
Hardware watchpoint 3: j
(gdb) c
Continuing.Hardware watchpoint 3: j
Old value = 0
New value = 5
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now j=5
debug info :x=======x
Hardware watchpoint 2: i
Old value = 0
New value = 1
0x0000000000400533 in main (argc=1, argv=0x7fffffffe538) at e.c:1010        for(i=0;i<10;i++){
(gdb) c
Continuing.Hardware watchpoint 3: j
Old value = 5
New value = 10
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now j=10
debug info :x=======x
Hardware watchpoint 2: i
Old value = 1
New value = 2
0x0000000000400533 in main (argc=1, argv=0x7fffffffe538) at e.c:1010        
for(i=0;i<10;i++){
(gdb) c
Continuing.Hardware watchpoint 3: j
Old value = 10
New value = 15
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now j=15
debug info :x=======x
Hardware watchpoint 2: i
Old value = 2
New value = 30x0000000000400533 in main (argc=1, argv=0x7fffffffe538) at e.c:1010        
for(i=0;i<10;i++){
(gdb) c
Continuing.Hardware watchpoint 3: j
Old value = 15
New value = 20
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now j=20
debug info :x=======x
Hardware watchpoint 2: i
Old value = 3
New value = 4
0x0000000000400533 in main (argc=1, argv=0x7fffffffe538) at e.c:1010        
for(i=0;i<10;i++){
(gdb) set var i=8
(gdb) c
Continuing.Hardware watchpoint 3: j
Old value = 20
New value = 25
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now j=25
debug info :x=======x
Hardware watchpoint 2: i
Old value = 8
New value = 9
0x0000000000400533 in main (argc=1, argv=0x7fffffffe538) at e.c:1010        for(i=0;i<10;i++){
(gdb) c
Continuing.Hardware watchpoint 3: j
Old value = 25
New value = 30
main (argc=1, argv=0x7fffffffe538) at e.c:1212            printf("now j=%d\n", j);
(gdb) c
Continuing.now j=30
debug info :x=======x
Hardware watchpoint 2: i
Old value = 9
New value = 10
0x0000000000400533 in main (argc=1, argv=0x7fffffffe538) at e.c:1010        
for(i=0;i<10;i++){
(gdb) c
Continuing.Watchpoint 2 deleted because the program has left the block inwhich its expression is valid.Watchpoint 3 deleted because the program has left the block inwhich its expression is valid.__libc_start_main (main=0x4004eb <main>, argc=1, ubp_av=0x7fffffffe538, init=<value optimized out>, fini=<value optimized out>, rtld_fini=<value optimized out>,     stack_end=0x7fffffffe528) at libc-start.c:258258      exit (result);
(gdb) c
Continuing.Program exited with code 026.

 

函数调用相关的

backtrace

可使用frame 查看堆栈中某一帧的信息

 

(gdb) list 1    
#include <stdio.h>2    
void debug(char *str)3    
{4        
printf("debug info :%s\n",str );5    
}6    
7    
main(int argc,char *argv[]){8        
int i,j;9        
j=0;10        
for(i=0;i<10;i++){
(gdb) 11            
j+=5;12            
printf("now j=%d\n", j);13            
debug("x=======x");14        
}15    
}
(gdb) Line number 16 out of range; e.c has 15 lines.
(gdb) b 13
Breakpoint 1 at 0x400525: file e.c, line 13.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/e1 now j=5Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:1313            debug("x=======x");
(gdb) s
debug (str=0x400652 "x=======x") at e.c:44        printf("debug info :%s\n",str );
(gdb) bt
#0  debug (str=0x400652 "x=======x") at e.c:4#1  0x000000000040052f in main (argc=1, argv=0x7fffffffe538) at e.c:13(gdb) s__printf (format=0x400638 "debug info :%s\n") at printf.c:3030    {
(gdb) bt
#0  __printf (format=0x400638 "debug info :%s\n") at printf.c:30
#1  0x00000000004004e9 in debug (str=0x400652 "x=======x") at e.c:4
#2  0x000000000040052f in main (argc=1, argv=0x7fffffffe538) at e.c:13(gdb) s34      va_start (arg, format);
(gdb) bt
#0  __printf (format=0x400638 "debug info :%s\n") at printf.c:34
#1  0x00000000004004e9 in debug (str=0x400652 "x=======x") at e.c:4
#2  0x000000000040052f in main (argc=1, argv=0x7fffffffe538) at e.c:13
(gdb) s35      
done = vfprintf (stdout, format, arg);
(gdb) s
_IO_vfprintf_internal (s=0x333a58f040, format=0x400638 "debug info :%s\n", ap=0x7fffffffe330) at vfprintf.c:236236      int save_errno = errno;
(gdb) bt
#0  _IO_vfprintf_internal (s=0x333a58f040, format=0x400638 "debug info :%s\n", ap=0x7fffffffe330) at vfprintf.c:236
#1  0x000000333a24effa in __printf (format=<value optimized out>) at printf.c:35
#2  0x00000000004004e9 in debug (str=0x400652 "x=======x") at e.c:4
#3  0x000000000040052f in main (argc=1, argv=0x7fffffffe538) at e.c:13
(gdb) c
Continuing.debug info :x=======x
now j=10
Breakpoint 1, main (argc=1, argv=0x7fffffffe538) at e.c:1313            debug("x=======x");
(gdb) bt
#0  main (argc=1, argv=0x7fffffffe538) at e.c:13

View Code

GDB段错误调试

所谓段错误就是对内存的非法访问

采用GDB调试段错误有2种方法

1.在GDB中运行目标程序,当发生段错误时,GDB中运行的程序会自动停下来

2.直接运行目标程序,使其在发生段错误时产生内存转储(core dump)文件,GDB对该文件进行调试

abort.c

#include <stdio.h>
#include <stdlib.h>
void recurse(void){    
static int i;    
if( ++i == 3)        
abort();    
else       
recurse();
}
int main(int argc,char ** argv){    
recurse();
}

**gcc -g -o abort abort.c**使用gdb调试
Line number 15 out of range; abort.c has 14 lines.
(gdb) r
Starting program: /mnt/hgfs/www/c/gcc/abort Program received signal SIGABRT, Aborted.0x000000333a232495 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:6464      
return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);
(gdb) bt
#0  0x000000333a232495 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x000000333a233c75 in abort () at abort.c:92
#2  0x00000000004004e7 in recurse () at abort.c:8
#3  0x00000000004004ec in recurse () at abort.c:10
#4  0x00000000004004ec in recurse () at abort.c:10
#5  0x0000000000400502 in main (argc=1, argv=0x7fffffffe528) at abort.c:13**显示在recurse函数调用了3次后调用了abort函数,产生段错误

**

使用内存转储文件

ulimit -a

[[email protected] gcc]# ulimit -a 
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7331
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7331
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
core file size 此时是0

ulimit -c unlimited 设置为不受限制

 

多线程调试

 

1. 线程的查看

  • 查看当前运行的进程 ps aux|grep a.out

  • 查看当前运行的轻量级进程 ps -aL|grep a.out

  • 查看主线程和新线程的关系 pstree -p 主线程id

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消

2. 线程栈结构的查看

  1. 获取线程ID

    1. 通过命令查看栈结构 ps stack 线程ID

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消

3.利用gdb查看线程信息

  1. 将进程附加到gdb调试器当中,查看是否创建了新线程:gdb attach 主线程ID

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消

4. 查看线程的一些信息

  1. 查看进程:info inferiors

  2. 查看线程:info threads //

  3. 查看线程栈结构:bt //

  4. 切换线程:thread n(n代表第几个线程)

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消

5. 利用gdb调试多线程

  当程序没有启动,线程还没有执行,此时利用gdb调试多线程和调试普通程序一样,通过设置断点,运行,查看信息等等,在这里不在演示,最后会加上调试线程的命令

  1. 设置断点:break 行号/函数名

  2. 查看断点:info b

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消Linux初体验之gdb调试正在上传…重新上传取消

6. 执行线程2的函数,指行完毕继续运行到断点处

  1. 继续使某一线程运行:thread apply 1-n(第几个线程) n

  2. 重新启动程序运行到断点处:r

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消Linux初体验之gdb调试正在上传…重新上传取消

7. 只运行当前线程

  1. 设置:set scheduler-locking on

  2. 运行:n

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消Linux初体验之gdb调试正在上传…重新上传取消

8. 所有线程并发执行

  1. 设置:set scheduler-locking off

  2. 运行:n

Linux初体验之gdb调试Linux初体验之gdb调试正在上传…重新上传取消Linux初体验之gdb调试正在上传…重新上传取消

总结调试多线程的命令

命令 用法
info threads 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程
thread ID(1,2,3…) 切换当前调试的线程为指定ID的线程
break thread_test.c:123 thread all(例:在相应函数的位置设置断点break pthread_run1) 在所有线程中相应的行上设置断点
thread apply ID1 ID2 command 让一个或者多个线程执行GDB命令command
thread apply all command 让所有被调试线程执行GDB命令command
set scheduler-locking 选项 command 设置线程是以什么方式来执行命令
set scheduler-locking off 不锁定任何线程,也就是所有线程都执行,这是默认值
set scheduler-locking on 只有当前被调试程序会执行
set scheduler-locking on step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行mu