Torque3D/Engine/source/platformWin32/minidump/winMiniDump.cpp

337 lines
12 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
#include "platformWin32/platformWin32.h"
#include "platformWin32/minidump/winStackWalker.h"
#include "core/fileio.h"
#include "core/strings/stringFunctions.h"
#include "console/console.h"
#include "app/net/serverQuery.h"
#pragma pack(push,8)
#include <DbgHelp.h>
#include <time.h>
#pragma pack(pop)
#pragma comment(lib, "dbghelp.lib")
extern Win32PlatState winState;
//Forward declarations for the dialog functions
BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner);
LPWORD lpwAlign(LPWORD lpIn);
char gUserInput[4096];
//Console variables
extern StringTableEntry gMiniDumpDir;
extern StringTableEntry gMiniDumpUser;
extern StringTableEntry gMiniDumpExec;
extern StringTableEntry gMiniDumpParams;
extern StringTableEntry gMiniDumpExecDir;
char* dStrrstr(char* dst, const char* src, const char* findStr, char* replaceStr)
{
//see if str contains findStr, if not then return
const char* findpos = strstr(src, findStr);
if(!findpos)
{
strcpy(dst, src);
}
else
{
//copy the new string to the buffer
dst[0]='\0';
strncat(dst, src, findpos-src);
strcat(dst, replaceStr);
const char* cur = findpos + strlen(findStr);
strcat(dst, cur);
}
return dst;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// CreateMiniDump()
//-----------------------------------------------------------------------------------------------------------------------------------------
INT CreateMiniDump( LPEXCEPTION_POINTERS ExceptionInfo)
{
//Get any information we can from the user and store it in gUserInput
try
{
while(ShowCursor(TRUE) < 0);
DisplayMiniDumpDialog(winState.appInstance, winState.appWindow);
}
catch(...)
{
//dSprintf(gUserInput, 4096, "The user could not enter a description of what was occurring.\n\n\n");
}
//Build a Game, Date and Time stamped MiniDump folder
time_t theTime;
time(&theTime);
tm* pLocalTime = localtime(&theTime);
char crashFolder[2048];
dSprintf(crashFolder, 2048, "%s_%02d.%02d_%02d.%02d.%02d",
Platform::getExecutableName(),
pLocalTime->tm_mon+1, pLocalTime->tm_mday,
pLocalTime->tm_hour, pLocalTime->tm_min, pLocalTime->tm_sec);
//Builed the fully qualified MiniDump path
char crashPath[2048];
char fileName[2048];
if(gMiniDumpDir==NULL)
{
dSprintf(crashPath, 2048, "%s/MiniDump/%s", Platform::getCurrentDirectory(), crashFolder);
}
else
{
dSprintf(crashPath, 2048, "%s/%s", gMiniDumpDir, crashFolder);
}
dSprintf(fileName, 2048, "%s/Minidump.dmp",crashPath);
if (!Platform::createPath (fileName))return false; //create the directory
//Save the minidump
File fileObject;
if(fileObject.open(fileName, File::Write) == File::Ok)
{
MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo;
DumpExceptionInfo.ThreadId = GetCurrentThreadId();
DumpExceptionInfo.ExceptionPointers = ExceptionInfo;
DumpExceptionInfo.ClientPointers = true;
MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), (HANDLE)fileObject.getHandle(), MiniDumpNormal, &DumpExceptionInfo, NULL, NULL );
fileObject.close();
}
//copy over the log file
char fromFile[2048];
dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), "console.log" );
dSprintf(fileName, 2048, "%s/console.log", crashPath);
Con::setLogMode(3); //ensure that the log file is closed (so it can be copied)
dPathCopy(fromFile, fileName, true);
//copy over the exe file
char exeName[1024];
dSprintf(exeName, 1024, Platform::getExecutableName());
exeName[dStrlen(exeName)-4]=0;
dSprintf(fromFile, 2048, "%s/%s.dll", Platform::getCurrentDirectory(), exeName );
dSprintf(fileName, 2048, "%s/%s.dll", crashPath, exeName );
dPathCopy(fromFile, fileName, true);
//copy over the pdb file
char pdbName[1024];
dStrcpy(pdbName, exeName, 1024);
dStrncat(pdbName, ".pdb", 4);
dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), pdbName );
dSprintf(fileName, 2048, "%s/%s", crashPath, pdbName );
dPathCopy(fromFile, fileName, true);
//save the call stack
char traceBuffer[65536];
traceBuffer[0] = 0;
dGetStackTrace( traceBuffer, *static_cast<const CONTEXT *>(ExceptionInfo->ContextRecord) );
//save the user input and the call stack to a file
char crashlogFile[2048];
dSprintf(crashlogFile, 2048, "%s/crash.log", crashPath);
if(fileObject.open(crashlogFile, File::Write) == File::Ok)
{
fileObject.write(strlen(gUserInput), gUserInput);
fileObject.write(strlen(traceBuffer), traceBuffer);
fileObject.close();
}
//call the external program indicated in script
if(gMiniDumpExec!= NULL)
{
//replace special variables in gMiniDumpParams
if(gMiniDumpParams)
{
char updateParams[4096];
char finalParams[4096];
dStrrstr(finalParams, gMiniDumpParams, "%crashpath%", crashPath);
dStrrstr(updateParams, finalParams, "%crashfolder%", crashFolder);
dStrrstr(finalParams, updateParams, "%crashlog%", crashlogFile);
ShellExecuteA(NULL, "", gMiniDumpExec, finalParams, gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
}
else
{
ShellExecuteA(NULL, "", gMiniDumpExec, "", gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// MiniDumpDialogProc - Used By DisplayMiniDumpDialog
//-----------------------------------------------------------------------------------------------------------------------------------------
const S32 ID_TEXT=200;
const S32 ID_USERTEXT=300;
const S32 ID_DONE=400;
BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
char text[128]= "";
switch (message)
{
case WM_INITDIALOG :
SetDlgItemTextA ( hwndDlg, ID_USERTEXT, text );
return TRUE ;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_DONE:
if( !GetDlgItemTextA(hwndDlg, ID_USERTEXT, gUserInput, 4096) ) gUserInput[0]='\0';
strcat(gUserInput, "\n\n\n");
EndDialog(hwndDlg, wParam);
return TRUE;
default:
return TRUE;
}
}
return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// Helper function to DWORD align the Dialog Box components (Used in DisplayMiniDumpDialog()
//-----------------------------------------------------------------------------------------------------------------------------------------
LPWORD lpwAlign(LPWORD lpIn)
{
ULONG ul;
ul = (ULONG)lpIn;
ul ++;
ul >>=1;
ul <<=1;
return (LPWORD)ul;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// Create the Dialog Box to get input from the user
//-----------------------------------------------------------------------------------------------------------------------------------------
LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner)
{
HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
if (!hgbl) return -1;
//-----------------------------------------------------------------
// Define the dialog box
//-----------------------------------------------------------------
LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
lpdt->style = WS_POPUP | WS_BORDER | DS_MODALFRAME | WS_CAPTION;
lpdt->cdit = 3; // Number of controls
lpdt->x = 100;
lpdt->y = 100;
lpdt->cx = 300;
lpdt->cy = 90;
LPWORD lpw = (LPWORD)(lpdt + 1);
*lpw++ = 0; // No menu
*lpw++ = 0; // Predefined dialog box class (by default)
LPWSTR lpwsz = (LPWSTR)lpw;
S32 nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "MiniDump Crash Report", -1, lpwsz, 50);
lpw += nchar;
//-----------------------------------------------------------------
// Define a static text message
//-----------------------------------------------------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10;
lpdit->y = 10;
lpdit->cx = 290;
lpdit->cy = 10;
lpdit->id = ID_TEXT; // Text identifier
lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0082; // Static class
LPSTR msg = "The program has crashed. Please describe what was happening:";
for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*msg++;);
lpw = (LPWORD)lpwsz;
*lpw++ = 0; // No creation data
//-----------------------------------------------------------------
// Define a DONE button
//-----------------------------------------------------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 265;
lpdit->y = 75;
lpdit->cx = 25;
lpdit->cy = 12;
lpdit->id = ID_DONE; // OK button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;// | BS_DEFPUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class
lpwsz = (LPWSTR)lpw;
nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Done", -1, lpwsz, 50);
lpw += nchar;
*lpw++ = 0; // No creation data
//-----------------------------------------------------------------
// Define a text entry message
//-----------------------------------------------------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10;
lpdit->y = 22;
lpdit->cx = 280;
lpdit->cy = 50;
lpdit->id = ID_USERTEXT; // Text identifier
lpdit->style = ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0081; // Text edit class
*lpw++ = 0; // No creation data
GlobalUnlock(hgbl);
LRESULT ret = DialogBoxIndirect( hinst,
(LPDLGTEMPLATE)hgbl,
hwndOwner,
(DLGPROC)MiniDumpDialogProc);
GlobalFree(hgbl);
return ret;
}
#endif