feat(Core/Debugging): improve crash reports (#2365)

* Enabled crash reports for Windows
This commit is contained in:
Kargatum
2019-11-10 22:42:47 +07:00
committed by GitHub
parent 5f21913378
commit 0fd565d557
8 changed files with 783 additions and 355 deletions

View File

@@ -1,4 +1,5 @@
# Copyright (C)
#
# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL3 v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without

View File

@@ -1,37 +1,82 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL3 v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#include "Errors.h"
#include <thread>
#include "StringFormat.h"
#include <cstdio>
#include <cstdlib>
#include <thread>
#include <cstdarg>
namespace Trinity {
/**
@file Errors.cpp
void Assert(char const* file, int line, char const* function, char const* message)
{
fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n",
file, line, function, message);
*((volatile int*)NULL) = 0;
@brief This file contains definitions of functions used for reporting critical application errors
It is very important that (std::)abort is NEVER called in place of *((volatile int*)NULL) = 0;
Calling abort() on Windows does not invoke unhandled exception filters - a mechanism used by WheatyExceptionReport
to log crashes. exit(1) calls here are for static analysis tools to indicate that calling functions defined in this file
terminates the application.
*/
#if PLATFORM == PLATFORM_WINDOWS
#include <Windows.h>
#define Crash(message) \
ULONG_PTR execeptionArgs[] = { reinterpret_cast<ULONG_PTR>(strdup(message)), reinterpret_cast<ULONG_PTR>(_ReturnAddress()) }; \
RaiseException(EXCEPTION_ASSERTION_FAILURE, 0, 2, execeptionArgs);
#else
// should be easily accessible in gdb
extern "C" { char const* TrinityAssertionFailedMessage = nullptr; }
#define Crash(message) \
TrinityAssertionFailedMessage = strdup(message); \
*((volatile int*)nullptr) = 0; \
exit(1);
#endif
namespace
{
std::string FormatAssertionMessage(char const* format, va_list args)
{
std::string formatted;
va_list len;
va_copy(len, args);
int32 length = vsnprintf(nullptr, 0, format, len);
va_end(len);
formatted.resize(length);
vsnprintf(&formatted[0], length + 1, format, args);
return formatted;
}
}
void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...)
namespace Trinity
{
void Assert(char const* file, int line, char const* function, std::string const& debugInfo, char const* message)
{
std::string formattedMessage = ACORE::StringFormat("\n%s:%i in %s ASSERTION FAILED:\n %s\n", file, line, function, message) + debugInfo + '\n';
fprintf(stderr, "%s", formattedMessage.c_str());
fflush(stderr);
Crash(formattedMessage.c_str());
}
void Assert(char const* file, int line, char const* function, std::string const& debugInfo, char const* message, char const* format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s ", file, line, function, message);
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
std::string formattedMessage = ACORE::StringFormat("\n%s:%i in %s ASSERTION FAILED:\n %s\n", file, line, function, message) + FormatAssertionMessage(format, args) + '\n' + debugInfo + '\n';
va_end(args);
fprintf(stderr, "%s", formattedMessage.c_str());
fflush(stderr);
va_end(args);
*((volatile int*)NULL) = 0;
exit(1);
Crash(formattedMessage.c_str());
}
void Fatal(char const* file, int line, char const* function, char const* message, ...)
@@ -39,22 +84,22 @@ void Fatal(char const* file, int line, char const* function, char const* message
va_list args;
va_start(args, message);
fprintf(stderr, "\n%s:%i in %s FATAL ERROR:\n ", file, line, function);
vfprintf(stderr, message, args);
fprintf(stderr, "\n");
std::string formattedMessage = ACORE::StringFormat("\n%s:%i in %s FATAL ERROR:\n", file, line, function) + FormatAssertionMessage(message, args) + '\n';
va_end(args);
fprintf(stderr, "%s", formattedMessage.c_str());
fflush(stderr);
std::this_thread::sleep_for(std::chrono::seconds(10));
*((volatile int*)NULL) = 0;
exit(1);
Crash(formattedMessage.c_str());
}
void Error(char const* file, int line, char const* function, char const* message)
{
fprintf(stderr, "\n%s:%i in %s ERROR:\n %s\n",
file, line, function, message);
*((volatile int*)NULL) = 0;
exit(1);
std::string formattedMessage = ACORE::StringFormat("\n%s:%i in %s ERROR:\n %s\n", file, line, function, message);
fprintf(stderr, "%s", formattedMessage.c_str());
fflush(stderr);
Crash(formattedMessage.c_str());
}
void Warning(char const* file, int line, char const* function, char const* message)
@@ -65,18 +110,24 @@ void Warning(char const* file, int line, char const* function, char const* messa
void Abort(char const* file, int line, char const* function)
{
fprintf(stderr, "\n%s:%i in %s ABORTED.\n",
file, line, function);
*((volatile int*)NULL) = 0;
exit(1);
std::string formattedMessage = ACORE::StringFormat("\n%s:%i in %s ABORTED.\n", file, line, function);
fprintf(stderr, "%s", formattedMessage.c_str());
fflush(stderr);
Crash(formattedMessage.c_str());
}
void AbortHandler(int /*sigval*/)
void AbortHandler(int sigval)
{
// nothing useful to log here, no way to pass args
*((volatile int*)NULL) = 0;
exit(1);
std::string formattedMessage = ACORE::StringFormat("Caught signal %i\n", sigval);
fprintf(stderr, "%s", formattedMessage.c_str());
fflush(stderr);
Crash(formattedMessage.c_str());
}
} // namespace Trinity
std::string GetDebugInfo()
{
return "";
}

View File

@@ -1,19 +1,19 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL3 v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#ifndef AZEROTHCORE_ERRORS_H
#define AZEROTHCORE_ERRORS_H
#ifndef _ACORE_ERRORS_H_
#define _ACORE_ERRORS_H_
#include "Define.h"
#include <string>
namespace Trinity
{
DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...) ATTR_NORETURN ATTR_PRINTF(5, 6);
DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, std::string const& debugInfo, char const* message) ATTR_NORETURN;
DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, std::string const& debugInfo, char const* message, char const* format, ...) ATTR_NORETURN ATTR_PRINTF(6, 7);
DECLSPEC_NORETURN void Fatal(char const* file, int line, char const* function, char const* message, ...) ATTR_NORETURN ATTR_PRINTF(4, 5);
@@ -27,6 +27,8 @@ namespace Trinity
} // namespace Trinity
std::string GetDebugInfo();
#if COMPILER == COMPILER_MICROSOFT
#define ASSERT_BEGIN __pragma(warning(push)) __pragma(warning(disable: 4127))
#define ASSERT_END __pragma(warning(pop))
@@ -35,13 +37,25 @@ namespace Trinity
#define ASSERT_END
#endif
#define WPAssert(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Assert(__FILE__, __LINE__, __FUNCTION__, #cond, ##__VA_ARGS__); } while(0) ASSERT_END
#if PLATFORM == PLATFORM_WINDOWS
#define EXCEPTION_ASSERTION_FAILURE 0xC0000420L
#endif
#define WPAssert(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Assert(__FILE__, __LINE__, __FUNCTION__, GetDebugInfo(), #cond, ##__VA_ARGS__); } while(0) ASSERT_END
#define WPAssert_NODEBUGINFO(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Assert(__FILE__, __LINE__, __FUNCTION__, "", #cond, ##__VA_ARGS__); } while(0) ASSERT_END
#define WPFatal(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Fatal(__FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); } while(0) ASSERT_END
#define WPError(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Error(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
#define WPWarning(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Warning(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
#define WPAbort() ASSERT_BEGIN do { Trinity::Abort(__FILE__, __LINE__, __FUNCTION__); } while(0) ASSERT_END
#ifdef PERFORMANCE_PROFILING
#define ASSERT(cond, ...) ((void)0)
#define ASSERT_NODEBUGINFO(cond, ...) ((void)0)
#else
#define ASSERT WPAssert
#define ASSERT_NODEBUGINFO WPAssert_NODEBUGINFO
#endif
#define ABORT WPAbort
template <typename T>
@@ -53,4 +67,4 @@ inline T* ASSERT_NOTNULL_IMPL(T* pointer, char const* expr)
#define ASSERT_NOTNULL(pointer) ASSERT_NOTNULL_IMPL(pointer, #pointer)
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,14 +3,19 @@
#if PLATFORM == PLATFORM_WINDOWS && !defined(__MINGW32__)
#include <winnt.h>
#include <winternl.h>
#include <dbghelp.h>
#include <set>
#if _MSC_VER < 1400
# define countof(array) (sizeof(array) / sizeof(array[0]))
#else
# include <stdlib.h>
# define countof _countof
#endif // _MSC_VER < 1400
#include <stdlib.h>
#include <stack>
#include <mutex>
#define countof _countof
#define WER_MAX_ARRAY_ELEMENTS_COUNT 10
#define WER_MAX_NESTING_LEVEL 4
#define WER_SMALL_BUFFER_SIZE 1024
#define WER_LARGE_BUFFER_SIZE WER_SMALL_BUFFER_SIZE * 16
enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK
{
@@ -37,40 +42,54 @@ enum BasicType // Stolen from CVCON
btStdString = 101
};
const char* const rgBaseType[] =
enum DataKind // Stolen from CVCONST.H in the DIA 2.0 SDK
{
" <user defined> ", // btNoType = 0,
" void ", // btVoid = 1,
" char* ", // btChar = 2,
" wchar_t* ", // btWChar = 3,
" signed char ",
" unsigned char ",
" int ", // btInt = 6,
" unsigned int ", // btUInt = 7,
" float ", // btFloat = 8,
" <BCD> ", // btBCD = 9,
" bool ", // btBool = 10,
" short ",
" unsigned short ",
" long ", // btLong = 13,
" unsigned long ", // btULong = 14,
" __int8 ",
" __int16 ",
" __int32 ",
" __int64 ",
" __int128 ",
" unsigned __int8 ",
" unsigned __int16 ",
" unsigned __int32 ",
" unsigned __int64 ",
" unsigned __int128 ",
" <currency> ", // btCurrency = 25,
" <date> ", // btDate = 26,
" VARIANT ", // btVariant = 27,
" <complex> ", // btComplex = 28,
" <bit> ", // btBit = 29,
" BSTR ", // btBSTR = 30,
" HRESULT " // btHresult = 31
DataIsUnknown,
DataIsLocal,
DataIsStaticLocal,
DataIsParam,
DataIsObjectPtr,
DataIsFileStatic,
DataIsGlobal,
DataIsMember,
DataIsStaticMember,
DataIsConstant
};
char const* const rgBaseType[] =
{
"<user defined>", // btNoType = 0,
"void", // btVoid = 1,
"char",//char* // btChar = 2,
"wchar_t*", // btWChar = 3,
"signed char",
"unsigned char",
"int", // btInt = 6,
"unsigned int", // btUInt = 7,
"float", // btFloat = 8,
"<BCD>", // btBCD = 9,
"bool", // btBool = 10,
"short",
"unsigned short",
"long", // btLong = 13,
"unsigned long", // btULong = 14,
"int8",
"int16",
"int32",
"int64",
"int128",
"uint8",
"uint16",
"uint32",
"uint64",
"uint128",
"<currency>", // btCurrency = 25,
"<date>", // btDate = 26,
"VARIANT", // btVariant = 27,
"<complex>", // btComplex = 28,
"<bit>", // btBit = 29,
"BSTR", // btBSTR = 30,
"HRESULT" // btHresult = 31
};
struct SymbolPair
@@ -81,9 +100,9 @@ struct SymbolPair
_offset = offset;
}
bool operator<(const SymbolPair& other) const
bool operator<(SymbolPair const& other) const
{
return _offset < other._offset ||
return _offset < other._offset ||
(_offset == other._offset && _type < other._type);
}
@@ -92,6 +111,26 @@ struct SymbolPair
};
typedef std::set<SymbolPair> SymbolPairs;
struct SymbolDetail
{
SymbolDetail() : Prefix(), Type(), Suffix(), Name(), Value(), Logged(false), HasChildren(false) {}
std::string ToString();
bool empty() const
{
return Value.empty() && !HasChildren;
}
std::string Prefix;
std::string Type;
std::string Suffix;
std::string Name;
std::string Value;
bool Logged;
bool HasChildren;
};
class WheatyExceptionReport
{
public:
@@ -103,6 +142,8 @@ class WheatyExceptionReport
static LONG WINAPI WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo);
static void __cdecl WheatyCrtHandler(wchar_t const* expression, wchar_t const* function, wchar_t const* file, unsigned int line, uintptr_t pReserved);
static void printTracesForAllThreads(bool);
private:
// where report info is extracted and generated
@@ -120,15 +161,18 @@ class WheatyExceptionReport
static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO, ULONG, PVOID);
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *, char * pszBuffer, unsigned cbBuffer);
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *);
static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, char*, char*);
static void DumpTypeIndex(DWORD64, DWORD, DWORD_PTR, bool &, char const*, char*, bool, bool);
static char * FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress);
static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride = 0);
static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase);
static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address);
static int __cdecl _tprintf(const TCHAR * format, ...);
static int __cdecl Log(const TCHAR * format, ...);
static int __cdecl StackLog(const TCHAR * format, va_list argptr);
static int __cdecl HeapLog(const TCHAR * format, va_list argptr);
static bool StoreSymbol(DWORD type , DWORD_PTR offset);
static void ClearSymbols();
@@ -137,13 +181,24 @@ class WheatyExceptionReport
static TCHAR m_szLogFileName[MAX_PATH];
static TCHAR m_szDumpFileName[MAX_PATH];
static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
static _invalid_parameter_handler m_previousCrtHandler;
static HANDLE m_hReportFile;
static HANDLE m_hDumpFile;
static HANDLE m_hProcess;
static SymbolPairs symbols;
static std::stack<SymbolDetail> symbolDetails;
static bool stackOverflowException;
static bool alreadyCrashed;
static std::mutex alreadyCrashedLock;
typedef NTSTATUS(NTAPI* pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);
static pRtlGetVersion RtlGetVersion;
static void PushSymbolDetail();
static void PopSymbolDetail();
static void PrintSymbolDetail();
};
extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class
#endif // _WIN32
#endif // _WHEATYEXCEPTIONREPORT_

