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