Pol  Revision:cb584c9
mdump.cpp
Go to the documentation of this file.
1 
8 #include "mdump.h"
9 #include "clib.h"
10 #include "logfacility.h"
11 #include "passert.h"
12 #include "strexcpt.h"
13 #include "threadhelp.h"
14 
15 #pragma warning( disable : 4091 ) // unused typedef
16 #include "../../lib/StackWalker/StackWalker.h"
17 
18 #include "Header_Windows.h"
19 #include <assert.h>
20 #include <time.h>
21 
22 // FIXME: 2008 Upgrades needed here? Need to check dbg headers to ensure compatibility
23 #if _MSC_VER < 1300
24 #define DECLSPEC_DEPRECATED
25 // VC6: change this path to your Platform SDK headers
26 // must be XP version of file
27 #include "C:\\Program Files\\Microsoft Visual Studio\\PlatformSDK\\include\\dbghelp.h"
28 #else
29 // VC7: ships with updated headers
30 #include "dbghelp.h"
31 #endif
32 
33 #ifdef _MSC_VER
34 #pragma warning( disable : 4100 ) // TODO: This file needs some serious rewrite, so I'm just
35  // ignoring the unreferenced parameters for now
36 #endif
37 
38 // based on dbghelp.h
39 typedef BOOL( WINAPI* MINIDUMPWRITEDUMP )( HANDLE hProcess, DWORD dwPid, HANDLE hFile,
40  MINIDUMP_TYPE DumpType,
41  CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
42  CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
43  CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam );
44 typedef BOOL( WINAPI* __SymInitialize )( _In_ HANDLE hProcess, _In_opt_ PCSTR UserSearchPath,
45  _In_ BOOL fInvadeProcess );
46 typedef BOOL( WINAPI* __SymFromAddr )( _In_ HANDLE hProcess, _In_ DWORD64 Address,
47  _Out_opt_ PDWORD64 Displacement,
48  _Inout_ PSYMBOL_INFO Symbol );
49 typedef DWORD( WINAPI* __SymGetOptions )( VOID );
50 typedef DWORD( WINAPI* __SymSetOptions )( _In_ DWORD SymOptions );
51 typedef BOOL( WINAPI* __SymGetLineFromAddr64 )( _In_ HANDLE hProcess, _In_ DWORD64 qwAddr,
52  _Out_ PDWORD pdwDisplacement,
53  _Out_ PIMAGEHLP_LINE64 Line64 );
54 
55 #include "mdumpimp.h"
56 namespace Pol
57 {
58 namespace Clib
59 {
61 {
63 }
64 void MiniDumper::SetMiniDumpType( const std::string& dumptype )
65 {
67 }
68 
73 
74 HMODULE hDbgHelpDll;
75 
77 {
78  /*
79  if this assert fires then you have initialized HiddenMiniDumper twice
80  which is not allowed
81  */
82  assert( !_Initialized );
83  _Initialized = true;
84 
85  auto time_tm = Clib::localtime( time( NULL ) );
86  strftime( _StartTimestamp, sizeof _StartTimestamp, "%Y%m%d%H%M%S", &time_tm );
87 
88  // appname will be obtained from progver
89 
90  // find a better value for your app
91  // HWND hParent = NULL;
92 
93  /*
94  firstly see if dbghelp.dll is around and has the function we need
95  look next to the EXE first, as the one in System32 might be old
96  (e.g. Windows 2000)
97  */
98  char szDbgHelpPath[_MAX_PATH];
99  char* szResult = NULL;
100 
101  if ( GetModuleFileName( NULL, szDbgHelpPath, _MAX_PATH ) )
102  {
103  char* pSlash = strchr( szDbgHelpPath, '\\' );
104  if ( pSlash )
105  {
106  strcpy( pSlash + 1, "DBGHELP.DLL" );
107  hDbgHelpDll = ::LoadLibrary( szDbgHelpPath );
108  }
109  }
110 
111  if ( hDbgHelpDll == NULL )
112  {
113  // load any version we can
114  hDbgHelpDll = ::LoadLibrary( "DBGHELP.DLL" );
115  }
116  if ( hDbgHelpDll )
117  {
118  MINIDUMPWRITEDUMP pDump =
119  ( MINIDUMPWRITEDUMP )::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
120  if ( pDump )
121  ::SetUnhandledExceptionFilter( TopLevelFilter );
122  else
123  szResult = "Warning: DBGHELP.DLL too old, version 5.1+ required.";
124  }
125  else
126  szResult = "Warning: DBGHELP.DLL not found, version 5.1+ required in POL directory.";
127 
128  if ( szResult )
129  {
130  POLLOG_INFO << szResult << "\n";
132  }
133 }
134 
135 
136 LONG HiddenMiniDumper::TopLevelFilter( struct _EXCEPTION_POINTERS* pExceptionInfo )
137 {
138  LONG retval = EXCEPTION_CONTINUE_SEARCH;
139  fmt::Writer result;
140  fmt::Writer dumppath;
141  if ( !_Initialized )
142  Initialize();
143 
144  MINIDUMPWRITEDUMP pDump =
145  ( MINIDUMPWRITEDUMP )::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
146  if ( pDump )
147  {
148  dumppath << _StartTimestamp << "-" << fmt::hex( _DumpCount++ ) << ".dmp";
149 
150  HANDLE hFile = ::CreateFile( dumppath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
151  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
152 
153  if ( hFile != INVALID_HANDLE_VALUE )
154  {
155  _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
156 
157  ExInfo.ThreadId = ::GetCurrentThreadId();
158  ExInfo.ExceptionPointers = pExceptionInfo;
159  ExInfo.ClientPointers = NULL;
160 
161  // write the dump
162  MINIDUMP_TYPE dumptype;
163  if ( _MiniDumpType == "large" )
164  dumptype = MiniDumpWithFullMemory;
165  else if ( _MiniDumpType == "variable" )
166  dumptype = MiniDumpWithDataSegs;
167  else
168  dumptype = MiniDumpNormal;
169 
170  ERROR_PRINT << "Unhandled Exception! Minidump started...\n";
171  BOOL bOK =
172  pDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, dumptype, &ExInfo, NULL, NULL );
173  if ( bOK )
174  {
175  result.Format(
176  "Unhandled Exception! Writing Minidump file. \nPost this file with explanation and "
177  "last lines from log files on http://forums.polserver.com/tracker.php for the "
178  "development team.\nSaved dump file to '{}'\n" )
179  << dumppath.str();
180  retval = EXCEPTION_EXECUTE_HANDLER;
181  }
182  else
183  {
184  result.Format( "Failed to save dump file to '{}' (error {})" )
185  << dumppath.str() << GetLastError();
186  }
187  ::CloseHandle( hFile );
188  }
189  else
190  {
191  result.Format( "Failed to create dump file '{}' (error {})" )
192  << dumppath.str() << GetLastError();
193  }
194  }
195  print_backtrace();
196  FreeLibrary( hDbgHelpDll );
197  _Initialized = false;
198 
199  if ( result.size() > 0 )
200  {
201  POLLOG_ERROR << "##########################################################\n"
202  << result.str() << "\n"
203  << "Last Script: " << scripts_thread_script << " PC: " << scripts_thread_scriptPC
204  << "\n##########################################################\n";
205  }
207  Clib::Logging::global_logger->wait_for_empty_queue(); // wait here for logging facility to make
208  // sure everything was printed
209  return retval;
210 }
211 
212 class StackWalkerLogger : public StackWalker
213 {
214 public:
215  StackWalkerLogger( int options ) : StackWalker( options ){};
217  {
218  if ( _log.size() > 0 )
219  POLLOG_ERROR << _log.str();
220  }
221  fmt::Writer _log;
222 
223 protected:
224  // no output
225  virtual void OnSymInit( LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName ) POL_OVERRIDE {}
226  virtual void OnLoadModule( LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result,
227  LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion ) POL_OVERRIDE
228  {
229  }
230  virtual void OnDbgHelpErr( LPCSTR szFuncName, DWORD gle, DWORD64 addr ) POL_OVERRIDE{};
231  virtual void OnOutput( LPCSTR szText ) POL_OVERRIDE { _log << szText; }
232  virtual void OnCallstackEntry( CallstackEntryType eType, CallstackEntry& entry ) POL_OVERRIDE
233  {
234  try
235  {
236  if ( ( eType != lastEntry ) && ( entry.offset != 0 ) )
237  {
238  if ( entry.undFullName[0] != 0 )
239  _log << entry.undFullName;
240  else if ( entry.undName[0] != 0 )
241  _log << entry.undName;
242  else
243  _log << "(function-name not available)";
244  _log.Format( " - {:p}\n" ) << (LPVOID)entry.offset;
245 
246  if ( entry.lineFileName[0] == 0 )
247  {
248  _log << " (filename not available)\n";
249  }
250  else
251  {
252  _log << " " << entry.lineFileName << " : " << entry.lineNumber << "\n";
253  }
254  }
255  }
256  catch ( std::exception& e )
257  {
258  POLLOG_ERROR << "failed to format backtrace " << e.what() << "\n";
259  }
260  }
261 };
262 
264 {
265  {
266  StackWalkerLogger sw( StackWalker::RetrieveLine );
267  sw._log
268  << "\n##########################################################\nCurrent StackBackTrace\n";
269  sw.ShowCallstack();
270  // threadhelp::ThreadMap::Contents contents;
271  // threadhelp::threadmap.CopyContents( contents );
272  // for ( auto &content : contents )
273  //{
274  // if ( content.second == "Main" ) // fixme main thread seems to be not suspendable
275  // continue;
276  // HANDLE handle = threadhelp::threadmap.getThreadHandle( content.first );
277  // if ( handle == GetCurrentThread() )
278  // continue;
279  // sw._log << "\nThread ID " << content.first << " (" << content.second << ")\n";
280  // if ( handle )
281  // sw.ShowCallstack( handle);
282  //}
283  sw._log << "##########################################################\n";
284  }
285 }
286 }
287 }
DWORD HANDLE MINIDUMP_TYPE DumpType
Definition: mdump.cpp:39
#define POL_OVERRIDE
DWORD HANDLE MINIDUMP_TYPE CONST PMINIDUMP_EXCEPTION_INFORMATION CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam
Definition: mdump.cpp:39
_In_opt_ PCSTR _In_ BOOL fInvadeProcess
Definition: mdump.cpp:44
_In_opt_ PCSTR UserSearchPath
Definition: mdump.cpp:44
std::string scripts_thread_script
Definition: passert.cpp:36
static unsigned _DumpCount
Definition: mdumpimp.h:21
_In_ DWORD64 _Out_opt_ PDWORD64 Displacement
Definition: mdump.cpp:46
static char _StartTimestamp[32]
Definition: mdumpimp.h:22
std::tm localtime(const std::time_t &t)
threadsafe version of localtime
Definition: clib.h:143
#define POLLOG_INFO
Definition: logfacility.h:213
static LONG WINAPI TopLevelFilter(struct _EXCEPTION_POINTERS *pExceptionInfo)
Definition: mdump.cpp:136
static void Initialize()
Definition: mdump.cpp:60
typedef DWORD(WINAPI *__SymGetOptions)(VOID)
#define POLLOG_ERROR
Definition: logfacility.h:207
virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) POL_OVERRIDE
Definition: mdump.cpp:226
StackWalkerLogger(int options)
Definition: mdump.cpp:215
DWORD HANDLE MINIDUMP_TYPE CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam
Definition: mdump.cpp:39
HMODULE hDbgHelpDll
Definition: mdump.cpp:74
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) POL_OVERRIDE
Definition: mdump.cpp:225
static void SetMiniDumpType(const std::string &dumptype)
Definition: mdump.cpp:64
_In_ DWORD64 Address
Definition: mdump.cpp:46
static std::string _MiniDumpType
Definition: mdumpimp.h:20
_In_ DWORD64 _Out_ PDWORD _Out_ PIMAGEHLP_LINE64 Line64
Definition: mdump.cpp:51
static bool _Initialized
Definition: mdumpimp.h:19
typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess
DWORD HANDLE MINIDUMP_TYPE CONST PMINIDUMP_EXCEPTION_INFORMATION CONST PMINIDUMP_USER_STREAM_INFORMATION CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
Definition: mdump.cpp:39
void InstallOldStructuredExceptionHandler(void)
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) POL_OVERRIDE
Definition: mdump.cpp:230
_In_ DWORD64 _Out_opt_ PDWORD64 _Inout_ PSYMBOL_INFO Symbol
Definition: mdump.cpp:46
_In_ DWORD64 _Out_ PDWORD pdwDisplacement
Definition: mdump.cpp:51
DWORD HANDLE hFile
Definition: mdump.cpp:39
DWORD dwPid
Definition: mdump.cpp:39
virtual void OnOutput(LPCSTR szText) POL_OVERRIDE
Definition: mdump.cpp:231
#define ERROR_PRINT
Definition: logfacility.h:230
virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) POL_OVERRIDE
Definition: mdump.cpp:232
LogFacility * global_logger
Definition: logfacility.cpp:62
Definition: berror.cpp:12
_In_ DWORD64 qwAddr
Definition: mdump.cpp:51
unsigned scripts_thread_scriptPC
Definition: passert.cpp:37
static void Initialize()
Definition: mdump.cpp:76
static void print_backtrace()
Definition: mdump.cpp:263