gdb 笔记(08)— 查看栈回溯信息、切换栈帧、查看帧信息

当程序进行函数调用时,这些调用信息(比如在哪里调用等)称为栈帧。每一个栈帧的内容还包括调用函数的参数、局部变量等。所有栈帧组成的信息称为调用栈(或者调用堆栈)。

当程序刚开始运行时,只有一个栈帧,即主函数 main。每调用一个函数,就产生一个新的栈帧;当函数调用结束时(即从函数返回后),该函数的调用随之结束,该栈帧也结束。如果该函数是一个递归函数,则调用该函数会产生多个栈帧。

1. 查看栈回溯信息

查看栈回溯信息的命令是 backtrace 。执行该栈回溯命令后,会显示程序执行到什么位置、包含哪些帧等信息。每一帧都有一个编号,从 0 开始。0 表示当前正在执行的函数,1 表示调用当前函数的函数,以此类推。栈回溯是倒序排列的。

下面来演示 backtrace 命令的用法。示例代码:

#include <iostream>
#include <string>

int call_fun_test_2(int level, const char *str)
{
    int number = 102;
    const char *name = "call_fun_test_2";
    printf("level is %d,str is %s,name is %s\n", level, str, name);
    return 2;
}

int call_fun_test_1(int level, const char *str)
{
    int number = 101;
    const char *name = "call_fun_test_1";
    printf("level is %d,str is %s,name is %s\n", level, str, name);
    call_fun_test_2(level + 1, "call_fun_test_2");
    return 1;
}

int main(int argc, char *argv[])
{
    call_fun_test_1(1, "call_fun_test_1");
}

其中 main 函数调用 call_fun_test_1 。启动 gdb 进入调试模式,为函数 call_fun_test_2 设置一个断点。当程序在 call_fun_test_2 中中断后,执行栈回溯 backtrace 命令,结果如图所示。

(gdb) b call_fun_test_2
Breakpoint 1 at 0x799: file demo.cpp, line 6.
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo 
level is 1,str is call_fun_test_1,name is call_fun_test_1

Breakpoint 1, call_fun_test_2 (level=2, str=0x555555554939 "call_fun_test_2")
    at demo.cpp:6
6	    int number = 102;
(gdb) backtrace
#0  call_fun_test_2 (level=2, str=0x555555554939 "call_fun_test_2") at demo.cpp:6
#1  0x0000555555554823 in call_fun_test_1 (level=1, str=0x555555554972 "call_fun_test_1") at demo.cpp:17
#2  0x000055555555484a in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:23
(gdb) 

命令 backtrace 可以简写为 bt 。从命令执行结果来看,一共有 3 个栈帧,编号分别为 0、1、2。每个栈帧中都包含函数名、调用函数的参数以及代码所在的行等。我们可以看到一个完整的函数调用链。

也可以执行命令来查看指定数量的栈帧:

bt 栈帧数量

这对于调用栈帧比较多的情况很有用处,可以忽略掉不太关心的那些栈帧。比如执行 bt 2 ,则只显示两个栈帧。

(gdb) bt 2
#0  call_fun_test_2 (level=2, str=0x555555554939 "call_fun_test_2") at demo.cpp:6
#1  0x0000555555554823 in call_fun_test_1 (level=1, str=0x555555554972 "call_fun_test_1") at demo.cpp:17
(More stack frames follow...)
(gdb) 

从上面可以看到,只显示了 0 和 1 两帧。如果想查看 1 和 2 这两个帧应该怎样做呢?
可以使用命令 bt -2 ,如下所示。

(gdb) bt -2
#1  0x0000555555554823 in call_fun_test_1 (level=1, str=0x555555554972 "call_fun_test_1") at demo.cpp:17
#2  0x000055555555484a in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:23
(gdb) 

如果 bt 后面跟的是一个正数,则从 0 开始计数。如果是一个负数,则从最大的栈帧编号开始倒序计数,但是最后显示时还是按照从小到大的编号顺序显示,只是显示的栈帧不同。比如一共有 10 个帧,编号为 0-9,如果执行 bt 4 ,则显示的帧为 0-3;如果执行命令 bt -4,则显示的帧编号为 6~9。

2. 切换栈帧

可以通过 frame 栈帧号 的方式来切换栈帧。为什么要切换栈帧呢?因为每一个栈帧所对应的程序的运行上下文都不同,比如栈帧 1 的局部变量和栈帧 2 的局部变量都不相同,只有切换到某个具体的栈帧之后才能查看该栈帧对应的局部变量信息。比如上面的栈回溯中,共有 3 个栈帧,我们想查看栈帧号为2(也就是 main 函数中所对应)的信息,则执行命令即可切换到 2 号帧:

frame 2 
# 或者
f 2

这时我们可以查看该帧对应的一些变量信息,比如局部变量 numbername 的值

(gdb) f 2
#2  0x000055555555485c in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:25
25	    call_fun_test_1(1, "call_fun_test_1");
(gdb) p name
$1 = 0x5555555549a2 "main"
(gdb) p number
$2 = 100
(gdb) 

