VS调试-崩溃和内存泄漏

2019年8月16日 0 条评论 86 次阅读 0 人点赞

程序崩溃定位

创建dump文件

对于程序崩溃,可以使用对崩溃的进程创建转存储文件(dmp文件)以定位程序崩溃的位置。

生成dump文件的两种方法:

  1. 任务管理器-详细信息,右键创建转存储文件

  2. 在程序中使用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);
    

    Create a dump file for an application whenever it crashes

使用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库)

CRT C运行时库

_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.

litmxs

这个人太懒什么东西都没留下

文章评论(0)