|
|
|
|
//========= Copyright <EFBFBD> 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
|