您的位置:首页 > 博客中心 > 数据库 >

linux 下C/C++程序常用调试方法(gdb)

时间:2022-03-14 03:16


     不管是在开发或者运行过程中,调试保证程序正常运行最基本的手段,熟悉这些调试方式,方便我们更快的定位程序问题所在,提高开发效率。  一 程序正常运行调试     (1)  直接使用gdb     开发过程中最常用的方式,我们可以在其过程中给程序添加断点,监视等辅助手段,监控其行为是否与我们设计相符,比如: gxlsystem.com,布布扣           





void dump(int __signal) { const int __max_stack_flow = 20; void* __array[__max_stack_flow]; char** __strings; size_t __size = backtrace(__array,__max_stack_flow); printf("backtrace() returned %d addresses\n", (int)__size); __strings = backtrace_symbols(__array,__size); if(NULL == __strings) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } fprintf (stderr,"obtained %zd stack frames.nm", __size); for (size_t __i = 0; __i < __size; ++__i) { printf("%s\n", __strings[__i]); } // This __strings is malloc(3)ed by backtrace_symbols(), and must be freed here free (__strings); exit(0); }
    ·    比如说,我们需要对SIGSEGV进行处理,那么只需要调用signal(SIGSEGV, dump),那么当产生SIGSEGV中断时,就会触发dump调用,打印出堆栈,默认设置最大堆栈数为20,如果实际的堆栈大于20,仅仅显示最近的20层。           测试代码:
void segv_fun()
{
 unsigned char* __ptr = 0x00;
 *__ptr = 0x00;
}
void register_signal(int __signal)
{
 signal(__signal, dump);
}
void TestDump::test_signal_segv()
{
 printf("TestDump::test_signal SIGSEGV\n");
 register_signal(SIGSEGV);
 segv_fun();
}
运行输出: gxlsystem.com,布布扣

从运行结果可以看到,程序SIGSEGV中断触发了dump函数,dump打印出了6帧, 第一帧:/easy_main(_Z4dumpi+0x26) [0x402896] 是在执行dump; 第二帧:/lib64/libc.so.6() [0x332ae329a0] 是调用libc的库函数; 第三帧:./easy_main(_ZN8TestDump5myRunEPKcb+0x5f) [0x402d2f],这个看起来有点奇怪,这一串貌似可以看出来一些信息,TestDump?不能很快定位。不用急,如果出现这个请看看,我们可以借助addr2line,通过地址转换到对应文件的行数。 gxlsystem.com,布布扣
再看看源代码文件,
void segv_fun()
{
	unsigned char* __ptr = 0x00;
	*__ptr = 0x00;
}

这下就知道问题所在之处了吧! 第N帧 ......
    (2) 分析core文件          如果不对信息进行任何接管,那么程序中断后,会产生一个core文件,core文件是当时程序中断时的内存的一个镜像,利用它可以还原场景。如果没有产生core  文件,请检查ulimit 参数,比如我的设置是这样,         gxlsystem.com,布布扣
        那么需要设置它的太小,单位为blocks,一般来说1 blocks = 1k,也就是1024bytes.         gxlsystem.com,布布扣
        现在我已将它设置为1M,那么如果程序占用内存小于1M的话,core文件是一个完整的内存镜像,大于1M也会保留最近的1M的内存信息。         gxlsystem.com,布布扣
        由输出结果可知,第一次执行时,没有生成core, 设置ulimit 值后,便产生了core dump了(segmentation fault core dumped).         接下来我们通过core 文件来定位错误信息。     gxlsystem.com,布布扣         显然,结果显示了产生中断的详细代码以及文件所在行数,这样是不是更方便呢!
void dump_for_gdb(int __signal) { const int __max_buf_size = 512; char __buf[__max_buf_size] = {}; char __cmd[__max_buf_size] = {}; FILE* __file; snprintf(__buf, sizeof(__buf), "/proc/%d/cmdline", getpid()); if(!(__file = fopen(__buf, "r"))) { exit(0); } fclose(__file); if(__buf[strlen(__buf) - 1] == '\n') // warning: multi-character character constant [-Wmultichar] { __buf[strlen(__buf) - 1] = '\0'; // warning: multi-character character constant [-Wmultichar] } snprintf(__cmd, sizeof(__cmd), "gdb %s %d",__buf, getpid()); system(__cmd); exit(0); }
void register_signal_for_gdb(int __signal)
{
	signal(__signal, dump_for_gdb);
}
void TestDump::test_dump_for_gdb()
{
	printf("TestDump::test_dump_for_gdb SIGSEGV\n");
	register_signal_for_gdb(SIGSEGV);
	segv_fun2();
}
输出结果 gxlsystem.com,布布扣

当然,我们可以把这些东西整合起来,比如在项目最终上线后,我们希望这个操作更加简单,因为到了运营阶段,操作者可能不是开发者,而是运维人员,我们希望用更简单,直接的方式,把这些信息提取出来,那就需要更进一步的工作了。我们之前采用的方法是:把dump的堆栈信息写的文件中,然后使用shell读取这些堆栈信息,病使用addr2line转化到具体的文件行数或者函数并保存最终文件。这样运维人员只需要把最终的文件给开发人员,便可分析定位问题了。 目前所常用的就这些了,如果有更好的方式,欢迎补充。
引用代码:

参考:




热门排行

今日推荐

热门手游