默认情况下,gcc在编译时不会将调试符号加到生成的二进制代码中,因为这样会增加可执行文件的大小。如果需要在编译时生成调试信息,需要使用gcc的-g或者-ggdb选项
gcc的调试信息同样采用分级(跟优化一样)的思想,分为g1、g2、g3,默认是g2
g1:只能够用于回溯跟踪和堆栈转储
g2:除了g1,还有拓展的符号表、行表、局部或外部变量信息
g3:除了g2, 还有源代码中定义的宏
生成调试信息
gcc efficient.c -g -o efficient
可以对比带不带调试信息的文件大小
用 ls -l efficient命令查看
gdb的功能
1.设置断点(断点可以是条件表达式)。
2.单步执行
3.查看程序中变量值的变化
4.动态改变程序的执行环境
5.分析崩溃程序产生的core文件
开始调试
gdb filename (这个可执行文件必须是带了gdb文件的)
输入quit 退出gdb
gdb常用命令
1.file 装入要调试的可执行文件
2.run 执行被调试的程序
-
kill 终止正在调试的程序
-
step 单步进入
-
next 下一步
6.break 设置断点
- print 打印表达式或变量的值,或打印内存中某个变量开始的一段连续区域的值。还可以用来赋值。
8.dispaly 设置自动显示的表达式或变量,当程序停住或者单步跟踪的时候,这些变量会显示。
-
list 列出可执行文件源代码的一部分
-
quit 退出
-
watch 监控一个变量,不管它何时改变。
-
backtrace 回溯跟踪
-
frame n 定位到发现错误的代码段,n为backtrace命令的输出结果的行号
-
examine 查看内存地址中的值
-
jump 使程序跳转执行
-
signal 产生信号量
-
return 强制函数返回
-
call 强制调用函数
-
make 使用户不退出gdb就可以重新产生可执行文件
-
shell 使用户不离开gdb就可以执行linux的shell命令。
break命令的用法
break 在进入指定函数时停住.c++中可以使用class::function或function(type, type)格式来指定函数名
break在指定行号停住
break + offset 在当前行号前面的offset行停住
break - offset 在当前行号后面的offset行停住
break filename::linenum 在源文件filename的第多少行停住
break filename::function 在源文件的function函数的入口处停住
break *address 在该内存地址处停住
break 表示在下一条指令处停住
break if 条件停住
查看运行数据的命令
主要是print和display命令
print命令的格式是
print /
是表达式,是输出的格式,比如16进制的格式输出 /X
display
它可以设置自动显示的变量,当程序停住,或者单步跟踪时,这些变量会自动显示
display
display /
display /
fmt同样表示格式
undisplay <dnums…> 或 delete display <dnums…>
擅除自动显示,dnums为已设置好的自动显示的编号。如果要同时删除几个编号,可以用空格分割;如果要删除一个范围内的变量,可以用2-5这样表示。
disable display <dnums…> enable display <dnums…>
不删除自动显示的设置,而只是让其失效或恢复
info display
查看所有自动显示的信息。
gdb查看源程序
查看源代码
用list命令
list 行号 显示第几行周围的源代码
list 函数名 显示该函数的源代码
list 显示当前行后面的源代码
list - 显示当前行前面的源程序。默认显示10行,一般为上5和后5行
set listsize 设置一次显示原代码的行数
show listsize 显示一次显示的行数
list , 显示first行到last行之间的源代码
list , 显示当前行到last行之间的源代码
list + 向后显示源代码
查看源代码的内存
info line + …来查看源代码在内存中的地址,跟list一样,它可以加行号,函数名,文件名
disassemble 查看源程序的当前执行时的机器码。这个命令会把目前内存中的指令dump出来。
gdb改变程序的运行
1.改变变量的值
print x = 8 这样可以把变量的值改变(不仅仅可以打印)
2.跳转执行(乱序执行的功能)
jump
可以是文件的行号,可以是file::line格式,可以是+num这种偏移格式,表示下一条语句从哪里开始。
jump
跳转到相应的内存地址3.产生信号量
使用signal命令可以产生一个信号量给被调试的程序。如中断信号Ctrl + C
signal
4.强制函数返回
如果断点在某个函数中,还有语句没有执行完,可以用return语句。
return
return
指定了 那么该表达式的值会被当做函数的返回值
5.强制调用函数
call 表达式可以是一个函数
print 加函数跟call是一样的,区别是如果返回值是void,call不显示,print会显示返回值,并把该值存到历史数据中
gdb调试代码1
#include<stdio.h>
int main()
{
int input = 0;
printf("Input an integar:"):
scanf("%d", input);
return 0;
}
1.产生带gdb的可执行文件
gcc -ggdb3 simple_gdb.c -o simple_gdb
2.开始调试
gdb simple_gdb
3.先run一下
提示发生了segment fault
4.backtrace(回溯一下)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rhERKt9-1624455331452)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a2432e92-95fe-46c5-a03c-362396d343e3/__20210623110033.png)]
看到错误已经定位到第7行了。
5.进入这一行
frame 2(注意不是第7行,而是前面那个行号)
gdb调试代码2
#include<stdio.h>
int calculate(intx, int y);
int main()
{
int num1 = 0, num2 = 0, result = 0;
while(1)
{
print("Enter two integers, or use 0 0 to exit:";
scanf("%d %d", &num1, &num2);
if(num1 == 0 && num2 == 0)
exit(0);
result = calculate(num1, num2);
print("This result is: %d\n", result);
}
return 0;
}
int calculate(int x, int y)
{
int res = 0;
res = x * x + y * y;
return res;
}
1.产生带gdb的可执行文件
gcc -ggdb3 calculate.c -o calculate
2.开始调试
gdb calculate
3.在main设一个断点
break main
4.运行
run
5.单步进入(一直单步进入,可以简写成s)
step
6下一步(配合单步进入用,可以简写成n,进入函数以后就下一步)
next
7.查看变量值
print num1
print num2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NiLA8RPN-1624455331458)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7d673ee9-3558-45a4-9427-2c63910edd88/__20210623165937.png)]
print $1也是可以的
display num1
8查看源代码
set listsize 15
show main
show 5
9显示内存信息
info line calculate
10查看机器码
disassemble calculate