初窥调试神器gdb

  工作上的内容调整,后期会有大量的时间与c/cpp狭路相逢,翻查了一些对于c/cpp的debug知识,发现了这个很强大的命令,本着独学而无友,必孤陋而寡闻的中心思想,将自己这两天对于gdb的理解,认识也包括一些问题总结下来,跟各位同仁一起交流,向大家学习


1、gdb是干什么的?

      gdb是linux环境下的标准调试器,最常使用到的功能有(就这两天我用的比较多的):

            1、启动并且运行待调试的程序。
    2、在待调试的程序中设置断点,查看断点发生时的堆栈信息
    3、查看内存地址中的值

2、gdb常用命令

2.1 设置断点:break(b) 断点可以通过函数名、当前文件内的行号来设置,也可以先指定文件名再指定行号,还可以指定与暂停位的偏移量或者用地址来设置。

       格式: b  函数名

                    b 行号

                    b 文件名:行号

                    b +偏移量

                    b -偏移量

                    b *地址

举例(见截图)

截图之前插个小插曲:

第一版本demo 代码:

#include <iostream>


int addition (int a, int b)
{
  int r;
  r=a+b;
  return (r);
}


int main ()
{
  int z;
  z = addition (5,3);
  cout<< "The result is "<<z;
  return 0;
}

很简单,很明白,就是想打出个8.

但是当我用g++进行编译的时候,碎了


不是吧?查看了几次代码,没发现哪里错啊,莫非多年没碰c、cpp的代码,写这么一个简单的都已经2了,,汗啊

google一把,原来是:

This is becuase C++ 1998 requires cout and endl be called 'std::cout' and 'std::endl', or that a proper using directives such as 'using namespace std;' be used.的原因。

这个还真没啥印象,,知道的BS我吧,,唉,枪不打会生锈,代码不编还真是会生疏啊

添加上之后的代码为:

#include <iostream>
using namespace std;


int addition (int a, int b)
{
  int r;
  r=a+b;
  return (r);
}


int main ()
{
  int z;
  z = addition (5,3);
  cout<< "The result is "<<z;
  return 0;
}

g++ -g xxx.cpp之后。通过

继续看断点(b)的截图

g++编译之后,会有一个a.out的文件


执行方式见下:



先查看下整个代码信息:list


OK,我们就在addition函数的return 那一行加个断点


ok,这样就表示我们已经在函数的第8行添加了一个断点。再次执行该函数,会在添加断点处停止,可以查看对应的堆栈信息

运行试一试


程序就在断点添加处暂停了

想着举个断点的例子来帮助自己日后回顾,但其实里面也已包含了其他的几个常用的gdb命令,用gdb调试的时候应该也都经常用到吧。


2.2、查看断点所在函数的堆栈信息

还是拿上面的简单的代码举例吧


看下是不是在main函数,整个函数的14行调的addition

list一把


源文件中的确是在第14行调用的addition函数,整个bt查看到的信息的意思就是main函数在第14行调用了addition函数。


2.3、info的使用

例如:


locals查看当前函数的局部变量

arg查看当前函数参数名和值


info f:打印出更加详细的当前栈层信息。这些信息包含:

  • 当前堆栈帧地址(什么是堆栈帧呢?)
  • 堆栈帧:堆栈中为当前正在运行的函数分配的区域。
  • rip地址(rip是什么?后面一起)
  • 开发语言(这里很明显是c++)
  • 当前函数的参数地址及值,局部变量的地址

其中rip,rbp(操作系统位数不同,可能看到的不同,32位os这里看到的应该是eip,ebp)
rip:CPU执行完当前的指令后,从RIP寄存器中读取下一条指令的内存地址,然后继续执行。所以这里存放的是下一条指令的内存地址
rbp:指向栈上数据的地址
这里有个问题?为啥rip会有两个不同的值呢?
实验代码和结果如下:
代码:
#include <iostream>
using namespace std;


int sub(int c,int d);
int addition (int a, int b)
{
  int r;
  r=a+b;
  sub(a,b);
  return (r);
}
int sub(int c,int d)
{
  int zo;
  zo=c-d;
  return (zo);
}
int main ()
{
  int z,g;
  z = addition (5,3);
  g = sub(5,3);
  cout<< "The result is "<<z;
  return 0;
}
结果:
saved rip的地址为调用函数的下一指令的内存地址
rip为当前函数的下一指令的内存地址
所以两者的值不同是正常的。

所以上面整个图的我的理解是:当前堆栈的堆栈帧地址是0x7fffc3de0f30,当前函数的下一指令内存地址是0x4007ab,调用该函数的上一函数下一指令的内存地址是0x400833,调用该函数的上一层堆栈帧地址是0x7fffc3de0f50.开发语言是c++.当前函数的参数地址是0x7fffc3deof20,参数值是5,3.
局部变量的地址是0x7fffc3deof20。被注册保存的信息是rbp和rip以及它们的内存地址空间地址。            


gdb很强大,里面还有很多东西需要研究和深入学习,初窥的过程中还有一些细节不是很了解,后面继续学习,实践。

参考资料:debug with gdb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值