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 static 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  */
65 static char *
convert_wbcs_to_ansi(const wchar_t * str)66 convert_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 */
81 static const char *
exception_string(int exception)82 exception_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. */
120 static BOOL
write_minidump_file(const char * file,PEXCEPTION_POINTERS ptrs,MINIDUMP_CALLBACK_ROUTINE module_callback,void * data)121 write_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 */
159 static BOOL CALLBACK
write_module_info_callback(void * data,CONST PMINIDUMP_CALLBACK_INPUT callback_input,PMINIDUMP_CALLBACK_OUTPUT callback_output)160 write_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 */
188 static void
write_process_info(EXCEPTION_RECORD * exception,CONTEXT * context,FILE * log_file)189 write_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. */
263 static void
write_basic_type(FILE * log_file,DWORD basic_type,DWORD64 length,void * address)264 write_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. */
311 static void
write_value(FILE * log_file,DWORD64 mod_base,DWORD type,void * value_addr)312 write_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 */
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
write_var_values(PSYMBOL_INFO sym_info,ULONG sym_size,void * baton)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 
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 */
444 static void
write_function_detail(STACKFRAME64 stack_frame,int nr_of_frame,FILE * log_file)445 write_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 */
515 static void
write_stacktrace(CONTEXT * context,FILE * log_file)516 write_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 */
596 static BOOL
is_debugger_present()597 is_debugger_present()
598 {
599   return IsDebuggerPresent();
600 }
601 
602 /* Load the dbghelp.dll file, try to find a version that matches our
603    requirements. */
604 static BOOL
load_dbghelp_dll()605 load_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 
664 cleanup:
665   if (dbghelp_dll)
666     FreeLibrary(dbghelp_dll);
667 
668   return FALSE;
669 }
670 
671 /* Cleanup the dbghelp.dll library */
672 static void
cleanup_debughlp()673 cleanup_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. */
682 BOOL
get_temp_filename(char * filename,const char * prefix,const char * ext)683 get_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() */
715 LONG WINAPI
svn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs)716 svn__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>
799 extern const apr_uint32_t svn__fake__win32_crashrpt;
800 const apr_uint32_t svn__fake__win32_crashrpt = 0xdeadbeef;
801 
802 #endif /* WIN32 */
803