mips-openwrt-linux在嵌入式设备上进行gdb调试segmentation fault错误

1、调试嵌入式设备时,出现了segmentation fault错误,先采用二分法,将程序一部分、一部分的注释掉,再分别运行,来定位源码中出现segmentation fault的错误点。但是一个小时后,仍然还有很大的源码范围没有排除。新生恐惧,如果以后维护更大的工程时,采用这种方法岂不是一个大坑,并且也太low了。于是在网上找调试工具。

2、网上搜索“Segmentation Fault的调试方法”,于是主要搜到如下两篇帖子:https://www.cnblogs.com/linux-37ge/p/12781176.htmlhttps://www.cnblogs.com/linux-37ge/p/11826729.html 。于是决定学会在嵌入式设备上使用gdb工具调试程序。

3、由于在调试嵌入式设备上并没有现成的gdb工具,网上搜出来的gdb工具又不一定可以在我的嵌入式设备上运行,于是向同事请教有没有在我们嵌入式设备上使用gdb工具的。此时有同事推荐使用strace工具,也可以调试segmentation fault错误,并且使用起来比给gdb更简单。正好前两天刚用过strace工具,于是用strace工具调试结果如下:

root@OpenWrt:~# 
root@OpenWrt:~# strace /relaymodule/AppPlc 
execve("/relaymodule/AppPlc", ["/relaymodule/AppPlc"], [/* 11 vars */]) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x77658000
stat("/etc/ld.so.cache", 0x7f887ab0)    = -1 ENOENT (No such file or directory)

.........

