问题描述

今天在写代码时,运行时奔溃了。segment fault,而且是在程序退出main()函数后,才报的。

唯一的信息是:Segmentation fault (core dumped)

简直是一头雾水。

查看core文件

系统默认是不会生成core文件的,ulimit -c unlimited把core文件设为无限大。

使用gdb查看core文件

gdb ./example/sudoku_batch_test core

提示如下:

Program terminated with signal SIGSEGV, Segmentation fault.
#0 __GI___libc_free (mem=0x313030303030300a) at malloc.c:2951
2951 malloc.c: No such file or directory.
(gdb)

可以确定崩溃发生在malloc.c中。但是提示没有malloc.c的源码。

首先安装glibc的符号表,命令如下:

sudo apt-get install libc6-dbg

再来是安装glibc的源文件,命令如下:

sudo apt-get source libc6-dev

安装完毕后在当前目录下会多出一个glibc-2.23文件夹,该文件夹包含了glibc的源码。

源码准备就绪后,接着上面,在gdb命令提示符下输入:

directory glibc-2.23/malloc/将glibc-2.23/malloc/设为gdb源码搜索目录。结果如下:

warning: core file may not match specified executable file.
[New LWP 24491]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./example/sudoku_batch_test ../example/test1000 127.0.0.1 1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 __GI___libc_free (mem=0x313030303030300a) at malloc.c:2951
2951 malloc.c: No such file or directory.
(gdb) directory glibc-2.23/malloc/
Source directories searched: /root/work/melon/build/glibc-2.23/malloc:$cdir:$cwd
(gdb)

现在我们就可以在gdb中查看崩溃处的源码了,执行list

(gdb) l
warning: Source file is more recent than executable.
2946 if (mem == 0) /* free(0) has no effect */
2947 return;
2948
2949 p = mem2chunk (mem);
2950
2951 if (chunk_is_mmapped (p)) /* release mmapped memory. */
2952 {
2953 /* see if the dynamic brk/mmap threshold needs adjusting */
2954 if (!mp_.no_dyn_threshold
2955 && p->size > mp_.mmap_threshold
(gdb)

虽然知道了崩溃发生在2951行,但是貌似没有更多有效的信息。这时我想到了是不是可以看下函数的调用栈,或许会有信息。

接着执行backtrace(或者bt):

(gdb) bt
#0 __GI___libc_free (mem=0x313030303030300a) at malloc.c:2951
#1 0x000000000048bc9d in melon::Coroutine::~Coroutine (this=0x1fc9120, __in_chrg=<optimized out>)
at /root/work/melon/src/Coroutine.cpp:56
#2 0x000000000048d099 in std::_Sp_counted_ptr<melon::Coroutine*, (__gnu_cxx::_Lock_policy)2>::_M_dispose (
this=0x1fc8190) at /usr/include/c++/5/bits/shared_ptr_base.h:374
#3 0x00000000004630f1 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x1fc8190)
at /usr/include/c++/5/bits/shared_ptr_base.h:150
#4 0x0000000000461f32 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x7f07f4ff1770,
__in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr_base.h:659
#5 0x00000000004749ed in std::__shared_ptr<melon::Coroutine, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (
this=0x7f07f4ff1768, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr_base.h:925
#6 0x0000000000474a39 in std::shared_ptr<melon::Coroutine>::~shared_ptr (this=0x7f07f4ff1768,
__in_chrg=<optimized out>) at /usr/include/c++/5/bits/shared_ptr.h:93
#7 0x00007f07f40915ff in __GI___call_tls_dtors () at cxa_thread_atexit_impl.c:155
#8 0x00007f07f4090f27 in __run_exit_handlers (status=0, listp=0x7f07f441b5f8 <__exit_funcs>,
run_list_atexit=run_list_atexit@entry=true) at exit.c:40
#9 0x00007f07f4091045 in __GI_exit (status=<optimized out>) at exit.c:104
#10 0x00007f07f4077837 in __libc_start_main (main=0x45f1c4 <main(int, char**)>, argc=4, argv=0x7ffcfb2ab218,
init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffcfb2ab208)
at ../csu/libc-start.c:325
#11 0x000000000045ec89 in _start ()

这下问题找到了,首先在线程结束或者程序运行结束会调用__GI___call_tls_dtors函数来析构线程本地存储。我确实用了thread_local关键字修饰Coroutine::Ptr变量。

#1 0x000000000048bc9d in melon::Coroutine::~Coroutine可知在melon::Coroutine类的析构函数中调用了free()导致奔溃。

这下问题基本明确了,我在Coroutine析构函数中会释放stack_这个指针,

 53 Coroutine::~Coroutine() {
54 LOG_DEBUG << "destroy coroutine:" << name_;
55 if (stack_) {
56 free(stack_);
57 }
58 }

有两个构造函数,其中一个如下:

 39 Coroutine::Coroutine()
40 :c_id_(++t_coroutine_id),
41 name_("Main-" + std::to_string(c_id_)),
42 cb_(nullptr),
43 state_(CoroutineState::INIT) {
44
45 if (getcontext(&context_)) {
46 LOG_ERROR << "getcontext: errno=" << errno
47 << " error string:" << strerror(errno);
58 }
59 }

