首页 > 系统 > Linux > 正文

linux的gdb总结

2024-06-28 16:04:18
字体:
来源:转载
供稿:网友

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上查看代码片gcc -g -o target source  

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

[plain] view%20plain copy gdb target  

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

(2)调试常用命令

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

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

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

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

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

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

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

 %20 2)高级调试命令

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

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

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

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

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

 %203)格式化输入查看变量%20print(p)

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

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

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

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

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

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

断点(BreakPoint):

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

break%20<function>    在进入指定函数时停住break%20<linenum>    在指定行号停住。break%20+/-offset    在当前行号的前面或后面的offset行停住。offiset为自然数。break%20filename:linenum    在源文件filename的linenum行处停住。break%20...%20if%20<condition>    ...可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置break%20if%20i=100,表示当i为100时停住程序。可以通过info%20breakpoints%20[n]命令查看当前断点信息。此外,还有如下几个配套的常用命令:

delete    删除所有断点delete%20breakpoint%20[n]    删除某个断点disable%20breakpoint%20[n]    禁用某个断点enable%20breakpoint%20[n]    使能某个断点观察点(WatchPoint):

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

watch%20<expr>    变量发生变化时中断rwatch%20<expr>    变量被读时中断awatch%20<expr>    %20变量值被读或被写时中断可以通过info watchpoints [n]命令查看当前观察点信息

捕捉点(CatchPoint):

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

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

throw    %20C++抛出的异常时中断catch    %20C++捕捉到的异常时中断exec    调用系统调用exec时(只在某些操作系统下有用)fork    调用系统调用fork时(只在某些操作系统下有用)vfork    调用系统调用vfork时(只在某些操作系统下有用)load%20或%20load%20<libname>%20    载入共享库时(只在某些操作系统下有用)unload%20或%20unload%20<libname>    卸载共享库时(只在某些操作系统下有用)另外,还有一个tcatch%20<event>,功能类似,不过他只设置一次捕捉点,当程序停住以后,应点被自动删除。

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

在特定线程中中断

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

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

    %20(gdb)%20break%20frik.c:13%20thread%2028%20if%20bartab%20>%20lim

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

恢复程序运行和单步调试

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

continue    继续运行程序直到下一个断点(类似于VS里的F5)next        逐过程步进,不会进入子函数(类似VS里的F10)setp        逐语句步进,会进入子函数(类似VS里的F11)until        运行至当前语句块结束finish    运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)PS:这些命令大部分可以简写为第一个字母,在日常使用过程中,往往只会输入第一个字符即可执行该命令,我标红的即是通常的使用方式。这几条命令使用非常频繁,并且可以带一些附加参数以实现高级功能,需要熟练掌握。

(2)对于Segmentfault(段错误)等不提示的信息的调试方法大概三步走~~~(%20#%20./core_dump_run运行时产生段错误,但是编译阶段gcc%20–o%20[]不报错)

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

#查看设置的大小

ulimit%20–c

[@sjs_37_33%20t_core_gdb]#%20ulimit-c

100000

 

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

 

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

 

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

#objdump%20–d%20 core_dump_run

代码:

[cpp] view%20plain copy 派生到我的代码片1 #include <stdio.h>   2    3 const char str[5] = "test";   4    5 void core_test()   6 {   7     //str[4]='T';   8     unsigned char *ptr = 0x00; // ·Ç·¨µÄ   9     *ptr = 0x00;  10     printf("str=:%s,str[1]=%d, [str[7]=%d]/n",str,str[1],str[7]);  11 }   12     13     14 int main()  15 {   16     core_test();  17     return 0;  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) rStarting 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  4 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)也许我们都会有类似的经历 —— 我们今天遇到的难题,前几天刚刚遇到过,处理过;但是,遗憾的是,自己忘记如何处理的了,更悲催的是,我们都忘了是哪一个模块里的;仅仅记得刚刚处理过这个问题;纠结纠结 ~~~浪费同样的时间处理一个相似的问题。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表