Remove no-longer-used svn_*_get_mergeinfo_for_tree APIs.
[svn.git] / subversion / libsvn_subr / win32_crashrpt.c
blob854bb496ed6aa47f2de8f79221b71afcd06ac2b4
1 /*
2 * win32_crashrpt.c : provides information after a crash
4 * ====================================================================
5 * Copyright (c) 2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 #ifdef WIN32
20 #ifdef SVN_USE_WIN32_CRASHHANDLER
22 /*** Includes. ***/
23 #ifdef APR_HAVE_IPV6
24 #include <winsock2.h>
25 #endif
26 #include <windows.h>
27 #include <dbghelp.h>
28 #include <stdio.h>
29 #include <stdlib.h>
31 #include "svn_version.h"
33 #include "win32_crashrpt.h"
34 #include "win32_crashrpt_dll.h"
36 /*** Global variables ***/
37 HANDLE dbghelp_dll = INVALID_HANDLE_VALUE;
39 /* email address where the crash reports should be sent too. */
40 #define CRASHREPORT_EMAIL "svn-breakage@subversion.tigris.org"
42 #define DBGHELP_DLL "dbghelp.dll"
44 #define VERSION_DLL "version.dll"
46 #define LOGFILE_PREFIX "svn-crash-log"
48 /*** Code. ***/
50 /* Convert a wide-character string to utf-8. This function will create a buffer
51 * large enough to hold the result string, the caller should free this buffer.
52 * If the string can't be converted, NULL is returned.
54 static char *
55 convert_wbcs_to_utf8(const wchar_t *str)
57 size_t len = wcslen(str);
58 char *utf8_str = malloc(sizeof(wchar_t) * len + 1);
59 len = wcstombs(utf8_str, str, len);
61 if (len == -1)
62 return NULL;
64 utf8_str[len] = '\0';
66 return utf8_str;
69 /* Convert the exception code to a string */
70 static const char *
71 exception_string(int exception)
73 #define EXCEPTION(x) case EXCEPTION_##x: return (#x);
75 switch (exception)
77 EXCEPTION(ACCESS_VIOLATION)
78 EXCEPTION(DATATYPE_MISALIGNMENT)
79 EXCEPTION(BREAKPOINT)
80 EXCEPTION(SINGLE_STEP)
81 EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
82 EXCEPTION(FLT_DENORMAL_OPERAND)
83 EXCEPTION(FLT_DIVIDE_BY_ZERO)
84 EXCEPTION(FLT_INEXACT_RESULT)
85 EXCEPTION(FLT_INVALID_OPERATION)
86 EXCEPTION(FLT_OVERFLOW)
87 EXCEPTION(FLT_STACK_CHECK)
88 EXCEPTION(FLT_UNDERFLOW)
89 EXCEPTION(INT_DIVIDE_BY_ZERO)
90 EXCEPTION(INT_OVERFLOW)
91 EXCEPTION(PRIV_INSTRUCTION)
92 EXCEPTION(IN_PAGE_ERROR)
93 EXCEPTION(ILLEGAL_INSTRUCTION)
94 EXCEPTION(NONCONTINUABLE_EXCEPTION)
95 EXCEPTION(STACK_OVERFLOW)
96 EXCEPTION(INVALID_DISPOSITION)
97 EXCEPTION(GUARD_PAGE)
98 EXCEPTION(INVALID_HANDLE)
100 default:
101 return "UNKNOWN_ERROR";
103 #undef EXCEPTION
106 /* Write the minidump to file. The callback function will at the same time
107 write the list of modules to the log file. */
108 static BOOL
109 write_minidump_file(const char *file, PEXCEPTION_POINTERS ptrs,
110 MINIDUMP_CALLBACK_ROUTINE module_callback,
111 void *data)
113 /* open minidump file */
114 HANDLE minidump_file = CreateFile(file, GENERIC_WRITE, 0, NULL,
115 CREATE_ALWAYS,
116 FILE_ATTRIBUTE_NORMAL,
117 NULL);
119 if (minidump_file != INVALID_HANDLE_VALUE)
121 MINIDUMP_EXCEPTION_INFORMATION expt_info;
122 MINIDUMP_CALLBACK_INFORMATION dump_cb_info;
124 expt_info.ThreadId = GetCurrentThreadId();
125 expt_info.ExceptionPointers = ptrs;
126 expt_info.ClientPointers = FALSE;
128 dump_cb_info.CallbackRoutine = module_callback;
129 dump_cb_info.CallbackParam = data;
131 MiniDumpWriteDump_(GetCurrentProcess(),
132 GetCurrentProcessId(),
133 minidump_file,
134 MiniDumpNormal,
135 ptrs ? &expt_info : NULL,
136 NULL,
137 &dump_cb_info);
139 CloseHandle(minidump_file);
140 return TRUE;
143 return FALSE;
146 /* Write module information to the log file */
147 static BOOL CALLBACK
148 write_module_info_callback(void *data,
149 CONST PMINIDUMP_CALLBACK_INPUT callback_input,
150 PMINIDUMP_CALLBACK_OUTPUT callback_output)
152 if (data != NULL &&
153 callback_input != NULL &&
154 callback_input->CallbackType == ModuleCallback)
156 FILE *log_file = (FILE *)data;
157 MINIDUMP_MODULE_CALLBACK module = callback_input->Module;
159 char *buf = convert_wbcs_to_utf8(module.FullPath);
160 fprintf(log_file, "0x%08x", module.BaseOfImage);
161 fprintf(log_file, " %s", buf);
162 free(buf);
164 fprintf(log_file, " (%d.%d.%d.%d, %d bytes)\n",
165 HIWORD(module.VersionInfo.dwFileVersionMS),
166 LOWORD(module.VersionInfo.dwFileVersionMS),
167 HIWORD(module.VersionInfo.dwFileVersionLS),
168 LOWORD(module.VersionInfo.dwFileVersionLS),
169 module.SizeOfImage);
172 return TRUE;
175 /* Write details about the current process, platform and the exception */
176 static void
177 write_process_info(EXCEPTION_RECORD *exception, CONTEXT *context,
178 FILE *log_file)
180 OSVERSIONINFO oi;
181 const char *cmd_line;
183 /* write the command line */
184 cmd_line = GetCommandLine();
185 fprintf(log_file,
186 "Cmd line: %.65s\n", cmd_line);
188 /* write the svn version number info. */
189 fprintf(log_file,
190 "Version: %s, compiled %s, %s\n",
191 SVN_VERSION, __DATE__, __TIME__);
193 /* write information about the OS */
194 oi.dwOSVersionInfoSize = sizeof(oi);
195 GetVersionEx(&oi);
197 fprintf(log_file,
198 "Platform: Windows OS version %d.%d build %d %s\n\n",
199 oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber,
200 oi.szCSDVersion);
202 /* write the exception code */
203 fprintf(log_file,
204 "Exception: %s\n\n",
205 exception_string(exception->ExceptionCode));
207 /* write the register info. */
208 fprintf(log_file,
209 "Registers:\n");
210 #if defined(_M_IX86)
211 fprintf(log_file,
212 "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n",
213 context->Eax, context->Ebx, context->Ecx,
214 context->Edx, context->Esi, context->Edi);
215 fprintf(log_file,
216 "eip=%08x esp=%08x ebp=%08x efl=%08x\n",
217 context->Eip, context->Esp,
218 context->Ebp, context->EFlags);
219 fprintf(log_file,
220 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x\n",
221 context->SegCs, context->SegSs, context->SegDs,
222 context->SegEs, context->SegFs, context->SegGs);
223 #elif defined(_M_X64)
224 fprintf(log_file,
225 "Rax=%016I64x Rcx=%016I64x Rdx=%016I64x Rbx=%016I64x\n",
226 context->Rax, context->Rcx, context->Rdx, context->Rbx);
227 fprintf(log_file,
228 "Rsp=%016I64x Rbp=%016I64x Rsi=%016I64x Rdi=%016I64x\n",
229 context->Rsp, context->Rbp, context->Rsi, context->Rdi);
230 fprintf(log_file,
231 "R8= %016I64x R9= %016I64x R10= %016I64x R11=%016I64x\n",
232 context->R8, context->R9, context->R10, context->R11);
233 fprintf(log_file,
234 "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n",
235 context->R12, context->R13, context->R14, context->R15);
237 fprintf(log_file,
238 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\n",
239 context->SegCs, context->SegDs, context->SegEs,
240 context->SegFs, context->SegGs, context->SegSs);
241 #else
242 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
243 #endif
246 /* formats the value at address based on the specified basic type
247 * (char, int, long ...). */
248 static void
249 format_basic_type(char *buf, DWORD basic_type, DWORD64 length, void *address)
251 switch(length)
253 case 1:
254 sprintf(buf, "%x", *(unsigned char *)address);
255 break;
256 case 2:
257 sprintf(buf, "%x", *(unsigned short *)address);
258 break;
259 case 4:
260 switch(basic_type)
262 case 2: /* btChar */
264 if (!IsBadStringPtr(*(PSTR*)address, 32))
265 sprintf(buf, "\"%.31s\"", *(unsigned long *)address);
266 else
267 sprintf(buf, "%x", *(unsigned long *)address);
269 case 6: /* btInt */
270 sprintf(buf, "%d", *(int *)address);
271 break;
272 case 8: /* btFloat */
273 sprintf(buf, "%f", *(float *)address);
274 break;
275 default:
276 sprintf(buf, "%x", *(unsigned long *)address);
277 break;
279 break;
280 case 8:
281 if (basic_type == 8) /* btFloat */
282 sprintf(buf, "%lf", *(double *)address);
283 else
284 sprintf(buf, "%I64X", *(unsigned __int64 *)address);
285 break;
289 /* formats the value at address based on the type (pointer, user defined,
290 * basic type). */
291 static void
292 format_value(char *value_str, DWORD64 mod_base, DWORD type, void *value_addr)
294 DWORD tag;
295 int ptr = 0;
296 HANDLE proc = GetCurrentProcess();
298 while (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMTAG, &tag))
300 /* SymTagPointerType */
301 if (tag == 14)
303 ptr++;
304 SymGetTypeInfo_(proc, mod_base, type, TI_GET_TYPE, &type);
305 continue;
307 break;
310 switch(tag)
312 case 11: /* SymTagUDT */
314 WCHAR *type_name_wbcs;
315 if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMNAME,
316 &type_name_wbcs))
318 char *type_name = convert_wbcs_to_utf8(type_name_wbcs);
319 LocalFree(type_name_wbcs);
321 if (ptr == 0)
322 sprintf(value_str, "(%s) 0x%08x",
323 type_name, (DWORD *)value_addr);
324 else if (ptr == 1)
325 sprintf(value_str, "(%s *) 0x%08x",
326 type_name, *(DWORD *)value_addr);
327 else
328 sprintf(value_str, "(%s **) 0x%08x",
329 type_name, (DWORD *)value_addr);
331 free(type_name);
334 break;
335 case 16: /* SymTagBaseType */
337 DWORD bt;
338 ULONG64 length;
339 SymGetTypeInfo_(proc, mod_base, type, TI_GET_LENGTH, &length);
341 /* print a char * as a string */
342 if (ptr == 1 && length == 1)
344 sprintf(value_str, "0x%08x \"%s\"",
345 *(DWORD *)value_addr, (char *)*(DWORD*)value_addr);
346 break;
348 if (ptr >= 1)
350 sprintf(value_str, "0x%08x", *(DWORD *)value_addr);
351 break;
353 if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_BASETYPE, &bt))
355 format_basic_type(value_str, bt, length, value_addr);
356 break;
359 break;
360 case 12: /* SymTagEnum */
361 sprintf(value_str, "%d", *(DWORD *)value_addr);
362 break;
363 case 13: /* SymTagFunctionType */
364 sprintf(value_str, "0x%08x", *(DWORD *)value_addr);
365 break;
366 default: break;
370 /* Internal structure used to pass some data to the enumerate symbols
371 * callback */
372 typedef struct {
373 STACKFRAME64 *stack_frame;
374 FILE *log_file;
375 int nr_of_frame;
376 BOOL log_params;
377 } symbols_baton_t;
379 /* write the details of one parameter or local variable to the log file */
380 static BOOL WINAPI
381 write_var_values(PSYMBOL_INFO sym_info, ULONG sym_size, void *baton)
383 static int last_nr_of_frame = 0;
384 DWORD_PTR var_data = 0; /* Will point to the variable's data in memory */
385 STACKFRAME64 *stack_frame = ((symbols_baton_t*)baton)->stack_frame;
386 FILE *log_file = ((symbols_baton_t*)baton)->log_file;
387 int nr_of_frame = ((symbols_baton_t*)baton)->nr_of_frame;
388 BOOL log_params = ((symbols_baton_t*)baton)->log_params;
389 char value_str[256] = "";
391 /* get the variable's data */
392 if (sym_info->Flags & SYMFLAG_REGREL)
394 var_data = (DWORD_PTR)stack_frame->AddrFrame.Offset;
395 var_data += (DWORD_PTR)sym_info->Address;
397 else
398 return FALSE;
400 if (log_params == TRUE && sym_info->Flags & SYMFLAG_PARAMETER)
402 if (last_nr_of_frame == nr_of_frame)
403 fprintf(log_file, ", ", 2);
404 else
405 last_nr_of_frame = nr_of_frame;
407 format_value(value_str, sym_info->ModBase, sym_info->TypeIndex,
408 (void *)var_data);
409 fprintf(log_file, "%s=%s", sym_info->Name, value_str);
411 if (log_params == FALSE && sym_info->Flags & SYMFLAG_LOCAL)
413 format_value(value_str, sym_info->ModBase, sym_info->TypeIndex,
414 (void *)var_data);
415 fprintf(log_file, " %s = %s\n", sym_info->Name, value_str);
418 return TRUE;
421 /* write the details of one function to the log file */
422 static void
423 write_function_detail(STACKFRAME64 stack_frame, void *data)
425 ULONG64 symbolBuffer[(sizeof(SYMBOL_INFO) +
426 MAX_PATH +
427 sizeof(ULONG64) - 1) /
428 sizeof(ULONG64)];
429 PSYMBOL_INFO pIHS = (PSYMBOL_INFO)symbolBuffer;
430 DWORD64 func_disp=0;
432 IMAGEHLP_STACK_FRAME ih_stack_frame;
433 IMAGEHLP_LINE64 ih_line;
434 DWORD line_disp=0;
436 HANDLE proc = GetCurrentProcess();
437 FILE *log_file = (FILE *)data;
439 symbols_baton_t ensym;
441 static int nr_of_frame = 0;
443 nr_of_frame++;
445 /* log the function name */
446 pIHS->SizeOfStruct = sizeof(SYMBOL_INFO);
447 pIHS->MaxNameLen = MAX_PATH;
448 if (SymFromAddr_(proc, stack_frame.AddrPC.Offset, &func_disp, pIHS) == TRUE)
450 fprintf(log_file,
451 "#%d 0x%08x in %.200s (",
452 nr_of_frame, stack_frame.AddrPC.Offset, pIHS->Name);
454 /* restrict symbol enumeration to this frame only */
455 ih_stack_frame.InstructionOffset = stack_frame.AddrPC.Offset;
456 SymSetContext_(proc, &ih_stack_frame, 0);
458 ensym.log_file = log_file;
459 ensym.stack_frame = &stack_frame;
460 ensym.nr_of_frame = nr_of_frame;
462 /* log all function parameters */
463 ensym.log_params = TRUE;
464 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym);
466 fprintf(log_file, ")");
468 else
470 fprintf(log_file,
471 "#%d 0x%08x in (unknown function)",
472 nr_of_frame, stack_frame.AddrPC.Offset);
475 /* find the source line for this function. */
476 ih_line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
477 if (SymGetLineFromAddr64_(proc, stack_frame.AddrPC.Offset,
478 &line_disp, &ih_line) != 0)
480 fprintf(log_file,
481 " at %s:%d\n", ih_line.FileName, ih_line.LineNumber);
483 else
485 fprintf(log_file, "\n");
488 /* log all function local variables */
489 ensym.log_params = FALSE;
490 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym);
493 /* walk over the stack and log all relevant information to the log file */
494 static void
495 write_stacktrace(CONTEXT *context, FILE *log_file)
497 #if defined (_M_IX86) || defined(_M_X64) || defined(_M_IA64)
498 HANDLE proc = GetCurrentProcess();
499 STACKFRAME64 stack_frame;
500 DWORD machine;
501 CONTEXT ctx;
502 int skip = 0, i = 0;
504 /* The thread information - if not supplied. */
505 if (context == NULL)
507 /* If no context is supplied, skip 1 frame */
508 skip = 1;
510 ctx.ContextFlags = CONTEXT_FULL;
511 if (GetThreadContext(GetCurrentThread(), &ctx))
512 context = &ctx;
515 if (context == NULL)
516 return;
518 /* Write the stack trace */
519 ZeroMemory(&stack_frame, sizeof(STACKFRAME64));
520 stack_frame.AddrPC.Mode = AddrModeFlat;
521 stack_frame.AddrStack.Mode = AddrModeFlat;
522 stack_frame.AddrFrame.Mode = AddrModeFlat;
524 #if defined(_M_IX86)
525 machine = IMAGE_FILE_MACHINE_I386;
526 stack_frame.AddrPC.Offset = context->Eip;
527 stack_frame.AddrStack.Offset = context->Esp;
528 stack_frame.AddrFrame.Offset = context->Ebp;
529 #elif defined(_M_X64)
530 machine = IMAGE_FILE_MACHINE_AMD64;
531 stack_frame.AddrPC.Offset = context->Rip;
532 stack_frame.AddrStack.Offset = context->Rsp;
533 stack_frame.AddrFrame.Offset = context->Rbp;
534 #elif defined(_M_IA64)
535 machine = IMAGE_FILE_MACHINE_IA64;
536 stack_frame.AddrPC.Offset = context->StIIP;
537 stack_frame.AddrStack.Offset = context->SP;
538 stack_frame.AddrBStore.Mode = AddrModeFlat;
539 stack_frame.AddrBStore.Offset = context->RsBSP;
540 #else
541 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
542 #endif
544 while (1)
546 if (! StackWalk64_(machine, proc, GetCurrentThread(),
547 &stack_frame, context, NULL,
548 SymFunctionTableAccess64_, SymGetModuleBase64_, NULL))
550 break;
553 if (i >= skip)
555 /* Try to include symbolic information.
556 Also check that the address is not zero. Sometimes StackWalk
557 returns TRUE with a frame of zero. */
558 if (stack_frame.AddrPC.Offset != 0)
560 write_function_detail(stack_frame, (void *)log_file);
563 i++;
565 #else
566 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
567 #endif
570 /* Check if a debugger is attached to this process */
571 static BOOL
572 is_debugger_present()
574 HANDLE kernel32_dll = LoadLibrary("kernel32.dll");
575 BOOL result;
577 ISDEBUGGERPRESENT IsDebuggerPresent_ =
578 (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent");
580 if (IsDebuggerPresent_ && IsDebuggerPresent_())
581 result = TRUE;
582 else
583 result = FALSE;
585 FreeLibrary(kernel32_dll);
587 return result;
590 /* Match the version of dbghelp.dll with the minimum expected version */
591 static BOOL
592 check_dbghelp_version(WORD exp_major, WORD exp_minor, WORD exp_build,
593 WORD exp_qfe)
595 HANDLE version_dll = LoadLibrary(VERSION_DLL);
596 GETFILEVERSIONINFOSIZE GetFileVersionInfoSize_ =
597 (GETFILEVERSIONINFOSIZE)GetProcAddress(version_dll,
598 "GetFileVersionInfoSizeA");
599 GETFILEVERSIONINFO GetFileVersionInfo_ =
600 (GETFILEVERSIONINFO)GetProcAddress(version_dll,
601 "GetFileVersionInfoA");
602 VERQUERYVALUE VerQueryValue_ =
603 (VERQUERYVALUE)GetProcAddress(version_dll, "VerQueryValueA");
605 DWORD version = 0,
606 exp_version = MAKELONG(MAKEWORD(exp_qfe, exp_build),
607 MAKEWORD(exp_minor, exp_major));
608 DWORD h = 0;
609 DWORD resource_size = GetFileVersionInfoSize_(DBGHELP_DLL, &h);
611 if (resource_size)
613 void *resource_data = malloc(resource_size);
614 if (GetFileVersionInfo_(DBGHELP_DLL, h, resource_size,
615 resource_data) != FALSE)
617 void *buf = NULL;
618 UINT len;
619 if (VerQueryValue_(resource_data, "\\", &buf, &len))
621 VS_FIXEDFILEINFO *info = (VS_FIXEDFILEINFO*)buf;
622 version = MAKELONG(MAKEWORD(LOWORD(info->dwFileVersionLS),
623 HIWORD(info->dwFileVersionLS)),
624 MAKEWORD(LOWORD(info->dwFileVersionMS),
625 HIWORD(info->dwFileVersionMS)));
628 free(resource_data);
631 FreeLibrary(version_dll);
633 if (version >= exp_version)
634 return TRUE;
636 return FALSE;
639 /* Load the dbghelp.dll file, try to find a version that matches our
640 requirements. */
641 static BOOL
642 load_dbghelp_dll()
644 /* check version of the dll, should be at least 6.6.7.5 */
645 if (check_dbghelp_version(6, 6, 7, 5) == FALSE)
646 return FALSE;
648 dbghelp_dll = LoadLibrary(DBGHELP_DLL);
649 if (dbghelp_dll != INVALID_HANDLE_VALUE)
651 DWORD opts;
653 /* load the functions */
654 MiniDumpWriteDump_ =
655 (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump");
656 SymInitialize_ =
657 (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize");
658 SymSetOptions_ =
659 (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions");
660 SymGetOptions_ =
661 (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions");
662 SymCleanup_ =
663 (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup");
664 SymGetTypeInfo_ =
665 (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo");
666 SymGetLineFromAddr64_ =
667 (SYMGETLINEFROMADDR64)GetProcAddress(dbghelp_dll,
668 "SymGetLineFromAddr64");
669 SymEnumSymbols_ =
670 (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols");
671 SymSetContext_ =
672 (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext");
673 SymFromAddr_ = (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr");
674 StackWalk64_ = (STACKWALK64)GetProcAddress(dbghelp_dll, "StackWalk64");
675 SymFunctionTableAccess64_ =
676 (SYMFUNCTIONTABLEACCESS64)GetProcAddress(dbghelp_dll,
677 "SymFunctionTableAccess64");
678 SymGetModuleBase64_ =
679 (SYMGETMODULEBASE64)GetProcAddress(dbghelp_dll, "SymGetModuleBase64");
681 if (! (MiniDumpWriteDump_ &&
682 SymInitialize_ && SymSetOptions_ && SymGetOptions_ &&
683 SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr64_ &&
684 SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ &&
685 SymGetModuleBase64_ && StackWalk64_ &&
686 SymFunctionTableAccess64_))
687 goto cleanup;
689 /* initialize the symbol loading code */
690 opts = SymGetOptions_();
692 /* Set the 'load lines' option to retrieve line number information;
693 set the Deferred Loads option to map the debug info in memory only
694 when needed. */
695 SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
697 /* Initialize the debughlp DLL with the default path and automatic
698 module enumeration (and loading of symbol tables) for this process.
700 SymInitialize_(GetCurrentProcess(), NULL, TRUE);
702 return TRUE;
705 cleanup:
706 if (dbghelp_dll)
707 FreeLibrary(dbghelp_dll);
709 return FALSE;
712 /* Cleanup the dbghelp.dll library */
713 static void
714 cleanup_debughlp()
716 SymCleanup_(GetCurrentProcess());
718 FreeLibrary(dbghelp_dll);
721 /* Create a filename based on a prefix, the timestamp and an extension.
722 check if the filename was already taken, retry 3 times. */
723 BOOL
724 get_temp_filename(char *filename, const char *prefix, const char *ext)
726 char temp_dir[MAX_PATH - 64];
727 int i;
729 if (! GetTempPath(MAX_PATH - 64, temp_dir))
730 return FALSE;
732 for (i = 0;i < 3;i++)
734 HANDLE file;
735 time_t now;
736 char time_str[64];
738 time(&now);
739 strftime(time_str, 64, "%Y%m%d%H%M%S", localtime(&now));
740 sprintf(filename, "%s%s%s.%s", temp_dir, prefix, time_str, ext);
742 file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW,
743 FILE_ATTRIBUTE_NORMAL, NULL);
744 if (file != INVALID_HANDLE_VALUE)
746 CloseHandle(file);
747 return TRUE;
751 filename[0] = '\0';
752 return FALSE;
755 /* unhandled exception callback set with SetUnhandledExceptionFilter() */
756 LONG WINAPI
757 svn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs)
759 char dmp_filename[MAX_PATH];
760 char log_filename[MAX_PATH];
761 FILE *log_file;
763 /* Check if the crash handler was already loaded (crash while handling the
764 crash) */
765 if (dbghelp_dll != INVALID_HANDLE_VALUE)
766 return EXCEPTION_CONTINUE_SEARCH;
768 /* don't log anything if we're running inside a debugger ... */
769 if (is_debugger_present() == TRUE)
770 return EXCEPTION_CONTINUE_SEARCH;
772 /* ... or if we can't create the log files ... */
773 if (get_temp_filename(dmp_filename, LOGFILE_PREFIX, "dmp") == FALSE ||
774 get_temp_filename(log_filename, LOGFILE_PREFIX, "log") == FALSE)
775 return EXCEPTION_CONTINUE_SEARCH;
777 /* If we can't load a recent version of the dbghelp.dll, pass on this
778 exception */
779 if (load_dbghelp_dll() == FALSE)
780 return EXCEPTION_CONTINUE_SEARCH;
782 /* open log file */
783 log_file = fopen(log_filename, "w+");
785 /* write information about the process */
786 fprintf(log_file, "\nProcess info:\n");
787 write_process_info(ptrs ? ptrs->ExceptionRecord : NULL,
788 ptrs ? ptrs->ContextRecord : NULL,
789 log_file);
791 /* write the stacktrace, if available */
792 fprintf(log_file, "\nStacktrace:\n");
793 write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, log_file);
795 /* write the minidump file and use the callback to write the list of modules
796 to the log file */
797 fprintf(log_file, "\n\nLoaded modules:\n");
798 write_minidump_file(dmp_filename, ptrs,
799 write_module_info_callback, (void *)log_file);
801 fclose(log_file);
803 cleanup_debughlp();
805 /* inform the user */
806 fprintf(stderr, "This application has halted due to an unexpected error.\n"
807 "A crash report and minidump file were saved to disk, you"
808 " can find them here:\n"
809 "%s\n%s\n"
810 "Please send the log file to %s to help us analyse\nand "
811 "solve this problem.\n\n"
812 "NOTE: The crash report and minidump files can contain some"
813 " sensitive information\n(filenames, partial file content, "
814 "usernames and passwords etc.)\n",
815 log_filename,
816 dmp_filename,
817 CRASHREPORT_EMAIL);
819 /* terminate the application */
820 return EXCEPTION_EXECUTE_HANDLER;
822 #endif /* SVN_USE_WIN32_CRASHHANDLER */
823 #endif /* WIN32 */