linux的gdb总结

来源:互联网 发布:微信朋友圈网络出错 编辑:程序博客网 时间:2024/05/22 22:28

Linux 静态链接库和动态连接库

一:起因

(1)也许我们非常熟悉Windows下的VC6.0 和 CodeBlocks的调试工具 —— 界面化的调试,但是你是否想过你的每一个按键或者快捷键的背后指令是什么,让我们一起走进Linux的gcc动态调试工具GDB

(2)程序调试无非就是:debug(gcc -g -o target source);设置断点(b n);观察变脸(info locals);观察特定值(print expr);执行(start);执行下一行(next);进入子函数(step);执行到当前函数结尾(finish);查看源代码(list)等等操作

(3)gcc 的过程:C程序的编辑 用到vim;  程序的编译和运行用到gcc;  程序的调试用到gdb (kdbg是可视化的调试工具)

二:实例讲解

(1)调试步骤分为两步

   1)编译时一定到 加上 -g 产生调试信息,让调试信息包含在可执行文件中exe —— 命令行下:

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. gcc -g -o target source  

   2)把可执行文件exe加载到调试环境中:   

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. gdb target  

   3) gdb为调试命令提示符,即可键入调试命令

(2)调试常用命令

   1)基本调试命令(重要)

        list 行号:列出产品从第几行开始的源代码;   list 函数名:列出某个函数的源代码

start:开始执行程序,停在main函数第一行语句前面等待命令;  next(n):执行下一列语句

step(s):执行下一行语句,如果有函数调用则进入到函数中  ;breaktrace(或bt):查看各级函数调用及参数

frame(f) 帧编号:选择栈帧 ;  info(i) locals:查看当前栈帧局部变量的值

finish:执行到当前函数返回,然后挺下来等待命令  ; print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数

set var:修改变量的值  ;quit:退出gdb

   2)高级调试命令

      break(b) 行号:在某一行设置断点 ;break 函数名:在某个函数开头设置断点 ;  break...if...:设置条件断点

continue(或c):从当前位置开始连续而非单步执行程序  ;  delete breakpoints:删除所有断点  ;delete breakpoints n:删除序号为n的断点

disable breakpoints:禁用断点 ;  enable breakpoints:启用断点

info(或i) breakpoints:参看当前设置了哪些断点; ;   run(或r):从开始连续而非单步执行程序

display 变量名:跟踪查看一个变量,每次停下来都显示它的值  ;undisplay:取消对先前设置的那些变量的跟踪

  3)格式化输入查看变量 print(p)

输出格式:一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。
例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要
做到这样,你可以使用GDB的数据显示格式:
 

x 按十六进制格式显示变量。                      d 按十进制格式显示变量。 

u 按十六进制格式显示无符号整型。         o 按八进制格式显示变量。 

t 按二进制格式显示变量。                           a 按十六进制格式显示变量。 

c 按字符格式显示变量。                               f 按浮点数格式显示变量。


Linux编程基础——GDB(设置断点)

启动GDB后,首先就是要设置断点,程序中断后才能调试。在gdb中,断点通常有三种形式:

断点(BreakPoint):

在代码的指定位置中断,这个是我们用得最多的一种。设置断点的命令是break,它通常有如下方式:

  • break <function>    在进入指定函数时停住
  • break <linenum>    在指定行号停住。
  • break +/-offset    在当前行号的前面或后面的offset行停住。offiset为自然数。
  • break filename:linenum    在源文件filename的linenum行处停住。
  • break ... if <condition>    ...可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置break if i=100,表示当i为100时停住程序。

可以通过info breakpoints [n]命令查看当前断点信息。此外,还有如下几个配套的常用命令:

  • delete    删除所有断点
  • delete breakpoint [n]    删除某个断点
  • disable breakpoint [n]    禁用某个断点
  • enable breakpoint [n]    使能某个断点

观察点(WatchPoint):

在变量读、写或变化时中断,这类方式常用来定位bug。

  • watch <expr>    变量发生变化时中断
  • rwatch <expr>    变量被读时中断
  • awatch <expr>     变量值被读或被写时中断

可以通过info watchpoints [n]命令查看当前观察点信息

捕捉点(CatchPoint):

捕捉点用来补捉程序运行时的一些事件。如:载入共享库(动态链接库)、C++的异常等。通常也是用来定位bug。

捕捉点的命令格式是:catch <event>event可以是下面的内容

  • throw     C++抛出的异常时中断
  • catch     C++捕捉到的异常时中断
  • exec    调用系统调用exec时(只在某些操作系统下有用)
  • fork    调用系统调用fork时(只在某些操作系统下有用)
  • vfork    调用系统调用vfork时(只在某些操作系统下有用)
  • load 或 load <libname>     载入共享库时(只在某些操作系统下有用)
  • unload 或 unload <libname>    卸载共享库时(只在某些操作系统下有用)

另外,还有一个tcatch <event>,功能类似,不过他只设置一次捕捉点,当程序停住以后,应点被自动删除。

捕捉点信息的查看方式和代码断点的命令是一样的,这里就不多介绍了。

在特定线程中中断

你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB很容易帮你完成这一工作。

  • break <linespec> thread <threadno>
  • break <linespec> thread <threadno> if ...

linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过"info threads"命令来查看正在运行程序中的线程信息。如果你不指定thread <threadno>则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如:

     (gdb) break frik.c:13 thread 28 if bartab > lim

