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