exceptionreport.cpp

来自「一个支持FTP,SFTP的客户端程序」· C++ 代码 · 共 703 行 · 第 1/2 页

CPP
703
字号
	// Print fault type
	AddToReport("\r\nException Details:\r\n------------------\r\n\r\nException code: ");
	AddToReportHex(pExceptionRecord->ExceptionCode, 8);
	AddToReport(" ");
	AddToReport(GetExceptionString(pExceptionRecord->ExceptionCode));
	
	// Add fault address and module
	TCHAR szModule[MAX_PATH];
	memset(szModule, 0, MAX_PATH);
	DWORD dwSection, dwOffset;
	GetAddrDetails(pExceptionRecord->ExceptionAddress,
					  szModule,
					  sizeof(szModule),
					  dwSection, dwOffset);

	AddToReport("\r\nFault address:  ");
	AddToReportHex((int)pExceptionRecord->ExceptionAddress, 8);
	AddToReport(" ");
	AddToReportHex(dwSection, 2);
	AddToReport(":");
	AddToReportHex(dwOffset, 8);
	AddToReport(" ");
	AddToReport(szModule);
	AddToReport("\r\n");

	// Set up the symbol engine.
	DWORD dwOptions = pSymGetOptions() ;

	// Turn on line loading and deferred loading.
	pSymSetOptions(dwOptions | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);

	PCONTEXT pContext = pExceptionInfo->ContextRecord;

	// Initialize DbgHelp
	if (!pSymInitialize(GetCurrentProcess(), 0, TRUE))
		return;

	StackWalk(*pContext);

	pSymCleanup(GetCurrentProcess());
}

LPTSTR CExceptionReport::GetExceptionString(DWORD dwCode)
{
	#define EXCEPTION(x) case EXCEPTION_##x: return _T(#x);

	switch (dwCode)
	{
		EXCEPTION(ACCESS_VIOLATION)
		EXCEPTION(DATATYPE_MISALIGNMENT)
		EXCEPTION(BREAKPOINT)
		EXCEPTION(SINGLE_STEP)
		EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
		EXCEPTION(FLT_DENORMAL_OPERAND)
		EXCEPTION(FLT_DIVIDE_BY_ZERO)
		EXCEPTION(FLT_INEXACT_RESULT)
		EXCEPTION(FLT_INVALID_OPERATION)
		EXCEPTION(FLT_OVERFLOW)
		EXCEPTION(FLT_STACK_CHECK)
		EXCEPTION(FLT_UNDERFLOW)
		EXCEPTION(INT_DIVIDE_BY_ZERO)
		EXCEPTION(INT_OVERFLOW)
		EXCEPTION(PRIV_INSTRUCTION)
		EXCEPTION(IN_PAGE_ERROR)
		EXCEPTION(ILLEGAL_INSTRUCTION)
		EXCEPTION(NONCONTINUABLE_EXCEPTION)
		EXCEPTION(STACK_OVERFLOW)
		EXCEPTION(INVALID_DISPOSITION)
		EXCEPTION(GUARD_PAGE)
		EXCEPTION(INVALID_HANDLE)
	}

	// Try to get descripbion of unknown exceptions
	static TCHAR buffer[512] = {0};

	FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
				  GetModuleHandle(_T("NTDLL.DLL")),
				  dwCode, 0, buffer, sizeof(buffer), 0);

	return buffer;
}

