1、问题现象
线上服务每隔一个多月会出现一次core,core在一个多线程库的queue里面。
queue使用循环数组实现,里面主要有以下数据成员:
{size_t len;/*数组长度*/ olatile size_t head;/*队列头*/ volatile size_t tail;/*队列尾*/ T* array;/*数组指针*/}
core的调用栈位置:array[tail] = std::move(val); core在向队列尾添加消息的语句。
dmesg信息:segfault at 2872fa0 ip 000000000047e9e3 sp 00007fcc94ffd360 error 4,非法地址是2872fa0,core的位置是47e9e3。
2、使用objdump命令将可执行文件反汇编。
array[tail] = std::move(val);语句对应的汇编代码如下:
%rbx存的是queue对象,(%rbx)对应qlen,0x8(%rbx)对应head,0x10(%rbx)对应tail,0x18(%rbx)对应array。
%rax中存的是无效地址2872fa0,%rax = array + 8*tail。
3、查看core文件中寄存器的内容,使用info registers命令。
4、查看%rbx指向的内存的内容,从中可以得到queue中成员变量的值,使用x命令。
可以看出:tail = 4712592,array = 4713248,array + 8 * tail = 4713248 + 8 * 4712592 = 42413984 = 2872fa0,正好是引起core的无效地址。
进一步发现 qlen = 4708768,head = 4708864,发现可疑的地方,首先qlen没有这么大,我们设置的qlen应该等于远小于这个值,另外qlen也不应该小于head和tail,这时候考虑可能由两种情况导致的:(1)queue对象的内容被飞踩了(2)queue对象是不是获取错了。考虑到queue对象被飞踩定位比较难,所以先检查queue对象获取的是否正确。
5、进一步检查代码,发现多线程库中会有多个Consumer,每个Consumer对应一个queue对象,将一个消息发送给哪个queue是由我们业务根据轮询选择的,轮询的代码(worker++%worker_num),而worker定义成int型,int型当加到最大值后会出现翻转变成负数,对负数取余还是负数,将一个负数付给一个ulong型就会变成一个很大的数,导致获取的queue对象不正确,最终导致core。
6、进一步验证,用f命令进入业务层代码的frame,用info locals命令查看worker变量的值。
worker为负数,验证了问题。