GDB调试命令以及GDB调试段错误

    xiaoxiao2021-12-14  17

    一、GDB的调试命令。

    C语言是:cc -g tst.c -o tst;C++是g++  -g -o (生成的文件) file.cpp

    C++调试程序命令:gdb  file 启动,罗列代码行数ist 1,break (行数),info break,run(r)调试运行,step(s)单步调试,查看变量 print(p) 变量名,查看堆栈式bt,继续调试continue(c),退出程序q

    二、Core文件的产生

    当Linux程序在运行过程中挂掉的时候,可能就出现core文件。通过调试core文件就可以看出来程序在代码那个位置挂掉了。会在指定的目录下生成core文件,core文件时内存的映像并加入了调试信息,主要用来调试

    用以下命令来阻止系统生成core文件: ulimit -c 0 下面的命令可以检查生成core文件的选项是否打开: ulimit -a 该命令将显示所有的用户定制,其中选项-a代表“all”。

    不产生core文件原因,1、没有足够内存空间,2、禁用了core文件的创建,3、设置一个进程当前目录没有写文件的权限

     

    调试core文件

    ulimit  -c  unlimited 表示要生成core文件 不限制core文件的大小

    三、利用gdb来调试段错误

    产生段错误就是访问了错误的内存段,一般是没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址. 在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的 1)访问系统数据区,尤其是往 系统保护的内存地址写数据 最常见就是给一个指针以0地址 2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域

    解决方法 我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工“除虫”(debug),往往是效率低下且让人厌烦的,本文将就"段错 误"这个内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。

    下面将就以下的一个存在段错误的程序介绍几种调试方法:

    以下是程序代码

     dummy_function(void)   2 {   3   unsigned char*ptr=0x00;   4   *ptr =0x00;   5   6 }   7 int main(void)   8 {   9 10    dummy_function(); 11    return 0; 12 }

     

    作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。我们尝试编译运行它:

    1.利用gdb逐步查找段错误

    这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g -rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:

    哦?!好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。 从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal),我们知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件,由此我们又产生了方法2

    2.通过对core文件进行调试

    哇,好历害,还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。 接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。

    Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了

    3.段错误时启用调试程序

     1 #include <stdio.h>   2 #include <stdlib.h>   3 #include <signal.h>   4 #include <string.h>   5 void dump(int signo)   6 {   7    char buf[1024];   8    char cmd[1024];   9    FILE*fh; 10   snprintf(buf,sizeof(buf),"/proc/%d/cmdline",getpid()); 11    if(!(fh=fopen(buf,"r"))) 12    exit(0); 13    if(!fgets(buf,sizeof(buf),fh)) 14    exit(0); 15    fclose(fh); 16    if(buf[strlen(buf)-1]='\n') 17    buf[strlen(buf)-1]='\0'; 18    sprintf(cmd,sizeof(cmd),"gdb %s%d",buf,getpid()); 19    system(cmd); 20    exit(0); 21 } 22 23 void dummy_function(void) 24 { 25   unsigned char*ptr=0x00; 26   *ptr=0x00; 27 } 28 29 int main(void) 30 { 31    signal(SIGSEGV,&dump); 32    dummy_function(); 33    return 0; 34 }

    怎么样?是不是依旧很酷? 以 上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见 /usr/include/execinfo.h(这些函数都没有提供man page,难怪我们找不到),另外你也可以通过gnu的手册进行学习

    4.利用backtrace和objdump进行分析

     1#include <execinfo.h>   2 #include <stdio.h>   3 #include <stdlib.h>   4 #include <signal.h>   5 void dummy_function(void)   6 {   7   8     unsigned char*ptr=0x00;   9     *ptr=0x00; 10 11 } 12 void dump(int signo) 13 { 14 15   void*array[10]; 16   size_t size; 17   char**strings; 18   size_t i; 19 20   size=backtrace(array,10); 21   strings =backtrace_symbols(array,size); 22 23   printf("Obtained %zd stack frames.\n",size); 24 25   for(i=0;i<size;i++) 26   { 27     printf("%s\n",strings[i]); 28 29 30   } 31 32   free(strings); 33 34   exit(0); 35 36 } 37 38 int main(void) 39 { 40    signal(SIGSEGV,&dump); 41    dummy_function(); 42    return 0; 43 } 

    这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:

    转载请注明原文地址: https://ju.6miu.com/read-972314.html

    最新回复(0)