gdb常用命令
gdb在命令的首字母唯一时是可以缩写的。
-
b/break 函数名 :给函数设置断点。 -
c/continue :继续执行。 -
b/break 文件名.c:行号 :在指定文件的指定行设置断点。 -
next :单步执行程序,如果是函数则将函数整体执行,不会进入。 -
回车键 :执行上次的命令。 -
file 可执行文件名 :调试指定的文件。 -
s/step :单步执行程序,如果是函数则会进入。 -
l/list :显示当前行下面的10行代码。 -
p/print 变量名 :查看变量的值。也可以查看函数的值(地址)。 -
i/info b/break :查询断点信息。 -
d/delete :删除所有的断点。 -
d/delete 断点序号 :删除第几个断点。 -
attach pid :调试运行中的进程id为pid的程序。
demo1
#include <stdio.h>
int add(int num1, int num2){
return num1 + num2;
}
int main(){
int a = 5;
int b = 8;
int ret = add(a,b);
printf("%d + %d = %d\n", a, b, ret);
return 0;
}
生成可执行文件
调试
选中编译文件
调试必须在编译时加入 -g ,然后再输入file 可执行文件名 ,编译时如果没有指定文件名,默认是a.out 。
查看代码
每次l 命令只能看10行。
运行代码
设置断点(函数)
-
先在add 函数处添加了断点,然后直接运行程序。 -
然后就停在了第4行(实际上是从第10行调用的),也就是add 中return的位置。 -
执行n 指令,继续走一步,来到第五行。 -
再次执行n 指令,也就是ret 赋值完毕,来到第11行。 -
按下回车 ,执行上一次的指令,也就是n ,执行printf 语句打印出信息,显示了下一行的语句:return 0;
打印变量信息
断点情况
- 查看断点
- 删除指定断点
只有一个断点,删除了一号断点 后,就没有断点了。
demo2
#include <stdio.h>
#include <unistd.h>
void printHello(int times){
printf("Hello -- %d\n", times);
}
int main(){
for(int i=0;i<10000;i++){
printHello(i);
sleep(3);
}
return 0;
}
生成可执行文件
for循环如果报错,要加一个-std=c99 。 -o 可以指定可执行程序的名称,这里我将demo2.c 的可执行文件名称指定为demo2 。 测试一下:
如果打印操作不是通过printHello 函数,并且printf中没有\n ,那么是没有打印结果的。 需要加上\n ,或者是fflush(stdout); 刷新一下缓冲区。 这是因为printf的输出结果被放在缓冲区,如果没有换行符或者刷新缓冲区,不会有输出。
调试
这次我们要调试运行中的程序,所以要获取该程序的进程id 。 ps -ef | grep 进程名 先运行程序,然后从另一个终端中搜索该程序的进程号:
这里的10237 就是PID(process identity)
调试运行中的程序
attach 10237 :调试PID为10237的程序。b demo2.c:10 在demo2.c 的第10行打一个断点,同时左边终端到Hello --58 就停住了,没有再输出信息。- 执行一步,也就是执行
sleep(3) ,提示我们下一次要执行的是:
9 for(int i=0;i<10000;i++)\{
也就是第9行的for循环体。
- 查看
i 的值 - 再执行一步
- 这里用
s 再执行下一步,s 是会进入函数的,s 就是step 的缩写
果然进入了第5行的函数体中。
- 我们使用
c 命令,也就是continue: 继续执行代码,因为我们设置过断点,所以会来到调用函数的地方。 - 这里我们用
n ,来看看是否会进入函数。 发现是没有的,程序直接执行完了当前行,并且下一行要执行sleep(3) 。 也打印出了结果。当然,Hello --59 是上一次打印的。 - 我们还可以通过
kill 命令结束程序。 kill 命令不是gdb中的,所以我们要先通过q 或quit 退出gdb的shell,调试会话还没结束,它会问你一下要不要退出,我们输入y 。 我们一结束调试,旁边的终端就又开始输出信息了。等着,马上kill掉它😡。 进程被kill掉,程序就结束啦。
|