1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
|
//========= 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
|