Linux初体验之gdb调试
使用gdb调试
使用gdb调试的方法有三中情况
-
程序崩溃了生成了coredump文件,调试coredump文件。
-
使用gdb启动程序。
-
程序在运行过程中切入。
参数列表
命令 | 命令缩写 | 命令说明 |
---|---|---|
list | l | 显示多行源代码 |
break | b | 设置断点,程序运行到断点的位置会停下来 |
info | i | 描述程序的状态 |
run | r | 开始运行程序 |
display | disp | 跟踪查看某个变量,每次停下来都显示它的值 |
step | s | 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句 |
next | n | 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) |
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
2. 线程栈结构的查看
-
获取线程ID
-
-
通过命令查看栈结构 ps stack 线程ID
-
3.利用gdb查看线程信息
-
将进程附加到gdb调试器当中,查看是否创建了新线程:gdb attach 主线程ID
4. 查看线程的一些信息
-
查看进程:info inferiors
-
查看线程:info threads //
-
查看线程栈结构:bt //
-
切换线程:thread n(n代表第几个线程)
5. 利用gdb调试多线程
当程序没有启动,线程还没有执行,此时利用gdb调试多线程和调试普通程序一样,通过设置断点,运行,查看信息等等,在这里不在演示,最后会加上调试线程的命令
-
设置断点:break 行号/函数名
-
查看断点:info b
6. 执行线程2的函数,指行完毕继续运行到断点处
-
继续使某一线程运行:thread apply 1-n(第几个线程) n
-
重新启动程序运行到断点处:r
7. 只运行当前线程
-
设置:set scheduler-locking on
-
运行:n
8. 所有线程并发执行
-
设置:set scheduler-locking off
-
运行:n
总结调试多线程的命令
命令 | 用法 |
---|---|
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 |