Blender V4.3
system_win32.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include <Windows.h>
9#include <stdio.h>
10
11#include <dbghelp.h>
12#include <shlwapi.h>
13#include <tlhelp32.h>
14
15#include "BLI_string.h"
16
17#include "MEM_guardedalloc.h"
18
19static EXCEPTION_POINTERS *current_exception = NULL;
20
21static const char *bli_windows_get_exception_description(const DWORD exceptioncode)
22{
23 switch (exceptioncode) {
24 case EXCEPTION_ACCESS_VIOLATION:
25 return "EXCEPTION_ACCESS_VIOLATION";
26 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
27 return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
28 case EXCEPTION_BREAKPOINT:
29 return "EXCEPTION_BREAKPOINT";
30 case EXCEPTION_DATATYPE_MISALIGNMENT:
31 return "EXCEPTION_DATATYPE_MISALIGNMENT";
32 case EXCEPTION_FLT_DENORMAL_OPERAND:
33 return "EXCEPTION_FLT_DENORMAL_OPERAND";
34 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
35 return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
36 case EXCEPTION_FLT_INEXACT_RESULT:
37 return "EXCEPTION_FLT_INEXACT_RESULT";
38 case EXCEPTION_FLT_INVALID_OPERATION:
39 return "EXCEPTION_FLT_INVALID_OPERATION";
40 case EXCEPTION_FLT_OVERFLOW:
41 return "EXCEPTION_FLT_OVERFLOW";
42 case EXCEPTION_FLT_STACK_CHECK:
43 return "EXCEPTION_FLT_STACK_CHECK";
44 case EXCEPTION_FLT_UNDERFLOW:
45 return "EXCEPTION_FLT_UNDERFLOW";
46 case EXCEPTION_ILLEGAL_INSTRUCTION:
47 return "EXCEPTION_ILLEGAL_INSTRUCTION";
48 case EXCEPTION_IN_PAGE_ERROR:
49 return "EXCEPTION_IN_PAGE_ERROR";
50 case EXCEPTION_INT_DIVIDE_BY_ZERO:
51 return "EXCEPTION_INT_DIVIDE_BY_ZERO";
52 case EXCEPTION_INT_OVERFLOW:
53 return "EXCEPTION_INT_OVERFLOW";
54 case EXCEPTION_INVALID_DISPOSITION:
55 return "EXCEPTION_INVALID_DISPOSITION";
56 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
57 return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
58 case EXCEPTION_PRIV_INSTRUCTION:
59 return "EXCEPTION_PRIV_INSTRUCTION";
60 case EXCEPTION_SINGLE_STEP:
61 return "EXCEPTION_SINGLE_STEP";
62 case EXCEPTION_STACK_OVERFLOW:
63 return "EXCEPTION_STACK_OVERFLOW";
64 default:
65 return "UNKNOWN EXCEPTION";
66 }
67}
68
69static void bli_windows_get_module_name(LPVOID address, PCHAR buffer, size_t size)
70{
71 HMODULE mod;
72 buffer[0] = 0;
73 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
74 if (GetModuleFileName(mod, buffer, size)) {
75 PathStripPath(buffer);
76 }
77 }
78}
79
80static void bli_windows_get_module_version(const char *file, char *buffer, size_t buffersize)
81{
82 buffer[0] = 0;
83 DWORD verHandle = 0;
84 UINT size = 0;
85 LPBYTE lpBuffer = NULL;
86 DWORD verSize = GetFileVersionInfoSize(file, &verHandle);
87 if (verSize != 0) {
88 LPSTR verData = (LPSTR)MEM_callocN(verSize, "crash module version");
89
90 if (GetFileVersionInfo(file, verHandle, verSize, verData)) {
91 if (VerQueryValue(verData, "\\", (VOID FAR * FAR *)&lpBuffer, &size)) {
92 if (size) {
93 VS_FIXEDFILEINFO *verInfo = (VS_FIXEDFILEINFO *)lpBuffer;
94 /* Magic value from
95 * https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
96 */
97 if (verInfo->dwSignature == 0xfeef04bd) {
98 BLI_snprintf(buffer,
99 buffersize,
100 "%d.%d.%d.%d",
101 (verInfo->dwFileVersionMS >> 16) & 0xffff,
102 (verInfo->dwFileVersionMS >> 0) & 0xffff,
103 (verInfo->dwFileVersionLS >> 16) & 0xffff,
104 (verInfo->dwFileVersionLS >> 0) & 0xffff);
105 }
106 }
107 }
108 }
109 MEM_freeN(verData);
110 }
111}
112
113static void bli_windows_system_backtrace_exception_record(FILE *fp, PEXCEPTION_RECORD record)
114{
115 char module[MAX_PATH];
116 fprintf(fp, "Exception Record:\n\n");
117 fprintf(fp,
118 "ExceptionCode : %s\n",
119 bli_windows_get_exception_description(record->ExceptionCode));
120 fprintf(fp, "Exception Address : 0x%p\n", record->ExceptionAddress);
121 bli_windows_get_module_name(record->ExceptionAddress, module, sizeof(module));
122 fprintf(fp, "Exception Module : %s\n", module);
123 fprintf(fp, "Exception Flags : 0x%.8x\n", record->ExceptionFlags);
124 fprintf(fp, "Exception Parameters : 0x%x\n", record->NumberParameters);
125 for (DWORD idx = 0; idx < record->NumberParameters; idx++) {
126 fprintf(fp, "\tParameters[%d] : 0x%p\n", idx, (LPVOID *)record->ExceptionInformation[idx]);
127 }
128 if (record->ExceptionRecord) {
129 fprintf(fp, "Nested ");
130 bli_windows_system_backtrace_exception_record(fp, record->ExceptionRecord);
131 }
132 fprintf(fp, "\n\n");
133}
134
135static bool BLI_windows_system_backtrace_run_trace(FILE *fp, HANDLE hThread, PCONTEXT context)
136{
137 const int max_symbol_length = 100;
138
139 bool result = true;
140
141 PSYMBOL_INFO symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + max_symbol_length * sizeof(char),
142 "crash Symbol table");
143 symbolinfo->MaxNameLen = max_symbol_length - 1;
144 symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO);
145
146 STACKFRAME frame = {0};
147 DWORD machineType = 0;
148#if defined(_M_AMD64)
149 frame.AddrPC.Offset = context->Rip;
150 frame.AddrPC.Mode = AddrModeFlat;
151 frame.AddrFrame.Offset = context->Rsp;
152 frame.AddrFrame.Mode = AddrModeFlat;
153 frame.AddrStack.Offset = context->Rsp;
154 frame.AddrStack.Mode = AddrModeFlat;
155 machineType = IMAGE_FILE_MACHINE_AMD64;
156#elif defined(_M_ARM64)
157 frame.AddrPC.Offset = context->Pc;
158 frame.AddrPC.Mode = AddrModeFlat;
159 frame.AddrFrame.Offset = context->Fp;
160 frame.AddrFrame.Mode = AddrModeFlat;
161 frame.AddrStack.Offset = context->Sp;
162 frame.AddrStack.Mode = AddrModeFlat;
163 machineType = IMAGE_FILE_MACHINE_ARM64;
164#endif
165
166 while (true) {
167 if (StackWalk64(machineType,
168 GetCurrentProcess(),
169 hThread,
170 &frame,
171 context,
172 NULL,
173 SymFunctionTableAccess64,
174 SymGetModuleBase64,
175 0))
176 {
177 if (frame.AddrPC.Offset) {
178 char module[MAX_PATH];
179
180 bli_windows_get_module_name((LPVOID)frame.AddrPC.Offset, module, sizeof(module));
181
182 if (SymFromAddr(GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), 0, symbolinfo)) {
183 fprintf(fp, "%-20s:0x%p %s", module, (LPVOID)symbolinfo->Address, symbolinfo->Name);
184 IMAGEHLP_LINE lineinfo;
185 lineinfo.SizeOfStruct = sizeof(lineinfo);
186 DWORD displacement = 0;
187 if (SymGetLineFromAddr(
188 GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), &displacement, &lineinfo))
189 {
190 fprintf(fp, " %s:%d", lineinfo.FileName, lineinfo.LineNumber);
191 }
192 fprintf(fp, "\n");
193 }
194 else {
195 fprintf(fp,
196 "%-20s:0x%p %s\n",
197 module,
198 (LPVOID)frame.AddrPC.Offset,
199 "Symbols not available");
200 result = false;
201 break;
202 }
203 }
204 else {
205 break;
206 }
207 }
208 else {
209 break;
210 }
211 }
212 MEM_freeN(symbolinfo);
213 fprintf(fp, "\n\n");
214 return result;
215}
216
217static bool bli_windows_system_backtrace_stack_thread(FILE *fp, HANDLE hThread)
218{
219 CONTEXT context = {0};
220 context.ContextFlags = CONTEXT_ALL;
221 /* GetThreadContext requires the thread to be in a suspended state, which is problematic for the
222 * currently running thread, RtlCaptureContext is used as an alternative to sidestep this */
223 if (hThread != GetCurrentThread()) {
224 SuspendThread(hThread);
225 bool success = GetThreadContext(hThread, &context);
226 ResumeThread(hThread);
227 if (!success) {
228 fprintf(fp, "Cannot get thread context : 0x0%.8x\n", GetLastError());
229 return false;
230 }
231 }
232 else {
233 RtlCaptureContext(&context);
234 }
235 return BLI_windows_system_backtrace_run_trace(fp, hThread, &context);
236}
237
239{
240 fprintf(fp, "Loaded Modules :\n");
241 HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
242 if (hModuleSnap == INVALID_HANDLE_VALUE) {
243 return;
244 }
245
246 MODULEENTRY32 me32;
247 me32.dwSize = sizeof(MODULEENTRY32);
248
249 if (!Module32First(hModuleSnap, &me32)) {
250 CloseHandle(hModuleSnap); /* Must clean up the snapshot object! */
251 fprintf(fp, " Error getting module list.\n");
252 return;
253 }
254
255 do {
256 if (me32.th32ProcessID == GetCurrentProcessId()) {
257 char version[MAX_PATH];
258 bli_windows_get_module_version(me32.szExePath, version, sizeof(version));
259
260 IMAGEHLP_MODULE64 m64;
261 m64.SizeOfStruct = sizeof(m64);
262 if (SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)me32.modBaseAddr, &m64)) {
263 fprintf(fp,
264 "0x%p %-20s %s %s %s\n",
265 me32.modBaseAddr,
266 version,
267 me32.szModule,
268 m64.LoadedPdbName,
269 m64.PdbUnmatched ? "[unmatched]" : "");
270 }
271 else {
272 fprintf(fp, "0x%p %-20s %s\n", me32.modBaseAddr, version, me32.szModule);
273 }
274 }
275 } while (Module32Next(hModuleSnap, &me32));
276}
277
279{
280 fprintf(fp, "Threads:\n");
281 HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
282 THREADENTRY32 te32;
283
284 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
285 if (hThreadSnap == INVALID_HANDLE_VALUE) {
286 fprintf(fp, "Unable to retrieve threads list.\n");
287 return;
288 }
289
290 te32.dwSize = sizeof(THREADENTRY32);
291
292 if (!Thread32First(hThreadSnap, &te32)) {
293 CloseHandle(hThreadSnap);
294 return;
295 }
296 do {
297 if (te32.th32OwnerProcessID == GetCurrentProcessId()) {
298 if (GetCurrentThreadId() != te32.th32ThreadID) {
299 fprintf(fp, "Thread : %.8x\n", te32.th32ThreadID);
300 HANDLE ht = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
302 CloseHandle(ht);
303 }
304 }
305 } while (Thread32Next(hThreadSnap, &te32));
306 CloseHandle(hThreadSnap);
307}
308
310{
311 fprintf(fp, "Stack trace:\n");
312 /* If we are handling an exception use the context record from that. */
313 if (current_exception && current_exception->ExceptionRecord->ExceptionAddress) {
314 /* The back trace code will write to the context record, to protect the original record from
315 * modifications give the backtrace a copy to work on. */
316 CONTEXT TempContext = *current_exception->ContextRecord;
317 return BLI_windows_system_backtrace_run_trace(fp, GetCurrentThread(), &TempContext);
318 }
319 else {
320 /* If there is no current exception or the address is not set, walk the current stack. */
321 return bli_windows_system_backtrace_stack_thread(fp, GetCurrentThread());
322 }
323}
324
326{
327 IMAGEHLP_MODULE64 m64;
328 m64.SizeOfStruct = sizeof(m64);
329 if (SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)GetModuleHandle(NULL), &m64)) {
330 return m64.GlobalSymbols;
331 }
332 return false;
333}
334
335static void bli_load_symbols()
336{
337 /* If this is a developer station and the private pdb is already loaded leave it be. */
339 return;
340 }
341
342 char pdb_file[MAX_PATH] = {0};
343
344 /* get the currently executing image */
345 if (GetModuleFileNameA(NULL, pdb_file, sizeof(pdb_file))) {
346 /* remove the filename */
347 PathRemoveFileSpecA(pdb_file);
348 /* append blender.pdb */
349 PathAppendA(pdb_file, "blender.pdb");
350 if (PathFileExistsA(pdb_file)) {
351 HMODULE mod = GetModuleHandle(NULL);
352 if (mod) {
353 WIN32_FILE_ATTRIBUTE_DATA file_data;
354 if (GetFileAttributesExA(pdb_file, GetFileExInfoStandard, &file_data)) {
355 /* SymInitialize will try to load symbols on its own, so we first must unload whatever it
356 * did trying to help */
357 SymUnloadModule64(GetCurrentProcess(), (DWORD64)mod);
358
359 DWORD64 module_base = SymLoadModule(GetCurrentProcess(),
360 NULL,
361 pdb_file,
362 NULL,
363 (DWORD64)mod,
364 (DWORD)file_data.nFileSizeLow);
365 if (module_base == 0) {
366 fprintf(stderr,
367 "Error loading symbols %s\n\terror:0x%.8x\n\tsize = %d\n\tbase=0x%p\n",
368 pdb_file,
369 GetLastError(),
370 file_data.nFileSizeLow,
371 (LPVOID)mod);
372 }
373 }
374 }
375 }
376 }
377}
378
383{
384 SymInitialize(GetCurrentProcess(), NULL, TRUE);
386 if (current_exception) {
388 }
390 /* When the blender symbols are missing the stack traces will be unreliable
391 * so only run if the previous step completed successfully. */
393 }
395}
396
397void BLI_windows_handle_exception(EXCEPTION_POINTERS *exception)
398{
399 current_exception = exception;
400 if (current_exception) {
401 fprintf(stderr,
402 "Error : %s\n",
403 bli_windows_get_exception_description(exception->ExceptionRecord->ExceptionCode));
404 fflush(stderr);
405
406 LPVOID address = exception->ExceptionRecord->ExceptionAddress;
407 fprintf(stderr, "Address : 0x%p\n", address);
408
409 CHAR modulename[MAX_PATH];
410 bli_windows_get_module_name(address, modulename, sizeof(modulename));
411 fprintf(stderr, "Module : %s\n", modulename);
412 fprintf(stderr, "Thread : %.8x\n", GetCurrentThreadId());
413 }
414 fflush(stderr);
415}
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define FALSE
Read Guarded memory(de)allocation.
#define NULL
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static struct PyModuleDef module
Definition python.cpp:991
static void bli_windows_get_module_version(const char *file, char *buffer, size_t buffersize)
static bool bli_windows_system_backtrace_stack_thread(FILE *fp, HANDLE hThread)
static void bli_windows_get_module_name(LPVOID address, PCHAR buffer, size_t size)
static void bli_windows_system_backtrace_modules(FILE *fp)
static void bli_windows_system_backtrace_exception_record(FILE *fp, PEXCEPTION_RECORD record)
static bool bli_private_symbols_loaded()
void BLI_windows_handle_exception(EXCEPTION_POINTERS *exception)
void BLI_system_backtrace(FILE *fp)
static EXCEPTION_POINTERS * current_exception
static void bli_windows_system_backtrace_threads(FILE *fp)
static bool BLI_windows_system_backtrace_stack(FILE *fp)
static void bli_load_symbols()
static bool BLI_windows_system_backtrace_run_trace(FILE *fp, HANDLE hThread, PCONTEXT context)
static const char * bli_windows_get_exception_description(const DWORD exceptioncode)
ccl_device_inline int mod(int x, int m)
Definition util/math.h:520