VC6调试版本C运行库内存申请的一个bug
2008-03-29 21:06
651 查看
VC调试版本C运行库内存申请的一个bug
遇到过一个通信方面的软件,需要长期运行,做压力测试时,高负荷连续运行一定天数时必定崩溃,而且都是在msvcrtd.dll中崩溃。负责维护的人百思不得其解,就去问微软的人,结果微软的人说这是VC6带的msvcrtd.dll的一个问题,VC2005已经没有这个问题了,请升级到新的版本。这个软件规模比较大,依赖于很多库,后台都是用VC6编译的调试版本,为了方便定位问题,没有Release版本。升级到VC2005后会不会出现别的问题,没有人敢冒这个风险,于是没有使用VC2005。
闲着没事的时候分析了一下,才发现问题其实很简单。msvcrtd.dll对每次内存申请都进行计数,当计数值达到设定的某个值时,就会调用_CrtDbgBreak()。MSDN对_CrtDbgBreak的说明是:Sets a break point on a particular line of code,其实_CrtDbgBreak在X86下只有一条指令就是int 3(0xCC)。
在dbgheap.c中定义了下面两个变量:
static long _lRequestCurr = 1; /* Current request number */
extern "C" _CRTIMP long _crtBreakAlloc = -1L; /* Break on allocation by request number */
_lRequestCurr表示当前的申请次数,_crtBreakAlloc表示当内存申请次数达到某个值时break,即调用_CrtDbgBreak。详情可参考debugheap.c中的_heap_alloc_dbg_impl函数:
lRequest = _lRequestCurr;
/* break into debugger at specific memory allocation */
if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
_CrtDbgBreak();
VC6附带的dbgheap.c中没有添加_crtBreakAlloc != -1L的判断,而是:
if (lRequest == _crtBreakAlloc)
_CrtDbgBreak();
_lRequestCurr初始化为1,每次申请内存都加1,当_lRequestCurr为-1时在VC6的dbgheap.c中就会触发int 3导致程序退出,而在新的版本中添加了_crtBreakAlloc != -1L的判断,所以默认的情况下是不会触发int 3 退出的。
可以通过调用_CrtSetBreakAlloc设置_crtBreakAlloc的值,当我们设置了新的_crtBreakAlloc,而且_crtBreakAlloc等于_lRequestCurr时就会触发int 3。
弄清楚了问题的所在,我们就可以着手解决问题了。VC6的dbgheap.c中有两个地方判断了lRequest 是否与_crtBreakAlloc相等,相等后执行指令int 3。我们不用复杂的处理,把int 3替换为nop(0x90)指令即可。首先得到“if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)” 对应的二进制指令,用UE打开msvcrtd.dll,使用16进制编辑模式,查找得到的二进制指令,发现确实只有二处,把紧接着它们的0xCC替换为0x90,问题解决。
遇到过一个通信方面的软件,需要长期运行,做压力测试时,高负荷连续运行一定天数时必定崩溃,而且都是在msvcrtd.dll中崩溃。负责维护的人百思不得其解,就去问微软的人,结果微软的人说这是VC6带的msvcrtd.dll的一个问题,VC2005已经没有这个问题了,请升级到新的版本。这个软件规模比较大,依赖于很多库,后台都是用VC6编译的调试版本,为了方便定位问题,没有Release版本。升级到VC2005后会不会出现别的问题,没有人敢冒这个风险,于是没有使用VC2005。
闲着没事的时候分析了一下,才发现问题其实很简单。msvcrtd.dll对每次内存申请都进行计数,当计数值达到设定的某个值时,就会调用_CrtDbgBreak()。MSDN对_CrtDbgBreak的说明是:Sets a break point on a particular line of code,其实_CrtDbgBreak在X86下只有一条指令就是int 3(0xCC)。
在dbgheap.c中定义了下面两个变量:
static long _lRequestCurr = 1; /* Current request number */
extern "C" _CRTIMP long _crtBreakAlloc = -1L; /* Break on allocation by request number */
_lRequestCurr表示当前的申请次数,_crtBreakAlloc表示当内存申请次数达到某个值时break,即调用_CrtDbgBreak。详情可参考debugheap.c中的_heap_alloc_dbg_impl函数:
lRequest = _lRequestCurr;
/* break into debugger at specific memory allocation */
if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
_CrtDbgBreak();
VC6附带的dbgheap.c中没有添加_crtBreakAlloc != -1L的判断,而是:
if (lRequest == _crtBreakAlloc)
_CrtDbgBreak();
_lRequestCurr初始化为1,每次申请内存都加1,当_lRequestCurr为-1时在VC6的dbgheap.c中就会触发int 3导致程序退出,而在新的版本中添加了_crtBreakAlloc != -1L的判断,所以默认的情况下是不会触发int 3 退出的。
可以通过调用_CrtSetBreakAlloc设置_crtBreakAlloc的值,当我们设置了新的_crtBreakAlloc,而且_crtBreakAlloc等于_lRequestCurr时就会触发int 3。
弄清楚了问题的所在,我们就可以着手解决问题了。VC6的dbgheap.c中有两个地方判断了lRequest 是否与_crtBreakAlloc相等,相等后执行指令int 3。我们不用复杂的处理,把int 3替换为nop(0x90)指令即可。首先得到“if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)” 对应的二进制指令,用UE打开msvcrtd.dll,使用16进制编辑模式,查找得到的二进制指令,发现确实只有二处,把紧接着它们的0xCC替换为0x90,问题解决。
相关文章推荐
- VC调试版本C运行库内存申请的一个bug(转)
- 记录一个expo连接调试的bug(2018年1月份左右的版本)
- 由一个内存错误发现的cocos2dx 引擎3.4版本的 一个bug
- 搞了一上午,原来是Cf1.0在windows mobile 2003 se版本的一个bug
- vc申请内存结构
- VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏
- 修正了版本管理实现中的一个bug
- VC使用CRT调试功能来检测内存泄漏
- 用Windbg调试一个Windows自带扫雷程序的逻辑bug
- VC使用CRT调试功能来检测内存泄漏
- IOS7版本中GameCenter的一个bug的解决方法
- VC使用CRT调试功能来检测内存泄漏(转)
- VC.NET的一个BUG
- Erlang 程序引发共享内存 bug 的一个例子
- VC使用CRT调试功能来检测内存泄漏
- VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结
- 记一次有意义的内存bug调试(完)
- 为 VC6 程序的 Release 版本添加调试信息
- r语言3.4.3之前版本的一个bug
- VC7.1 编译器的一个不大不小的bug