第一次解BUG,有感
刚工作,就给我安排解决BUG。刚解到第二个,就难了我很久————大概一个月。这是一个“奇怪”的BUG,至今还有很多疑问。
1.现象描述:基于C/S架构的一个远程设备管理系统。能在server端对client端开关机操作。但是有时(5次就会有)重启后client就挂了。
2.解决流程回顾:
step1.按照现象重搭环境:client[centos 6.5] + server[windows 7]----------几十次重启无现象,与设备有关?
step2.远端原BUG环境:client[centos 6.5] + server[windows 7]----------数次重启出现现象。
step3.dd出BUG镜像并dd到本地:数次重启出现现象,与系统有关?!
step4.发现client端需要调用两个自己写的共享库[lib3,lib4],只要没有lib3就不会出问题。库使用c/C++写的,clint和server用C#;在 Linux上用mono制作成可脱离mono环境运行的可执行文件。
step5.既然挂了无非程序退出或者被系统杀死(可能性大);由于是服务进程,开启core文件生成可得知情况,发现被11号信号杀死(段错误)。
step6.可能是lib3的问题,获取源码加打印信息(函数首尾加打印信息到文件);结果该打印的地方都打印了。貌似在lib3返回到C#部分或者之后出问题了。放到自己做的centos6.5上试试,结果这个lib3的库压根没有运行[显示缺少系统库支持libx86-devel]。那么问题来了--之前在上面测试就没有走lib3;于是发现还有lib2,真是太糟糕了(也怪自己之前没有问问这个软件的依赖)!询问得知:
目前的依赖关系:
就是说两条通路都能达到目的。其中lib3的路有问题。然后我在自己做的centos6.5上去掉lib2通路,装上支持的运行库libx86-devel后。BUG现象100%出现。确实lib3有问题。但是打印信息显示lib3的函数都有正确执行!!
难道是返回[return]时出问题了,这种情况就是破坏了堆栈信息。也就是破坏了返回地址,或着桟帧的栈底指针ebp。于是采用内嵌汇编的方式得到这两个值,在函数开始和返回前打印,内嵌汇编部分如下:
1 int addr = 0; 2 int ebp = 0; 3 __asm__ __volatile__( 4 "movl 4(%%ebp), %%eax \t\n" 5 "movl %%eax, %0 \t\n" 6 "movl %%ebp, %%eax \t\n" 7 "movl %%eax, %1 \t\n" 8 :"=r" (addr), "=r" (ebp) 9 : 10 :"%eax");
结果是首尾打印的值相同!!没有这方面的问题。
另外一个疑点是:为什么两个系统(BUG-os和自己做的os)表现不一样,bug-os不时发生;自己做的os却100%出现。
step7.走投无路的情况下,在师傅指导下在挂之前的lib3库的那个函数中注释掉部分代码测试。结果发现了“罪魁祸首”——LRMI_init();[源于libx86.h]。发现只要存在它就会出现问题。看起来似乎是LRMI_init()与C#代码之间的问题?!因为挂的时候总是在C#部分。
step8.写一个C#程序[使用mono-2.10.8进行脱离mono“包装”]加载一个只有LRMI_init()函数的共享库,执行发生段错误。进一步追查发现有LRMI_init()后导致mono中一个断言失效!所以发生了crash。具体怎么导致的还未追查清楚。
3.总结:
1)存留两个问题:
(1)为什么两个系统的现象存在差异?两个系统有什么不同?
(2)具体导致断言失败的原因是什么?这个问题新版的mono还存在吗[后续验证]?
2)学到的非技术经验:
(1)相信自己合理的分析;我在这其中走了很多弯路,如果早点按自己的推断做去,可能会早点解决.
(2)别人的建议很重要;像程序return崩溃的情况我以前没有听过,是别人告诉我的.
(3)面对一个未知的软件,多从开发前辈那里获取信息会节省很多时间。