Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
9.5 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "pch_tier0.h"
  7. #include "tier0/minidump.h"
  8. #include "tier0/platform.h"
  9. #if defined( _WIN32 ) && !defined(_X360 ) && ( _MSC_VER >= 1300 )
  10. #include "tier0/valve_off.h"
  11. #define WIN_32_LEAN_AND_MEAN
  12. #define _WIN32_WINNT 0x0403
  13. #include <windows.h>
  14. #include <time.h>
  15. #include <dbghelp.h>
  16. #endif
  17. // NOTE: This has to be the last file included!
  18. #include "tier0/memdbgon.h"
  19. #if defined( _WIN32 ) && !defined( _X360 )
  20. #if _MSC_VER >= 1300
  21. // MiniDumpWriteDump() function declaration (so we can just get the function directly from windows)
  22. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)
  23. (
  24. HANDLE hProcess,
  25. DWORD dwPid,
  26. HANDLE hFile,
  27. MINIDUMP_TYPE DumpType,
  28. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  29. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  30. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  31. );
  32. // true if we're currently writing a minidump caused by an assert
  33. static bool g_bWritingNonfatalMinidump = false;
  34. // counter used to make sure minidump names are unique
  35. static int g_nMinidumpsWritten = 0;
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Creates a new file and dumps the exception info into it
  38. // Input : uStructuredExceptionCode - windows exception code, unused.
  39. // pExceptionInfo - call stack.
  40. // minidumpType - type of minidump to write.
  41. // ptchMinidumpFileNameBuffer - if not-NULL points to a writable tchar buffer
  42. // of length at least _MAX_PATH to contain the name
  43. // of the written minidump file on return.
  44. //-----------------------------------------------------------------------------
  45. bool WriteMiniDumpUsingExceptionInfo(
  46. unsigned int uStructuredExceptionCode,
  47. ExceptionInfo_t * pExceptionInfo,
  48. uint32 minidumpType,
  49. tchar *ptchMinidumpFileNameBuffer /* = NULL */
  50. )
  51. {
  52. if ( ptchMinidumpFileNameBuffer )
  53. {
  54. *ptchMinidumpFileNameBuffer = tchar( 0 );
  55. }
  56. // get the function pointer directly so that we don't have to include the .lib, and that
  57. // we can easily change it to using our own dll when this code is used on win98/ME/2K machines
  58. HMODULE hDbgHelpDll = ::LoadLibrary( "DbgHelp.dll" );
  59. if ( !hDbgHelpDll )
  60. return false;
  61. bool bReturnValue = false;
  62. MINIDUMPWRITEDUMP pfnMiniDumpWrite = (MINIDUMPWRITEDUMP) ::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
  63. if ( pfnMiniDumpWrite )
  64. {
  65. // create a unique filename for the minidump based on the current time and module name
  66. struct tm curtime;
  67. Plat_GetLocalTime( &curtime );
  68. ++g_nMinidumpsWritten;
  69. // strip off the rest of the path from the .exe name
  70. tchar rgchModuleName[MAX_PATH];
  71. #ifdef TCHAR_IS_WCHAR
  72. ::GetModuleFileNameW( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) );
  73. #else
  74. ::GetModuleFileName( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) );
  75. #endif
  76. tchar *pch = _tcsrchr( rgchModuleName, '.' );
  77. if ( pch )
  78. {
  79. *pch = 0;
  80. }
  81. pch = _tcsrchr( rgchModuleName, '\\' );
  82. if ( pch )
  83. {
  84. // move past the last slash
  85. pch++;
  86. }
  87. else
  88. {
  89. pch = _T("unknown");
  90. }
  91. // can't use the normal string functions since we're in tier0
  92. tchar rgchFileName[MAX_PATH];
  93. _sntprintf( rgchFileName, sizeof(rgchFileName) / sizeof(tchar),
  94. _T("%s_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp"),
  95. pch,
  96. g_bWritingNonfatalMinidump ? "assert" : "crash",
  97. curtime.tm_year + 1900, /* Year less 2000 */
  98. curtime.tm_mon + 1, /* month (0 - 11 : 0 = January) */
  99. curtime.tm_mday, /* day of month (1 - 31) */
  100. curtime.tm_hour, /* hour (0 - 23) */
  101. curtime.tm_min, /* minutes (0 - 59) */
  102. curtime.tm_sec, /* seconds (0 - 59) */
  103. g_nMinidumpsWritten // ensures the filename is unique
  104. );
  105. BOOL bMinidumpResult = FALSE;
  106. #ifdef TCHAR_IS_WCHAR
  107. HANDLE hFile = ::CreateFileW( rgchFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  108. #else
  109. HANDLE hFile = ::CreateFile( rgchFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  110. #endif
  111. if ( hFile )
  112. {
  113. // dump the exception information into the file
  114. _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
  115. ExInfo.ThreadId = ::GetCurrentThreadId();
  116. ExInfo.ExceptionPointers = (PEXCEPTION_POINTERS)pExceptionInfo;
  117. ExInfo.ClientPointers = FALSE;
  118. bMinidumpResult = (*pfnMiniDumpWrite)( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)minidumpType, &ExInfo, NULL, NULL );
  119. ::CloseHandle( hFile );
  120. if ( bMinidumpResult )
  121. {
  122. bReturnValue = true;
  123. if ( ptchMinidumpFileNameBuffer )
  124. {
  125. // Copy the file name from "pSrc = rgchFileName" into "pTgt = ptchMinidumpFileNameBuffer"
  126. tchar *pTgt = ptchMinidumpFileNameBuffer;
  127. tchar const *pSrc = rgchFileName;
  128. while ( ( *( pTgt ++ ) = *( pSrc ++ ) ) != tchar( 0 ) )
  129. continue;
  130. }
  131. }
  132. // fall through to trying again
  133. }
  134. // mark any failed minidump writes by renaming them
  135. if ( !bMinidumpResult )
  136. {
  137. tchar rgchFailedFileName[MAX_PATH];
  138. _sntprintf( rgchFailedFileName, sizeof(rgchFailedFileName) / sizeof(tchar), "(failed)%s", rgchFileName );
  139. rename( rgchFileName, rgchFailedFileName );
  140. }
  141. }
  142. ::FreeLibrary( hDbgHelpDll );
  143. // call the log flush function if one is registered to try to flush any logs
  144. //CallFlushLogFunc();
  145. return bReturnValue;
  146. }
  147. void InternalWriteMiniDumpUsingExceptionInfo( unsigned int uStructuredExceptionCode, ExceptionInfo_t * pExceptionInfo )
  148. {
  149. // First try to write it with all the indirectly referenced memory (ie: a large file).
  150. // If that doesn't work, then write a smaller one.
  151. uint32 iType = MINIDUMP_WithDataSegs | MINIDUMP_WithIndirectlyReferencedMemory;
  152. if ( !WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, iType ) )
  153. {
  154. iType = MINIDUMP_WithDataSegs;
  155. WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, iType );
  156. }
  157. }
  158. // minidump function to use
  159. static FnMiniDump g_pfnWriteMiniDump = InternalWriteMiniDumpUsingExceptionInfo;
  160. //-----------------------------------------------------------------------------
  161. // Purpose: Set a function to call which will write our minidump, overriding
  162. // the default function
  163. // Input : pfn - Pointer to minidump function to set
  164. // Output : Previously set function
  165. //-----------------------------------------------------------------------------
  166. FnMiniDump SetMiniDumpFunction( FnMiniDump pfn )
  167. {
  168. FnMiniDump pfnTemp = g_pfnWriteMiniDump;
  169. g_pfnWriteMiniDump = pfn;
  170. return pfnTemp;
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Unhandled exceptions
  174. //-----------------------------------------------------------------------------
  175. static FnMiniDump g_UnhandledExceptionFunction;
  176. static LONG STDCALL ValveUnhandledExceptionFilter( _EXCEPTION_POINTERS* pExceptionInfo )
  177. {
  178. uint uStructuredExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
  179. g_UnhandledExceptionFunction( uStructuredExceptionCode, (ExceptionInfo_t*)pExceptionInfo );
  180. return EXCEPTION_CONTINUE_SEARCH;
  181. }
  182. void MinidumpSetUnhandledExceptionFunction( FnMiniDump pfn )
  183. {
  184. g_UnhandledExceptionFunction = pfn;
  185. SetUnhandledExceptionFilter( ValveUnhandledExceptionFilter );
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: writes out a minidump from the current process
  189. //-----------------------------------------------------------------------------
  190. typedef void (*FnMiniDumpInternal_t)( unsigned int uStructuredExceptionCode, _EXCEPTION_POINTERS * pExceptionInfo );
  191. void WriteMiniDump()
  192. {
  193. // throw an exception so we can catch it and get the stack info
  194. g_bWritingNonfatalMinidump = true;
  195. __try
  196. {
  197. ::RaiseException
  198. (
  199. 0, // dwExceptionCode
  200. EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
  201. 0, // nNumberOfArguments,
  202. NULL // const ULONG_PTR* lpArguments
  203. );
  204. // Never get here (non-continuable exception)
  205. }
  206. // Write the minidump from inside the filter (GetExceptionInformation() is only
  207. // valid in the filter)
  208. __except ( g_pfnWriteMiniDump( 0, (ExceptionInfo_t*)GetExceptionInformation() ), EXCEPTION_EXECUTE_HANDLER )
  209. {
  210. }
  211. g_bWritingNonfatalMinidump = false;
  212. }
  213. PLATFORM_OVERLOAD bool g_bInException = false;
  214. #include <eh.h>
  215. //-----------------------------------------------------------------------------
  216. // Purpose: Catches and writes out any exception throw by the specified function
  217. //-----------------------------------------------------------------------------
  218. void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
  219. {
  220. if ( Plat_IsInDebugSession() )
  221. {
  222. // don't mask exceptions when running in the debugger
  223. pfn( argc, argv );
  224. }
  225. else
  226. {
  227. try
  228. {
  229. #pragma warning(push)
  230. #pragma warning(disable : 4535) // warning C4535: calling _set_se_translator() requires /EHa
  231. _set_se_translator( (FnMiniDumpInternal_t)g_pfnWriteMiniDump );
  232. #pragma warning(pop)
  233. pfn( argc, argv );
  234. }
  235. catch (...)
  236. {
  237. g_bInException = true;
  238. Log_Msg( LOG_CONSOLE, _T("Fatal exception caught, minidump written\n") );
  239. // handle everything and just quit, we've already written out our minidump
  240. }
  241. }
  242. }
  243. #else
  244. PLATFORM_INTERFACE void WriteMiniDump()
  245. {
  246. }
  247. PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
  248. {
  249. pfn( argc, argv );
  250. }
  251. #endif
  252. #elif defined(_X360 )
  253. PLATFORM_INTERFACE void WriteMiniDump()
  254. {
  255. #if !defined( _CERT )
  256. DmCrashDump(false);
  257. #endif
  258. }
  259. #else // !_WIN32
  260. PLATFORM_INTERFACE void WriteMiniDump()
  261. {
  262. }
  263. PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
  264. {
  265. pfn( argc, argv );
  266. }
  267. #endif