_newselect(5, [4], NULL, NULL, {0, 0})  = 0 (Timeout)
nanosleep({0, 20000000}, NULL)          = 0
_newselect(5, [4], NULL, NULL, {0, 0})  = 0 (Timeout)
nanosleep({0, 20000000}, NULL)          = 0
_newselect(5, [4], NULL, NULL, {0, 0})  = 0 (Timeout)
nanosleep({0, 20000000}, NULL)          = 0
_newselect(5, [4], NULL, NULL, {0, 0})  = 0 (Timeout)
nanosleep({0, 20000000},  <unfinished ...>
+++ killed by SIGSEGV +++
Segmentation fault
root@OpenWrt:~# 

通过以上结果没有看出有用信息,捣腾1个小时后,又转向gdb的怀抱。

4、继续向同事问gdb的安装包,于是同事帮我在openwrt内核中进行了配置,并编译出了gdb工具的安装包。gdb工具的安装包的编译过程如下:

openwrt内核配置主页面、openwrt内核配置utilities页面分别如下图所示:

    4.1  进入内核源码目录...../clean_qsdk/qsdk”下,包含以下内容bin  dl  feeds.conf.default  package  scripts  toolchain  BSD  makefile  docs  include  qca  staging_dir  tools  build_dir  feeds  LICENSE  README  target  update Config.in  feeds.conf  Makefile  rules.mk  tmp”(不同内核配置可能不一样)

  4.2 在该目录下执行“make menuconfig”命令,可以进入内核配置页面。在内核配置的菜单项中,可以选择将gdb源码编译到单板程序中,通过烧录到单板中;也可以选择将gdb源码编译成.ibk的安装包形式,安装到Linux/openwrt中。页面如下,按照每个页面顶端的部分进行配置。

  4.3 配置完内核菜单之后,通过执行“make -j8”命令,编译出gbk的.ibk安装包。(命令解释见:https://blog.csdn.net/clarkness/article/details/86633681

  4.4 编译出来的安装包都存储在“....../clean_qsdk/qsdk/bin/ar71xx/packages”目录下。在该目录下找到“gdb_6.8a-4_ar71xx.ipk”即为gdb的安装包。(当然,本目录下还存在其他很多的.ipk的工具安装包,这些安装包都可在内核配置后编译产生。关于内核的配置预编译,还可参考:https://www.cnblogs.com/xiaocen/p/3717993.html

5、通过FileZilla工具,将“gdb_6.8a-4_ar71xx.ipk”工具拷到嵌入式设备中,用“opkg install gdb_6.8a-4_ar71xx.ipk ”命令进行安装,提示如下,缺少依赖库libncurses,需要先安装libncurses。

root@OpenWrt:/tmp# 
root@OpenWrt:/tmp# opkg install gdb_6.8a-4_ar71xx.ipk 
Installing gdb (6.8a-4) to root...
Collected errors:
 * satisfy_dependencies_for: Cannot satisfy the following dependencies for gdb:
 *     libncurses * 
 * opkg_install_cmd: Cannot install package gdb.
root@OpenWrt:/tmp# 
6、libncurses的安装走了不少弯路。先在网上搜索libncurses,搜出两条帖子:https://blog.csdn.net/Sunnyside_/article/details/107896603https://blog.csdn.net/ldl22847/article/details/8612993,于是先按照帖子说明下载了ncurses-5.6.tar.gz,然后通过如下7个步骤去操作。发现ncurses-5.6.tar.gz是libncurses的源码,并不是工具,而下面7个步骤就是将源码解压、根据build=编译平台 --host=运行平台 --target=目标平台自动生成Makefile文件,然后make在安装的。以嵌入式设备的资源,几乎不能完成这整个编译过程。但是这个过程还是有收获的,对Makefile的自动生成过程有了了解,见:https://blog.csdn.net/hebbely/article/details/53993141

(1) $ tar xvf /lfs-sources/ncurses-5.6.tar.gz  

(2) $ cd ncurses-5.6

(3) $ ./configure --host=mips-openwrt-linux --target=mips-openwrt-linux

(4) $ make

(5) $ make install

(6) $ cd ..

(7) $ rm -rf ncurses-5.6

7、libncurses安装包的生成过程,其实和“gdb_6.8a-4_ar71xx.ipk”的生产过程是一样的,也只需要在内核文件中做配置、然后编译,去相关目录下找“libncurses_5.7-5_ar71xx.ipk”即可。同样通过FileZilla工具将安装包弄到嵌入式设备中。安装结果如下:

root@OpenWrt:/tmp# 
root@OpenWrt:/tmp# opkg install libncurses_5.7-5_ar71xx.ipk 
Installing libncurses (5.7-5) to root...
Collected errors:
 * satisfy_dependencies_for: Cannot satisfy the following dependencies for libncurses:
 *     terminfo * 
 * opkg_install_cmd: Cannot install package libncurses.
root@OpenWrt:/tmp# 
8、有了上一次的经验,直接重复之前的步骤,生成“terminfo_5.7-5_ar71xx.ipk”安装包并安装,结果如下,安装成功。

root@OpenWrt:/tmp# 
root@OpenWrt:/tmp# opkg install terminfo_5.7-5_ar71xx.ipk 
Installing terminfo (5.7-5) to root...
Configuring terminfo.
root@OpenWrt:/tmp# 
9、反过来再重新、按顺序先安装libncurses_5.7-5_ar71xx.ipk、再安装gdb_6.8a-4_ar71xx.ipk,都安装成功,同时用tab键发现此时系统中已经有了gdb命令。

root@OpenWrt:/tmp# 
root@OpenWrt:/tmp# opkg install libncurses_5.7-5_ar71xx.ipk 
Installing libncurses (5.7-5) to root...
Configuring libncurses.
root@OpenWrt:/tmp# 
root@OpenWrt:/tmp# 
root@OpenWrt:/tmp# opkg install gdb_6.8a-4_ar71xx.ipk 
Installing gdb (6.8a-4) to root...
Configuring gdb.
root@OpenWrt:/tmp# g
gcom       gdb         grep       gunzip     gzip
root@OpenWrt:

10、接下来便可以使用gdb工具调试程序,(gdb的使用可见帖子:https://www.cnblogs.com/linux-37ge/p/11826729.html)如下

root@OpenWrt:/#
root@OpenWrt:/# gdb ./AppPlc


dlopen failed on 'libthread_db.so.1' - File not found
GDB will not be able to debug pthreads.

GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mips-openwrt-linux"...
(no debugging symbols found)
(gdb) r
Starting program:........

......................

[New LWP 10807]

Program received signal SIGSEGV, Segmentation fault.
[Switching to LWP 10807]
0x004107a4 in ?? ()
(gdb) bt
#0  0x004107a4 in ?? ()
#1  0x004147a8 in ?? ()
#2  0x00414fa4 in ?? ()
#3  0x00415170 in ?? ()
#4  0x77cc1db4 in ?? ()
warning: GDB can't find the start of the function at 0x77cc1db3.

    GDB is unable to find the start of the function at 0x77cc1db3
and thus can't determine the size of that function's stack frame.
This means that GDB may be unable to access that stack frame, or
the frames below it.
    This problem is most likely caused by an invalid program counter or
stack pointer.
    However, if you think GDB should simply search farther back
from 0x77cc1db3 for code which looks like the beginning of a
function, you can increase the range of the search using the `set
heuristic-fence-post' command.
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) bt
#0  0x004107a4 in ?? ()
#1  0x004147a8 in ?? ()
#2  0x00414fa4 in ?? ()
#3  0x00415170 in ?? ()
#4  0x77cc1db4 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) quit
The program is running.  Exit anyway? (y or n) y
root@OpenWrt:/#
root@OpenWrt:/#

上面这一过程中,运行到“Program received signal SIGSEGV, Segmentation fault.”就说明程序已经出错了,但是输入“bt”命令后,输出的都是“?? ()”,并不能实际显示出出问题的函数/文件/行数等具体信息。工具使用失败。

10、经过询问同时,并且在网上搜索发现,单单使用gdb工具是不够的,还要在编译应用程序的时候,在gcc/gcc+编译过程中添加使用“-g”选项,并且去掉编译过程中的优化过程。(在去掉优化选项STRIP时,使用#注释掉是不起作用的,要直接删除这句话。)然后再将编译后的应用添加到嵌入式设备中,用gdb去调试。

11、在应用程序中保留了编译信息后,再用gdb工具调试的效果如下:

root@OpenWrt:/#
root@OpenWrt:/# gdb ./AppPlc


dlopen failed on 'libthread_db.so.1' - File not found
GDB will not be able to debug pthreads.

GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mips-openwrt-linux"...
(no debugging symbols found)
(gdb) r
Starting program:........

......................

[New LWP 24696]

Program received signal SIGSEGV, Segmentation fault.
[Switching to LWP 24696]
0x004107a4 in CPlcDetectEvent::DetectValueEvent (this=<value optimized out>, element_value=160, 
    old_element_value=@0x763be1cc, trigger_mode=<value optimized out>, value_left=0, value_right=0, 
    element_type=4 '\004', trigger_type=1 '\001', change_event_flag=0x0)
    at /media/sf_share/IOT-AppPlc/SVN/02-Source/AppPlc/core/plc_detect_event.cpp:425
425    /media/sf_share/IOT-AppPlc/SVN/02-Source/AppPlc/core/plc_detect_event.cpp: No such file or directory.
    in /media/sf_share/IOT-AppPlc/SVN/02-Source/AppPlc/core/plc_detect_event.cpp
(gdb) bt full
#0  0x004107a4 in CPlcDetectEvent::DetectValueEvent (this=<value optimized out>, element_value=160, 
    old_element_value=@0x763be1cc, trigger_mode=<value optimized out>, value_left=0, value_right=0, 
    element_type=4 '\004', trigger_type=1 '\001', change_event_flag=0x0)
    at /media/sf_share/IOT-AppPlc/SVN/02-Source/AppPlc/core/plc_detect_event.cpp:425
    compare_result = Unhandled dwarf expression opcode 0x9f
(gdb) q
The program is running.  Exit anyway? (y or n) y
root@OpenWrt:/relaymodule# 
root@OpenWrt:/relaymodule# 

        通过以上调试输出,在错误出现后,直接打印了源码信息“CPlcDetectEvent::DetectValueEvent”、“/media/sf_share/IOT-AppPlc/SVN/02-Source/AppPlc/core/plc_detect_event.cpp:425”,帮助我们定位。顺着这些信息分析,两分钟解决了源码中的问题(给值为NULL的指针变量赋值了)。(注意,这里用了gbd的命令“bt full”可以将出错误地方的提示信息全部输出)。

12、在解决完问题之后,还要将应用程序中的“-g”选项去掉,并且加上优化过程,这样应用程序才能恢复正常运行。

13、总结:学习使用工具的过程是比较磨人的,但是学会一个工具之后,瞬间高效、体面了有木有。下次再遇到segmentation fault的时候,可以蔑视一笑了。记录今天的学习、工作过程,并感谢三位同事和网上未知朋友的帮助。

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值