Linux_调试器-gdb/cgdb的使用
目录
一、调试器 - gdb/cgdb使⽤
1.调试样例:
2 预备
makefile文件:
📌 安装cgdb:
3.常见的调试命令:
cgbd+[可执行文件] ;
b+[行号] 加断点:
r(run)跑程序
info b 查看断点的信息:
d + [断点编号] 删除断点:
next / n 逐语句:
step / s 进入函数内部
backtrace / bt 查看函数调用栈的结构顺序:
finish 执行到当前函数返回位置:
p + [变量],指定打印变量的值;
b + [函数名] 就是给函数的入口处打上断点:
disable + [断点编号] 禁用断点:
enable + [断点编号] 启用断点:
quit 退出程序:
调试的本质:
1.找到问题
continue / c 从当前位置开始连续执行,直到下一个断点处:
until + [行号] 直接跳转到某一行:本质是跑完前面的行数再进行跳转:
display + [变量名] :像vs一样直接进行常显示变量:
watch + [变量值]:能够每次都查看变量的新旧值的变换;
📌 注意:
set var + [变量名] = 修改值 直接在调试过程中进行修改变量值
b + [行号] if + [条件] :设置条件断点:
📌 注意:
总结一下吧~以上就是Linux常用工具的最后一部分内容,vim调式工具cgdb,我自认为还是总结的非常全面的,对我自己有着很大的帮助与收获,希望对你也是~!!!
一、调试器 - gdb/cgdb使⽤
1.调试样例:
#include <stdio.h>
int Sum(int s, int e)
{
int result = 0;
for(int i = s; i <= e; i++)
{
result += i;
}
return result;
}
int main()
{
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d-%d]=%d\n", start, end, n);
return 0;
}
2 预备
• 程序的发布⽅式有两种, debug 模式和 release 模式, Linux gcc/g++ 出来的⼆进制程序,默认是 release 模式。• 要使⽤gdb调试,必须在源代码⽣成⼆进制程序的时候, 加上 -g 选项,如果没有添加,程序⽆法被编译
$ gcc mycmd.c -o mycmd # 默认模式,不⽀持调试$ file mycmdmycmd: ELF 64 -bit LSB shared object, x86 -64 , version 1 (SYSV), dynamicallylinked, interpreter /lib64/ld-linux-x86 -64. so .2 ,BuildID[sha1]= 82f 5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux3.2.0 , not stripped$ gcc mycmd.c -o mycmd -g # debug模式$ file mycmdmycmd: ELF 64 -bit LSB shared object, x86 -64 , version 1 (SYSV), dynamicallylinked, interpreter /lib64/ld-linux-x86 -64. so .2 ,BuildID[sha1]= 3 d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux3.2.0 , with debug_info, not stripped
makefile文件:
编译通过后加了-g选项,就是debug版本,可以通过cgdb来进行调试:
查看程序是否右debug信息:注意这里的S是大写
readelf -S mycode | grep -i debug
有debug信息就说明可以进行调试;
📌 安装cgdb:
• 推荐安装cgdb:• Ubuntu: sudo apt-get install -y cgdb• Centos: sudo yum install -y cgdb
3.常见的调试命令:
cgbd+[可执行文件] ;
不是跟源文件,一定要跟可执行文件!!!
进入cgbd模式,进行调试;
b+[行号] 加断点:
b 16
可以看到,我现在16好添加了一个断点,然后就让程序开始跑起来了;
r(run)跑程序
程序跑起来后就在断点出停下来,所以断点的本质,就是让程序能够停在断点处;
info b 查看断点的信息:
info b
d + [断点编号] 删除断点:
跟行数没有关系,是要看具体删除的是第几个断点:
添加一个端点后,删除第一个断点:
d 1
再次查看就没有断点了;
next / n 逐语句:
不进入函数内部
n
可以看到n一下就是走一步,直到越过printf语句,打印出程序结果;
当越过return 0;时程序就会结束;
看到图中程序已经结束,然后再次info b查看断点,可以发现断点还是存在,并且已经被命中了一次;
当程序再次跑起来的时候,可以发现,程序仍然回到了断点处;所以这就可以证明,如果在调试过程中发现前面信息有漏洞,没能调试好,那么就直接无脑run就可以了,让程序再回到 最初的起点~
step / s 进入函数内部
逐过程:
s
backtrace / bt 查看函数调用栈的结构顺序:
bt
能够看到,函数先完成Sum的出栈,然后再结束main函数;
finish 执行到当前函数返回位置:
现在我的程序正在Sum函数内部一步步运行,但是我现在就想一步直接结束这个Sum函数:
finish
finish后,执行过程直接结束掉Sum函数,回到main函数内部;
这一步 n 要取值其实是通过两步进行的,首先return 返回result后的结果要先存入寄存器(%eax),然后再通过寄存器(%eax)取值传给n这个变量;
此时结束完Sum函数后,想观察n有没有取到Sum返回的值,就 :
p + [变量],指定打印变量的值;
p n
发现再16行时,并没有执行完这个过程,n=0;
17行时,n取到了寄存器里面的值,n=5050;
b + [函数名] 就是给函数的入口处打上断点:
断点被使能:
Enb,表示当前断点是否被禁用:
disable + [断点编号] 禁用断点:
disable + [断点编号]
出来打断点是+[断点行号] , 其余的都是加断点编号;
enable + [断点编号] 启用断点:
enable + [断点编号]
quit 退出程序:
quit
调试的本质:
1.找到问题
找到问题的命令;
断点的本质就是把代码进行划分,以块为单位进行快速定位有问题的区域;
finish可以单独来确认是否是哪一个函数出现了问题;
until 直接跳过局部区域,快速执行;
continue / c 从当前位置开始连续执行,直到下一个断点处:
until + [行号] 直接跳转到某一行:本质是跑完前面的行数再进行跳转:
until + [行号]
当我想直接结束这个循环的时候,可以选择直接跳过这个循环;
display + [变量名] :像vs一样直接进行常显示变量:
display + [变量名]
如果不想查看该变量了,就直接undisplay + [变量编号] :
undisplay + [变量编号]
2.查看上下文代码
3.调试技巧:
watch + [变量值]:能够每次都查看变量的新旧值的变换;
如果当前变量被修改了,watch就会通知我
watch + [变量值]
可以看到info b断点内有一个当前断点值,watchpoint result , 如果不想观察了,就把他当作断点直接删掉就ok
📌 注意:
如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你.
set var + [变量名] = 修改值 直接在调试过程中进行修改变量值
在调试期间,如果能够发现错误,但是又不想返回到源文件进行修改,然后又要重新进行编译,那么就可以直接用set var + [变量名] = 修改值 ;直接进行修改即可;
b + [行号] if + [条件] :设置条件断点:
如果在调试过程中,不使用单步调式,直接c/continue,那么就会直接跳转到条件断点处:
📌 注意:
• 条件断点添加常⻅两种⽅式:1. 新增 2. 给已有断点追加• 注意两者的语法有区别,不要写错了。• 新增: b ⾏号/⽂件名:⾏号/函数名 if i == 30(条件)• 给已有断点追加:condition 2 i==30, 其中2是已有断点编号,没有if