win32_crashrpt.c revision 309512
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 */
25typedef 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 ***/
46HANDLE 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%08Ix"
57#elif defined(_M_X64)
58#define FORMAT_PTR "0x%016Ix"
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 */
68static char *
69convert_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 */
84static const char *
85exception_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. */
122static BOOL
123write_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 */
161static BOOL CALLBACK
162write_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, (UINT_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 */
190static void
191write_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/* Writes the value at address based on the specified basic type
264 * (char, int, long ...) to LOG_FILE. */
265static void
266write_basic_type(FILE *log_file, DWORD basic_type, DWORD64 length,
267                 void *address)
268{
269  switch(length)
270    {
271      case 1:
272        fprintf(log_file, "0x%02x", (int)*(unsigned char *)address);
273        break;
274      case 2:
275        fprintf(log_file, "0x%04x", (int)*(unsigned short *)address);
276        break;
277      case 4:
278        switch(basic_type)
279          {
280            case 2:  /* btChar */
281              {
282                if (!IsBadStringPtr(*(PSTR*)address, 32))
283                  fprintf(log_file, "\"%.31s\"", *(const char **)address);
284                else
285                  fprintf(log_file, FORMAT_PTR, *(DWORD_PTR *)address);
286              }
287            case 6:  /* btInt */
288              fprintf(log_file, "%d", *(int *)address);
289              break;
290            case 8:  /* btFloat */
291              fprintf(log_file, "%f", *(float *)address);
292              break;
293            default:
294              fprintf(log_file, FORMAT_PTR, *(DWORD_PTR *)address);
295              break;
296          }
297        break;
298      case 8:
299        if (basic_type == 8) /* btFloat */
300          fprintf(log_file, "%lf", *(double *)address);
301        else
302          fprintf(log_file, "0x%016I64X", *(unsigned __int64 *)address);
303        break;
304      default:
305        fprintf(log_file, "[unhandled type 0x%08x of length " FORMAT_PTR "]",
306                basic_type, (UINT_PTR)length);
307        break;
308    }
309}
310
311/* Writes the value at address based on the type (pointer, user defined,
312 * basic type) to LOG_FILE. */
313static void
314write_value(FILE *log_file, DWORD64 mod_base, DWORD type, void *value_addr)
315{
316  DWORD tag = 0;
317  int ptr = 0;
318  HANDLE proc = GetCurrentProcess();
319
320  while (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMTAG, &tag))
321    {
322      /* SymTagPointerType */
323      if (tag == 14)
324        {
325          ptr++;
326          SymGetTypeInfo_(proc, mod_base, type, TI_GET_TYPE, &type);
327          continue;
328        }
329      break;
330    }
331
332  switch(tag)
333    {
334      case 11: /* SymTagUDT */
335        {
336          WCHAR *type_name_wbcs;
337          if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMNAME,
338                              &type_name_wbcs))
339            {
340              char *type_name = convert_wbcs_to_ansi(type_name_wbcs);
341              LocalFree(type_name_wbcs);
342
343              if (ptr == 0)
344                fprintf(log_file, "(%s) " FORMAT_PTR,
345                        type_name, (UINT_PTR)(DWORD_PTR *)value_addr);
346              else if (ptr == 1)
347                fprintf(log_file, "(%s *) " FORMAT_PTR,
348                        type_name, *(DWORD_PTR *)value_addr);
349              else
350                fprintf(log_file, "(%s **) " FORMAT_PTR,
351                        type_name, *(DWORD_PTR *)value_addr);
352
353              free(type_name);
354            }
355          else
356            fprintf(log_file, "[no symbol tag]");
357        }
358        break;
359      case 16: /* SymTagBaseType */
360        {
361          DWORD bt;
362          ULONG64 length;
363          SymGetTypeInfo_(proc, mod_base, type, TI_GET_LENGTH, &length);
364
365          /* print a char * as a string */
366          if (ptr == 1 && length == 1)
367            {
368              fprintf(log_file, FORMAT_PTR " \"%s\"",
369                      *(DWORD_PTR *)value_addr, *(const char **)value_addr);
370            }
371          else if (ptr >= 1)
372            {
373              fprintf(log_file, FORMAT_PTR, *(DWORD_PTR *)value_addr);
374            }
375          else if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_BASETYPE, &bt))
376            {
377              write_basic_type(log_file, bt, length, value_addr);
378            }
379        }
380        break;
381      case 12: /* SymTagEnum */
382          fprintf(log_file, "%d", *(DWORD_PTR *)value_addr);
383          break;
384      case 13: /* SymTagFunctionType */
385          fprintf(log_file, FORMAT_PTR, *(DWORD_PTR *)value_addr);
386          break;
387      default:
388          fprintf(log_file, "[unhandled tag: %d]", tag);
389          break;
390    }
391}
392
393/* Internal structure used to pass some data to the enumerate symbols
394 * callback */
395typedef struct symbols_baton_t {
396  STACKFRAME64 *stack_frame;
397  FILE *log_file;
398  int nr_of_frame;
399  BOOL log_params;
400} symbols_baton_t;
401
402/* Write the details of one parameter or local variable to the log file */
403static BOOL WINAPI
404write_var_values(PSYMBOL_INFO sym_info, ULONG sym_size, void *baton)
405{
406  static int last_nr_of_frame = 0;
407  DWORD_PTR var_data = 0;    /* Will point to the variable's data in memory */
408  STACKFRAME64 *stack_frame = ((symbols_baton_t*)baton)->stack_frame;
409  FILE *log_file   = ((symbols_baton_t*)baton)->log_file;
410  int nr_of_frame = ((symbols_baton_t*)baton)->nr_of_frame;
411  BOOL log_params = ((symbols_baton_t*)baton)->log_params;
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, ", ");
426      else
427        last_nr_of_frame = nr_of_frame;
428
429      fprintf(log_file, "%.*s=", (int)sym_info->NameLen, sym_info->Name);
430      write_value(log_file, sym_info->ModBase, sym_info->TypeIndex,
431                  (void *)var_data);
432    }
433  if (!log_params && sym_info->Flags & SYMFLAG_LOCAL)
434    {
435      fprintf(log_file, "        %.*s = ", (int)sym_info->NameLen,
436              sym_info->Name);
437      write_value(log_file, sym_info->ModBase, sym_info->TypeIndex,
438                  (void *)var_data);
439      fprintf(log_file, "\n");
440    }
441
442  return TRUE;
443}
444
445/* Write the details of one function to the log file */
446static void
447write_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 */
517static void
518write_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 */
598static BOOL
599is_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. */
619static BOOL
620load_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
679cleanup:
680  if (dbghelp_dll)
681    FreeLibrary(dbghelp_dll);
682
683  return FALSE;
684}
685
686/* Cleanup the dbghelp.dll library */
687static void
688cleanup_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. */
697BOOL
698get_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() */
730LONG WINAPI
731svn__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