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