当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。

恢复程序运行和单步调试

在gdb中,和调试步进相关的命令主要有如下几条:

  • continue    继续运行程序直到下一个断点(类似于VS里的F5)
  • next        逐过程步进,不会进入子函数(类似VS里的F10)
  • setp        逐语句步进,会进入子函数(类似VS里的F11)
  • until        运行至当前语句块结束
  • finish    运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)

PS:这些命令大部分可以简写为第一个字母,在日常使用过程中,往往只会输入第一个字符即可执行该命令,我标红的即是通常的使用方式。这几条命令使用非常频繁,并且可以带一些附加参数以实现高级功能,需要熟练掌握。

(2)对于Segmentfault(段错误)等不提示的信息的调试方法

大概三步走~~~( # ./core_dump_run运行时产生段错误,但是编译阶段gcc –o []不报错)

第一步:设置core文件大小为无限(默认为0,即不产生)如果想让系统在信号中断造成的错误时产生core文件,我们需要在shell中按如下设置:
#
设置core文件大小为无限
ulimit -c unlimited
#设置core文件大小为1000kb
ulimit –c 1000

#查看设置的大小

ulimit –c

[@sjs_37_33 t_core_gdb]# ulimit-c

100000

 

第二步:#gcc(g++)-g -o core_dump_run core_dump_test.c,并运行#./core_dump_run产生core.pid文件

 

第三步:#gdb core_dump_runcore.pid 即可调试 (gdb)start -à nextà bk直接到错误之处

 

附加:对于第三步中提示信息太少的,但是提供了错误地址的可以反编译:objdump

#objdump –d  core_dump_run

代码:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 1 #include <stdio.h>  
  2.  2   
  3.  3 const char str[5] = "test";  
  4.  4   
  5.  5 void core_test()  
  6.  6 {  
  7.  7     //str[4]='T';  
  8.  8     unsigned char *ptr = 0x00; // ·Ç·¨µÄ  
  9.  9     *ptr = 0x00;  
  10. 10     printf("str=:%s,str[1]=%d, [str[7]=%d]\n",str,str[1],str[7]);  
  11. 11 }   
  12. 12     
  13. 13     
  14. 14 int main()  
  15. 15 {   
  16. 16     core_test();  
  17. 17     return 0;  
  18. 18 }  

(3)gdb多线程调试

先介绍一下GDB多线程调试的基本命令。

info threads 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID前面有*的是当前调试的线程。

thread ID 切换当前调试的线程为指定ID的线程。

break thread_test.c:123 thread all 在所有线程中相应的行上设置断点

thread apply ID1 ID2 command 让一个或者多个线程执行GDB命令command 

thread apply all command 让所有被调试线程执行GDB命令command

set scheduler-locking off|on|step 估计是实际使用过多线程调试的人都可以发现,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。off不锁定任何线程,也就是所有线程都执行,这是默认值。 on只有当前被调试程序会执行。 step在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。

 

gdb对于多线程程序的调试有如下的支持:

·        线程产生通知:在产生新的线程时, gdb会给出提示信息

(gdb) r
Starting program: /root/thread 

[New Thread 1073951360 (LWP 12900)] 
[New Thread 1082342592 (LWP 12907)]---以下三个为新产生的线程
[New Thread 1090731072 (LWP 12908)]
[New Thread 1099119552 (LWP 12909)]

·        查看线程:使用info threads可以查看运行的线程。

(gdb) info threads
  
Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
  
3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
  
2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
1 Thread 1073951360 (LWP 12931)   main (argc=1,argv=0xbfffda04) at thread.c:21
(gdb)

注意,行首的蓝色文字为gdb分配的线程号,对线程进行切换时,使用该该号码,而不是上文标出的绿色数字。

另外,行首的红色星号标识了当前活动的线程

·        切换线程:使用 threadTHREADNUMBER 进行切换,THREADNUMBER 为上文提到的线程号。下例显示将活动线程从 1 切换至 4

(gdb) info threads
   4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
   3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
   2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb) 
thread 4
[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0  0xffffe002 in ?? ()
(gdb) info threads
* 4 Thread 1099119552 (LWP12940)   0xffffe002 in ?? ()

   3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
   2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
   1 Thread 1073951360 (LWP 12931)   main (argc=1,argv=0xbfffda04) at thread.c:21
(gdb)

 

后面就是直接在你的线程函数里面设置断点,然后continue到那个断点,一般情况下多线程的时候,由于是同时运行的,最好设置 setscheduler-locking on

这样的话,只调试当前线程 

三:个人心得

(0)记录个人成长中的收获的点点滴滴,哪怕跌倒再次爬起来,也是一种难得的经验 —— 我一直为自己前几年没有留下成长记忆而苦恼,如今时时刻刻的督促自己写一写自己的声音。

(1)哪怕一周花一到两个小时的时间,整理一下自己当下的心得和学习所得;从短期看:这会加深自己对知识的理解;从长远来看:虽然花费了一两个小时,但是最终你是赚了的。

(2)也许我们都会有类似的经历 —— 我们今天遇到的难题,前几天刚刚遇到过,处理过;但是,遗憾的是,自己忘记如何处理的了,更悲催的是,我们都忘了是哪一个模块里的;仅仅记得刚刚处理过这个问题;纠结纠结 ~~~浪费同样的时间处理一个相似的问题。

0 0