刚才看了一篇文章是关于一种10000次才出现一次的BUG,用的是设置条件断点,用VC的话其实也可以直接调用Debug方面的API。
http://blog.csdn.net/ATField/archive/2009/08/28/4493003.aspx
但是,我有个问题,100000000次才出现一次的BUG怎么办?
别以为这是天方夜谭,在我的开发中,真真实实的出现过这种情况。当时具体作的项目记不大清了,不过这个问题是关于多线程,多公共变量的。我的任务是维护这个相当复杂的产品代码。
这个产品算是公司相当稳定的产品了,只有这个BUG很奇怪运行10几天后,会莫名其妙的死掉。当时找了很久都没有找到BUG,不得不用另外一个软件在10天重企一下这个服务。
不过我很幸运的找到了这个BUG.这个BUG的原因是多线程时 X++ 这个不是被编译器翻译成单条指令,而是三条指令(如下),并非原子性的,在极个别的情况下会出现其他线程访问冲突,读了错数据,导致后面越错越厉害,然后程序崩掉。
#include <windows.h>
int _stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR, int)
{
00401000 push ebp
00401001 mov ebp,esp
00401003 push ecx
int x = 0;
00401004 mov dword ptr [x],0
x++;
0040100B mov eax,dword ptr [x]
0040100E add eax,1
00401011 mov dword ptr [x],eax
return 0;
00401014 xor eax,eax
}
00401016 mov esp,ebp
00401018 pop ebp
00401019 ret 10h
找到它的方法其实也很常见,就是在崩溃的地方察看调用堆栈,不仅是当前线程的调用堆栈,还有其他线程的调用堆栈,然后从头到尾把每个线程的流程好好分析一遍。最后,这个BUG的狐狸尾巴就露出来了。
其实,这个BUG就是一个变量没有加锁导致的,所以规范的代码是粉重要粉重要滴~
以下是这两个窗口的示例,线程窗口Thread显示当前进程所有的线程,调用堆栈Call Stack窗口显示该进程所有的函数。
大家也可以到http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx去下载符号,然后在VS里面设置路径,就可以显示出来ntdll的函数名称了,这样可以便于理解错误的原因。(我本来也可以显示的,自从装了SP2……还要重下个1G多的大包)
保证这个数据安全还可以用volatile这个关键字标示这个数据,因为volatile在多线程的时候会告诉编译器把类似于X++这种语句优化翻译成一条指令(速度稍慢),但是它就是原子的了,就不会出这种问题了.
可惜的是,当时我们公司为了保证多线程的代码稳定和节约成本( 主要原因:'( ),Release版本是不允许开优化的(其实根本没有Debug版本),这样,volatile这个关键字加不加都一样了.