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