文章目录
- 显示gdb版本信息
- 启动时不显示提示信息
- 列出函数的名字
- 单步调试源码
- 单步调试汇编
- [si ni s n的归纳](#si ni s n的归纳)
- 退出正在调试的函数
- 直接执行函数
- 打印函数堆栈帧信息
- 打印尾调用堆栈帧信息
- 选择函数堆栈帧
- 向上或向下切换函数堆栈帧
显示gdb版本信息
启动时不显示提示信息
gdb在启动时会显示如上类似的提示信息。
如果不想显示这个信息,则可以使用-q选项把提示信息关掉:
.bashrc
.bashrc是home目录下的一个shell文件,用于储存用户的个性化设置。在bash每次启动时都会加载.bashrc文件中的内容,并根据内容定制当前bash的配置和环境。
可以在~/.bashrc中,为gdb设置一个别名:
bash
alias gdb="gdb -q"
关于~和/
~是主目录
/是根目录
此时运行gdb
列出函数的名字
单步调试源码
n/s都是C语言级的断点定位。 s会进入C函数内部,但是不会进入没有调试信息的函数(比如没有加-g编译的代码,因为其没有C代码的行数标记,没办法定位,可以执行"set step-mode on"命令,这样gdb就不会跳过没有调试信息的函数,而是进入其汇编,接下来可以使用调试汇编程序的办法去调试函数。),n不会。
说白了就是要一行一行源码的执行
单步调试汇编
ni/si都是汇编级别的断点定位。si会进入汇编和C函数内部,ni不会。
说白了就是一行一行汇编代码的执行
si ni s n的归纳
归纳:当要进入没有调试信息的库函数调试的时候,用si是唯一的方法。
当进入有调试信息的函数,用si和s都可以,但是他们不同,si是定位到汇编级别的第一个语句,但是s是进入到C级别的第一个语句
退出正在调试的函数
c
#include <stdio.h>
int func(void)
{
int i = 0;
i += 2;
i *= 10;
return i;
}
int main(void)
{
int a = 0;
a = func();
printf("%d\n", a);
return 0;
}
当单步调试一个函数时,如果不想继续跟踪下去了,可以有两种方式退出。
第一种用"finish"命令,这样函数会继续执行完,然后达到返回地址的对应的指令位置
第二种用"return"命令,这样函数不会继续执行下面的语句,而是直接返回。也可以用"return expression"命令指定函数的返回值。仍以上面代码为例:
此时执行return,此时到返回地址的对应指令的寄存器信息和执行return时的一样
通常,所选的堆栈帧具有调试信息。当调试信息可用时,GDB将始终使用调试信息而不是隐式表达式类型。例如,如果你输入return -1,并且当前堆栈框架中的函数被声明为返回一个long long int, gdb会透明地将-1的隐式int值转换为一个long long int:
但是,如果选择的堆栈帧没有调试信息,例如,如果函数在没有调试信息的情况下编译,gdb必须找出要从用户返回的类型。错误地指定不同的类型可能会在不同的下级寄存器中设置值,而不是调用方代码所期望的值。例如,键入return -1和它的隐式类型int,对于调试信息较少的函数(在32位体系结构上),只会设置long long int结果的一部分。因此,用户需要通过适当的强制转换显式指定返回类型:
直接执行函数
call和print在执行函数上没有什么区别
c
#include <stdio.h>
int global = 1;
int func(void)
{
return (++global);
}
int main(void)
{
printf("%d\n", global);
return 0;
}
打印函数堆栈帧信息
c
#include <stdio.h>
int func(int a, int b)
{
int c = a * b;
printf("c is %d\n", c);
}
int main(void)
{
func(1, 2);
return 0;
}
gdb 调试器为了方便用户在调试程序时查看某个栈帧中记录的信息,提供了 frame 和 backtrace 命令。
Linux下 gdb 调试中,常常会查看函数的堆栈帧信息。
bt(backtrace的缩写)命令:查看当前函数向上的堆栈的函数的堆栈信息。
i frame (info frame的缩写)命令:查看当前调试函数的堆栈帧信息。这种方式查看的堆栈信息比较详细。
输出了当前函数堆栈帧的地址,指令寄存器的值,局部变量地址及值等信息
打印尾调用堆栈帧信息
c
#include<stdio.h>
void a(void)
{
printf("Tail call frame\n");
}
void b(void)
{
a();
}
void c(void)
{
b();
}
int main(void)
{
c();
return 0;
}
当一个函数最后一条指令是调用另外一个函数时,开启优化选项的编译器常常以最后被调用的函数返回值作为调用者的返回值,这称之为"尾调用(Tail call)"。以上面程序为例,编译程序(使用'-O'):
可以看到main函数直接调用了函数a
可以设置"debug entry-values"选项为非0的值,这样除了输出正常的函数堆栈帧信息以外,还可以输出尾调用的相关信息:但会和与是否带-g参数编译有关
没有带-g编译
带-g编译
选择函数堆栈帧
c
#include <stdio.h>
int func1(int a)
{
return 2 * a;
}
int func2(int a)
{
int c = 0;
c = 2 * func1(a);
return c;
}
int func3(int a)
{
int c = 0;
c = 2 * func2(a);
return c;
}
int main(void)
{
printf("%d\n", func3(10));
return 0;
}
可以用"frame n"命令选择函数堆栈帧,其中n是层数。然后通过info frame会查看到选择的函数堆栈帧,或者直接frame也可以查看到当前选择的堆栈帧。显示信息格式不同而已
向上或向下切换函数堆栈帧
c
#include <stdio.h>
int func1(int a)
{
return 2 * a;
}
int func2(int a)
{
int c = 0;
c = 2 * func1(a);
return c;
}
int func3(int a)
{
int c = 0;
c = 2 * func2(a);
return c;
}
int main(void)
{
printf("%d\n", func3(10));
return 0;
}
可以用"up n"或"down n"命令向上或向下选择函数堆栈帧,其中n是层数。以上面程序为例:
up是将其编号+1,down是将编号-1
up-silently n"和"down-silently n"这两个命令,与"up n"和"down n"命令区别在于,切换堆栈帧后,不会打印信息,仍以上面程序为例: