第01课:调试信息与调试原理
Linux下C/C++程序开发,即使使用Makefile、Cmake等编译工具,其最终都是调用GCC这一编译工具组的。这里说的工具组,是因为编译C程序和C++程序使用的编译工具还是有一点差别的,一般编译C程序使用GCC,编译C++程序使用G++。(下文统一使用GCC这一名词代指)。
我使用的操作系统是Deepin。如果你机器上没有GCC和GDB,可以安装一下。
debian系列的使用 apt-get
redhat系列的使用 yum
一般要调试某个程序,为了能清晰地看到调试的每一行代码、调用的堆栈信息、变量名和函数名等信息,需要调试程序含有调试符号信息。使用GCC编译程序时,如果加上-g选项即可在编译后的程序中保留调试符号信息。举个例子,以下命令将生成一个带调试信息的程序hello_server。
gcc -g -o hello_server hello_server.c
那么如何判断hello_server是否带有调试信息呢?我们使用GDB来调试一下这个程序,GDB会显示正确读取到改程序的调试信息,在打开的Linux Shell终端输入gdb hello_server查看显示结果即可:
GDB加载成功以后,会显示如下信息:
Reading symbols from hello_server…done.
另外补充两点说明:
– 本课程里虽然以GCC为例,但-g选项实际上同样适用于MakeFile、Cmake等工具编译生成的Linux程序。
– 在实际生成调试程序时,一般不仅要加上-g选项,也建议关闭编译器的程序优化选项。编译器的程序优化选项一般有五个级别,从O0-O4,O0表示不优化,从O1-O4优化级别越来越高,O4最高。这样做的目的是为了调试的时候,符号文件显示的调试变量等能与源代码完全对应起来。举个例子,假设有以下代码:
int func() { int a = 1; int b = a + 1; int c = a + b; return a + b + c; } int main() { int a = func(); printf("%d\n",a); }
在此代码中,由于在main()函数中调用了func()函数,而func()函数值可以在编译期间直接算出来,如果开启了优化选项,在实际调试的时候,这个函数中的局部变量a,b,c,被直接的值取而代之(即编译器计算得出的值,a直接使用1,b直接使用2,c直接使用3,不会产生通过a计算b,通过a、b计算c的指令),甚至连函数func()也可能被优化掉。如果出现这种情况,调试显示的代码和实际代码可能就会有差异了,这会给排查问题带来困难。当然,上面说的优化现象是否一定会出现,不同版本的编译器可能会有不同的行为。总之一句话,建议生成调试文件时关闭编译器优化选项。