程序的调试过程主要有:单步执行,跳入函数,跳出函数,设置断点,设置观察点,查看变量。
正确的学习方式, 使用 man gdb
: 查看官方文档,
You can run "gdb" with no arguments or options; but the most usual
way to start GDB is with one argument or two, specifying an
executable program as the argument:
gdb program
You can also start with both an executable program and a core file
specified:
gdb program core
You can, instead, specify a process ID as a second argument or use
option "-p", if you want to debug a running process:
gdb program 1234
gdb -p 1234
would attach GDB to process 1234. With option -p you can omit the
program filename.
本文将主要介绍linux下运行。
GDB中的命令很多,但我们只需掌握其中十个左右的命令,就大致可以完成日常的基本的程序调试工作。
每一个命令可以使用, 对应的首字母进行简写,
新建test.c
输入以下内容:
#include
void f(){
printf(" f is called \n");
}
int i =1;
int main(){
f();
i = 4;
printf(" Hello world!\n");
}
要调试C/C++的程序,首先在编译时,要使用gdb调试程序,在使用gcc编译源代码时必须加上“-g”参数。
gcc -g test.c
保留调试信息,否则不能使用GDB进行调试.
有一种情况,有一个编译好的二进制文件,你不确定是不是带有-g参数,带有GDB调试,这个时候你可以使用如下的命令验证:
gdb a.out
如果没有调试信息,则会出现:
Reading symbols from a.out…
(No debugging symbols found in a.out
如果带有调试功能,下面会提示
Reading symbols from a.out……done.
使用命令readelf查看可执行文件是否带有调试功能:
readelf -S main|grep debug
如果有debug说明有调试功能,如果没有debug。说明没有带有调试功能,则不能被调试。
gdb调试 出现value optimized out解决方法
由于gcc在编译过程中默认使用-O2优化选项,
希望进行单步跟踪调试时,应使用-O0选项。
> -O0:这个等级(字母“O”后面跟个零)关闭所有优化选项,也是CFLAGS或CXXFLAGS中没有设置-O等级时的默认等级。这样就不会优化代码,这通常不是我们想要的。
-O1:这是最基本的优化等级。编译器会在不花费太多编译时间的同时试图生成更快更小的代码。这些优化是非常基础的,但一般这些任务肯定能顺利完成。
-O2:-O1的进阶。这是推荐的优化等级,除非你有特殊的需求。-O2会比-O1启用多一些标记。设置了-O2后,编译器会试图提高代码性能而不会增大体积和大量占用的编译时间。
-O3:这是最高最危险的优化等级。用这个选项会延长编译代码的时间,并且在使用gcc4.x的系统里不应全局启用。自从3.x版本以来gcc的行为已经有了极大地改变。在3.x,-O3生成的代码也只是比-O2快一点点而已,而gcc4.x中还未必更快。用-O3来编译所有的软件包将产生更大体积更耗内存的二进制文件,大大增加编译失败的机会或不可预知的程序行为(包括错误)。这样做将得不偿失,记住过犹不及。在gcc 4.x.中使用-O3是不推荐的。
-Os:这个等级用来优化代码尺寸。其中启用了-O2中不会增加磁盘空间占用的代码生成选项。这对于磁盘空间极其紧张或者CPU缓存较小的机器非常有用。但也可能产生些许问题,因此软件树中的大部分ebuild都过滤掉这个等级的优化。使用-Os是不推荐的。
正如前面所提到的,-O2是推荐的优化等级。如果编译软件出现错误,请先检查是否启用了-O3。再试试把CFLAGS和CXXFLAGS倒回到较低的等级,如-O1甚或-O0 -g2 -ggdb(用来报告错误和检查可能存在的问题),再重新编译。
-O0 不进行优化处理。
-O 或 -O1 优化生成代码。
-O2 进一步优化。
-O3 比 -O2 更进一步优化,包括 inline 函数。
通过 gdb + 可执行文件
, 开始调试需要的文件。
gdb a.out
输入,
run
:将运行整个程序,
list
: 显示原始的代码, 可以输入多次, 直到显示完整的代码。
quit
: 退出gdb 模式。
(gdb) run
Starting program: /home/respecting-life/Desktop/os_上交/lab1/a.out
f is called
Hello world!
[Inferior 1 (process 5403) exited normally]
(gdb) list
1 #include
2
3 void f(){
4 printf(" f is called \n");
5 }
6
7
8 int i =1;
9
10 int main(){
(gdb) list
11 f();
12 i = 4;
13 printf(" Hello world!\n");
14 }
gdb 中打断点的方式, 有两种
(gdb) break main
Breakpoint 1 at 0x555555555160: file test.c, line 10.
(gdb) b 11
Breakpoint 2 at 0x555555555168: file test.c, line 11
info b
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000555555555160 in main at test.c:10
2 breakpoint keep y 0x0000555555555168 in main at test.c:11
此时,继续运行run
时,将会停在断点处,
移除某个断点, d 1
单步调试 next
, n
,
c
: 从一个断点跳到下一个断点处,
p
: print
的简写,
(gdb) p i
$2 = 4
打印变量的地址, p &i
加上取地址符,
(gdb) p &i
$3 = (int *) 0x555555558010 <i>
step
: 进入该函数中, 按 s
,
bt
: backtrace ,
查看函数调用栈, 即最先调用的函数,是在最下面。
使用print
输出带观察变量的地址,
print
p:
p/x
: 使用十六进制 显示出来。
(gdb) p &i
$2 = (int *) 0x201010 <i>
使用 watch
该地址的数值,
(gdb) watch *0x201010
Hardware watchpoint 1: *0x201010
或者直接watch 该变量:
(gdb) watch num
Hardware watchpoint 2: num
(gdb) n
Hardware watchpoint 2: num
Old value = 0
New value = 1
main (argc=1, argv=0x7fffffffdbc8) at 12.c:5
5 while (num <= 100){
(gdb)
6 num *= 2;
(gdb)
Hardware watchpoint 2: num
Old value = 1
New value = 2
main (argc=1, argv=0x7fffffffdbc8) at 12.c:5
5 while (num <= 100){
(gdb)
set logging on
: 将每一次的输出保存到另外一个文件中去,
(gdb) set logging on
Copying output to gdb.txt.
(gdb) list
上述介绍的是已经生成可执行的二进制文件,
现实情况中,往往需要调试的是出错的文件,
通过 man ulimit
查看, 是对shell 终端 可以获得资源的一种限制,
需要使用 ulimit -a
查看,
ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 63296
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 63296
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
使用unlimite -c unlimited
解除 生成 core 文件大小的限制,
ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 63296
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 63296
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
调试时, 使用 gdb ./a.out core.1234
;
gdb + 二进制文件 + core 文件
layout src
: 开启调试源代码的模式,
此后, 退出 和开启 该图形界面 按 ctrl + x, 然后 a
,
接着按 n
进行 单步调试,
layout asm
:
汇编的单步: si
info r
: register;