1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
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.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "log.h"
22
23 #include <excpt.h>
24 #include <DbgHelp.h>
25
26 #pragma comment(lib, "DbgHelp.lib")
27
28 #define STACKWALK_MAX_NAMELEN 4096
29
30 #define ZBX_LSHIFT(value, bits) (((unsigned __int64)value) << bits)
31
32 extern const char *progname;
33
34 #ifdef _M_X64
35
36 #define ZBX_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_AMD64
37
print_register(const char * name,unsigned __int64 value)38 static void print_register(const char *name, unsigned __int64 value)
39 {
40 zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16I64x = %20I64u = %20I64d", name, value, value, value);
41 }
42
print_fatal_info(CONTEXT * pctx)43 static void print_fatal_info(CONTEXT *pctx)
44 {
45 zabbix_log(LOG_LEVEL_CRIT, "====== Fatal information: ======");
46
47 zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%08lx", pctx->Rip);
48 zabbix_log(LOG_LEVEL_CRIT, "=== Registers: ===");
49
50 print_register("r8", pctx->R8);
51 print_register("r9", pctx->R9);
52 print_register("r10", pctx->R10);
53 print_register("r11", pctx->R11);
54 print_register("r12", pctx->R12);
55 print_register("r13", pctx->R13);
56 print_register("r14", pctx->R14);
57 print_register("r15", pctx->R15);
58
59 print_register("rdi", pctx->Rdi);
60 print_register("rsi", pctx->Rsi);
61 print_register("rbp", pctx->Rbp);
62
63 print_register("rbx", pctx->Rbx);
64 print_register("rdx", pctx->Rdx);
65 print_register("rax", pctx->Rax);
66 print_register("rcx", pctx->Rcx);
67
68 print_register("rsp", pctx->Rsp);
69 print_register("efl", pctx->EFlags);
70 print_register("csgsfs", ZBX_LSHIFT(pctx->SegCs, 24) | ZBX_LSHIFT(pctx->SegGs, 16) | ZBX_LSHIFT(pctx->SegFs, 8));
71 }
72
73 #else
74
75 #define ZBX_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_I386
76
print_register(const char * name,unsigned __int32 value)77 static void print_register(const char *name, unsigned __int32 value)
78 {
79 zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16lx = %20lu = %20ld", name, value, value, value);
80 }
81
print_fatal_info(CONTEXT * pctx)82 static void print_fatal_info(CONTEXT *pctx)
83 {
84 zabbix_log(LOG_LEVEL_CRIT, "====== Fatal information: ======");
85
86 zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%04x", pctx->Eip);
87 zabbix_log(LOG_LEVEL_CRIT, "=== Registers: ===");
88
89 print_register("edi", pctx->Edi);
90 print_register("esi", pctx->Esi);
91 print_register("ebp", pctx->Ebp);
92
93 print_register("ebx", pctx->Ebx);
94 print_register("edx", pctx->Edx);
95 print_register("eax", pctx->Eax);
96 print_register("ecx", pctx->Ecx);
97
98 print_register("esp", pctx->Esp);
99 print_register("efl", pctx->EFlags);
100 print_register("csgsfs", ZBX_LSHIFT(pctx->SegCs, 24) | ZBX_LSHIFT(pctx->SegGs, 16) | ZBX_LSHIFT(pctx->SegFs, 8));
101 }
102
103 #endif
104
105 typedef BOOL (WINAPI *SymGetLineFromAddrW64_func_t)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
106 typedef BOOL (WINAPI *SymFromAddr_func_t)(HANDLE a, DWORD64 b , PDWORD64 c, PSYMBOL_INFO d);
107
zbx_backtrace(void)108 void zbx_backtrace(void)
109 {
110 }
111
print_backtrace(CONTEXT * pctx)112 static void print_backtrace(CONTEXT *pctx)
113 {
114 SymGetLineFromAddrW64_func_t zbx_SymGetLineFromAddrW64 = NULL;
115 SymFromAddr_func_t zbx_SymFromAddr = NULL;
116
117 CONTEXT ctx, ctxcount;
118 STACKFRAME64 s, scount;
119 PSYMBOL_INFO pSym = NULL;
120 HMODULE hModule;
121 HANDLE hProcess, hThread;
122 DWORD64 offset;
123 wchar_t szProcessName[MAX_PATH];
124 char *process_name = NULL, *process_path = NULL, *frame = NULL;
125 size_t frame_alloc = 0, frame_offset;
126 int nframes = 0;
127
128 ctx = *pctx;
129
130 zabbix_log(LOG_LEVEL_CRIT, "=== Backtrace: ===");
131
132 memset(&s, 0, sizeof(s));
133
134 s.AddrPC.Mode = AddrModeFlat;
135 s.AddrFrame.Mode = AddrModeFlat;
136 s.AddrStack.Mode = AddrModeFlat;
137
138 #ifdef _M_X64
139 s.AddrPC.Offset = ctx.Rip;
140 s.AddrFrame.Offset = ctx.Rbp;
141 s.AddrStack.Offset = ctx.Rsp;
142 #else
143 s.AddrPC.Offset = ctx.Eip;
144 s.AddrFrame.Offset = ctx.Ebp;
145 s.AddrStack.Offset = ctx.Esp;
146 #endif
147 hProcess = GetCurrentProcess();
148 hThread = GetCurrentThread();
149
150 if (0 != GetModuleFileNameEx(hProcess, NULL, szProcessName, ARRSIZE(szProcessName)))
151 {
152 char *ptr;
153 size_t path_alloc = 0, path_offset = 0;
154
155 process_name = zbx_unicode_to_utf8(szProcessName);
156
157 if (NULL != (ptr = strstr(process_name, progname)))
158 zbx_strncpy_alloc(&process_path, &path_alloc, &path_offset, process_name, ptr - process_name);
159 }
160
161 if (NULL != (hModule = GetModuleHandle(TEXT("DbgHelp.DLL"))))
162 {
163 zbx_SymGetLineFromAddrW64 = (SymGetLineFromAddrW64_func_t)GetProcAddress(hModule,
164 "SymGetLineFromAddr64");
165 zbx_SymFromAddr = (SymFromAddr_func_t)GetProcAddress(hModule, "SymFromAddr");
166 }
167
168 if (NULL != zbx_SymFromAddr || NULL != zbx_SymGetLineFromAddrW64)
169 {
170 SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES);
171
172 if (FALSE != SymInitialize(hProcess, process_path, TRUE))
173 {
174 pSym = (PSYMBOL_INFO) zbx_malloc(NULL, sizeof(SYMBOL_INFO) + MAX_SYM_NAME);
175 memset(pSym, 0, sizeof(SYMBOL_INFO) + MAX_SYM_NAME);
176 pSym->SizeOfStruct = sizeof(SYMBOL_INFO);
177 pSym->MaxNameLen = MAX_SYM_NAME;
178 }
179 }
180
181 scount = s;
182 ctxcount = ctx;
183
184 /* get number of frames, ctxcount may be modified during StackWalk64() calls */
185 while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &scount, &ctxcount, NULL, NULL, NULL,
186 NULL))
187 {
188 if (0 == scount.AddrReturn.Offset)
189 break;
190 nframes++;
191 }
192
193 while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &s, &ctx, NULL, NULL, NULL, NULL))
194 {
195 frame_offset = 0;
196 zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, "%d: %s", nframes--,
197 NULL == process_name ? "(unknown)" : process_name);
198
199 if (NULL != pSym)
200 {
201 DWORD dwDisplacement;
202 IMAGEHLP_LINE64 line = {sizeof(IMAGEHLP_LINE64)};
203
204 zbx_chrcpy_alloc(&frame, &frame_alloc, &frame_offset, '(');
205 if (NULL != zbx_SymFromAddr &&
206 TRUE == zbx_SymFromAddr(hProcess, s.AddrPC.Offset, &offset, pSym))
207 {
208 zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, "%s+0x%lx", pSym->Name, offset);
209 }
210
211 if (NULL != zbx_SymGetLineFromAddrW64 && TRUE == zbx_SymGetLineFromAddrW64(hProcess,
212 s.AddrPC.Offset, &dwDisplacement, &line))
213 {
214 zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, " %s:%d", line.FileName,
215 line.LineNumber);
216 }
217 zbx_chrcpy_alloc(&frame, &frame_alloc, &frame_offset, ')');
218 }
219
220 zabbix_log(LOG_LEVEL_CRIT, "%s [0x%lx]", frame, s.AddrPC.Offset);
221
222 if (0 == s.AddrReturn.Offset)
223 break;
224 }
225
226 SymCleanup(hProcess);
227
228 zbx_free(frame);
229 zbx_free(process_path);
230 zbx_free(process_name);
231 zbx_free(pSym);
232 }
233
zbx_win_exception_filter(unsigned int code,struct _EXCEPTION_POINTERS * ep)234 int zbx_win_exception_filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
235 {
236 zabbix_log(LOG_LEVEL_CRIT, "Unhandled exception %x detected at 0x%p. Crashing ...", code,
237 ep->ExceptionRecord->ExceptionAddress);
238
239 print_fatal_info(ep->ContextRecord);
240 print_backtrace(ep->ContextRecord);
241
242 zabbix_log(LOG_LEVEL_CRIT, "================================");
243
244 return EXCEPTION_CONTINUE_SEARCH;
245 }
246