coredump时如何过滤掉共享内存

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了coredump时如何过滤掉共享内存相关的知识,希望对你有一定的参考价值。

查看 error log:

我们拿到了崩溃位置0xee36f1,如何找到与之相对的代码位置呢?

找台测试机,获取对应版本的安装包:

解压:

然后用 GDB 打开 mysqld:

在 0xee36f1 位置打一个断点:

我们可以看到,gdb 将崩溃位置的文件名和行号都打印出来,

剩下的事情,就可以交给开发工程师,按照这个崩溃堆栈来进行问题排查。

赠送章节

红框内的这串信息是什么?我们来解开看一下,

这段信息分为两段,"+0x71" 是一个偏移量,前面是一串文字,我们将文字解析出来:

可以看到前面这串文字是一个函数签名的编码,用 c++filt 还原编码以后,可以看到完整的函数签名。

红框内的这串信息的意思就是崩溃位置是 一个函数起始位置 + 偏移量。

我们大概可以猜到,这个 MySQL 的缺陷是在为 binlog 产生新的文件名时发生的。

小贴士:

函数起始位置 + 偏移量 是一种内存位置的表示方法,但该位置不一定是这个函数内的代码。

以本例来说,0xee36f1 这个位置,程序找到了就近的函数 generate_new_name 的起始位置,计算出有 0x71 这么多偏移,就表示成了 generate_new_name+0x71 这种形式。

但 0xee36f1 这个位置的代码,大概率是,但,不一定是 generate_new_name 这个函数内部的一段代码。

参考技术A 首先要定位越界点通过单步调试或者是加打印找到出现错误的点然后,查附近代码90%以上的越界,都是出现在附近几行代码里面尤其是数组指针操作,要细查如果出现的崩溃点不一致,或者附近代码确定没问题那就得通过coredump分析哪个部分的内存被修改了,然后,到map中,查附近的其它数组变量然后查这些数组变量的写操作,看哪些可能出现越界。这个是一个漫长的工作了很烦

罕见的coredump了

最近,项目在越南版删档测试的时候,发生了罕见的coredump,简单记一点排查日志

 

目前的敏感词过滤是在C层做判定的,先后经过几个项目考验,模块算是比较稳定了。越南版有个需求,需要将敏感词里的空格去掉。比如敏感词是abc,现在不能说abc了,但是玩家可以输入“a b c”,所以需要过滤掉空格。有同事就对C层改了一下,判断rune是32的时候,就继续判断后续字符,出事的代码大致如下:

        lua_rawgeti(L, 1, j);             
        uint32_t rune = (uint32_t)lua_tointeger(L, -1); + if (rune == 32) { + continue; + } lua_pop(L, 1); - if(node == NULL) { node = table_get(dict, rune); } else {

这里会跳过lua_pop语句,导致之前lua_rawgeti的结果残留在栈上。但这个缺陷不会马上让进程挂掉,而是将栈弄坏一点点。我们来看Lua的栈定义:

 1 /*
 2 ** ‘per thread‘ state
 3 */
 4 struct lua_State {
 5   CommonHeader;
 6   unsigned short nci;  /* number of items in ‘ci‘ list */
 7   lu_byte status;
 8   StkId top;  /* first free slot in the stack */
 9   global_State *l_G;
10   CallInfo *ci;  /* call info for current function */
11   const Instruction *oldpc;  /* last pc traced */
12   StkId stack_last;  /* last free slot in the stack */
13   StkId stack;  /* stack base */
14   UpVal *openupval;  /* list of open upvalues in this stack */
15   GCObject *gclist;
16   struct lua_State *twups;  /* list of threads with open upvalues */
17   struct lua_longjmp *errorJmp;  /* current error recover point */
18   CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */
19   volatile lua_Hook hook;
20   ptrdiff_t errfunc;  /* current error handling function (stack index) */
21   int stacksize;
22   int basehookcount;
23   int hookcount;
24   unsigned short nny;  /* number of non-yieldable calls in stack */
25   unsigned short nCcalls;  /* number of nested C calls */
26   l_signalT hookmask;
27   lu_byte allowhook;
28 };

lua_State的stack是一个指针,指向一个动态申请的TValue指针数组。这个栈不仅是lua和C交互的时候,用于双方交换数据;lua函数调用的时候,也会将函数参数压栈(当然,调用关系不在这个栈上,而是通过CallInfo指针组织的双向链表来记录)Lua默认会给函数初始化20个格子,也可以通过lua_checkstack函数去增加栈的大小。L->top指向的是栈上的第一个可用空槽,L->top在正常使用的时候会小于L->ci->top,lua自带有api_check来检查。之前为了压榨性能,api_check也关掉了,所以没检查出stack overflow。

 

当一个C函数不断往栈上push函数,超过栈的大小后,会写坏什么内存就没法确定了。出事的时候,写坏的是另一个协程的stack,另一个协程正准备resume回来,但是栈上存的ci->func是TValue(正数32),不是一个函数类型,就coredump了。

 

稳妥起见,以后改C代码还是走一下code review吧,自己也打开api_check检查一下。。查这个问题花了很久,还有一个原因是其他同学搞混了线上版本,我看的是有问题的版本,结果另一个分支上的是没问题的版本,以为正式服上跑的是没问题的版本,查了好久。。。

以上是关于coredump时如何过滤掉共享内存的主要内容,如果未能解决你的问题,请参考以下文章

访问共享进程内存时出现分段错误(核心转储)

内存溢出,死锁怎么办?教你如何排查

将数据从全局加载到共享内存时如何避免银行冲突

如何跨应用程序在多个 Datagridviews 之间共享 DataTable 并节省宝贵的内存

如何使用命名共享内存?

如何制作具有特定数组大小的共享内存?