前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >日更系列:排查一次gcc的复杂core

日更系列:排查一次gcc的复杂core

原创
作者头像
mariolu
修改2022-05-14 14:32:51
1K0
修改2022-05-14 14:32:51
举报

线上服务出现了重启告警,而且重启后继续。排查线上出现了一堆core,core信息显示如下:

这个是一个线程抛出的异常,但是看不到线程本身的代码,只可看到core文件堆栈栈钉出现在libstdc++库上。唯一业务代码在第6个栈帧。catch方块里的throw e代码上。

这里有几个问题。这里的异常实际上是在线程调度的函数抛出了异常。也就是thread->m_Runner->Execute()的时候,当时在异常抛出的时候,我们其实想要看到的是那个时候的堆栈内存快照。但是这个core发生的时候,已经退出了那个Execute函数,这个实际函数已经退出了,当时core点的堆栈已经展开了。然后函数跳过Execute函数,走到catch模块,到throw e位置,由于外部再无补货这个exception e的函数,函数在此结束。core信息反应的是此时throw e的内存快照。

1. 问题

1.1 c++的throw或者abort机制

这里引入了一个知识点就是:C++ 程序,如果 代码throw了 exception ,但是外部又没有 catch,那么一般会产生 coredump。而如果外面catch住了异常,这个就不会产生coredump,但是你也可以继续选择向上级抛异常,也可以忽略打点异常日志出来e.what()。 gcc有个宏_GLIBCXX_THROW_OR_ABORT定义你的行为是throw还是abort

我记得谷歌c++规范也是建议大家不写异常。但是如果团队内有人不遵守怎么办,我们可以在makefile加上(-gcc的 -fno-exceptions ,在编译阶段禁用异常机制。这样写了异常的代码不会通过。

gcc有个宏_GLIBCXX_THROW_OR_ABORT 的宏,該宏定義為

代码语言:javascript
复制
#ifndef _GLIBCXX_THROW_OR_ABORT
# if __cpp_exceptions
#  define _GLIBCXX_THROW_OR_ABORT(_EXC) (throw (_EXC))
# else
#  define _GLIBCXX_THROW_OR_ABORT(_EXC) (__builtin_abort())
# endif
#endif

1.2 线程执行机制

有了第一个问题的铺垫,我们知道上层代码如果catch住了调用函数的异常,会导致core信息显示的不是调用函数本身的堆栈。所以要么函数外面不cacth,要么让抛__cxa_throw的函数直接挂掉。

本文的第一个问题正是,在 gcc 4.x 版本(gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) )产生的 coredump 文件中,没有 throw 时候的堆栈信息,导致不知道是哪里 throw 的,没法查问题。

我们继续看gcc 4.x 的 /libstdc++-v3/src/c++11/thread.cc:92 的代码就发现原因。这个gcc 4.x的线程执行的实现里面有个 catch(…),所以 stack unwind 了,就没了 throw 时候的 stack 。

以下是gcc4.x-6.x的实现

而gcc7.x以上已经重新实现了,以下是gcc 7.x以上的实现

这里就没有了cache。https://abcdabcd987.com/libstdc++-bug/

二、解决办法

2.1 升级到gcc7以上

一个解决办法是可以升级 GCC 7 ,

2.2 强行改写throw方法

因为不是所有的项目都方便切换到新版本的gcc,历史代码不好兼容新版本gcc,可以用更简单的办法:

1.代码 hook __cxa_throw

一个解决办法是通过改代码,hook __cxa_throw() 让每次生成的 coredump 都带上堆栈:

https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/cxxabi.h#L616

vim /usr/include/c++/4.8.2/cxxabi.h 看到__cxa_throw的声明。__cxa_throw() 是 libstdc++/libc++ 用于实现 throw 的函数。

https://libcxxabi.llvm.org/spec.html

我们可以重写(或者叫hook)

这个函数要加到你调用线程的那个文件里。比如我这里的Util/CThread.cpp(项目代码中线程执行函数)。

效果如下:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 问题
    • 1.1 c++的throw或者abort机制
      • 1.2 线程执行机制
      • 二、解决办法
        • 2.1 升级到gcc7以上
          • 2.2 强行改写throw方法
            • 1.代码 hook __cxa_throw ,
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
        http://www.vxiaotou.com