VC:快速侦测断言错误导致的内存泄露
2010-09-18 21:40:16| 分类: 疑难杂症 |字号 订阅
程序在退出时,由于断言错误,导致内存大量泄露。之前曾经遇到过类似的情况,当时是一步一步地调试,最后解决。这次再次遇到,而且情况更加复杂,所以,我想出了一个快速侦测的方法。
替换系统默认的断言语句:(加在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
2010-09-18 21:40:16| 分类: 疑难杂症 |字号 订阅
程序在退出时,由于断言错误,导致内存大量泄露。之前曾经遇到过类似的情况,当时是一步一步地调试,最后解决。这次再次遇到,而且情况更加复杂,所以,我想出了一个快速侦测的方法。
替换系统默认的断言语句:(加在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