void CExceptionReport::StackWalk(CONTEXT Context)
{
	USES_CONVERSION;
	AddToReport("\r\nCall stack:\r\n-----------\r\n\r\n");
	AddToReport("Address   Frame     Function			SourceFile\r\n");
	
	DWORD dwMachineType = 0;

	STACKFRAME sf;
	memset(&sf, 0, sizeof(sf));

#ifdef _M_IX86
	// Initialize the STACKFRAME structure for the first call.  This is only
	// necessary for Intel CPUs, and isn't mentioned in the documentation.
	sf.AddrPC.Offset	= Context.Eip;
	sf.AddrPC.Mode		= AddrModeFlat;
	sf.AddrStack.Offset	= Context.Esp;
	sf.AddrStack.Mode	= AddrModeFlat;
	sf.AddrFrame.Offset	= Context.Ebp;
	sf.AddrFrame.Mode	= AddrModeFlat;

	dwMachineType = IMAGE_FILE_MACHINE_I386;
#endif

#ifdef _M_IX86
	while (TRUE)
	{
		// Get next stack frame
		if (!pStackWalk(dwMachineType, GetCurrentProcess(), GetCurrentThread(),
						&sf, &Context, 0, 
						pSymFunctionTableAccess, pSymGetModuleBase,	0))
			break;

		if (!sf.AddrFrame.Offset)
			break; //Invalid frame

		AddToReportHex(sf.AddrPC.Offset, 8);
		AddToReport("  ");
		AddToReportHex(sf.AddrFrame.Offset, 8);
		AddToReport("  ");
		
		// Get function name for stack frame entry
		BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
		PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
		pSymbol->SizeOfStruct = sizeof(symbolBuffer);
		pSymbol->MaxNameLen = 1024;
					
		DWORD64 symDisplacement = 0;	// Displacement of the input address,
										// relative to the start of the symbol

		if (pSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol))
		{
			AddToReport(pSymbol->Name);
			AddToReport("+");
			AddToReportHex(symDisplacement);
		}
		else	// No symbol found.  Print out the logical address instead.
		{
			TCHAR szModule[MAX_PATH] = _T("");
			DWORD section = 0, offset = 0;

			GetAddrDetails((PVOID)sf.AddrPC.Offset,
								szModule, sizeof(szModule), section, offset);

			AddToReportHex(section, 4);
			AddToReport(":");
			AddToReportHex(offset, 8);
			AddToReport(" ");
			AddToReport(szModule);
		}

		// Get the source line for this stack frame entry
		IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
		DWORD dwLineDisplacement;
		if (pSymGetLineFromAddr(GetCurrentProcess(), sf.AddrPC.Offset,
								&dwLineDisplacement, &lineInfo))
		{
			AddToReport("  ");
			AddToReport(lineInfo.FileName);
			AddToReport(" line ");
			AddToReport(lineInfo.LineNumber);
		}

		AddToReport("\r\n");
	}
#else
	AddToReport("Cannot dump stack yet on non-x86 systems!\r\n");
#endif

}

bool CExceptionReport::writeMiniDump(PEXCEPTION_POINTERS pExceptionInfo)
{
	// Write the minidump to the file
	MINIDUMP_EXCEPTION_INFORMATION eInfo;
	eInfo.ThreadId = GetCurrentThreadId();
	eInfo.ExceptionPointers = pExceptionInfo;
	eInfo.ClientPointers = FALSE;

	MINIDUMP_CALLBACK_INFORMATION cbMiniDump;
	cbMiniDump.CallbackRoutine = 0;
	cbMiniDump.CallbackParam = 0;


	pMiniDumpWriteDump(
		GetCurrentProcess(),
		GetCurrentProcessId(),
		m_hDumpFile,
		MiniDumpNormal,
		pExceptionInfo ? &eInfo : NULL,
		NULL,
		&cbMiniDump);

	// Close file
	CloseHandle(m_hDumpFile);

	return true;
}

int CExceptionReport::sendMail()
{
	CMailMsg mail;

	mail.SetTo(_T("tim.kosse@gmx.de"), _T("Tim Kosse"));

	TCHAR str[4096];
	_stprintf(str, _T("Exception report created by %s\r\n\r\n"), (LPCTSTR)GetVersionString());
	mail.SetSubject(str);

	mail.SetMessage(_T("Enter your comments here, what did you do with FileZilla before it crashed?"));

	mail.AddAttachment(m_pLogFileName, _T("FileZilla.rpt"));
	mail.AddAttachment(m_pDmpFileName, _T("FileZilla.dmp"));

	return mail.Send();
}