因为大意犯了个非常低级的错误,这个构造函数没有正确初始化statck_指针,将statck_初始化为nullptr后,问题就解决了。

update:2019-10-31

其实不用这么麻烦,gdb有个where命令,能直接打印出函数栈信息。

总结

遇到这类问题,一般用gdb查看core文件都能定位到崩溃的位置,如果不是直接引发的,可以查看函数调用栈,一般都能找到问题原因。

记录一次gdb debug经历的更多相关文章

  1. Linux GDB Debug

    http://blog.jobbole.com/107925/ gdb 调试入门,大牛写的高质量指南 http://blog.jobbole.com/107759/ gdb是the GNU Debug ...

  2. 记录一次OOM排查经历(一)

    一.经历概要 程序里有个跑数据的job,这个job的主要功能是往数据库写假数据. 既需要跑历史数据(传给job的日期是过去的时间),也需要能够上线后,实时跑(十秒钟触发一次,传入触发时的当前时间). ...

  3. Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  4. DEBUG经历

    在两年有余的学习生活中,我不仅在课堂上学到了很多东西,我也在一次次的错误中得到了宝贵的经验和教训.Bug和debug,构成了我生活中不可或缺的一部分. 我在编程中犯过的错误很多,无法一一阐述,再次说一 ...

  5. 记录一次ceph recovery经历

    一次ceph recovery经历 背景 这是一个測试环境. 该环境中是cephfs 一共12个节点, 2个client.2个mds.8个osd mds: 2颗CPU,每一个4核.一共是8核. 128 ...

  6. 记一次难忘的排错debug经历(找了5天左右)(涉及内存覆盖)

    strcpy和memcpy都没有处理内存覆盖问题. 函数描述 The memcpy function copies count bytes of src to dest. If the source ...

  7. 小记录:flask的DEBUG开关

    请求站点的如下位置: http://www.ahfu.com/ahfuzhang/?debugger=yes&cmd=resource&f=style.css 居然正常范围了CSS文件 ...

  8. 记录一次无聊的(经历了Nodejs -> Shell -> C)的探索问题过程

    提出问题 在运行项目的服务器的git是1.8.3.1版本的时候,pm2 deploy 项目,服务器fetch不到最新的一次commit. 对于这个问题,在pm2的github也有issues讨论.然后 ...

  9. gdb debug

    http://www.cnblogs.com/life2refuel/p/5396538.html

随机推荐

  1. Linux线程唤醒与等待

    生产者消费者模式在程序设计中出现频率非常高,经常会有线程间通过消息队列或其他共享变量进行交互的场景.而这时就会出现一个问题,消费者如何知道生产者已经生产了数据呢?有的程序会采取消费者循环判断消息队列大 ...

  2. JavaScript之深入函数(二)

    上一篇我们主要讲解了函数的执行过程和原理,本篇我们将介绍函数的另外两个特殊表现:闭包和立即执行函数. 一 闭包 1,  闭包的形成 之前我们提到,函数执行完毕,马上就会销毁自己的AO对象.但是如果遇到 ...

  3. 新手学习FFmpeg - 调用API完成视频的读取和输出

    在写了几个avfilter之后,原本以为对ffmpeg应该算是入门了. 结果今天想对一个视频文件进行转码操作,才发现基本的视频读取,输出都搞不定. 痛定思痛,仔细研究了一下ffmpeg提供的examp ...

  4. CocosCreator实现动物同化

    获取源码 关注微信公众号『一枚小工 』,发送『动物同化 』获取完整游戏源码. 游戏玩法 游戏目标是将游戏区域的动物全部同化成同一种动物.游戏从左上角开始,从右边点击需要变成的目标动物头像,如果被同化动 ...

  5. 通过python代码对域名ssl证书进行监控

    根据公司要求,要求用zabbix对域名的ssl证书进行到期监控 直接上代码 #!/usr/bin/env python3 from urllib3.contrib import pyopenssl f ...

  6. Spring MVC-从零开始-第一个控制器(不考虑命名规范)

    1.目录结构 (log4j.properties.mybatis-config.xml可忽略) 2.配置web.xml文件 <?xml version="1.0" encod ...

  7. 创建FTP访问的YUM源

    创建FTP访问的YUM源 一.安装vsftpd(步骤详见“在linux中搭建vsftpd.docx”) 在主机A上安装FTP,安装后的ftp信息如下:ftp://192.168.43.300  账号密 ...

  8. Jquery Ztree异步加载树

    1. 下载jquery的JS文件/ztree的CSS文件和JS文件 https://jquery.com/download/ https://gitee.com/zTree/zTree_v3/tree ...

  9. httpd2.4

    更换网站主页目录 1.在httpd.conf文件中更改documentroot为新的路径,并为其相应的路径给与相应的权限. Documentroot "/www/http" < ...

  10. LeetCode正则表达式匹配

    题目描述 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 ...