我们知道,C++程序如果带着调试信息的话会比较大,所以一般发布版本都会去掉调试信息,但是我们又希望如果程序崩溃了可以使用core转储文件进行调试,如果不带调试信息就不能方便的进行调试,那要怎么办呢,这篇文章会简单的讨论一下。
我们知道C++程序瘦身的办法就是去掉调试信息。
先看代码
//main.cpp
#include
void Func(void)
{
int* p = nullptr;
int a = *p;
std::cout << a << std::endl;
}
int main(int argc, char* argv[])
{
Func();
return 0;
}
我们可以看到函数Func()
中使用了空指针,这样就会导致程序崩溃。我们使用这个代码生成main程序,然后执行main生成core转储文件,然后利用core转储文件进行调试。
这里要注意的是可能需要先开启生成core转储文件(比如执行命令ulimit -c unlimited
),具体这篇文章不展开了。
我们使用-g选项生成一个带调试信息的main,然后看下大小。
这个时候我们执行命令readelf -S ./main
看一下section-headers信息的话,会发现调试信息都包含在内。
执行main程序生成core转储文件,使用gdb调试。
我们可以看到gdb调试的时候可以定位到具体问题。
现在使用命令strip --strip-debug ./main
去掉main程序里的debug信息(和不开启-g选项生成的main程序基本一样),然后看下大小。
可以发现main程序的大小下降了很多。
这个时候如果看下section-headers信息,会发现debug信息去掉了,但是符号表信息还包含。
重新执行main程序生成core转储文件,使用gdb调试。
我们可以看到这个时候gdb调试虽然可以定位问题,但是不够具体。
现在使用命令strip --strip-all ./main
去掉所有调试信息,这个时候main程序的大小还会下降的,而且如果看下section-headers信息,会发现符号表信息也去掉了。然后重新执行main程序生成core转储文件,使用gdb调试。
我们可以看到这个时候gdb调试基本上不能定位问题了。
所以这个时候就是程序瘦身完成了,但是却不能使用core转储文件调试了,我们看下怎么解决。
我们使用-g选项重新生成一个带调试信息的main程序,然后看下大小。
然后执行命令eu-strip ./main -f ./main.debug
去掉main程序的调试信息并且生成调试信息文件main.debug。这个时候main程序的大小以及section-headers的信息和执行命令strip --strip-all ./main
基本是一样的(section-headers的信息稍微不同,因为需要记录生成的调试信息文件)。
重新执行main程序生成core转储文件,使用gdb调试。
我们可以看到这个时候使用gdb调试可以定位到具体的问题,而且可以看到gdb调试的时候读取了调试文件main.debug。
这样我们就可以只发布main程序,然后备份main.debug,在需要调试的时候拿到main.debug就可以了。这样就可以既给程序瘦了身,还不影响调试。
使用gdb调试的时候怎么读取调试信息文件呢,我们讨论一下。
gdb调试的时候会在同级目录下读取记录里的调试信息文件,也会在其他的一些目录读取,可以使用命令show debug-file-directory
查看。
也可以在调试的时候指定需要读取的调试信息文件。
我们给main.debug改个名,重新执行main程序生成core转储文件,使用gdb调试。
我们可以看到这个时候gdb调试基本上不能定位问题了,因为gdb读取不到调试信息文件main.debug。
这个时候我们可以使用symbol-file
指定读取调试信息文件。
这样也可以定位到问题。
启动gdb调试的时候也可以直接指定,而且这样调试还会方便一些。
eu-strip可以使用命令apt install elfutils
进行安装。
欢迎讨论,欢迎指正,欢迎转载。