CrashReporter.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. // To compile link with Dbghelp.lib
  11. // The callstack in release is the same as usual, which means it isn't all that accurate.
  12. #ifdef WIN32
  13. #include <stdio.h>
  14. #include "WindowsIncludes.h"
  15. #include <DbgHelp.h>
  16. #include <stdlib.h>
  17. #include <time.h>
  18. #include "SendFileTo.h"
  19. #include "CrashReporter.h"
  20. #include "EmailSender.h"
  21. #include "FileList.h"
  22. #include "FileOperations.h"
  23. #include "SimpleMutex.h"
  24. using namespace RakNet;
  25. CrashReportControls CrashReporter::controls;
  26. // More info at:
  27. // http://www.codeproject.com/debug/postmortemdebug_standalone1.asp
  28. // http://www.codeproject.com/debug/XCrashReportPt3.asp
  29. // http://www.codeproject.com/debug/XCrashReportPt1.asp
  30. // http://www.microsoft.com/msj/0898/bugslayer0898.aspx
  31. LONG ProcessException(struct _EXCEPTION_POINTERS *ExceptionInfo)
  32. {
  33. char appDescriptor[_MAX_PATH];
  34. if ((CrashReporter::controls.actionToTake & AOC_SILENT_MODE) == 0)
  35. {
  36. sprintf(appDescriptor, "%s has crashed.\nGenerate a report?", CrashReporter::controls.appName);
  37. if (::MessageBox( NULL, appDescriptor, "Crash Reporter", MB_YESNO )==IDNO)
  38. {
  39. return EXCEPTION_CONTINUE_SEARCH;
  40. }
  41. }
  42. char dumpFilepath[_MAX_PATH];
  43. char dumpFilename[_MAX_PATH];
  44. sprintf(appDescriptor, "%s %s - %s %s", CrashReporter::controls.appName, CrashReporter::controls.appVersion, __DATE__, __TIME__);
  45. if ((CrashReporter::controls.actionToTake & AOC_EMAIL_WITH_ATTACHMENT) ||
  46. (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
  47. )
  48. {
  49. if (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
  50. {
  51. strcpy(dumpFilepath, CrashReporter::controls.pathToMinidump);
  52. WriteFileWithDirectories(dumpFilepath,0,0);
  53. AddSlash(dumpFilepath);
  54. }
  55. else
  56. {
  57. // Write to a temporary directory if the user doesn't want the dump on the harddrive.
  58. if (!GetTempPath( _MAX_PATH, dumpFilepath ))
  59. dumpFilepath[0]=0;
  60. }
  61. unsigned i, dumpFilenameLen;
  62. strcpy(dumpFilename, appDescriptor);
  63. dumpFilenameLen=(unsigned) strlen(appDescriptor);
  64. for (i=0; i < dumpFilenameLen; i++)
  65. if (dumpFilename[i]==':' || dumpFilename[i]=='/' || dumpFilename[i]=='\\')
  66. dumpFilename[i]='.'; // Remove illegal characters from filename
  67. strcat(dumpFilepath, dumpFilename);
  68. strcat(dumpFilepath, ".dmp");
  69. HANDLE hFile = CreateFile(dumpFilepath,GENERIC_WRITE, FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  70. if (hFile==INVALID_HANDLE_VALUE)
  71. return EXCEPTION_CONTINUE_SEARCH;
  72. MINIDUMP_EXCEPTION_INFORMATION eInfo;
  73. eInfo.ThreadId = GetCurrentThreadId();
  74. eInfo.ExceptionPointers = ExceptionInfo;
  75. eInfo.ClientPointers = FALSE;
  76. if (MiniDumpWriteDump(
  77. GetCurrentProcess(),
  78. GetCurrentProcessId(),
  79. hFile,
  80. (MINIDUMP_TYPE)CrashReporter::controls.minidumpType,
  81. ExceptionInfo ? &eInfo : NULL,
  82. NULL,
  83. NULL)==false)
  84. return EXCEPTION_CONTINUE_SEARCH;
  85. CloseHandle(hFile);
  86. }
  87. char silentModeEmailBody[1024];
  88. char subject[1204];
  89. if (CrashReporter::controls.actionToTake & AOC_EMAIL_NO_ATTACHMENT)
  90. {
  91. strcpy(subject, CrashReporter::controls.emailSubjectPrefix);
  92. strcat(subject, appDescriptor);
  93. if (CrashReporter::controls.actionToTake & AOC_SILENT_MODE)
  94. {
  95. sprintf(silentModeEmailBody, "%s%s version %s has crashed.\r\nIt was compiled on %s %s.\r\n", CrashReporter::controls.emailBody, CrashReporter::controls.appName,CrashReporter::controls.appVersion, __DATE__, __TIME__);
  96. if (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
  97. sprintf(silentModeEmailBody+strlen(silentModeEmailBody), "Minidump written to %s \r\n", dumpFilepath);
  98. // Silently send email with attachment
  99. EmailSender emailSender;
  100. emailSender.Send(CrashReporter::controls.SMTPServer,
  101. 25,
  102. CrashReporter::controls.SMTPAccountName,
  103. CrashReporter::controls.emailRecipient,
  104. CrashReporter::controls.emailSender,
  105. CrashReporter::controls.emailRecipient,
  106. subject,
  107. silentModeEmailBody,
  108. 0,
  109. false,
  110. CrashReporter::controls.emailPassword);
  111. }
  112. else
  113. {
  114. CSendFileTo sendFile;
  115. sendFile.SendMail(0, 0, 0, subject, CrashReporter::controls.emailBody, CrashReporter::controls.emailRecipient);
  116. }
  117. }
  118. else if (CrashReporter::controls.actionToTake & AOC_EMAIL_WITH_ATTACHMENT)
  119. {
  120. strcpy(subject, CrashReporter::controls.emailSubjectPrefix);
  121. strcat(subject, dumpFilename);
  122. strcat(dumpFilename, ".dmp");
  123. if (CrashReporter::controls.actionToTake & AOC_SILENT_MODE)
  124. {
  125. sprintf(silentModeEmailBody, "%s%s version %s has crashed.\r\nIt was compiled on %s %s.\r\n", CrashReporter::controls.emailBody, CrashReporter::controls.appName,CrashReporter::controls.appVersion, __DATE__, __TIME__);
  126. if (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
  127. sprintf(silentModeEmailBody+strlen(silentModeEmailBody), "Minidump written to %s \r\n", dumpFilepath);
  128. // Silently send email with attachment
  129. EmailSender emailSender;
  130. FileList files;
  131. files.AddFile(dumpFilepath,dumpFilename,FileListNodeContext(0,0,0,0));
  132. emailSender.Send(CrashReporter::controls.SMTPServer,
  133. 25,
  134. CrashReporter::controls.SMTPAccountName,
  135. CrashReporter::controls.emailRecipient,
  136. CrashReporter::controls.emailSender,
  137. CrashReporter::controls.emailRecipient,
  138. subject,
  139. silentModeEmailBody,
  140. &files,
  141. false,
  142. CrashReporter::controls.emailPassword);
  143. }
  144. else
  145. {
  146. CSendFileTo sendFile;
  147. sendFile.SendMail(0, dumpFilepath, dumpFilename, subject, CrashReporter::controls.emailBody, CrashReporter::controls.emailRecipient);
  148. }
  149. }
  150. return EXCEPTION_EXECUTE_HANDLER;
  151. }
  152. LONG WINAPI CrashExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
  153. {
  154. // Mutex here due to http://www.jenkinssoftware.com/raknet/forum/index.php?topic=2305.0;topicseen
  155. static SimpleMutex crashExceptionFilterMutex;
  156. crashExceptionFilterMutex.Lock();
  157. LONG retVal = ProcessException(ExceptionInfo);
  158. crashExceptionFilterMutex.Unlock();
  159. return retVal;
  160. }
  161. void DumpMiniDump(PEXCEPTION_POINTERS excpInfo)
  162. {
  163. if (excpInfo == NULL)
  164. {
  165. // Generate exception to get proper context in dump
  166. __try
  167. {
  168. RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
  169. }
  170. __except(DumpMiniDump(GetExceptionInformation()),EXCEPTION_EXECUTE_HANDLER)
  171. {
  172. }
  173. }
  174. else
  175. {
  176. ProcessException(excpInfo);
  177. }
  178. }
  179. // #define _DEBUG_CRASH_REPORTER
  180. void CrashReporter::Start(CrashReportControls *input)
  181. {
  182. memcpy(&controls, input, sizeof(CrashReportControls));
  183. #ifndef _DEBUG_CRASH_REPORTER
  184. SetUnhandledExceptionFilter(CrashExceptionFilter);
  185. #endif
  186. }
  187. #endif //WIN32
粤ICP备19079148号