2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 Victor Kirhenshtein
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU Lesser General Public License as published
7 ** by the Free Software Foundation; either version 3 of the License, or
8 ** (at your option) any later version.
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
15 ** You should have received a copy of the GNU Lesser General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "libnetxms.h"
25 #include "StackWalker.h"
27 #pragma warning(disable : 4482)
30 * Customized StackWalker class
32 class NxStackWalker
: public StackWalker
35 virtual void OnOutput(LPCSTR pszText
);
36 virtual void OnSymInit(LPCSTR szSearchPath
, DWORD symOptions
, LPCSTR szUserName
) { }
37 virtual void OnDbgHelpErr(LPCSTR szFuncName
, DWORD gle
, DWORD64 addr
) { }
40 NxStackWalker(int options
, LPCSTR szSymPath
, DWORD dwProcessId
, HANDLE hProcess
)
41 : StackWalker(options
, szSymPath
, dwProcessId
, hProcess
) { }
49 static BOOL (*m_pfExceptionHandler
)(EXCEPTION_POINTERS
*) = NULL
;
50 static void (*m_pfWriter
)(const TCHAR
*pszText
) = NULL
;
51 static HANDLE m_hExceptionLock
= INVALID_HANDLE_VALUE
;
52 static TCHAR m_szBaseProcessName
[64] = _T("netxms");
53 static TCHAR m_szDumpDir
[MAX_PATH
] = _T("C:\\");
54 static DWORD m_dwLogMessageCode
= 0;
55 static BOOL m_printToScreen
= FALSE
;
56 static BOOL m_writeFullDump
= FALSE
;
59 * Output for stack walker
61 void NxStackWalker::OnOutput(LPCSTR pszText
)
63 if (m_pfWriter
!= NULL
)
67 WCHAR
*wtext
= WideStringFromMBString(pszText
);
77 fputs(pszText
, stdout
);
82 * Initialize SEH functions
86 m_hExceptionLock
= CreateMutex(NULL
, FALSE
, NULL
);
90 * Set exception handler
92 void LIBNETXMS_EXPORTABLE
SetExceptionHandler(BOOL (*pfHandler
)(EXCEPTION_POINTERS
*),
93 void (*pfWriter
)(const TCHAR
*), const TCHAR
*pszDumpDir
,
94 const TCHAR
*pszBaseProcessName
, DWORD dwLogMsgCode
,
95 BOOL writeFullDump
, BOOL printToScreen
)
97 m_pfExceptionHandler
= pfHandler
;
98 m_pfWriter
= pfWriter
;
99 if (pszBaseProcessName
!= NULL
)
100 _tcslcpy(m_szBaseProcessName
, pszBaseProcessName
, 64);
101 if (pszDumpDir
!= NULL
)
102 _tcslcpy(m_szDumpDir
, pszDumpDir
, MAX_PATH
);
103 m_dwLogMessageCode
= dwLogMsgCode
;
104 m_writeFullDump
= writeFullDump
;
105 m_printToScreen
= printToScreen
;
109 * Get exception name from code
111 TCHAR LIBNETXMS_EXPORTABLE
*SEHExceptionName(DWORD code
)
119 { EXCEPTION_ACCESS_VIOLATION
, _T("Access violation") },
120 { EXCEPTION_ARRAY_BOUNDS_EXCEEDED
, _T("Array bounds exceeded") },
121 { EXCEPTION_BREAKPOINT
, _T("Breakpoint") },
122 { EXCEPTION_DATATYPE_MISALIGNMENT
, _T("Alignment error") },
123 { EXCEPTION_FLT_DENORMAL_OPERAND
, _T("FLT_DENORMAL_OPERAND") },
124 { EXCEPTION_FLT_DIVIDE_BY_ZERO
, _T("FLT_DIVIDE_BY_ZERO") },
125 { EXCEPTION_FLT_INEXACT_RESULT
, _T("FLT_INEXACT_RESULT") },
126 { EXCEPTION_FLT_INVALID_OPERATION
, _T("FLT_INVALID_OPERATION") },
127 { EXCEPTION_FLT_OVERFLOW
, _T("Floating point overflow") },
128 { EXCEPTION_FLT_STACK_CHECK
, _T("FLT_STACK_CHECK") },
129 { EXCEPTION_FLT_UNDERFLOW
, _T("Floating point underflow") },
130 { EXCEPTION_ILLEGAL_INSTRUCTION
, _T("Illegal instruction") },
131 { EXCEPTION_IN_PAGE_ERROR
, _T("Page error") },
132 { EXCEPTION_INT_DIVIDE_BY_ZERO
, _T("Divide by zero") },
133 { EXCEPTION_INT_OVERFLOW
, _T("Integer overflow") },
134 { EXCEPTION_INVALID_DISPOSITION
, _T("Invalid disposition") },
135 { EXCEPTION_NONCONTINUABLE_EXCEPTION
, _T("Non-continuable exception") },
136 { EXCEPTION_PRIV_INSTRUCTION
, _T("Priveledged instruction") },
137 { EXCEPTION_SINGLE_STEP
, _T("Single step") },
138 { EXCEPTION_STACK_OVERFLOW
, _T("Stack overflow") },
143 for(i
= 0; exceptionList
[i
].name
!= NULL
; i
++)
144 if (code
== exceptionList
[i
].code
)
145 return exceptionList
[i
].name
;
147 return _T("Unknown");
151 * Default exception handler for console applications
153 BOOL LIBNETXMS_EXPORTABLE
SEHDefaultConsoleHandler(EXCEPTION_POINTERS
*pInfo
)
155 _tprintf(_T("EXCEPTION: %08X (%s) at %p\n"),
156 pInfo
->ExceptionRecord
->ExceptionCode
,
157 SEHExceptionName(pInfo
->ExceptionRecord
->ExceptionCode
),
158 pInfo
->ExceptionRecord
->ExceptionAddress
);
160 _tprintf(_T("\nRegister information:\n")
161 _T(" eax=%08X ebx=%08X ecx=%08X edx=%08X\n")
162 _T(" esi=%08X edi=%08X ebp=%08X esp=%08X\n")
163 _T(" cs=%04X ds=%04X es=%04X ss=%04X fs=%04X gs=%04X flags=%08X\n"),
164 pInfo
->ContextRecord
->Eax
, pInfo
->ContextRecord
->Ebx
,
165 pInfo
->ContextRecord
->Ecx
, pInfo
->ContextRecord
->Edx
,
166 pInfo
->ContextRecord
->Esi
, pInfo
->ContextRecord
->Edi
,
167 pInfo
->ContextRecord
->Ebp
, pInfo
->ContextRecord
->Esp
,
168 pInfo
->ContextRecord
->SegCs
, pInfo
->ContextRecord
->SegDs
,
169 pInfo
->ContextRecord
->SegEs
, pInfo
->ContextRecord
->SegSs
,
170 pInfo
->ContextRecord
->SegFs
, pInfo
->ContextRecord
->SegGs
,
171 pInfo
->ContextRecord
->EFlags
);
174 _tprintf(_T("\nCall stack:\n"));
175 NxStackWalker
sw(NxStackWalker::StackWalkOptions::OptionsAll
, NULL
, GetCurrentProcessId(), GetCurrentProcess());
176 sw
.ShowCallstack(GetCurrentThread(), pInfo
->ContextRecord
);
178 _tprintf(_T("\nPROCESS TERMINATED\n"));
185 void LIBNETXMS_EXPORTABLE
SEHShowCallStack(CONTEXT
*pCtx
)
187 NxStackWalker
sw(NxStackWalker::StackWalkOptions::OptionsAll
, NULL
, GetCurrentProcessId(), GetCurrentProcess());
188 sw
.ShowCallstack(GetCurrentThread(), pCtx
);
194 int LIBNETXMS_EXPORTABLE
___ExceptionHandler(EXCEPTION_POINTERS
*pInfo
)
196 if ((m_pfExceptionHandler
!= NULL
) &&
197 (pInfo
->ExceptionRecord
->ExceptionCode
!= EXCEPTION_BREAKPOINT
) &&
198 (pInfo
->ExceptionRecord
->ExceptionCode
!= EXCEPTION_SINGLE_STEP
))
200 // Only one exception handler can be executed at a time
201 // We will never release mutex because __except block
202 // will call ExitProcess anyway and we want to log only first
203 // exception in case of multiple simultaneous exceptions
204 WaitForSingleObject(m_hExceptionLock
, INFINITE
);
205 return m_pfExceptionHandler(pInfo
) ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
207 return EXCEPTION_CONTINUE_SEARCH
;
211 * Starter for threads
213 THREAD_RESULT LIBNETXMS_EXPORTABLE THREAD_CALL
SEHThreadStarter(void *pArg
)
217 ((THREAD_START_DATA
*)pArg
)->start_address(((THREAD_START_DATA
*)pArg
)->args
);
220 __except(___ExceptionHandler((EXCEPTION_POINTERS
*)_exception_info()))
228 * Windows service exception handling
229 * ****************************************************
233 * Exception info file handle
235 static FILE *m_pExInfoFile
= NULL
;
238 * Writer for SEHShowCallStack()
240 void LIBNETXMS_EXPORTABLE
SEHServiceExceptionDataWriter(const TCHAR
*pszText
)
242 if (m_pExInfoFile
!= NULL
)
243 _fputts(pszText
, m_pExInfoFile
);
249 BOOL LIBNETXMS_EXPORTABLE
SEHServiceExceptionHandler(EXCEPTION_POINTERS
*pInfo
)
251 TCHAR szWindowsVersion
[256] = _T("ERROR"), szInfoFile
[MAX_PATH
], szDumpFile
[MAX_PATH
], szProcNameUppercase
[64];
257 _tcscpy(szProcNameUppercase
, m_szBaseProcessName
);
258 _tcsupr(szProcNameUppercase
);
261 _sntprintf(szInfoFile
, MAX_PATH
, _T("%s\\%s-%d-%u.info"),
262 m_szDumpDir
, m_szBaseProcessName
, GetCurrentProcessId(), (DWORD
)t
);
263 m_pExInfoFile
= _tfopen(szInfoFile
, _T("w"));
264 if (m_pExInfoFile
!= NULL
)
266 _ftprintf(m_pExInfoFile
, _T("%s CRASH DUMP\n%s\n"), szProcNameUppercase
, _tctime(&t
));
268 _ftprintf(m_pExInfoFile
, _T("EXCEPTION: %08X (%s) at %08X\n"),
270 _ftprintf(m_pExInfoFile
, _T("EXCEPTION: %08X (%s) at %016I64X\n"),
272 pInfo
->ExceptionRecord
->ExceptionCode
,
273 SEHExceptionName(pInfo
->ExceptionRecord
->ExceptionCode
),
274 (ULONG_PTR
)pInfo
->ExceptionRecord
->ExceptionAddress
);
276 // NetXMS and OS version
277 GetWindowsVersionString(szWindowsVersion
, 256);
278 _ftprintf(m_pExInfoFile
,
279 _T("\nNetXMS Version: ") NETXMS_VERSION_STRING
280 _T("\nNetXMS Build Tag: ") NETXMS_BUILD_TAG
281 _T("\nOS Version: %s\n"), szWindowsVersion
);
283 // Processor architecture
284 _ftprintf(m_pExInfoFile
, _T("Processor architecture: "));
285 GetSystemInfo(&sysInfo
);
286 switch(sysInfo
.wProcessorArchitecture
)
288 case PROCESSOR_ARCHITECTURE_INTEL
:
289 _ftprintf(m_pExInfoFile
, _T("Intel x86\n"));
291 case PROCESSOR_ARCHITECTURE_MIPS
:
292 _ftprintf(m_pExInfoFile
, _T("MIPS\n"));
294 case PROCESSOR_ARCHITECTURE_ALPHA
:
295 _ftprintf(m_pExInfoFile
, _T("ALPHA\n"));
297 case PROCESSOR_ARCHITECTURE_PPC
:
298 _ftprintf(m_pExInfoFile
, _T("PowerPC\n"));
300 case PROCESSOR_ARCHITECTURE_IA64
:
301 _ftprintf(m_pExInfoFile
, _T("Intel IA-64\n"));
303 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
:
304 _ftprintf(m_pExInfoFile
, _T("Intel x86 on Win64\n"));
306 case PROCESSOR_ARCHITECTURE_AMD64
:
307 _ftprintf(m_pExInfoFile
, _T("AMD64 (Intel EM64T)\n"));
310 _ftprintf(m_pExInfoFile
, _T("UNKNOWN\n"));
315 _ftprintf(m_pExInfoFile
, _T("\nRegister information:\n")
316 _T(" eax=%08X ebx=%08X ecx=%08X edx=%08X\n")
317 _T(" esi=%08X edi=%08X ebp=%08X esp=%08X\n")
318 _T(" cs=%04X ds=%04X es=%04X ss=%04X fs=%04X gs=%04X flags=%08X\n"),
319 pInfo
->ContextRecord
->Eax
, pInfo
->ContextRecord
->Ebx
,
320 pInfo
->ContextRecord
->Ecx
, pInfo
->ContextRecord
->Edx
,
321 pInfo
->ContextRecord
->Esi
, pInfo
->ContextRecord
->Edi
,
322 pInfo
->ContextRecord
->Ebp
, pInfo
->ContextRecord
->Esp
,
323 pInfo
->ContextRecord
->SegCs
, pInfo
->ContextRecord
->SegDs
,
324 pInfo
->ContextRecord
->SegEs
, pInfo
->ContextRecord
->SegSs
,
325 pInfo
->ContextRecord
->SegFs
, pInfo
->ContextRecord
->SegGs
,
326 pInfo
->ContextRecord
->EFlags
);
329 _ftprintf(m_pExInfoFile
, _T("\nCall stack:\n"));
330 SEHShowCallStack(pInfo
->ContextRecord
);
332 fclose(m_pExInfoFile
);
336 _sntprintf(szDumpFile
, MAX_PATH
, _T("%s\\%s-%u-%u.mdmp"),
337 m_szDumpDir
, m_szBaseProcessName
, GetCurrentProcessId(), (DWORD
)t
);
338 hFile
= CreateFile(szDumpFile
, GENERIC_WRITE
, 0, NULL
,
339 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
340 if (hFile
!= INVALID_HANDLE_VALUE
)
342 MINIDUMP_EXCEPTION_INFORMATION mei
;
343 mei
.ThreadId
= GetCurrentThreadId();
344 mei
.ExceptionPointers
= pInfo
;
345 mei
.ClientPointers
= FALSE
;
347 static const char *comments
= "Version=" NETXMS_VERSION_STRING_A
"; BuildTag=" NETXMS_BUILD_TAG_A
;
348 MINIDUMP_USER_STREAM us
;
349 us
.Type
= CommentStreamA
;
350 us
.Buffer
= (void*)comments
;
351 us
.BufferSize
= static_cast<ULONG
>(strlen(comments
) + 1);
353 MINIDUMP_USER_STREAM_INFORMATION usi
;
354 usi
.UserStreamCount
= 1;
355 usi
.UserStreamArray
= &us
;
357 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile
,
358 static_cast<MINIDUMP_TYPE
>((m_writeFullDump
? MiniDumpWithFullMemory
: MiniDumpNormal
) | MiniDumpWithHandleData
| MiniDumpWithProcessThreadData
),
364 nxlog_write(m_dwLogMessageCode
, EVENTLOG_ERROR_TYPE
, "xsxss",
365 pInfo
->ExceptionRecord
->ExceptionCode
,
366 SEHExceptionName(pInfo
->ExceptionRecord
->ExceptionCode
),
367 pInfo
->ExceptionRecord
->ExceptionAddress
,
368 szInfoFile
, szDumpFile
);
372 _tprintf(_T("\n\n*************************************************************\n")
374 _T("EXCEPTION: %08X (%s) at %08X\nPROCESS TERMINATED"),
376 _T("EXCEPTION: %08X (%s) at %016I64X\nPROCESS TERMINATED"),
378 pInfo
->ExceptionRecord
->ExceptionCode
,
379 SEHExceptionName(pInfo
->ExceptionRecord
->ExceptionCode
),
380 (ULONG_PTR
)pInfo
->ExceptionRecord
->ExceptionAddress
);
383 return TRUE
; // Terminate process