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