winMiniDump.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
  23. #include "platformWin32/platformWin32.h"
  24. #include "platformWin32/minidump/winStackWalker.h"
  25. #include "core/fileio.h"
  26. #include "core/strings/stringFunctions.h"
  27. #include "console/console.h"
  28. #include "app/net/serverQuery.h"
  29. #pragma pack(push,8)
  30. #include <DbgHelp.h>
  31. #include <time.h>
  32. #pragma pack(pop)
  33. #pragma comment(lib, "dbghelp.lib")
  34. extern Win32PlatState winState;
  35. //Forward declarations for the dialog functions
  36. BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
  37. LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner);
  38. LPWORD lpwAlign(LPWORD lpIn);
  39. char gUserInput[4096];
  40. //Console variables
  41. extern StringTableEntry gMiniDumpDir;
  42. extern StringTableEntry gMiniDumpUser;
  43. extern StringTableEntry gMiniDumpExec;
  44. extern StringTableEntry gMiniDumpParams;
  45. extern StringTableEntry gMiniDumpExecDir;
  46. char* dStrrstr(char* dst, const char* src, const char* findStr, char* replaceStr)
  47. {
  48. //see if str contains findStr, if not then return
  49. const char* findpos = strstr(src, findStr);
  50. if(!findpos)
  51. {
  52. strcpy(dst, src);
  53. }
  54. else
  55. {
  56. //copy the new string to the buffer
  57. dst[0]='\0';
  58. strncat(dst, src, findpos-src);
  59. strcat(dst, replaceStr);
  60. const char* cur = findpos + strlen(findStr);
  61. strcat(dst, cur);
  62. }
  63. return dst;
  64. }
  65. //-----------------------------------------------------------------------------------------------------------------------------------------
  66. // CreateMiniDump()
  67. //-----------------------------------------------------------------------------------------------------------------------------------------
  68. INT CreateMiniDump( LPEXCEPTION_POINTERS ExceptionInfo)
  69. {
  70. //Get any information we can from the user and store it in gUserInput
  71. try
  72. {
  73. while(ShowCursor(TRUE) < 0);
  74. DisplayMiniDumpDialog(winState.appInstance, winState.appWindow);
  75. }
  76. catch(...)
  77. {
  78. //dSprintf(gUserInput, 4096, "The user could not enter a description of what was occurring.\n\n\n");
  79. }
  80. //Build a Game, Date and Time stamped MiniDump folder
  81. time_t theTime;
  82. time(&theTime);
  83. tm* pLocalTime = localtime(&theTime);
  84. char crashFolder[2048];
  85. dSprintf(crashFolder, 2048, "%s_%02d.%02d_%02d.%02d.%02d",
  86. Platform::getExecutableName(),
  87. pLocalTime->tm_mon+1, pLocalTime->tm_mday,
  88. pLocalTime->tm_hour, pLocalTime->tm_min, pLocalTime->tm_sec);
  89. //Builed the fully qualified MiniDump path
  90. char crashPath[2048];
  91. char fileName[2048];
  92. if(gMiniDumpDir==NULL)
  93. {
  94. dSprintf(crashPath, 2048, "%s/MiniDump/%s", Platform::getCurrentDirectory(), crashFolder);
  95. }
  96. else
  97. {
  98. dSprintf(crashPath, 2048, "%s/%s", gMiniDumpDir, crashFolder);
  99. }
  100. dSprintf(fileName, 2048, "%s/Minidump.dmp",crashPath);
  101. if (!Platform::createPath (fileName))return false; //create the directory
  102. //Save the minidump
  103. File fileObject;
  104. if(fileObject.open(fileName, File::Write) == File::Ok)
  105. {
  106. MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo;
  107. DumpExceptionInfo.ThreadId = GetCurrentThreadId();
  108. DumpExceptionInfo.ExceptionPointers = ExceptionInfo;
  109. DumpExceptionInfo.ClientPointers = true;
  110. MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), (HANDLE)fileObject.getHandle(), MiniDumpNormal, &DumpExceptionInfo, NULL, NULL );
  111. fileObject.close();
  112. }
  113. //copy over the log file
  114. char fromFile[2048];
  115. dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), "console.log" );
  116. dSprintf(fileName, 2048, "%s/console.log", crashPath);
  117. Con::setLogMode(3); //ensure that the log file is closed (so it can be copied)
  118. dPathCopy(fromFile, fileName, true);
  119. //copy over the exe file
  120. char exeName[1024];
  121. dSprintf(exeName, 1024, Platform::getExecutableName());
  122. exeName[dStrlen(exeName)-4]=0;
  123. dSprintf(fromFile, 2048, "%s/%s.dll", Platform::getCurrentDirectory(), exeName );
  124. dSprintf(fileName, 2048, "%s/%s.dll", crashPath, exeName );
  125. dPathCopy(fromFile, fileName, true);
  126. //copy over the pdb file
  127. char pdbName[1024];
  128. dStrcpy(pdbName, exeName, 1024);
  129. dStrncat(pdbName, ".pdb", 4);
  130. dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), pdbName );
  131. dSprintf(fileName, 2048, "%s/%s", crashPath, pdbName );
  132. dPathCopy(fromFile, fileName, true);
  133. //save the call stack
  134. char traceBuffer[65536];
  135. traceBuffer[0] = 0;
  136. dGetStackTrace( traceBuffer, *static_cast<const CONTEXT *>(ExceptionInfo->ContextRecord) );
  137. //save the user input and the call stack to a file
  138. char crashlogFile[2048];
  139. dSprintf(crashlogFile, 2048, "%s/crash.log", crashPath);
  140. if(fileObject.open(crashlogFile, File::Write) == File::Ok)
  141. {
  142. fileObject.write(strlen(gUserInput), gUserInput);
  143. fileObject.write(strlen(traceBuffer), traceBuffer);
  144. fileObject.close();
  145. }
  146. //call the external program indicated in script
  147. if(gMiniDumpExec!= NULL)
  148. {
  149. //replace special variables in gMiniDumpParams
  150. if(gMiniDumpParams)
  151. {
  152. char updateParams[4096];
  153. char finalParams[4096];
  154. dStrrstr(finalParams, gMiniDumpParams, "%crashpath%", crashPath);
  155. dStrrstr(updateParams, finalParams, "%crashfolder%", crashFolder);
  156. dStrrstr(finalParams, updateParams, "%crashlog%", crashlogFile);
  157. ShellExecuteA(NULL, "", gMiniDumpExec, finalParams, gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
  158. }
  159. else
  160. {
  161. ShellExecuteA(NULL, "", gMiniDumpExec, "", gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
  162. }
  163. }
  164. return EXCEPTION_EXECUTE_HANDLER;
  165. }
  166. //-----------------------------------------------------------------------------------------------------------------------------------------
  167. // MiniDumpDialogProc - Used By DisplayMiniDumpDialog
  168. //-----------------------------------------------------------------------------------------------------------------------------------------
  169. const S32 ID_TEXT=200;
  170. const S32 ID_USERTEXT=300;
  171. const S32 ID_DONE=400;
  172. BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
  173. {
  174. char text[128]= "";
  175. switch (message)
  176. {
  177. case WM_INITDIALOG :
  178. SetDlgItemTextA ( hwndDlg, ID_USERTEXT, text );
  179. return TRUE ;
  180. case WM_COMMAND:
  181. switch (LOWORD(wParam))
  182. {
  183. case ID_DONE:
  184. if( !GetDlgItemTextA(hwndDlg, ID_USERTEXT, gUserInput, 4096) ) gUserInput[0]='\0';
  185. strcat(gUserInput, "\n\n\n");
  186. EndDialog(hwndDlg, wParam);
  187. return TRUE;
  188. default:
  189. return TRUE;
  190. }
  191. }
  192. return FALSE;
  193. }
  194. //-----------------------------------------------------------------------------------------------------------------------------------------
  195. // Helper function to DWORD align the Dialog Box components (Used in DisplayMiniDumpDialog()
  196. //-----------------------------------------------------------------------------------------------------------------------------------------
  197. LPWORD lpwAlign(LPWORD lpIn)
  198. {
  199. ULONG ul;
  200. ul = (ULONG)lpIn;
  201. ul ++;
  202. ul >>=1;
  203. ul <<=1;
  204. return (LPWORD)ul;
  205. }
  206. //-----------------------------------------------------------------------------------------------------------------------------------------
  207. // Create the Dialog Box to get input from the user
  208. //-----------------------------------------------------------------------------------------------------------------------------------------
  209. LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner)
  210. {
  211. HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
  212. if (!hgbl) return -1;
  213. //-----------------------------------------------------------------
  214. // Define the dialog box
  215. //-----------------------------------------------------------------
  216. LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
  217. lpdt->style = WS_POPUP | WS_BORDER | DS_MODALFRAME | WS_CAPTION;
  218. lpdt->cdit = 3; // Number of controls
  219. lpdt->x = 100;
  220. lpdt->y = 100;
  221. lpdt->cx = 300;
  222. lpdt->cy = 90;
  223. LPWORD lpw = (LPWORD)(lpdt + 1);
  224. *lpw++ = 0; // No menu
  225. *lpw++ = 0; // Predefined dialog box class (by default)
  226. LPWSTR lpwsz = (LPWSTR)lpw;
  227. S32 nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "MiniDump Crash Report", -1, lpwsz, 50);
  228. lpw += nchar;
  229. //-----------------------------------------------------------------
  230. // Define a static text message
  231. //-----------------------------------------------------------------
  232. lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
  233. LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE)lpw;
  234. lpdit->x = 10;
  235. lpdit->y = 10;
  236. lpdit->cx = 290;
  237. lpdit->cy = 10;
  238. lpdit->id = ID_TEXT; // Text identifier
  239. lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
  240. lpw = (LPWORD)(lpdit + 1);
  241. *lpw++ = 0xFFFF;
  242. *lpw++ = 0x0082; // Static class
  243. LPSTR msg = "The program has crashed. Please describe what was happening:";
  244. for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*msg++;);
  245. lpw = (LPWORD)lpwsz;
  246. *lpw++ = 0; // No creation data
  247. //-----------------------------------------------------------------
  248. // Define a DONE button
  249. //-----------------------------------------------------------------
  250. lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
  251. lpdit = (LPDLGITEMTEMPLATE)lpw;
  252. lpdit->x = 265;
  253. lpdit->y = 75;
  254. lpdit->cx = 25;
  255. lpdit->cy = 12;
  256. lpdit->id = ID_DONE; // OK button identifier
  257. lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;// | BS_DEFPUSHBUTTON;
  258. lpw = (LPWORD)(lpdit + 1);
  259. *lpw++ = 0xFFFF;
  260. *lpw++ = 0x0080; // Button class
  261. lpwsz = (LPWSTR)lpw;
  262. nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Done", -1, lpwsz, 50);
  263. lpw += nchar;
  264. *lpw++ = 0; // No creation data
  265. //-----------------------------------------------------------------
  266. // Define a text entry message
  267. //-----------------------------------------------------------------
  268. lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
  269. lpdit = (LPDLGITEMTEMPLATE)lpw;
  270. lpdit->x = 10;
  271. lpdit->y = 22;
  272. lpdit->cx = 280;
  273. lpdit->cy = 50;
  274. lpdit->id = ID_USERTEXT; // Text identifier
  275. lpdit->style = ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
  276. lpw = (LPWORD)(lpdit + 1);
  277. *lpw++ = 0xFFFF;
  278. *lpw++ = 0x0081; // Text edit class
  279. *lpw++ = 0; // No creation data
  280. GlobalUnlock(hgbl);
  281. LRESULT ret = DialogBoxIndirect( hinst,
  282. (LPDLGTEMPLATE)hgbl,
  283. hwndOwner,
  284. (DLGPROC)MiniDumpDialogProc);
  285. GlobalFree(hgbl);
  286. return ret;
  287. }
  288. #endif