再切换到 1号帧。因为 1 号帧中也包含两个临时变量 numbername ,执行 f 1 ,然后查看 numbername 的值,

(gdb) f 1
#1  0x0000555555554823 in call_fun_test_1 (level=1, str=0x555555554992 "call_fun_test_1") at demo.cpp:17
17	    call_fun_test_2(level + 1, "call_fun_test_2");
(gdb) p name
$3 = 0x555555554992 "call_fun_test_1"
(gdb) p number
$4 = 101
(gdb) 

除使用 print 查看局部变量外,还可以使用 info locals 来查看当前帧的所有局部变量的值,也可以使用info args 来查看当前帧所有的函数参数,

(gdb) info locals
number = 101
name = 0x555555554992 "call_fun_test_1"
(gdb) i locals
number = 101
name = 0x555555554992 "call_fun_test_1"
(gdb) info args
level = 1
str = 0x555555554992 "call_fun_test_1"
(gdb) i args
level = 1
str = 0x555555554992 "call_fun_test_1"
(gdb) 

还可以使用命令 updown 来切换帧。updown 都是基于当前帧来计数的。比如,当前帧号为1,up 1 则切换到 2 号帧,down 1 则切换到 0 号帧,

(gdb) bt
#0  call_fun_test_2 (level=2, str=0x555555554959 "call_fun_test_2") at demo.cpp:6
#1  0x0000555555554823 in call_fun_test_1 (level=1, str=0x555555554992 "call_fun_test_1") at demo.cpp:17
#2  0x000055555555485c in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:25
(gdb) up 1
#2  0x000055555555485c in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:25
25	    call_fun_test_1(1, "call_fun_test_1");
(gdb) up 1
#2  0x000055555555485c in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:25
25	    call_fun_test_1(1, "call_fun_test_1");
(gdb) up 2
#2  0x000055555555485c in main (argc=1, argv=0x7fffffffdea8) at demo.cpp:25
25	    call_fun_test_1(1, "call_fun_test_1");
(gdb) down 1
#1  0x0000555555554823 in call_fun_test_1 (level=1, str=0x555555554992 "call_fun_test_1") at demo.cpp:17
17	    call_fun_test_2(level + 1, "call_fun_test_2");
(gdb) down 2
#0  call_fun_test_2 (level=2, str=0x555555554959 "call_fun_test_2") at demo.cpp:6
6	    int number = 102;
(gdb) 

还可以使用以下命令来切换帧:

f 帧地址

其中,帧地址是栈帧所对应的地址。如果程序崩溃,栈回溯信息可能会遭到破坏,这时就可以使用该命令来进行栈帧切换。假设有一个栈帧的地址为 0x7fffffffe3a0 ,则使用命令即可切换到该栈帧,

84

3. 查看帧信息

可以使用 info frame 命令(包括前面介绍的 info localsinfo args 命令)来查看帧的详细信息,还可以使用 info frame 命令来查看具体的某一帧的详细信息。

比如要查看编号为 1 的帧的详细信息,可以直接使用 info frame 1 (可以简写为 i f 1 )命令,而不用先进行帧的切换操作。如下所示为连续查看 1 号帧和 2 号帧的详细信息。

86
从图中可以看到,帧的详细信息包括帧地址、rip 地址、函数名、函数参数等信息。这里可以用 f 帧地址命令来切换帧地址。这个帧地址也可以用到 i f 命令中,比如使用 i f 0x7fffffffe400 可以查看 2 号帧的详细信息。

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用GDB查看函数调用,你可以按照以下步骤进行操作: 1. 首先,启动GDB并加载你的程序。你可以使用以下命令:`gdb <your_program>`。 2. 在GDB中,你可以使用`backtrace`命令或简写的`bt`命令来查看函数调用。这将显示当前函数调用的顺序以及每个函数的输入参数和局部变量。例如,你可以输入`bt`命令来查看函数调用。 3. 如果你想查看更详细的信息,你可以使用`frame`命令或简写的`f`命令,后跟号。号从0开始,表示最新的函数调用。例如,你可以输入`f 0`命令来查看最新的函数调用的详细信息。 4. 如果你想查看特定函数的调用,你可以使用`up`命令或简写的`u`命令来向上移动到上一个函数调用。例如,你可以输入`u`命令来查看上一个函数调用的详细信息。 总结起来,使用GDB查看函数调用的步骤如下: 1. 启动GDB并加载你的程序。 2. 使用`bt`命令查看函数调用。 3. 使用`f`命令和查看特定函数调用的详细信息。 4. 使用`u`命令向上移动到上一个函数调用。 希望这些信息对你有帮助!\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [GDB使用技巧(3)——查看信息](https://blog.csdn.net/li_wen01/article/details/105223367)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【软件开发底层知识修炼】十七 快速学习GDB调试四 使用GDB进行函数调用查看](https://blog.csdn.net/qq_37375427/article/details/85226496)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [GDB入门教程之查看函数调用堆](https://blog.csdn.net/qq_39107832/article/details/119206954)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值