程序崩溃定位
创建dump文件
对于程序崩溃,可以使用对崩溃的进程创建转存储文件(dmp文件)以定位程序崩溃的位置。
生成dump文件的两种方法:
-
任务管理器-详细信息,右键创建转存储文件
-
在程序中使用
SetUnhandledExceptionFilter(unhandled_handler);
,未被捕获的异常会交给unhandled_handler
处理,利用这个特性我们可以让程序崩溃前自动生成dump文件:#ifdef WIN32 #include <windows.h> #include <Dbghelp.h> #include <tchar.h> typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); void create_minidump(struct _EXCEPTION_POINTERS* apExceptionInfo) { HMODULE mhLib = ::LoadLibrary(_T("dbghelp.dll")); MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(mhLib, "MiniDumpWriteDump"); HANDLE hFile = ::CreateFile(_T("core.dmp"), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); _MINIDUMP_EXCEPTION_INFORMATION ExInfo; ExInfo.ThreadId = ::GetCurrentThreadId(); ExInfo.ExceptionPointers = apExceptionInfo; ExInfo.ClientPointers = FALSE; pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL); ::CloseHandle(hFile); } LONG WINAPI unhandled_handler(struct _EXCEPTION_POINTERS* apExceptionInfo) { create_minidump(apExceptionInfo); return EXCEPTION_CONTINUE_SEARCH; } #endif
自定义一个
unhandled_handler
函数并在程序开始处调用:SetUnhandledExceptionFilter(unhandled_handler);
使用dump文件
我们编写一个会随机崩溃的程序
void dfs()
{
int x = rand() % 1000;
if (!x) (*(int*)nullptr) = 0;
dfs();
}
int main()
{
SetUnhandledExceptionFilter(unhandled_handler);
dfs();
return 0;
}
编译运行崩溃,在程序所在路径得到一个core.dmp文件,用vs打开即可(需要有pdb文件才能看到代码而不是汇编文件),按F5调试,就可以看到程序崩溃时所在的位置和调用堆栈,和调试正在运行的程序一样,也可以看到运行时内存中各个变量的值。
内存泄漏(CRT库)
_CRTDBG_MAP_ALLOC
在开头添加以下头文件和宏,_CRTDBG_MAP_ALLOC
会使用debug版的malloc和free代替普通的malloc和free(需要使用debug模式编译)
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
并在程序结束前调用
_CrtDumpMemoryLeaks();
然后使用debug模式编译运行便会在debug中输出检测报告,所有没有释放的内存都会被输出
void dfs()
{
int x = rand() % 1000;
if(x % 10 == 0) int *p = new int;
if (x == 1) return;
dfs();
}
int main()
{
dfs();
_CrtDumpMemoryLeaks();
getchar();
return 0;
}
输出报告:
Detected memory leaks!
Dumping objects ->
{158} normal block at 0x02D43870, 4 bytes long.
Data: < > CD CD CD CD
{157} normal block at 0x02D43AB0, 4 bytes long.
Data: < > CD CD CD CD
...
{78} normal block at 0x02D36268, 4 bytes long.
Data: < > CD CD CD CD
Object dump complete.
要想输出详细的信息,需要使用new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
代替普通的new
以输出文件名和行号
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
#ifdef _DEBUG
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
// allocations to be of _CLIENT_BLOCK type
#else
#define DBG_NEW new
#endif
char buff[1000];
void dfs()
{
int x = rand() % 1000;
if(x % 10 == 0) int *p = DBG_NEW int;
if (x == 1) return;
dfs();
}
int main()
{
dfs();
_CrtDumpMemoryLeaks();
getchar();
return 0;
}
输出报告:
Detected memory leaks!
Dumping objects ->
c:\users\litianxiong\documents\visual studio 2015\projects\testdump\testdump\testdump.cpp(22) : {158} normal block at 0x00594118, 4 bytes long.
Data: < > CD CD CD CD
c:\users\litianxiong\documents\visual studio 2015\projects\testdump\testdump\testdump.cpp(22) : {157} normal block at 0x00593FC8, 4 bytes long.
Data: < > CD CD CD CD
内存快照
_CrtMemState s1;
_CrtMemCheckpoint( &s1 ); // 创建内存快照
_CrtMemDumpStatistics( &s1 ); // 输出
输出如下:
0 bytes in 0 Free Blocks.
316 bytes in 79 Normal Blocks.
17521 bytes in 69 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 17837 bytes.
Total allocations: 26316 bytes.
在某个语句块前后创建快照,然后使用_CrtMemDifference( &s3, &s1, &s2)
可以比较s1和s2两个快照差异并输出到s3,便可以确定该语句块是否存在内存泄漏
_CrtMemState s1, s2, s3;
_CrtMemCheckpoint(&s1);
int *p = new int;
_CrtMemCheckpoint(&s2);
if (_CrtMemDifference(&s3, &s1, &s2))
_CrtMemDumpStatistics(&s3);
输出如下
0 bytes in 0 Free Blocks.
4 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 4 bytes.
Total allocations: 4 bytes.