输出CallStack

在程序异常处理中有时需要输出CallStack,这就要用到DbgHelp

示例代码

#include <Windows.h>
#include <DbgHelp.h>
#include <stdio.h>

#pragma comment(lib, "dbghelp.lib")

void PrintCallStack(const CONTEXT * pContext)
{
	HANDLE hProcess = GetCurrentProcess();

	if (!SymInitialize(hProcess, NULL, TRUE)) {
		exit(0);
	}

	HANDLE hThread = GetCurrentThread();

	CONTEXT Context = *pContext;

	struct ReadMemoryRoutine {
		static BOOL CALLBACK Proc(HANDLE hProcess, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead) {
			SIZE_T st;
			BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)lpBaseAddress, lpBuffer, nSize, &st);
			*lpNumberOfBytesRead = st;
			return bRet;
		}
	};

	STACKFRAME64 StackFrame;
	ZeroMemory(&StackFrame, sizeof(StackFrame));
	StackFrame.AddrPC.Offset    = Context.Eip;
	StackFrame.AddrPC.Mode      = AddrModeFlat;
	StackFrame.AddrFrame.Offset = Context.Ebp;
	StackFrame.AddrFrame.Mode   = AddrModeFlat;
	StackFrame.AddrStack.Offset = Context.Esp;
	StackFrame.AddrStack.Mode   = AddrModeFlat;
	while (1) {
		if (!StackWalk64(IMAGE_FILE_MACHINE_I386, hProcess, hThread, &StackFrame, &Context, &ReadMemoryRoutine::Proc, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
			break;
		}
		if (StackFrame.AddrPC.Offset == 0) {
			break;
		}
		DWORD64 Displacement64;
		ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
		PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
		pSymbol->SizeOfStruct = sizeof(buffer);
		pSymbol->MaxNameLen = MAX_SYM_NAME;
		if (!SymFromAddr(hProcess, StackFrame.AddrPC.Offset, &Displacement64, pSymbol)) {
			strcpy_s(pSymbol->Name, MAX_SYM_NAME, "unknown symbol name");
		}
		DWORD Displacement;
		IMAGEHLP_LINE64 Line;
		ZeroMemory(&Line, sizeof(Line));
		Line.SizeOfStruct = sizeof(Line);
		if (!SymGetLineFromAddr64(hProcess, StackFrame.AddrPC.Offset, &Displacement, &Line)) {
			Line.FileName = "unknown file name";
		}
		printf_s("%s (%u): %s\n", Line.FileName, Line.LineNumber, pSymbol->Name);
	}
}

void WriteMiniDump(struct _EXCEPTION_POINTERS * pException) {
	HANDLE hFile = CreateFileA("mini_dump.dmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (INVALID_HANDLE_VALUE == hFile) {
		exit(0);
	}
	MINIDUMP_EXCEPTION_INFORMATION cbif;
	cbif.ThreadId = GetCurrentThreadId();
	cbif.ClientPointers = TRUE;
	cbif.ExceptionPointers = pException;
	MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &cbif, NULL, NULL);
	CloseHandle(hFile);
}

static LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS * pException) {

	PrintCallStack(pException->ContextRecord);

	WriteMiniDump(pException);

	return EXCEPTION_EXECUTE_HANDLER;
}

int main(int argv, char ** argc)
{
	SetUnhandledExceptionFilter(MyExceptionFilter);

	_asm {cli};

	return 0;
}

当需要在某个函数中输出调用堆栈,则可以这样

void foo(void)
{
	CONTEXT Context;
	RtlCaptureContext(&Context);
	PrintCallStack(&Context);
}

但是由于RtlCaptureContext不包含顶层调用堆栈,所以还可以直接从汇编中获取Eip的方式

参见:http://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/

void foo(void)
{
	CONTEXT Context;
	ZeroMemory( &Context, sizeof( CONTEXT ) );
	Context.ContextFlags = CONTEXT_CONTROL;
	__asm {
Label:
		mov [Context.Ebp], ebp;
		mov [Context.Esp], esp;
		mov eax, [Label];
		mov [Context.Eip], eax;
	}
	PrintCallStack(&Context);
}


你可能感兴趣的:(输出CallStack)