注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

mie

 
 
 

日志

 
 

VC:快速侦测断言错误导致的内存泄露  

2010-09-18 21:40:16|  分类: 疑难杂症 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

        程序在退出时,由于断言错误,导致内存大量泄露。之前曾经遇到过类似的情况,当时是一步一步地调试,最后解决。这次再次遇到,而且情况更加复杂,所以,我想出了一个快速侦测的方法。

        替换系统默认的断言语句:(加在stdafx.h文件的前面,或者是第一个编译的文件里面,以便能够替换所有的断言)
#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s \n", __FILE__, __LINE__, __FUNCTION__);\
 _ASSERTE((x));

 

        这里使用ATLASSERT。因为我使用的是VC 2008,2008中,MFC的ASSERT是采用的ATLASSERT的,而ATL也是ATLASSERT的,同时,console控制台程序也可以方便地使用ATLASSERT。

g_TraceNoTime的实现:
void g_TraceNoTime(const char* pszFormat, ...); //打印调试信息
void g_TraceNoTime(const wchar_t* pszFormat, ...); //打印调试信息
#define PACKET_BUFFER_MAX_SIZE (64*1024)
void g_TraceNoTime(const char* pszFormat, ...)
{
 char buf[PACKET_BUFFER_MAX_SIZE]={0};
 va_list arglist;
 SYSTEMTIME t;
 GetLocalTime(&t);
 va_start(arglist, pszFormat);
 vsprintf_s(buf+strlen(buf), sizeof(buf)-strlen(buf), pszFormat, arglist);
 strcat_s(buf, sizeof(buf), "\r\n");
 va_end(arglist);

 OutputDebugStringA(buf);
}

void g_TraceNoTime(const wchar_t* pszFormat, ...)
{
 wchar_t buf[PACKET_BUFFER_MAX_SIZE]={0};
 va_list arglist;
 SYSTEMTIME t;
 GetLocalTime(&t);
 va_start(arglist, pszFormat);
 vswprintf_s(buf+wcslen(buf), sizeof(buf)/sizeof(wchar_t)-wcslen(buf), pszFormat, arglist);
 wcscat_s(buf, sizeof(buf)/sizeof(wchar_t), L"\r\n");
 va_end(arglist);

 OutputDebugStringW(buf);
}

      这样虽然可以,但是还是不能看到断言的内容,于是改为:
#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s "#x"\n", __FILE__, __LINE__, __FUNCTION__);\
 _ASSERTE((x));

      对Unicode的支持:#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s "#x"\n", __FILEW__, __LINE__, __FUNCTIONW__);\
 _ASSERTE((x));

       暂时就这样。运行一下,看看结果。最后一个TRACE打印的结果:
****.cpp(1072):****::CreateBitmapEx (eCompression == BI_RGB) || (eCompression == BI_BITFIELDS)

      是我自己写的一个函数里面的断言!

      基本上知道问题出哪儿了,但是,如果改了这一处断言,其他地方还是会出问题!因为这些断言发生时,程序正在退出,系统资源正在释放!所以,在发现程序退出时,就不能再执行这些断言语句!——只要正确地回收某些资源,就可以保证程序不会在退出时执行到这里,这样就可以解决问题。

 

      于是:
#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s, "#x"\n", __FILE__, __LINE__, __FUNCTION__);\
 try\
 {\
  _ASSERTE((x));\
 }\
 catch (...)\
 {\
  g_Break();\
 }\

//断点函数
int g_Break();

int g_Break()
{
 return 0;            //断点
}

        这样的代码,执行的效果应该很好!当断言导致进程崩溃时,异常被捕获到g_Break();,所以,我只要在g_Break()里面下一个断点,然后就等着断言崩溃就行了!

        然而,这只是我的一厢情愿。程序并不能执行到这里,这个断言崩溃很严重,直接是进程崩溃,异常都捕获不到!

 

        没有关系,查看TRACE信息。
*****.cpp(57):****::Close, Close socket! m_is_closed_notify=1

        在断言崩溃前面,最后一个正常的TRACE调试信息是这句,那么,就在这里下断点!

        即:先在这句下断点,中断发生后,再在g_Break()里面下断点。

#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s, "#x, __FILE__, __LINE__, __FUNCTION__);\
 g_Break();\
 _ASSERTE((x));\

 

         经过数个小时的跟踪,最后找到问题所在。将下面的代码

void C****Dlg::OnCancel()
{
 HWND hwnd=m_hWnd;

 DestroyWindow();

........

DWORD thread_id=GetWindowThreadProcessId(hwnd, NULL);
 PostThreadMessage(thread_id, WM_QUIT, 0, 0);

}

 改为:
void C****Dlg::OnCancel()
{
 HWND hwnd=m_hWnd;

 DWORD thread_id=GetWindowThreadProcessId(hwnd, NULL);
 PostThreadMessage(thread_id, WM_QUIT, 0, 0);

........

 DestroyWindow();

}

By:zhanyonhu

 


 

  评论这张
 
阅读(954)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016