• GDB查看栈信息

    使用 GDB 调试程序时,当程序发生中断,我们首先应该知道程序在哪里产生中断以及产生中断的原因是什么?函数发生调用时,相关的调试信息就已经产生,并且被存储在一块被称为栈帧的数据里。

    栈帧是在调用栈的内存区域里分配的,是调用栈划分的连续的区块,简称为栈。每个帧是一个函数调用另一个函数的相关数据,包含了传递给本地用函数的参数,这个函数的本地变量和这个函数的执行地址。
     
    在函数开始的时候栈中只有一个帧,是 main 函数的,这个帧称为初始帧或者是最外层的帧。每当一个函数被调用,就产生一个新的栈帧。当函数返回时,这个调用所属的帧就被销毁了。如果调用的是递归函数,那么同一个函数就可能有多个帧。当前正在执行的函数调用的帧成为最内层的帧,这是最近创建的帧,同时还有别的帧存在。程序内部的栈帧用地址标识,一个栈帧有许多的字节组成,每个字节都有自己的地址。

    GDB为所有现存的栈帧编号,从最内层帧 0 开始,1 是这个函数调用的帧,以此类推。这些编号并不真正存在于程序里:他们是由 GDB 分配,用于 GDB 的命令来区分栈帧。

    显示栈帧信息

    显示栈帧信息的命令主要有 frame 和 backtrace,下面是对这两个命令的介绍。

    1.frame的命令格式展示如下:

    frame

    使用 frame 命令会打印出当前调用栈的信息,这些信息包含:栈帧的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。命令可以缩写为 f。

    2.backtrace的命令格式展示如下:

    backtrace <n>

    不带参数:打印当前调用函数的栈帧信息,每个栈帧显示一行。
    带参数:n 为正整数时,表示打印栈顶 n 层的栈信息;n 为负整数时,那么表示打印栈底 n 层的栈信息。
     
    如果我们想要获取更详细的当前栈帧层的信息,可以使用命令:

    info frame

    打印出的大多数都是运行时的内地址。比如:函数地址,被调用的函数地址,当前函数是由什么样的语言写成的、函数参数地址及值、局部变量的地址。

    切换到其他栈帧

    1.切换到任意的栈帧使用 frame 相关的命令格式如下:

    frame <n>

    n 表示栈帧的标号,这个命令可以从一个堆栈帧转到另一个,并打印所选的堆栈帧。对于多个堆栈帧,从当前执行的栈帧开始,下面就是显示这个在调用的函数的栈帧。实例:

    (gdb) frame 3

    切换到标号为 3 的栈帧。

    2.从当前的栈帧层向上移动,命令格式表示:

    up <n>

    n 表示栈帧的标号,在堆栈里上移 n 帧,对于正数向外层的帧移动,更高编号的帧,存在更长的时间的帧。实例:

    (gdb) up 3

    移动到编号为“当前栈帧的编号 + 3”的栈帧。

    3.从当前的栈帧层向下移动,命令格式表示:

    down <n>

    n 表示栈帧的标号,在堆栈里下移 n 帧。对于正数n,向内层的帧移动,更低编号的帧,新创建的帧。实例:

    (gdb) down 3

    移动到编号为“当前栈帧的编号 - 3”的栈帧。

    实例:代码中在main函数中调用的是一个递归函数,可以形成多个帧,使用上面的命令进行调试。

    (gdb) l
    1     #include <stdio.h>
    2     int func(int a)
    3     {
    4            if(a == 1)
    5                   return 1;
    6            else
    7                   return a + func(a - 1);
    8     }
    9    
    10  
    (gdb)
    11   int main(void)
    12   {
    13          int sum = 0;
    14          sum = func(100);
    15          printf("%d\n",sum);
    16  
    17          return 0;
    18   }

    (gdb)
    Line number 19 out of range; test.c has 18 lines.
    (gdb) break func           //设置断点,func函数入口地址
    Breakpoint 1 at 0x555555554655: file test.c, line 4.
    (gdb) run
    Starting program: /home/wjc/hsxy/lianxi/10/test/a.out
     
    Breakpoint 1, func (a=100) at test.c:4
    4            if(a == 1)

    (gdb) continue
    Continuing.
     
    Breakpoint 1, func (a=99) at test.c:4
    4            if(a == 1)

    (gdb) continue
    Continuing.
     
    Breakpoint 1, func (a=98) at test.c:4
    4            if(a == 1)
    (gdb) bt
    #0  func (a=98) at test.c:4
    #1  0x000055555555466f in func (a=99) at test.c:7
    #2  0x000055555555466f in func (a=100) at test.c:7
    #3  0x0000555555554691 in main () at test.c:14

    (gdb) info frame            //打印当前堆栈帧的信息
    Stack level 0, frame at 0x7fffffffddb0:
     rip = 0x555555554655 in func (test.c:4); saved rip = 0x55555555466f
     called by frame at 0x7fffffffddd0
     source language c.
     Arglist at 0x7fffffffdda0, args: a=98
     Locals at 0x7fffffffdda0, Previous frame's sp is 0x7fffffffddb0
     Saved registers:
      rbp at 0x7fffffffdda0, rip at 0x7fffffffdda8

    (gdb) bt 2                  //显示栈顶的两层的信息
    #0  func (a=98) at test.c:4
    #1  0x000055555555466f in func (a=99) at test.c:7
    (More stack frames follow...)

    (gdb) bt -2               //显示栈底的两层的信息
    #2  0x000055555555466f in func (a=100) at test.c:7
    #3  0x0000555555554691 in main () at test.c:14

    (gdb) frame 0            //转到0层的堆栈帧
    #0  func (a=98) at test.c:4
    4            if(a == 1)

    (gdb) up 1
    #1  0x000055555555466f in func (a=99) at test.c:7
    7                   return a + func(a - 1);

    (gdb) down 1
    #0  func (a=98) at test.c:4
    4            if(a == 1)

    (gdb) info args
    a = 98

更多...

加载中...