View File

@@ -1,4 +1,5 @@
# Copyright (C)
#
# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL3 v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
@@ -8,14 +9,14 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
if( SERVERS )
set(sources_windows_Debugging
# Crash logs for windows
if (WIN32)
set(winDebugging
${CMAKE_SOURCE_DIR}/src/common/Debugging/WheatyExceptionReport.cpp
${CMAKE_SOURCE_DIR}/src/common/Debugging/WheatyExceptionReport.h
)
${CMAKE_SOURCE_DIR}/src/common/Debugging/WheatyExceptionReport.h)
endif()
add_subdirectory(game)
add_subdirectory(authserver)
add_subdirectory(scripts)
add_subdirectory(worldserver)
endif()

View File

@@ -18,7 +18,7 @@ CollectSourceFiles(
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
if( WIN32 )
list(APPEND PRIVATE_SOURCES ${sources_windows})
list(APPEND PRIVATE_SOURCES ${winDebugging})
if ( MSVC )
list(APPEND PRIVATE_SOURCES authserver.rc)
endif()

View File

@@ -16,12 +16,12 @@ CollectSourceFiles(
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
if( WIN32 )
list(APPEND PRIVATE_SOURCES ${sources_windows})
list(APPEND PRIVATE_SOURCES ${winDebugging})
if ( MSVC )
list(APPEND PRIVATE_SOURCES worldserver.rc)
endif()
endif()
if (USE_COREPCH)
set(PRIVATE_PCH_HEADER PrecompiledHeaders/worldPCH.h)
endif()