void CExceptionReport::SuspendThreads()
{
	// Try to get OpenThread and Thread32* function from kernel32.dll, since it's not available on Win95/98
	typedef HANDLE (WINAPI *tOpenThread)	(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId);
	typedef BOOL (WINAPI *tThread32First)	(HANDLE hSnapshot, LPTHREADENTRY32 lpte);
	typedef BOOL (WINAPI *tThread32Next)	(HANDLE hSnapshot, LPTHREADENTRY32 lpte);
	typedef HANDLE (WINAPI *tCreateToolhelp32Snapshot)	(DWORD dwFlags, DWORD th32ProcessID);

	HMODULE hKernel32Dll = LoadLibrary(_T("kernel32.dll"));
	if (!hKernel32Dll)
		return;
	tOpenThread		pOpenThread		= (tOpenThread)		GetProcAddress(hKernel32Dll, "OpenThread");
	tThread32First	pThread32First	= (tThread32First)	GetProcAddress(hKernel32Dll, "Thread32First");
	tThread32Next	pThread32Next	= (tThread32Next)	GetProcAddress(hKernel32Dll, "Thread32Next");
	tCreateToolhelp32Snapshot pCreateToolhelp32Snapshot	= (tCreateToolhelp32Snapshot)	GetProcAddress(hKernel32Dll, "CreateToolhelp32Snapshot");
	if (!pOpenThread	||
		!pThread32First	||
		!pThread32Next	||
		!pCreateToolhelp32Snapshot)
	{
		CloseHandle(hKernel32Dll);
		return;
	}

	HANDLE hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

	// Get information about own process/thread
	DWORD ownProcessId = GetCurrentProcessId();
	DWORD ownThreadId = GetCurrentThreadId();

	// Enumerate threads
	THREADENTRY32 entry;
	entry.dwSize = sizeof(THREADENTRY32);
	BOOL bNext = pThread32First(hSnapshot, &entry);
	while (bNext)
	{
		if (entry.th32OwnerProcessID == ownProcessId &&
			entry.th32ThreadID != ownThreadId)
		{
			// Suspen threads of own process
			HANDLE hThread = pOpenThread(THREAD_SUSPEND_RESUME, FALSE, entry.th32ThreadID);
			if (hThread)
				SuspendThread(hThread);
		}
		bNext = pThread32Next(hSnapshot, &entry);
	}
	CloseHandle(hKernel32Dll);
}

void CExceptionReport::AddToReport(const char * pText)
{
	DWORD bytesWritten = 0;
	WriteFile(m_hReportFile, pText, strlen(pText), &bytesWritten, 0);
}

void CExceptionReport::AddToReport(const WCHAR * pText)
{
	USES_CONVERSION;
	AddToReport(W2A(pText));
}

void CExceptionReport::AddToReport(int number)
{
	char buffer[sizeof(int) * 4];
	sprintf(buffer, "%d", number);
	AddToReport(buffer);
}

void CExceptionReport::AddToReportHex(_int64 number, int minDigits /*=0*/)
{
	char buffer[sizeof(_int64) * 2 + 1];
	if (!minDigits)
        sprintf(buffer, "%I64X", number);
	else
	{
		char fmt[10];
		sprintf(fmt, "%%0%dI64X", minDigits);
		sprintf(buffer, fmt, number);
	}
	AddToReport(buffer);
}

BOOL CExceptionReport::GetAddrDetails(PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset)
{
	// Get details about an address: Module name, section and offet
	
	// Get information about the provided address
	MEMORY_BASIC_INFORMATION mbi;
	if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
		return FALSE;

	// Get module
	DWORD hMod = (DWORD)mbi.AllocationBase;
	if (!GetModuleFileName((HMODULE)hMod, szModule, len))
		return FALSE;

	
	// Get DOS header of module
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMod;

	// Advance to PE header and get the section information
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(hMod + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader);

	// Get module load address
	DWORD lAddr = (DWORD)addr - hMod; 

	// Search for a section which contains the address
	for (unsigned int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
	{
		// Calculate section start and end addresses
		DWORD startAddr = pSection->VirtualAddress;
		DWORD endAddr = startAddr;
		if (pSection->SizeOfRawData > pSection->Misc.VirtualSize)
			endAddr += pSection->SizeOfRawData;
		else
			pSection->Misc.VirtualSize;

		// Look if provided address is between startAddr and endAddr
		if (lAddr >= startAddr && lAddr <= endAddr)
		{
			// We've found the section, set section index and offset
			section = i+1;
			offset = lAddr - startAddr;
			return TRUE;
		}
		pSection++;
	}

	// Section not found, very strange
	return FALSE;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?