Linux下coredump调试3:补录 | 迟思堂工作室
A-A+

Linux下coredump调试3:补录

2016-05-31 22:49 GNU/Linux程序 暂无评论 阅读 984 次

本篇文章记录在coredump调试过程中记录的其它事项。
一般地,调试的方式多种多样,不可能将其一网打尽。就笔者而言,一般喜欢用print大法,分段注解法,版本回退法,等等。实在无招,则用coredump文件调试了。在笔者“众多”经验中,程序挂掉原因多种多样,像内存泄漏造成无内存可用,文件/socket打开未关闭被耗尽。所以编程的规范还是很关键的,这不单单是说编码命名风格,还有整体编程的设计和细心程度,比如指针的判断,数组范围不要越界,自己申请的内存要记得释放,等等。

嵌入式ARM Linux使用注意事项

在嵌入式上进行调试的方法很多,网上很多文章介绍了gdbserver的方式,同时结合codeblock则可以达到IDE调试目的。但可惜往往是些简单例子。实际中,嵌入式设备运行的程序,可能和驱动模块实时交互,还和网络密切频繁通信,另外还开了N个线程,M个进程,在如此庞大的系统中,一旦出现段错误,除了靠打印信息外,还可以靠开发人员自身的经验,靠对系统的熟悉程度进行排查。而coredump作为事后分析手段,也大量被应用在日常工作中。

但是,嵌入式的调试还是比较困难的。因为,要么设备所用的系统是只读的,要么存储介质的容量很小,当大型系统崩溃时,产生的coredump文件体积很大,无法在存储介质上保存,则会丢失很多信息,无法用gdb调试。
另外一个因素是看门狗。很多系统都有看门狗机制,一旦程序崩溃则会即刻复位,无法拿到coredump进行分析。
下面说说笔者的经验:
1、设置ulimit,可以在程序代码中设置,也可以在启动脚本中设置,只要保证在程序运行环境中设置就行了。比如,你是telnet到系统中手动执行程序的话,一定要telnet终端中设置ulimit后再运行程序。
2、将看门狗禁止掉或延长时间。有的设备可以短接主板的排针禁止狗,有的则可以通过代码禁止狗。比如我移植的系统,直接用echo就可以把狗禁止掉。
3、使用NFS挂载,以便存储生成的coredump文件。
4、编译器和调试器版本一定要对应起来。否则会出现很多莫名其妙的问题,如下文将讲述到的问题。

用bt查看函数为问号(???)

1、使用-O2优化,或者strip过后的二进制程序,是无法产生调试信息的,此时用gdb查看不到函数名称。所以要用-g编译选项,使其产生调试信息,同时不要将程序strip。
2、库版本不对。可能是真的库版本不正确,也可能是路径不正确。遇到库不对应的问题,可以先打开gdb,使用set solib-absolute-prefix 和set solib-search-path来设置库路径。示例如下(按实际路径填写):

#gdb
#set solib-absolute-prefix "/home/latelee/cross/arm-linux/lib"
#set solib-search-path "/home/latelee/cross/arm-linux/lib"
#file a.out
#core-file core

网上有文章对此做了介绍:《》,本文就不展开讨论了。

编码的一些心得

1、一般在函数内申请内存的,要在本函数内释放掉,如果申请空间少,建议直接用数组,以免忘记申请造成内存泄漏。如果实在要使用二级指针来存储函数内申请的空间,则要在函数声明处注明其用法。
2、任何时候,一定要判断指针的有效性。另外要特别注意多级指针情况。比如onvif获取编码参数时,有一项是获取IP地址的代码片段示例:resp.Configuration->Multicast->Address->IPv4Address。这个示例中,一定要逐级判断指针是否合法。
3、使用printf、scanf等函数时,要注意参数一致性。默认的C库在GCC中是有检查的,注意编译警告可减小其段错误概率。但如果是自己写的打印log函数,未尽能检查。笔者的这篇文章可参考:《一个可变参数类型检查的示例
4、打开的资源要及时释放,特别是return、break、goto这些地方,很容易忘记关闭。资源包括但不限于:socket、普通文件句柄(文件描述符)、目录,等等。笔者的这篇文章可参考:《遇到一个因socket未关闭引发的文件句柄用完问题
5、建议使用cppcheck进行一次代码检查。笔者的这篇文章可参考:《使用cppcheck检测代码警告、错误

李迟 2016.5.31 周二 夜



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate



标签:

给我留言