[转] Linux C语言 段错误bug的调试
2011-12-29 13:41
337 查看
原来看过一次,后来当自己有段错误的时候,才想起来这个很有用.如果不用他的方法,段错误很恶心的,不好找出来的.
下面就是原文,不过最初出处不得而知
=======================
复制存在问题,格式不正确...先给链接吧.
http://blog.sina.com.cn/s/blog_606c49090100eohs.html
core文件的资料:http://blog.sina.com.cn/s/blog_489c2413010080ml.html
Linux系统下的多线程编程入门http://blog.sina.com.cn/s/blog_489c241301008nco.html这个应该是IBM上文章,之前我看过.
1)访问系统数据区,尤其是往系统保护的内存地址写数据最常见就是给一个指针以
0地址
2)内存越界(数组越界,变量类型不一致等)访问到不属于你的内存区域
解决方法
我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工“除虫”(debug),往往是效率低下且让人厌烦的,本文将就"段错误"这个内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法:
下面就是原文,不过最初出处不得而知
=======================
复制存在问题,格式不正确...先给链接吧.
core文件的资料:
Linux系统下的多线程编程入门
1)访问系统数据区,尤其是往系统保护的内存地址写数据最常见就是给一个指针以
0地址
2)内存越界(数组越界,变量类型不一致等)访问到不属于你的内存区域
解决方法
我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工“除虫”(debug),往往是效率低下且让人厌烦的,本文将就"段错误"这个内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法:
dummy_function(void) { unsignedchar*ptr=0x00; *ptr=0x00; } intmain(void) { dummy_function(); return0; }
作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。我们尝试编译运行它: xiaosuo@gentuxtest$./a.out 段错误 果然不出所料,它出错并退出了。 1.利用gdb逐步查找段错误: 这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g-rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:
xiaosuo@gentuxtest$gcc-g-rdynamicd.c xiaosuo@gentuxtest$gdb./a.out GNUgdb6.5 Copyright(C)2006FreeSoftwareFoundation,Inc. GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare welcometochangeitand/ordistributecopiesofitundercertainconditions. Type"showcopying"toseetheconditions. ThereisabsolutelynowarrantyforGDB.Type"showwarranty"fordetails. ThisGDBwasconfiguredas"i686-pc-linux-gnu"...Usinghostlibthread_dblibrary"/lib/libthread_db.so.1". (gdb)r Startingprogram:/home/xiaosuo/test/a.out ProgramreceivedsignalSIGSEGV,Segmentationfault. 0x08048524indummy_function()atd.c:4 4*ptr=0x00; (gdb)
哦?!好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。 从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man7signal),我们知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件,由此我们又产生了方法二。 2.分析Core文件: Core文件是什么呢? Thedefaultactionofcertainsignalsistocauseaprocesstoterminateandproduceacoredumpfile,adiskfilecontaininganimageoftheprocess'smemoryatthetimeoftermination.Alistofthesignalswhichcauseaprocesstodumpcorecanbefoundinsignal(7). 以上资料摘自manpage(man5core)。不过奇怪了,我的系统上并没有找到core文件。后来,忆起为了渐少系统上的拉圾文件的数量(本人有些洁癖,这也是我喜欢Gentoo的原因之一),禁止了core文件的生成,查看了以下果真如此,将系统的core文件的大小限制在512K大小,再试:
xiaosuo@gentuxtest$ulimit-c 0 xiaosuo@gentuxtest$ulimit-c1000 xiaosuo@gentuxtest$ulimit-c 1000 xiaosuo@gentuxtest$./a.out 段错误(coredumped) xiaosuo@gentuxtest$ls a.outcored.cf.cg.cpango.ctest_iconv.ctest_regex.c
core文件终于产生了,用gdb调试一下看看吧:
xiaosuo@gentuxtest$gdb./a.outcore GNUgdb6.5 Copyright(C)2006FreeSoftwareFoundation,Inc. GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare welcometochangeitand/ordistributecopiesofitundercertainconditions. Type"showcopying"toseetheconditions. ThereisabsolutelynowarrantyforGDB.Type"showwarranty"fordetails. ThisGDBwasconfiguredas"i686-pc-linux-gnu"...Usinghostlibthread_dblibrary"/lib/libthread_db.so.1". warning:Can'treadpathnameforloadmap:输入/输出错误. Readingsymbolsfrom/lib/libc.so.6...done. Loadedsymbolsfor/lib/libc.so.6 Readingsymbolsfrom/lib/ld-linux.so.2...done. Loadedsymbolsfor/lib/ld-linux.so.2 Corewasgeneratedby`./a.out'. Programterminatedwithsignal11,Segmentationfault. #00x08048524indummy_function()atd.c:4 4*ptr=0x00;
哇,好历害,还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。 接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。 Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了: 3.段错误时启动调试:
#include #include #include #include voiddump(intsigno) { charbuf[1024]; charcmd[1024]; FILE*fh; snprintf(buf,sizeof(buf),"/proc/%d/cmdline",getpid()); if(!(fh=fopen(buf,"r"))) exit(0); if(!fgets(buf,sizeof(buf),fh)) exit(0); fclose(fh); if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]='\0'; snprintf(cmd,sizeof(cmd),"gdb%s%d",buf,getpid()); system(cmd); exit(0); } void dummy_function(void) { unsignedchar*ptr=0x00; *ptr=0x00; } int main(void) { signal(SIGSEGV,&dump); dummy_function(); return0; }
编译运行效果如下:
xiaosuo@gentuxtest$gcc-g-rdynamicf.c xiaosuo@gentuxtest$./a.out GNUgdb6.5 Copyright(C)2006FreeSoftwareFoundation,Inc. GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare welcometochangeitand/ordistributecopiesofitundercertainconditions. Type"showcopying"toseetheconditions. ThereisabsolutelynowarrantyforGDB.Type"showwarranty"fordetails. ThisGDBwasconfiguredas"i686-pc-linux-gnu"...Usinghostlibthread_dblibrary"/lib/libthread_db.so.1". Attachingtoprogram:/home/xiaosuo/test/a.out,process9563 Readingsymbolsfrom/lib/libc.so.6...done. Loadedsymbolsfor/lib/libc.so.6 Readingsymbolsfrom/lib/ld-linux.so.2...done. Loadedsymbolsfor/lib/ld-linux.so.2 0xffffe410in__kernel_vsyscall() (gdb)bt #00xffffe410in__kernel_vsyscall() #10xb7ee4b53inwaitpid()from/lib/libc.so.6 #20xb7e925c9instrtold_l()from/lib/libc.so.6 #30x08048830indump(signo=11)atf.c:22 #4 #50x0804884cindummy_function()atf.c:31 #60x08048886inmain()atf.c:38
怎么样?是不是依旧很酷? 以上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h(这些函数都没有提供manpage,难怪我们找不到),另外你也可以通过gnu的手册进行学习。 4.利用backtrace和objdump进行分析: 重写的代码如下:
编译运行结果如下:
xiaosuo@gentuxtest$gcc-g-rdynamicg.c
xiaosuo@gentuxtest$./a.out
Obtained5stackframes.
./a.out(dump+0x19)[0x80486c2]
[0xffffe420]
./a.out(main+0x35)[0x804876f]
/lib/libc.so.6(__libc_start_main+0xe6)[0xb7e02866]
./a.out[0x8048601]
这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:
xiaosuo@gentuxtest$objdump-da.out
8048765:e802feffffcall804856c
804876a:e825ffffffcall8048694
804876f:b800000000mov$0x0,%eax
8048774:c9leave
我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!
后记:
本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。
相关文章推荐
- linux c/c++ 段错误bug的调试 详细介绍
- linux 调试语句BUG_ON WARN_ON dump_stack panic
- Linux下C语言的调试
- linux下C语言__FILE__,__LINE__,FUNCTION__实现代码跟踪调试
- linux调试C语言时加颜色引起注意
- Linux下C语言的调试
- linux下的C语言开发(gdb调试)
- linux驱动调试--段错误之oops信息分析
- sysrq调试linux驱动bug
- 段错误bug的调试
- 【转载】Linux下的段错误产生的原因及调试方法
- Linux下的段错误(Segmentation fault)产生的原因及调试方法(经典)
- 解决一个Linux下C语言工程无法调试的问题
- 转载:Linux下的段错误产生的原因及调试方法
- Linux下的段错误(Segmentation fault)产生的原因及调试方法(经典)
- linux内核bug调试指南(一)
- 调试:Linux下的段错误(Segmentation fault)产生的原因及调试方法(经典)
- 段错误bug的调试
- 段错误bug的调试
- Linux下的段错误(Segmentation fault)产生的原因及调试方法