前段在开发中遇到了测试组报过来的程序coredump 问题,stack如下: (Linux X86-64位系统,RHEL6,隐去程序名字更名为APP)
Stack: [0x0000000030074000,0x0000000030a75000], sp=0x0000000030a73830, free space=10238k
[plain] view plaincopy
- Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
- C [APP+0x4b6768] ACRManager::getLastFailOverEventTS(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*)+0x2ae
- C [APP+0xacefe9] INCACRSessionData::INCACRSessionData(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*, int, int, std::string)+0x369
- C [APP+0xad193c] INCACRSessionDataMap::INCACRSessionDataMap(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*, int, std::string)+0xd4
- C [APP+0x4b52e6] ACRManager::generateINCfile(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*, int, std::string)+0x162
- C [APP+0x5002b6] Audit::closeACR(std::string const&, std::vector<PreACRRec*, std::allocator<PreACRRec*> >, DBConn*, int, int)+0x3c8
- C [APP+0x4ffbd5] Audit::processACR(std::string const&, int)+0x55b
- C [APP+0x4ff4b2] Audit::acr_audit_process()+0x338
- C [APP+0x4fcf65] Audit::acrStart()+0xcf
- C [APP+0x50195f] acr_thread_run+0x54
程序 没有加入-g选项,看不到对应的行号。只能使用objdump -Cd 生成反汇编代码,然后对着看。
红色标出的就是coredump对应的那行的汇编语言
00000000008b64ba<ACRManager::getLastFailOverEventTS(std::vector<PreACRRec*,std::allocator<PreACRRec*> >&, PreACRRec*)>:
8b64ba: 55 push %rbp
8b64bb: 48 89 e5 mov %rsp,%rbp
8b64be: 41 56 push %r14
…..
8b6760: e8 39 9a e9 ff callq 75019e <std::vector<PreACRRec*,std::allocator<PreACRRec*> >::operator[](unsigned long)>
8b6765: 48 8b 00 mov (%rax),%rax
8b6768: 8b 40 34 mov 0x34(%rax),%eax -> 0x8b64ba+0x2ae
8b676b: 83 c0 01 add $0x1,%eax
8b676e: 41 39 c4 cmp %eax,%r12d
结合C++代码,发现出问题的是下面if判断中!=号后面的取成员变量的语句:
[cpp] view plaincopy
- int curPos = 0;
- int nextPos = curPos + 1;
- for (int i=0; i<=acrInDB.size()-2; i++)
- {//forexample:5,7....
- if (acrInDB[curPos]->Accounting_Record_Number != acrInDB[nextPos]->Accounting_Record_Number+1)
- {
- AdditionalChargingInfo*acrPP=acrInDB[curPos]->acr_processing_parameter;
- …
- }
这段代码不是我写的,看到这个问题,我首先联想到是不是多线程访问acrInDB变量出现了问题。
经查代码发现,acrInDB是个局部变量,排除了多线程的问题。那么,这行简单的语句,到底出现了什么问题,会导致coredump呢?
仔细看看上下文,下面的这行代码具有很高的嫌疑——当acrInDB.size() == 1时,程序可能会出现非预期的行为。
[cpp] view plaincopy
- for (int i=0; i<=acrInDB.size()-2; i++)
问题是否真的和猜测的一样呢?让我们深入到汇编代码中寻找答案。注意看我后面添加的注释,是对应的c++代码行
-----------------------------------------------------------------------------------------------
8b69fe: 8b 45 cc mov -0x34(%rbp),%eax // this line is int i = 0;
8b6a01: 4c 63 e0 movslq%eax,%r12 // convert it to 8-byte long (unsignedlong) to compare with acrInDB.size()
8b6a04: 48 8b 85 70 ff ff ff mov -0x90(%rbp),%rax
8b6a0b: 48 89 c7 mov %rax,%rdi
8b6a0e: e8 61 97 e9 ff callq 750174 <std::vector<PreACRRec*,std::allocator<PreACRRec*> >::size() const>
8b6a13: 48 83 e8 02 sub $0x2,%rax //acrInDB.size() - 2
8b6a17: 49 39 c4 cmp %rax,%r12 //i<=acrInDB.size()-2
8b6a1a: 0f 96 c0 setbe %al // get the result
太高兴了,在最后一行看到了一个setbe指令,这个指令的具体作用如下:(参考intel汇编指令手册)
setbe / setna D Set if below or equal (unsigned) CF|ZF 187
确实是按照unsigned类型去做比较的。也就是说,<=号两边的数据都要转成unsigned然后比较。当acrInDB.size() == 1的时候,<=号右边是-1,转成unsigned
则变成了最大的无符号整数,这样就进入了这个for循环。然后nextPos是1,在访问acrInDB[1]的时候,相当于访问了一个random的内存地址,导致出现了SIGSEGV,
程序coredump。
再回头看看core文件,出错指令:
8b6768: 8b 40 34 mov 0x34(%rax),%eax
是将MEM[%RAX+0x34]放到EAX寄存器中,我们使用info register命令看一下RAX寄存器的值,RAX=0x646f4d6e6f697463,非常明显,是一个非法的内存地址,访问则会SIGSEGV。
调试总结:
1. 在做数据比较时,类型匹配那是必须的。
2. 编译时加入-g选项。
3. 熟悉X86-64上的汇编,虽然是C++开发,但对于服务器上的高并发程序开发,汇编语言也是必要的。