1 /*
2 * win32_crashrpt.c : provides information after a crash
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* prevent "empty compilation unit" warning on e.g. UNIX */
25 typedef int win32_crashrpt__dummy;
26
27 #ifdef WIN32
28 #ifdef SVN_USE_WIN32_CRASHHANDLER
29
30 /*** Includes. ***/
31 #include <apr.h>
32 #include <dbghelp.h>
33 #include <direct.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <time.h>
37
38 #include "svn_version.h"
39
40 #include "sysinfo.h"
41
42 #include "win32_crashrpt.h"
43 #include "win32_crashrpt_dll.h"
44
45 /*** Global variables ***/
46 HANDLE dbghelp_dll = INVALID_HANDLE_VALUE;
47
48 /* Email address where the crash reports should be sent too. */
49 #define CRASHREPORT_EMAIL "users@subversion.apache.org"
50
51 #define DBGHELP_DLL "dbghelp.dll"
52
53 #define LOGFILE_PREFIX "svn-crash-log"
54
55 #if defined(_M_IX86)
56 #define FORMAT_PTR "0x%08x"
57 #elif defined(_M_X64)
58 #define FORMAT_PTR "0x%016I64x"
59 #endif
60
61 /*** Code. ***/
62
63 /* Convert a wide-character string to the current windows locale, suitable
64 * for directly using stdio. This function will create a buffer large
65 * enough to hold the result string, the caller should free this buffer.
66 * If the string can't be converted, NULL is returned.
67 */
68 static char *
convert_wbcs_to_ansi(const wchar_t * str)69 convert_wbcs_to_ansi(const wchar_t *str)
70 {
71 size_t len = wcslen(str);
72 char *utf8_str = malloc(sizeof(wchar_t) * len + 1);
73 len = wcstombs(utf8_str, str, len);
74
75 if (len == -1)
76 return NULL;
77
78 utf8_str[len] = '\0';
79
80 return utf8_str;
81 }
82
83 /* Convert the exception code to a string */
84 static const char *
exception_string(int exception)85 exception_string(int exception)
86 {
87 #define EXCEPTION(x) case EXCEPTION_##x: return (#x);
88
89 switch (exception)
90 {
91 EXCEPTION(ACCESS_VIOLATION)
92 EXCEPTION(DATATYPE_MISALIGNMENT)
93 EXCEPTION(BREAKPOINT)
94 EXCEPTION(SINGLE_STEP)
95 EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
96 EXCEPTION(FLT_DENORMAL_OPERAND)
97 EXCEPTION(FLT_DIVIDE_BY_ZERO)
98 EXCEPTION(FLT_INEXACT_RESULT)
99 EXCEPTION(FLT_INVALID_OPERATION)
100 EXCEPTION(FLT_OVERFLOW)
101 EXCEPTION(FLT_STACK_CHECK)
102 EXCEPTION(FLT_UNDERFLOW)
103 EXCEPTION(INT_DIVIDE_BY_ZERO)
104 EXCEPTION(INT_OVERFLOW)
105 EXCEPTION(PRIV_INSTRUCTION)
106 EXCEPTION(IN_PAGE_ERROR)
107 EXCEPTION(ILLEGAL_INSTRUCTION)
108 EXCEPTION(NONCONTINUABLE_EXCEPTION)
109 EXCEPTION(STACK_OVERFLOW)
110 EXCEPTION(INVALID_DISPOSITION)
111 EXCEPTION(GUARD_PAGE)
112 EXCEPTION(INVALID_HANDLE)
113
114 default:
115 return "UNKNOWN_ERROR";
116 }
117 #undef EXCEPTION
118 }
119
120 /* Write the minidump to file. The callback function will at the same time
121 write the list of modules to the log file. */
122 static BOOL
write_minidump_file(const char * file,PEXCEPTION_POINTERS ptrs,MINIDUMP_CALLBACK_ROUTINE module_callback,void * data)123 write_minidump_file(const char *file, PEXCEPTION_POINTERS ptrs,
124 MINIDUMP_CALLBACK_ROUTINE module_callback,
125 void *data)
126 {
127 /* open minidump file */
128 HANDLE minidump_file = CreateFile(file, GENERIC_WRITE, 0, NULL,
129 CREATE_ALWAYS,
130 FILE_ATTRIBUTE_NORMAL,
131 NULL);
132
133 if (minidump_file != INVALID_HANDLE_VALUE)
134 {
135 MINIDUMP_EXCEPTION_INFORMATION expt_info;
136 MINIDUMP_CALLBACK_INFORMATION dump_cb_info;
137
138 expt_info.ThreadId = GetCurrentThreadId();
139 expt_info.ExceptionPointers = ptrs;
140 expt_info.ClientPointers = FALSE;
141
142 dump_cb_info.CallbackRoutine = module_callback;
143 dump_cb_info.CallbackParam = data;
144
145 MiniDumpWriteDump_(GetCurrentProcess(),
146 GetCurrentProcessId(),
147 minidump_file,
148 MiniDumpNormal,
149 ptrs ? &expt_info : NULL,
150 NULL,
151 &dump_cb_info);
152
153 CloseHandle(minidump_file);
154 return TRUE;
155 }
156
157 return FALSE;
158 }
159
160 /* Write module information to the log file */
161 static BOOL CALLBACK
write_module_info_callback(void * data,CONST PMINIDUMP_CALLBACK_INPUT callback_input,PMINIDUMP_CALLBACK_OUTPUT callback_output)162 write_module_info_callback(void *data,
163 CONST PMINIDUMP_CALLBACK_INPUT callback_input,
164 PMINIDUMP_CALLBACK_OUTPUT callback_output)
165 {
166 if (data != NULL &&
167 callback_input != NULL &&
168 callback_input->CallbackType == ModuleCallback)
169 {
170 FILE *log_file = (FILE *)data;
171 MINIDUMP_MODULE_CALLBACK module = callback_input->Module;
172
173 char *buf = convert_wbcs_to_ansi(module.FullPath);
174 fprintf(log_file, FORMAT_PTR, module.BaseOfImage);
175 fprintf(log_file, " %s", buf);
176 free(buf);
177
178 fprintf(log_file, " (%d.%d.%d.%d, %d bytes)\n",
179 HIWORD(module.VersionInfo.dwFileVersionMS),
180 LOWORD(module.VersionInfo.dwFileVersionMS),
181 HIWORD(module.VersionInfo.dwFileVersionLS),
182 LOWORD(module.VersionInfo.dwFileVersionLS),
183 module.SizeOfImage);
184 }
185
186 return TRUE;
187 }
188
189 /* Write details about the current process, platform and the exception */
190 static void
write_process_info(EXCEPTION_RECORD * exception,CONTEXT * context,FILE * log_file)191 write_process_info(EXCEPTION_RECORD *exception, CONTEXT *context,
192 FILE *log_file)
193 {
194 OSVERSIONINFOEXW oi;
195 const char *cmd_line;
196 char workingdir[8192];
197
198 /* write the command line */
199 cmd_line = GetCommandLine();
200 fprintf(log_file,
201 "Cmd line: %s\n", cmd_line);
202
203 _getcwd(workingdir, sizeof(workingdir));
204 fprintf(log_file,
205 "Working Dir: %s\n", workingdir);
206
207 /* write the svn version number info. */
208 fprintf(log_file,
209 "Version: %s, compiled %s, %s\n",
210 SVN_VERSION, __DATE__, __TIME__);
211
212 /* write information about the OS */
213 if (svn_sysinfo___fill_windows_version(&oi))
214 fprintf(log_file,
215 "Platform: Windows OS version %d.%d build %d %S\n\n",
216 oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber,
217 oi.szCSDVersion);
218
219 /* write the exception code */
220 fprintf(log_file,
221 "Exception: %s\n\n",
222 exception_string(exception->ExceptionCode));
223
224 /* write the register info. */
225 fprintf(log_file,
226 "Registers:\n");
227 #if defined(_M_IX86)
228 fprintf(log_file,
229 "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n",
230 context->Eax, context->Ebx, context->Ecx,
231 context->Edx, context->Esi, context->Edi);
232 fprintf(log_file,
233 "eip=%08x esp=%08x ebp=%08x efl=%08x\n",
234 context->Eip, context->Esp,
235 context->Ebp, context->EFlags);
236 fprintf(log_file,
237 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x\n",
238 context->SegCs, context->SegSs, context->SegDs,
239 context->SegEs, context->SegFs, context->SegGs);
240 #elif defined(_M_X64)
241 fprintf(log_file,
242 "Rax=%016I64x Rcx=%016I64x Rdx=%016I64x Rbx=%016I64x\n",
243 context->Rax, context->Rcx, context->Rdx, context->Rbx);
244 fprintf(log_file,
245 "Rsp=%016I64x Rbp=%016I64x Rsi=%016I64x Rdi=%016I64x\n",
246 context->Rsp, context->Rbp, context->Rsi, context->Rdi);
247 fprintf(log_file,
248 "R8= %016I64x R9= %016I64x R10=%016I64x R11=%016I64x\n",
249 context->R8, context->R9, context->R10, context->R11);
250 fprintf(log_file,
251 "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n",
252 context->R12, context->R13, context->R14, context->R15);
253
254 fprintf(log_file,
255 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x\n",
256 context->SegCs, context->SegSs, context->SegDs,
257 context->SegEs, context->SegFs, context->SegGs);
258 #else
259 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
260 #endif
261 }
262
263 /* Formats the value at address based on the specified basic type
264 * (char, int, long ...). */
265 static void
format_basic_type(char * buf,DWORD basic_type,DWORD64 length,void * address)266 format_basic_type(char *buf, DWORD basic_type, DWORD64 length, void *address)
267 {
268 switch(length)
269 {
270 case 1:
271 sprintf(buf, "0x%02x", (int)*(unsigned char *)address);
272 break;
273 case 2:
274 sprintf(buf, "0x%04x", (int)*(unsigned short *)address);
275 break;
276 case 4:
277 switch(basic_type)
278 {
279 case 2: /* btChar */
280 {
281 if (!IsBadStringPtr(*(PSTR*)address, 32))
282 sprintf(buf, "\"%.31s\"", *(const char **)address);
283 else
284 sprintf(buf, FORMAT_PTR, *(DWORD_PTR *)address);
285 }
286 case 6: /* btInt */
287 sprintf(buf, "%d", *(int *)address);
288 break;
289 case 8: /* btFloat */
290 sprintf(buf, "%f", *(float *)address);
291 break;
292 default:
293 sprintf(buf, FORMAT_PTR, *(DWORD_PTR *)address);
294 break;
295 }
296 break;
297 case 8:
298 if (basic_type == 8) /* btFloat */
299 sprintf(buf, "%lf", *(double *)address);
300 else
301 sprintf(buf, "0x%016I64X", *(unsigned __int64 *)address);
302 break;
303 default:
304 sprintf(buf, "[unhandled type 0x%08x of length " FORMAT_PTR "]",
305 basic_type, length);
306 break;
307 }
308 }
309
310 /* Formats the value at address based on the type (pointer, user defined,
311 * basic type). */
312 static void
format_value(char * value_str,DWORD64 mod_base,DWORD type,void * value_addr)313 format_value(char *value_str, DWORD64 mod_base, DWORD type, void *value_addr)
314 {
315 DWORD tag = 0;
316 int ptr = 0;
317 HANDLE proc = GetCurrentProcess();
318
319 while (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMTAG, &tag))
320 {
321 /* SymTagPointerType */
322 if (tag == 14)
323 {
324 ptr++;
325 SymGetTypeInfo_(proc, mod_base, type, TI_GET_TYPE, &type);
326 continue;
327 }
328 break;
329 }
330
331 switch(tag)
332 {
333 case 11: /* SymTagUDT */
334 {
335 WCHAR *type_name_wbcs;
336 if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMNAME,
337 &type_name_wbcs))
338 {
339 char *type_name = convert_wbcs_to_ansi(type_name_wbcs);
340 LocalFree(type_name_wbcs);
341
342 if (ptr == 0)
343 sprintf(value_str, "(%s) " FORMAT_PTR,
344 type_name, (DWORD_PTR *)value_addr);
345 else if (ptr == 1)
346 sprintf(value_str, "(%s *) " FORMAT_PTR,
347 type_name, *(DWORD_PTR *)value_addr);
348 else
349 sprintf(value_str, "(%s **) " FORMAT_PTR,
350 type_name, *(DWORD_PTR *)value_addr);
351
352 free(type_name);
353 }
354 else
355 sprintf(value_str, "[no symbol tag]");
356 }
357 break;
358 case 16: /* SymTagBaseType */
359 {
360 DWORD bt;
361 ULONG64 length;
362 SymGetTypeInfo_(proc, mod_base, type, TI_GET_LENGTH, &length);
363
364 /* print a char * as a string */
365 if (ptr == 1 && length == 1)
366 {
367 sprintf(value_str, FORMAT_PTR " \"%s\"",
368 *(DWORD_PTR *)value_addr, *(const char **)value_addr);
369 }
370 else if (ptr >= 1)
371 {
372 sprintf(value_str, FORMAT_PTR, *(DWORD_PTR *)value_addr);
373 }
374 else if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_BASETYPE, &bt))
375 {
376 format_basic_type(value_str, bt, length, value_addr);
377 }
378 }
379 break;
380 case 12: /* SymTagEnum */
381 sprintf(value_str, "%d", *(DWORD_PTR *)value_addr);
382 break;
383 case 13: /* SymTagFunctionType */
384 sprintf(value_str, FORMAT_PTR, *(DWORD_PTR *)value_addr);
385 break;
386 default:
387 sprintf(value_str, "[unhandled tag: %d]", tag);
388 break;
389 }
390 }
391
392 /* Internal structure used to pass some data to the enumerate symbols
393 * callback */
394 typedef struct symbols_baton_t {
395 STACKFRAME64 *stack_frame;
396 FILE *log_file;
397 int nr_of_frame;
398 BOOL log_params;
399 } symbols_baton_t;
400
401 /* Write the details of one parameter or local variable to the log file */
402 static BOOL WINAPI
write_var_values(PSYMBOL_INFO sym_info,ULONG sym_size,void * baton)403 write_var_values(PSYMBOL_INFO sym_info, ULONG sym_size, void *baton)
404 {
405 static int last_nr_of_frame = 0;
406 DWORD_PTR var_data = 0; /* Will point to the variable's data in memory */
407 STACKFRAME64 *stack_frame = ((symbols_baton_t*)baton)->stack_frame;
408 FILE *log_file = ((symbols_baton_t*)baton)->log_file;
409 int nr_of_frame = ((symbols_baton_t*)baton)->nr_of_frame;
410 BOOL log_params = ((symbols_baton_t*)baton)->log_params;
411 char value_str[256] = "";
412
413 /* get the variable's data */
414 if (sym_info->Flags & SYMFLAG_REGREL)
415 {
416 var_data = (DWORD_PTR)stack_frame->AddrFrame.Offset;
417 var_data += (DWORD_PTR)sym_info->Address;
418 }
419 else
420 return FALSE;
421
422 if (log_params && sym_info->Flags & SYMFLAG_PARAMETER)
423 {
424 if (last_nr_of_frame == nr_of_frame)
425 fprintf(log_file, ", ", 2);
426 else
427 last_nr_of_frame = nr_of_frame;
428
429 format_value(value_str, sym_info->ModBase, sym_info->TypeIndex,
430 (void *)var_data);
431 fprintf(log_file, "%.*s=%s", (int)sym_info->NameLen, sym_info->Name,
432 value_str);
433 }
434 if (!log_params && sym_info->Flags & SYMFLAG_LOCAL)
435 {
436 format_value(value_str, sym_info->ModBase, sym_info->TypeIndex,
437 (void *)var_data);
438 fprintf(log_file, " %.*s = %s\n", (int)sym_info->NameLen,
439 sym_info->Name, value_str);
440 }
441
442 return TRUE;
443 }
444
445 /* Write the details of one function to the log file */
446 static void
write_function_detail(STACKFRAME64 stack_frame,int nr_of_frame,FILE * log_file)447 write_function_detail(STACKFRAME64 stack_frame, int nr_of_frame, FILE *log_file)
448 {
449 ULONG64 symbolBuffer[(sizeof(SYMBOL_INFO) +
450 MAX_SYM_NAME +
451 sizeof(ULONG64) - 1) /
452 sizeof(ULONG64)];
453 PSYMBOL_INFO pIHS = (PSYMBOL_INFO)symbolBuffer;
454 DWORD64 func_disp=0;
455
456 IMAGEHLP_STACK_FRAME ih_stack_frame;
457 IMAGEHLP_LINE64 ih_line;
458 DWORD line_disp=0;
459
460 HANDLE proc = GetCurrentProcess();
461
462 symbols_baton_t ensym;
463
464 nr_of_frame++; /* We need a 1 based index here */
465
466 /* log the function name */
467 pIHS->SizeOfStruct = sizeof(SYMBOL_INFO);
468 pIHS->MaxNameLen = MAX_SYM_NAME;
469 if (SymFromAddr_(proc, stack_frame.AddrPC.Offset, &func_disp, pIHS))
470 {
471 fprintf(log_file,
472 "#%d 0x%08I64x in %.*s(",
473 nr_of_frame, stack_frame.AddrPC.Offset,
474 pIHS->NameLen > 200 ? 200 : (int)pIHS->NameLen,
475 pIHS->Name);
476
477 /* restrict symbol enumeration to this frame only */
478 ih_stack_frame.InstructionOffset = stack_frame.AddrPC.Offset;
479 SymSetContext_(proc, &ih_stack_frame, 0);
480
481 ensym.log_file = log_file;
482 ensym.stack_frame = &stack_frame;
483 ensym.nr_of_frame = nr_of_frame;
484
485 /* log all function parameters */
486 ensym.log_params = TRUE;
487 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym);
488
489 fprintf(log_file, ")");
490 }
491 else
492 {
493 fprintf(log_file,
494 "#%d 0x%08I64x in (unknown function)",
495 nr_of_frame, stack_frame.AddrPC.Offset);
496 }
497
498 /* find the source line for this function. */
499 ih_line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
500 if (SymGetLineFromAddr64_(proc, stack_frame.AddrPC.Offset,
501 &line_disp, &ih_line) != 0)
502 {
503 fprintf(log_file,
504 " at %s:%d\n", ih_line.FileName, ih_line.LineNumber);
505 }
506 else
507 {
508 fprintf(log_file, "\n");
509 }
510
511 /* log all function local variables */
512 ensym.log_params = FALSE;
513 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym);
514 }
515
516 /* Walk over the stack and log all relevant information to the log file */
517 static void
write_stacktrace(CONTEXT * context,FILE * log_file)518 write_stacktrace(CONTEXT *context, FILE *log_file)
519 {
520 #if defined (_M_IX86) || defined(_M_X64) || defined(_M_IA64)
521 HANDLE proc = GetCurrentProcess();
522 STACKFRAME64 stack_frame;
523 DWORD machine;
524 CONTEXT ctx;
525 int skip = 0, i = 0;
526
527 /* The thread information - if not supplied. */
528 if (context == NULL)
529 {
530 /* If no context is supplied, skip 1 frame */
531 skip = 1;
532
533 ctx.ContextFlags = CONTEXT_FULL;
534 if (!GetThreadContext(GetCurrentThread(), &ctx))
535 return;
536 }
537 else
538 {
539 ctx = *context;
540 }
541
542 if (context == NULL)
543 return;
544
545 /* Write the stack trace */
546 ZeroMemory(&stack_frame, sizeof(STACKFRAME64));
547 stack_frame.AddrPC.Mode = AddrModeFlat;
548 stack_frame.AddrStack.Mode = AddrModeFlat;
549 stack_frame.AddrFrame.Mode = AddrModeFlat;
550
551 #if defined(_M_IX86)
552 machine = IMAGE_FILE_MACHINE_I386;
553 stack_frame.AddrPC.Offset = context->Eip;
554 stack_frame.AddrStack.Offset = context->Esp;
555 stack_frame.AddrFrame.Offset = context->Ebp;
556 #elif defined(_M_X64)
557 machine = IMAGE_FILE_MACHINE_AMD64;
558 stack_frame.AddrPC.Offset = context->Rip;
559 stack_frame.AddrStack.Offset = context->Rsp;
560 stack_frame.AddrFrame.Offset = context->Rbp;
561 #elif defined(_M_IA64)
562 machine = IMAGE_FILE_MACHINE_IA64;
563 stack_frame.AddrPC.Offset = context->StIIP;
564 stack_frame.AddrStack.Offset = context->SP;
565 stack_frame.AddrBStore.Mode = AddrModeFlat;
566 stack_frame.AddrBStore.Offset = context->RsBSP;
567 #else
568 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
569 #endif
570
571 while (1)
572 {
573 if (! StackWalk64_(machine, proc, GetCurrentThread(),
574 &stack_frame, &ctx, NULL,
575 SymFunctionTableAccess64_, SymGetModuleBase64_, NULL))
576 {
577 break;
578 }
579
580 if (i >= skip)
581 {
582 /* Try to include symbolic information.
583 Also check that the address is not zero. Sometimes StackWalk
584 returns TRUE with a frame of zero. */
585 if (stack_frame.AddrPC.Offset != 0)
586 {
587 write_function_detail(stack_frame, i, log_file);
588 }
589 }
590 i++;
591 }
592 #else
593 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
594 #endif
595 }
596
597 /* Check if a debugger is attached to this process */
598 static BOOL
is_debugger_present()599 is_debugger_present()
600 {
601 HANDLE kernel32_dll = LoadLibrary("kernel32.dll");
602 BOOL result;
603
604 ISDEBUGGERPRESENT IsDebuggerPresent_ =
605 (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent");
606
607 if (IsDebuggerPresent_ && IsDebuggerPresent_())
608 result = TRUE;
609 else
610 result = FALSE;
611
612 FreeLibrary(kernel32_dll);
613
614 return result;
615 }
616
617 /* Load the dbghelp.dll file, try to find a version that matches our
618 requirements. */
619 static BOOL
load_dbghelp_dll()620 load_dbghelp_dll()
621 {
622 dbghelp_dll = LoadLibrary(DBGHELP_DLL);
623 if (dbghelp_dll != INVALID_HANDLE_VALUE)
624 {
625 DWORD opts;
626
627 /* load the functions */
628 MiniDumpWriteDump_ =
629 (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump");
630 SymInitialize_ =
631 (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize");
632 SymSetOptions_ =
633 (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions");
634 SymGetOptions_ =
635 (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions");
636 SymCleanup_ =
637 (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup");
638 SymGetTypeInfo_ =
639 (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo");
640 SymGetLineFromAddr64_ =
641 (SYMGETLINEFROMADDR64)GetProcAddress(dbghelp_dll,
642 "SymGetLineFromAddr64");
643 SymEnumSymbols_ =
644 (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols");
645 SymSetContext_ =
646 (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext");
647 SymFromAddr_ = (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr");
648 StackWalk64_ = (STACKWALK64)GetProcAddress(dbghelp_dll, "StackWalk64");
649 SymFunctionTableAccess64_ =
650 (SYMFUNCTIONTABLEACCESS64)GetProcAddress(dbghelp_dll,
651 "SymFunctionTableAccess64");
652 SymGetModuleBase64_ =
653 (SYMGETMODULEBASE64)GetProcAddress(dbghelp_dll, "SymGetModuleBase64");
654
655 if (! (MiniDumpWriteDump_ &&
656 SymInitialize_ && SymSetOptions_ && SymGetOptions_ &&
657 SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr64_ &&
658 SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ &&
659 SymGetModuleBase64_ && StackWalk64_ &&
660 SymFunctionTableAccess64_))
661 goto cleanup;
662
663 /* initialize the symbol loading code */
664 opts = SymGetOptions_();
665
666 /* Set the 'load lines' option to retrieve line number information;
667 set the Deferred Loads option to map the debug info in memory only
668 when needed. */
669 SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
670
671 /* Initialize the debughlp DLL with the default path and automatic
672 module enumeration (and loading of symbol tables) for this process.
673 */
674 SymInitialize_(GetCurrentProcess(), NULL, TRUE);
675
676 return TRUE;
677 }
678
679 cleanup:
680 if (dbghelp_dll)
681 FreeLibrary(dbghelp_dll);
682
683 return FALSE;
684 }
685
686 /* Cleanup the dbghelp.dll library */
687 static void
cleanup_debughlp()688 cleanup_debughlp()
689 {
690 SymCleanup_(GetCurrentProcess());
691
692 FreeLibrary(dbghelp_dll);
693 }
694
695 /* Create a filename based on a prefix, the timestamp and an extension.
696 check if the filename was already taken, retry 3 times. */
697 BOOL
get_temp_filename(char * filename,const char * prefix,const char * ext)698 get_temp_filename(char *filename, const char *prefix, const char *ext)
699 {
700 char temp_dir[MAX_PATH - 64];
701 int i;
702
703 if (! GetTempPath(MAX_PATH - 64, temp_dir))
704 return FALSE;
705
706 for (i = 0;i < 3;i++)
707 {
708 HANDLE file;
709 time_t now;
710 char time_str[64];
711
712 time(&now);
713 strftime(time_str, 64, "%Y%m%d%H%M%S", localtime(&now));
714 sprintf(filename, "%s%s%s.%s", temp_dir, prefix, time_str, ext);
715
716 file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW,
717 FILE_ATTRIBUTE_NORMAL, NULL);
718 if (file != INVALID_HANDLE_VALUE)
719 {
720 CloseHandle(file);
721 return TRUE;
722 }
723 }
724
725 filename[0] = '\0';
726 return FALSE;
727 }
728
729 /* Unhandled exception callback set with SetUnhandledExceptionFilter() */
730 LONG WINAPI
svn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs)731 svn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs)
732 {
733 char dmp_filename[MAX_PATH];
734 char log_filename[MAX_PATH];
735 FILE *log_file;
736
737 /* Check if the crash handler was already loaded (crash while handling the
738 crash) */
739 if (dbghelp_dll != INVALID_HANDLE_VALUE)
740 return EXCEPTION_CONTINUE_SEARCH;
741
742 /* don't log anything if we're running inside a debugger ... */
743 if (is_debugger_present())
744 return EXCEPTION_CONTINUE_SEARCH;
745
746 /* ... or if we can't create the log files ... */
747 if (!get_temp_filename(dmp_filename, LOGFILE_PREFIX, "dmp") ||
748 !get_temp_filename(log_filename, LOGFILE_PREFIX, "log"))
749 return EXCEPTION_CONTINUE_SEARCH;
750
751 /* If we can't load a recent version of the dbghelp.dll, pass on this
752 exception */
753 if (!load_dbghelp_dll())
754 return EXCEPTION_CONTINUE_SEARCH;
755
756 /* open log file */
757 log_file = fopen(log_filename, "w+");
758
759 /* write information about the process */
760 fprintf(log_file, "\nProcess info:\n");
761 write_process_info(ptrs ? ptrs->ExceptionRecord : NULL,
762 ptrs ? ptrs->ContextRecord : NULL,
763 log_file);
764
765 /* write the stacktrace, if available */
766 fprintf(log_file, "\nStacktrace:\n");
767 write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, log_file);
768
769 /* write the minidump file and use the callback to write the list of modules
770 to the log file */
771 fprintf(log_file, "\n\nLoaded modules:\n");
772 write_minidump_file(dmp_filename, ptrs,
773 write_module_info_callback, (void *)log_file);
774
775 fclose(log_file);
776
777 /* inform the user */
778 fprintf(stderr, "This application has halted due to an unexpected error.\n"
779 "A crash report and minidump file were saved to disk, you"
780 " can find them here:\n"
781 "%s\n%s\n"
782 "Please send the log file to %s to help us analyze\nand "
783 "solve this problem.\n\n"
784 "NOTE: The crash report and minidump files can contain some"
785 " sensitive information\n(filenames, partial file content, "
786 "usernames and passwords etc.)\n",
787 log_filename,
788 dmp_filename,
789 CRASHREPORT_EMAIL);
790
791 if (getenv("SVN_DBG_STACKTRACES_TO_STDERR") != NULL)
792 {
793 fprintf(stderr, "\nProcess info:\n");
794 write_process_info(ptrs ? ptrs->ExceptionRecord : NULL,
795 ptrs ? ptrs->ContextRecord : NULL,
796 stderr);
797 fprintf(stderr, "\nStacktrace:\n");
798 write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, stderr);
799 }
800
801 fflush(stderr);
802 fflush(stdout);
803
804 cleanup_debughlp();
805
806 /* terminate the application */
807 return EXCEPTION_EXECUTE_HANDLER;
808 }
809 #endif /* SVN_USE_WIN32_CRASHHANDLER */
810 #endif /* WIN32 */
811