|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/minidump.h"
#include "tier0/platform.h"
#if defined( _WIN32 ) && !defined(_X360 ) && ( _MSC_VER >= 1300 )
#include "tier0/valve_off.h"
#define WIN_32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0403
#include <windows.h>
#include <time.h>
#include <dbghelp.h>
#endif
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
#if defined( _WIN32 ) && !defined( _X360 )
#if _MSC_VER >= 1300
// MiniDumpWriteDump() function declaration (so we can just get the function directly from windows)
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 );
// true if we're currently writing a minidump caused by an assert
static bool g_bWritingNonfatalMinidump = false; // counter used to make sure minidump names are unique
static int g_nMinidumpsWritten = 0;
//-----------------------------------------------------------------------------
// Purpose: Creates a new file and dumps the exception info into it
// Input : uStructuredExceptionCode - windows exception code, unused.
// pExceptionInfo - call stack.
// minidumpType - type of minidump to write.
// ptchMinidumpFileNameBuffer - if not-NULL points to a writable tchar buffer
// of length at least _MAX_PATH to contain the name
// of the written minidump file on return.
//-----------------------------------------------------------------------------
bool WriteMiniDumpUsingExceptionInfo( unsigned int uStructuredExceptionCode, ExceptionInfo_t * pExceptionInfo, uint32 minidumpType, tchar *ptchMinidumpFileNameBuffer /* = NULL */ ) { if ( ptchMinidumpFileNameBuffer ) { *ptchMinidumpFileNameBuffer = tchar( 0 ); }
// get the function pointer directly so that we don't have to include the .lib, and that
// we can easily change it to using our own dll when this code is used on win98/ME/2K machines
HMODULE hDbgHelpDll = ::LoadLibrary( "DbgHelp.dll" ); if ( !hDbgHelpDll ) return false;
bool bReturnValue = false; MINIDUMPWRITEDUMP pfnMiniDumpWrite = (MINIDUMPWRITEDUMP) ::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
if ( pfnMiniDumpWrite ) { // create a unique filename for the minidump based on the current time and module name
struct tm curtime; Plat_GetLocalTime( &curtime ); ++g_nMinidumpsWritten;
// strip off the rest of the path from the .exe name
tchar rgchModuleName[MAX_PATH]; #ifdef TCHAR_IS_WCHAR
::GetModuleFileNameW( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) ); #else
::GetModuleFileName( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) ); #endif
tchar *pch = _tcsrchr( rgchModuleName, '.' ); if ( pch ) { *pch = 0; } pch = _tcsrchr( rgchModuleName, '\\' ); if ( pch ) { // move past the last slash
pch++; } else { pch = _T("unknown"); }
// can't use the normal string functions since we're in tier0
tchar rgchFileName[MAX_PATH]; _sntprintf( rgchFileName, sizeof(rgchFileName) / sizeof(tchar), _T("%s_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp"), pch, g_bWritingNonfatalMinidump ? "assert" : "crash", curtime.tm_year + 1900, /* Year less 2000 */ curtime.tm_mon + 1, /* month (0 - 11 : 0 = January) */ curtime.tm_mday, /* day of month (1 - 31) */ curtime.tm_hour, /* hour (0 - 23) */ curtime.tm_min, /* minutes (0 - 59) */ curtime.tm_sec, /* seconds (0 - 59) */ g_nMinidumpsWritten // ensures the filename is unique
);
BOOL bMinidumpResult = FALSE; #ifdef TCHAR_IS_WCHAR
HANDLE hFile = ::CreateFileW( rgchFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); #else
HANDLE hFile = ::CreateFile( rgchFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); #endif
if ( hFile ) { // dump the exception information into the file
_MINIDUMP_EXCEPTION_INFORMATION ExInfo; ExInfo.ThreadId = ::GetCurrentThreadId(); ExInfo.ExceptionPointers = (PEXCEPTION_POINTERS)pExceptionInfo; ExInfo.ClientPointers = FALSE;
bMinidumpResult = (*pfnMiniDumpWrite)( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)minidumpType, &ExInfo, NULL, NULL ); ::CloseHandle( hFile );
if ( bMinidumpResult ) { bReturnValue = true;
if ( ptchMinidumpFileNameBuffer ) { // Copy the file name from "pSrc = rgchFileName" into "pTgt = ptchMinidumpFileNameBuffer"
tchar *pTgt = ptchMinidumpFileNameBuffer; tchar const *pSrc = rgchFileName; while ( ( *( pTgt ++ ) = *( pSrc ++ ) ) != tchar( 0 ) ) continue; } }
// fall through to trying again
} // mark any failed minidump writes by renaming them
if ( !bMinidumpResult ) { tchar rgchFailedFileName[MAX_PATH]; _sntprintf( rgchFailedFileName, sizeof(rgchFailedFileName) / sizeof(tchar), "(failed)%s", rgchFileName ); rename( rgchFileName, rgchFailedFileName ); } }
::FreeLibrary( hDbgHelpDll );
// call the log flush function if one is registered to try to flush any logs
//CallFlushLogFunc();
return bReturnValue; }
void InternalWriteMiniDumpUsingExceptionInfo( unsigned int uStructuredExceptionCode, ExceptionInfo_t * pExceptionInfo ) { // First try to write it with all the indirectly referenced memory (ie: a large file).
// If that doesn't work, then write a smaller one.
uint32 iType = MINIDUMP_WithDataSegs | MINIDUMP_WithIndirectlyReferencedMemory; if ( !WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, iType ) ) { iType = MINIDUMP_WithDataSegs; WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, iType ); } }
// minidump function to use
static FnMiniDump g_pfnWriteMiniDump = InternalWriteMiniDumpUsingExceptionInfo;
//-----------------------------------------------------------------------------
// Purpose: Set a function to call which will write our minidump, overriding
// the default function
// Input : pfn - Pointer to minidump function to set
// Output : Previously set function
//-----------------------------------------------------------------------------
FnMiniDump SetMiniDumpFunction( FnMiniDump pfn ) { FnMiniDump pfnTemp = g_pfnWriteMiniDump; g_pfnWriteMiniDump = pfn; return pfnTemp; }
//-----------------------------------------------------------------------------
// Unhandled exceptions
//-----------------------------------------------------------------------------
static FnMiniDump g_UnhandledExceptionFunction; static LONG STDCALL ValveUnhandledExceptionFilter( _EXCEPTION_POINTERS* pExceptionInfo ) { uint uStructuredExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode; g_UnhandledExceptionFunction( uStructuredExceptionCode, (ExceptionInfo_t*)pExceptionInfo ); return EXCEPTION_CONTINUE_SEARCH; }
void MinidumpSetUnhandledExceptionFunction( FnMiniDump pfn ) { g_UnhandledExceptionFunction = pfn; SetUnhandledExceptionFilter( ValveUnhandledExceptionFilter ); }
//-----------------------------------------------------------------------------
// Purpose: writes out a minidump from the current process
//-----------------------------------------------------------------------------
typedef void (*FnMiniDumpInternal_t)( unsigned int uStructuredExceptionCode, _EXCEPTION_POINTERS * pExceptionInfo );
void WriteMiniDump() { // throw an exception so we can catch it and get the stack info
g_bWritingNonfatalMinidump = true; __try { ::RaiseException ( 0, // dwExceptionCode
EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
0, // nNumberOfArguments,
NULL // const ULONG_PTR* lpArguments
);
// Never get here (non-continuable exception)
} // Write the minidump from inside the filter (GetExceptionInformation() is only
// valid in the filter)
__except ( g_pfnWriteMiniDump( 0, (ExceptionInfo_t*)GetExceptionInformation() ), EXCEPTION_EXECUTE_HANDLER ) { } g_bWritingNonfatalMinidump = false; }
PLATFORM_OVERLOAD bool g_bInException = false; #include <eh.h>
//-----------------------------------------------------------------------------
// Purpose: Catches and writes out any exception throw by the specified function
//-----------------------------------------------------------------------------
void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] ) { if ( Plat_IsInDebugSession() ) { // don't mask exceptions when running in the debugger
pfn( argc, argv ); } else { try { #pragma warning(push)
#pragma warning(disable : 4535) // warning C4535: calling _set_se_translator() requires /EHa
_set_se_translator( (FnMiniDumpInternal_t)g_pfnWriteMiniDump ); #pragma warning(pop)
pfn( argc, argv ); } catch (...) { g_bInException = true; Log_Msg( LOG_CONSOLE, _T("Fatal exception caught, minidump written\n") ); // handle everything and just quit, we've already written out our minidump
} } }
#else
PLATFORM_INTERFACE void WriteMiniDump() { }
PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] ) { pfn( argc, argv ); }
#endif
#elif defined(_X360 )
PLATFORM_INTERFACE void WriteMiniDump() { #if !defined( _CERT )
DmCrashDump(false); #endif
}
#else // !_WIN32
PLATFORM_INTERFACE void WriteMiniDump() { }
PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] ) { pfn( argc, argv ); }
#endif
|