224 lines
7.5 KiB
C++
224 lines
7.5 KiB
C++
/*
|
|
* Original work: Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
|
|
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
|
|
*
|
|
*
|
|
* Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt)
|
|
*
|
|
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
|
|
* license found in the license.txt file in the root directory of this source tree.
|
|
*/
|
|
|
|
// To compile link with Dbghelp.lib
|
|
// The callstack in release is the same as usual, which means it isn't all that accurate.
|
|
#ifdef WIN32
|
|
|
|
#include <stdio.h>
|
|
#include "slikenet/WindowsIncludes.h"
|
|
#pragma warning(push)
|
|
// disable warning 4091 (triggers for enum typedefs in DbgHelp.h in Windows SDK 7.1 and Windows SDK 8.1)
|
|
#pragma warning(disable:4091)
|
|
#include <DbgHelp.h>
|
|
#pragma warning(pop)
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include "SendFileTo.h"
|
|
#include "CrashReporter.h"
|
|
#include "slikenet/EmailSender.h"
|
|
#include "slikenet/FileList.h"
|
|
#include "slikenet/FileOperations.h"
|
|
#include "slikenet/SimpleMutex.h"
|
|
#include "slikenet/linux_adapter.h"
|
|
#include "slikenet/osx_adapter.h"
|
|
|
|
using namespace SLNet;
|
|
|
|
CrashReportControls CrashReporter::controls;
|
|
|
|
// More info at:
|
|
// http://www.codeproject.com/debug/postmortemdebug_standalone1.asp
|
|
// http://www.codeproject.com/debug/XCrashReportPt3.asp
|
|
// http://www.codeproject.com/debug/XCrashReportPt1.asp
|
|
// http://www.microsoft.com/msj/0898/bugslayer0898.aspx
|
|
|
|
LONG ProcessException(struct _EXCEPTION_POINTERS *ExceptionInfo)
|
|
{
|
|
char appDescriptor[_MAX_PATH];
|
|
if ((CrashReporter::controls.actionToTake & AOC_SILENT_MODE) == 0)
|
|
{
|
|
sprintf_s(appDescriptor, "%s has crashed.\nGenerate a report?", CrashReporter::controls.appName);
|
|
if (::MessageBoxA(nullptr, appDescriptor, "Crash Reporter", MB_YESNO )==IDNO)
|
|
{
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
|
|
char dumpFilepath[_MAX_PATH];
|
|
char dumpFilename[_MAX_PATH];
|
|
sprintf_s(appDescriptor, "%s %s - %s %s", CrashReporter::controls.appName, CrashReporter::controls.appVersion, __DATE__, __TIME__);
|
|
|
|
if ((CrashReporter::controls.actionToTake & AOC_EMAIL_WITH_ATTACHMENT) ||
|
|
(CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
|
|
)
|
|
{
|
|
if (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
|
|
{
|
|
strcpy_s(dumpFilepath, CrashReporter::controls.pathToMinidump);
|
|
WriteFileWithDirectories(dumpFilepath,0,0);
|
|
AddSlash(dumpFilepath);
|
|
}
|
|
else
|
|
{
|
|
// Write to a temporary directory if the user doesn't want the dump on the harddrive.
|
|
if (!GetTempPathA( _MAX_PATH, dumpFilepath ))
|
|
dumpFilepath[0]=0;
|
|
}
|
|
unsigned i, dumpFilenameLen;
|
|
strcpy_s(dumpFilename, appDescriptor);
|
|
dumpFilenameLen=(unsigned) strlen(appDescriptor);
|
|
for (i=0; i < dumpFilenameLen; i++)
|
|
if (dumpFilename[i]==':' || dumpFilename[i]=='/' || dumpFilename[i]=='\\')
|
|
dumpFilename[i]='.'; // Remove illegal characters from filename
|
|
strcat_s(dumpFilepath, dumpFilename);
|
|
strcat_s(dumpFilepath, ".dmp");
|
|
|
|
HANDLE hFile = CreateFileA(dumpFilepath,GENERIC_WRITE, FILE_SHARE_READ, nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
if (hFile==INVALID_HANDLE_VALUE)
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION eInfo;
|
|
eInfo.ThreadId = GetCurrentThreadId();
|
|
eInfo.ExceptionPointers = ExceptionInfo;
|
|
eInfo.ClientPointers = FALSE;
|
|
|
|
if (MiniDumpWriteDump(
|
|
GetCurrentProcess(),
|
|
GetCurrentProcessId(),
|
|
hFile,
|
|
(MINIDUMP_TYPE)CrashReporter::controls.minidumpType,
|
|
ExceptionInfo ? &eInfo : nullptr,
|
|
nullptr,
|
|
nullptr)==false)
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
char silentModeEmailBody[1024];
|
|
char subject[1204];
|
|
if (CrashReporter::controls.actionToTake & AOC_EMAIL_NO_ATTACHMENT)
|
|
{
|
|
strcpy_s(subject, CrashReporter::controls.emailSubjectPrefix);
|
|
strcat_s(subject, appDescriptor);
|
|
|
|
if (CrashReporter::controls.actionToTake & AOC_SILENT_MODE)
|
|
{
|
|
sprintf_s(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__);
|
|
|
|
if (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
|
|
sprintf_s(silentModeEmailBody+strlen(silentModeEmailBody), 1024-strlen(silentModeEmailBody), "Minidump written to %s \r\n", dumpFilepath);
|
|
|
|
// Silently send email with attachment
|
|
EmailSender emailSender;
|
|
emailSender.Send(CrashReporter::controls.SMTPServer,
|
|
25,
|
|
CrashReporter::controls.SMTPAccountName,
|
|
CrashReporter::controls.emailRecipient,
|
|
CrashReporter::controls.emailSender,
|
|
CrashReporter::controls.emailRecipient,
|
|
subject,
|
|
silentModeEmailBody,
|
|
0,
|
|
false,
|
|
CrashReporter::controls.emailPassword);
|
|
}
|
|
else
|
|
{
|
|
CSendFileTo sendFile;
|
|
sendFile.SendMail(0, 0, 0, subject, CrashReporter::controls.emailBody, CrashReporter::controls.emailRecipient);
|
|
}
|
|
}
|
|
else if (CrashReporter::controls.actionToTake & AOC_EMAIL_WITH_ATTACHMENT)
|
|
{
|
|
strcpy_s(subject, CrashReporter::controls.emailSubjectPrefix);
|
|
strcat_s(subject, dumpFilename);
|
|
strcat_s(dumpFilename, ".dmp");
|
|
|
|
if (CrashReporter::controls.actionToTake & AOC_SILENT_MODE)
|
|
{
|
|
sprintf_s(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__);
|
|
|
|
if (CrashReporter::controls.actionToTake & AOC_WRITE_TO_DISK)
|
|
sprintf_s(silentModeEmailBody+strlen(silentModeEmailBody), 1024-strlen(silentModeEmailBody), "Minidump written to %s \r\n", dumpFilepath);
|
|
|
|
// Silently send email with attachment
|
|
EmailSender emailSender;
|
|
FileList files;
|
|
files.AddFile(dumpFilepath,dumpFilename,FileListNodeContext(0,0,0,0));
|
|
emailSender.Send(CrashReporter::controls.SMTPServer,
|
|
25,
|
|
CrashReporter::controls.SMTPAccountName,
|
|
CrashReporter::controls.emailRecipient,
|
|
CrashReporter::controls.emailSender,
|
|
CrashReporter::controls.emailRecipient,
|
|
subject,
|
|
silentModeEmailBody,
|
|
&files,
|
|
false,
|
|
CrashReporter::controls.emailPassword);
|
|
}
|
|
else
|
|
{
|
|
CSendFileTo sendFile;
|
|
sendFile.SendMail(0, dumpFilepath, dumpFilename, subject, CrashReporter::controls.emailBody, CrashReporter::controls.emailRecipient);
|
|
}
|
|
}
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
LONG WINAPI CrashExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
|
|
{
|
|
// Mutex here due to http://www.jenkinssoftware.com/raknet/forum/index.php?topic=2305.0;topicseen
|
|
static SimpleMutex crashExceptionFilterMutex;
|
|
crashExceptionFilterMutex.Lock();
|
|
LONG retVal = ProcessException(ExceptionInfo);
|
|
crashExceptionFilterMutex.Unlock();
|
|
return retVal;
|
|
}
|
|
|
|
void DumpMiniDump(PEXCEPTION_POINTERS excpInfo)
|
|
{
|
|
if (excpInfo == nullptr)
|
|
{
|
|
// Generate exception to get proper context in dump
|
|
__try
|
|
{
|
|
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, nullptr);
|
|
}
|
|
__except(DumpMiniDump(GetExceptionInformation()),EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProcessException(excpInfo);
|
|
}
|
|
}
|
|
|
|
// #define _DEBUG_CRASH_REPORTER
|
|
|
|
void CrashReporter::Start(CrashReportControls *input)
|
|
{
|
|
memcpy(&controls, input, sizeof(CrashReportControls));
|
|
|
|
#ifndef _DEBUG_CRASH_REPORTER
|
|
SetUnhandledExceptionFilter(CrashExceptionFilter);
|
|
#endif
|
|
}
|
|
#endif //WIN32
|