polserver/pol-core/clib/mdump.cpp
2018-10-09 19:07:53 +02:00

287 lines
9.2 KiB
C++

/** @file
*
* @par History
* - 2005/09/16 Shinigami: added scripts_thread_script* to support better debugging
* - 2009-07-18 MuadDib: Updated dump messages for bug tracker
*/
#include "clib.h"
#include "logfacility.h"
#include "mdump.h"
#include "passert.h"
#include "strexcpt.h"
#include "threadhelp.h"
#pragma warning( disable : 4091 ) // unused typedef
#include "../../lib/StackWalker/StackWalker.h"
#include "Header_Windows.h"
#include <assert.h>
#include <time.h>
// FIXME: 2008 Upgrades needed here? Need to check dbg headers to ensure compatibility
#if _MSC_VER < 1300
#define DECLSPEC_DEPRECATED
// VC6: change this path to your Platform SDK headers
// must be XP version of file
#include "C:\\Program Files\\Microsoft Visual Studio\\PlatformSDK\\include\\dbghelp.h"
#else
// VC7: ships with updated headers
#include "dbghelp.h"
#endif
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // TODO: This file needs some serious rewrite, so I'm just
// ignoring the unreferenced parameters for now
#endif
// based on dbghelp.h
typedef BOOL( WINAPI* MINIDUMPWRITEDUMP )( HANDLE hProcess, DWORD dwPid, HANDLE hFile,
MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam );
typedef BOOL( WINAPI* __SymInitialize )( _In_ HANDLE hProcess, _In_opt_ PCSTR UserSearchPath,
_In_ BOOL fInvadeProcess );
typedef BOOL( WINAPI* __SymFromAddr )( _In_ HANDLE hProcess, _In_ DWORD64 Address,
_Out_opt_ PDWORD64 Displacement,
_Inout_ PSYMBOL_INFO Symbol );
typedef DWORD( WINAPI* __SymGetOptions )( VOID );
typedef DWORD( WINAPI* __SymSetOptions )( _In_ DWORD SymOptions );
typedef BOOL( WINAPI* __SymGetLineFromAddr64 )( _In_ HANDLE hProcess, _In_ DWORD64 qwAddr,
_Out_ PDWORD pdwDisplacement,
_Out_ PIMAGEHLP_LINE64 Line64 );
#include "mdumpimp.h"
namespace Pol
{
namespace Clib
{
void MiniDumper::Initialize()
{
HiddenMiniDumper::Initialize();
}
void MiniDumper::SetMiniDumpType( const std::string& dumptype )
{
HiddenMiniDumper::_MiniDumpType = dumptype;
}
bool HiddenMiniDumper::_Initialized;
unsigned HiddenMiniDumper::_DumpCount;
std::string HiddenMiniDumper::_MiniDumpType;
char HiddenMiniDumper::_StartTimestamp[32];
HMODULE hDbgHelpDll;
void HiddenMiniDumper::Initialize()
{
/*
if this assert fires then you have initialized HiddenMiniDumper twice
which is not allowed
*/
assert( !_Initialized );
_Initialized = true;
auto time_tm = Clib::localtime( time( nullptr ) );
strftime( _StartTimestamp, sizeof _StartTimestamp, "%Y%m%d%H%M%S", &time_tm );
// appname will be obtained from progver
// find a better value for your app
// HWND hParent = nullptr;
/*
firstly see if dbghelp.dll is around and has the function we need
look next to the EXE first, as the one in System32 might be old
(e.g. Windows 2000)
*/
char szDbgHelpPath[_MAX_PATH];
char* szResult = nullptr;
if ( GetModuleFileName( nullptr, szDbgHelpPath, _MAX_PATH ) )
{
char* pSlash = strchr( szDbgHelpPath, '\\' );
if ( pSlash )
{
strcpy( pSlash + 1, "DBGHELP.DLL" );
hDbgHelpDll = ::LoadLibrary( szDbgHelpPath );
}
}
if ( hDbgHelpDll == nullptr )
{
// load any version we can
hDbgHelpDll = ::LoadLibrary( "DBGHELP.DLL" );
}
if ( hDbgHelpDll )
{
MINIDUMPWRITEDUMP pDump =
( MINIDUMPWRITEDUMP )::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
if ( pDump )
::SetUnhandledExceptionFilter( TopLevelFilter );
else
szResult = "Warning: DBGHELP.DLL too old, version 5.1+ required.";
}
else
szResult = "Warning: DBGHELP.DLL not found, version 5.1+ required in POL directory.";
if ( szResult )
{
POLLOG_INFO << szResult << "\n";
InstallOldStructuredExceptionHandler();
}
}
LONG HiddenMiniDumper::TopLevelFilter( struct _EXCEPTION_POINTERS* pExceptionInfo )
{
LONG retval = EXCEPTION_CONTINUE_SEARCH;
fmt::Writer result;
fmt::Writer dumppath;
if ( !_Initialized )
Initialize();
MINIDUMPWRITEDUMP pDump =
( MINIDUMPWRITEDUMP )::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
if ( pDump )
{
dumppath << _StartTimestamp << "-" << fmt::hex( _DumpCount++ ) << ".dmp";
HANDLE hFile = ::CreateFile( dumppath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );
if ( hFile != INVALID_HANDLE_VALUE )
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = 0;
// write the dump
MINIDUMP_TYPE dumptype;
if ( _MiniDumpType == "large" )
dumptype = MiniDumpWithFullMemory;
else if ( _MiniDumpType == "variable" )
dumptype = MiniDumpWithDataSegs;
else
dumptype = MiniDumpNormal;
ERROR_PRINT << "Unhandled Exception! Minidump started...\n";
BOOL bOK = pDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, dumptype, &ExInfo,
nullptr, nullptr );
if ( bOK )
{
result.Format(
"Unhandled Exception! Writing Minidump file. \nPost this file with explanation and "
"last lines from log files on http://forums.polserver.com/tracker.php for the "
"development team.\nSaved dump file to '{}'\n" )
<< dumppath.str();
retval = EXCEPTION_EXECUTE_HANDLER;
}
else
{
result.Format( "Failed to save dump file to '{}' (error {})" )
<< dumppath.str() << GetLastError();
}
::CloseHandle( hFile );
}
else
{
result.Format( "Failed to create dump file '{}' (error {})" )
<< dumppath.str() << GetLastError();
}
}
print_backtrace();
FreeLibrary( hDbgHelpDll );
_Initialized = false;
if ( result.size() > 0 )
{
POLLOG_ERROR << "##########################################################\n"
<< result.str() << "\n"
<< "Last Script: " << scripts_thread_script << " PC: " << scripts_thread_scriptPC
<< "\n##########################################################\n";
}
if ( Clib::Logging::global_logger )
Clib::Logging::global_logger->wait_for_empty_queue(); // wait here for logging facility to make
// sure everything was printed
return retval;
}
class StackWalkerLogger : public StackWalker
{
public:
StackWalkerLogger( int options ) : StackWalker( options ){};
virtual ~StackWalkerLogger()
{
if ( _log.size() > 0 )
POLLOG_ERROR << _log.str();
}
fmt::Writer _log;
protected:
// no output
virtual void OnSymInit( LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName ) override {}
virtual void OnLoadModule( LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result,
LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion ) override
{
}
virtual void OnDbgHelpErr( LPCSTR szFuncName, DWORD gle, DWORD64 addr ) override{};
virtual void OnOutput( LPCSTR szText ) override { _log << szText; }
virtual void OnCallstackEntry( CallstackEntryType eType, CallstackEntry& entry ) override
{
try
{
if ( ( eType != lastEntry ) && ( entry.offset != 0 ) )
{
if ( entry.undFullName[0] != 0 )
_log << entry.undFullName;
else if ( entry.undName[0] != 0 )
_log << entry.undName;
else
_log << "(function-name not available)";
_log.Format( " - {:p}\n" ) << (LPVOID)entry.offset;
if ( entry.lineFileName[0] == 0 )
{
_log << " (filename not available)\n";
}
else
{
_log << " " << entry.lineFileName << " : " << entry.lineNumber << "\n";
}
}
}
catch ( std::exception& e )
{
POLLOG_ERROR << "failed to format backtrace " << e.what() << "\n";
}
}
};
void HiddenMiniDumper::print_backtrace()
{
{
StackWalkerLogger sw( StackWalker::RetrieveLine );
sw._log
<< "\n##########################################################\nCurrent StackBackTrace\n";
sw.ShowCallstack();
// threadhelp::ThreadMap::Contents contents;
// threadhelp::threadmap.CopyContents( contents );
// for ( auto &content : contents )
//{
// if ( content.second == "Main" ) // fixme main thread seems to be not suspendable
// continue;
// HANDLE handle = threadhelp::threadmap.getThreadHandle( content.first );
// if ( handle == GetCurrentThread() )
// continue;
// sw._log << "\nThread ID " << content.first << " (" << content.second << ")\n";
// if ( handle )
// sw.ShowCallstack( handle);
//}
sw._log << "##########################################################\n";
}
}
}
}