]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/win32_crashrpt.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_subr / win32_crashrpt.c
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */