mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
337 lines
12 KiB
C++
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 |