Win32: fix an incorrect error status being propagated to the caller in case
[svn/apache.git] / subversion / libsvn_subr / win32_crashrpt.c
blob1f8cdd0c75110f15372fcf13d925a65fb73d97f6
1 /*
2 * win32_crashrpt.c : provides information after a crash
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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 * ====================================================================
24 /* prevent "empty compilation unit" warning on e.g. UNIX */
25 typedef int win32_crashrpt__dummy;
27 #ifdef WIN32
28 #ifdef SVN_USE_WIN32_CRASHHANDLER
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>
38 #include "svn_version.h"
40 #include "sysinfo.h"
42 #include "win32_crashrpt.h"
43 #include "win32_crashrpt_dll.h"
45 /*** Global variables ***/
46 static HANDLE dbghelp_dll = INVALID_HANDLE_VALUE;
48 #define DBGHELP_DLL "dbghelp.dll"
50 #define LOGFILE_PREFIX "svn-crash-log"
52 #if defined(_M_IX86)
53 #define FORMAT_PTR "0x%08Ix"
54 #elif defined(_M_X64)
55 #define FORMAT_PTR "0x%016Ix"
56 #endif
58 /*** Code. ***/
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.
65 static char *
66 convert_wbcs_to_ansi(const wchar_t *str)
68 size_t len = wcslen(str);
69 char *utf8_str = malloc(sizeof(wchar_t) * len + 1);
70 len = wcstombs(utf8_str, str, len);
72 if (len == -1)
73 return NULL;
75 utf8_str[len] = '\0';
77 return utf8_str;
80 /* Convert the exception code to a string */
81 static const char *
82 exception_string(int exception)
84 #define EXCEPTION(x) case x: return (#x);
86 switch (exception)
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)
112 default:
113 return "UNKNOWN_ERROR";
115 #undef EXCEPTION
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
121 write_minidump_file(const char *file, PEXCEPTION_POINTERS ptrs,
122 MINIDUMP_CALLBACK_ROUTINE module_callback,
123 void *data)
125 /* open minidump file */
126 HANDLE minidump_file = CreateFile(file, GENERIC_WRITE, 0, NULL,
127 CREATE_ALWAYS,
128 FILE_ATTRIBUTE_NORMAL,
129 NULL);
131 if (minidump_file != INVALID_HANDLE_VALUE)
133 MINIDUMP_EXCEPTION_INFORMATION expt_info;
134 MINIDUMP_CALLBACK_INFORMATION dump_cb_info;
136 expt_info.ThreadId = GetCurrentThreadId();
137 expt_info.ExceptionPointers = ptrs;
138 expt_info.ClientPointers = FALSE;
140 dump_cb_info.CallbackRoutine = module_callback;
141 dump_cb_info.CallbackParam = data;
143 MiniDumpWriteDump_(GetCurrentProcess(),
144 GetCurrentProcessId(),
145 minidump_file,
146 MiniDumpNormal,
147 ptrs ? &expt_info : NULL,
148 NULL,
149 &dump_cb_info);
151 CloseHandle(minidump_file);
152 return TRUE;
155 return FALSE;
158 /* Write module information to the log file */
159 static BOOL CALLBACK
160 write_module_info_callback(void *data,
161 CONST PMINIDUMP_CALLBACK_INPUT callback_input,
162 PMINIDUMP_CALLBACK_OUTPUT callback_output)
164 if (data != NULL &&
165 callback_input != NULL &&
166 callback_input->CallbackType == ModuleCallback)
168 FILE *log_file = (FILE *)data;
169 MINIDUMP_MODULE_CALLBACK module = callback_input->Module;
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);
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);
184 return TRUE;
187 /* Write details about the current process, platform and the exception */
188 static void
189 write_process_info(EXCEPTION_RECORD *exception, CONTEXT *context,
190 FILE *log_file)
192 OSVERSIONINFOEXW oi;
193 const char *cmd_line;
194 char workingdir[8192];
196 /* write the command line */
197 cmd_line = GetCommandLine();
198 fprintf(log_file,
199 "Cmd line: %s\n", cmd_line);
201 _getcwd(workingdir, sizeof(workingdir));
202 fprintf(log_file,
203 "Working Dir: %s\n", workingdir);
205 /* write the svn version number info. */
206 fprintf(log_file,
207 "Version: %s, compiled %s, %s\n",
208 SVN_VERSION, __DATE__, __TIME__);
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);
217 /* write the exception code */
218 fprintf(log_file,
219 "Exception: %s\n\n",
220 exception_string(exception->ExceptionCode));
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);
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
261 /* Writes the value at address based on the specified basic type
262 * (char, int, long ...) to LOG_FILE. */
263 static void
264 write_basic_type(FILE *log_file, DWORD basic_type, DWORD64 length,
265 void *address)
267 switch(length)
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)
278 case 2: /* btChar */
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);
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;
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;
309 /* Writes the value at address based on the type (pointer, user defined,
310 * basic type) to LOG_FILE. */
311 static void
312 write_value(FILE *log_file, DWORD64 mod_base, DWORD type, void *value_addr)
314 DWORD tag = 0;
315 int ptr = 0;
316 HANDLE proc = GetCurrentProcess();
318 while (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMTAG, &tag))
320 /* SymTagPointerType */
321 if (tag == 14)
323 ptr++;
324 SymGetTypeInfo_(proc, mod_base, type, TI_GET_TYPE, &type);
325 continue;
327 break;
330 switch(tag)
332 case 11: /* SymTagUDT */
334 WCHAR *type_name_wbcs;
335 if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMNAME,
336 &type_name_wbcs))
338 char *type_name = convert_wbcs_to_ansi(type_name_wbcs);
339 LocalFree(type_name_wbcs);
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);
351 free(type_name);
353 else
354 fprintf(log_file, "[no symbol tag]");
356 break;
357 case 16: /* SymTagBaseType */
359 DWORD bt;
360 ULONG64 length;
361 SymGetTypeInfo_(proc, mod_base, type, TI_GET_LENGTH, &length);
363 /* print a char * as a string */
364 if (ptr == 1 && length == 1)
366 fprintf(log_file, FORMAT_PTR " \"%s\"",
367 *(DWORD_PTR *)value_addr, *(const char **)value_addr);
369 else if (ptr >= 1)
371 fprintf(log_file, FORMAT_PTR, *(DWORD_PTR *)value_addr);
373 else if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_BASETYPE, &bt))
375 write_basic_type(log_file, bt, length, value_addr);
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;
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;
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)
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;
411 /* get the variable's data */
412 if (sym_info->Flags & SYMFLAG_REGREL)
414 var_data = (DWORD_PTR)stack_frame->AddrFrame.Offset;
415 var_data += (DWORD_PTR)sym_info->Address;
417 else
418 return FALSE;
420 if (log_params && sym_info->Flags & SYMFLAG_PARAMETER)
422 if (last_nr_of_frame == nr_of_frame)
423 fprintf(log_file, ", ");
424 else
425 last_nr_of_frame = nr_of_frame;
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);
431 if (!log_params && sym_info->Flags & SYMFLAG_LOCAL)
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");
440 return TRUE;
443 /* Write the details of one function to the log file */
444 static void
445 write_function_detail(STACKFRAME64 stack_frame, int nr_of_frame, FILE *log_file)
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;
454 IMAGEHLP_STACK_FRAME ih_stack_frame;
455 IMAGEHLP_LINE64 ih_line;
456 DWORD line_disp=0;
458 HANDLE proc = GetCurrentProcess();
460 symbols_baton_t ensym;
462 nr_of_frame++; /* We need a 1 based index here */
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))
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);
475 /* restrict symbol enumeration to this frame only */
476 ih_stack_frame.InstructionOffset = stack_frame.AddrPC.Offset;
477 SymSetContext_(proc, &ih_stack_frame, 0);
479 ensym.log_file = log_file;
480 ensym.stack_frame = &stack_frame;
481 ensym.nr_of_frame = nr_of_frame;
483 /* log all function parameters */
484 ensym.log_params = TRUE;
485 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym);
487 fprintf(log_file, ")");
489 else
491 fprintf(log_file,
492 "#%d 0x%08I64x in (unknown function)",
493 nr_of_frame, stack_frame.AddrPC.Offset);
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)
501 fprintf(log_file,
502 " at %s:%d\n", ih_line.FileName, ih_line.LineNumber);
504 else
506 fprintf(log_file, "\n");
509 /* log all function local variables */
510 ensym.log_params = FALSE;
511 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym);
514 /* Walk over the stack and log all relevant information to the log file */
515 static void
516 write_stacktrace(CONTEXT *context, FILE *log_file)
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;
525 /* The thread information - if not supplied. */
526 if (context == NULL)
528 /* If no context is supplied, skip 1 frame */
529 skip = 1;
531 ctx.ContextFlags = CONTEXT_FULL;
532 if (!GetThreadContext(GetCurrentThread(), &ctx))
533 return;
535 else
537 ctx = *context;
540 if (context == NULL)
541 return;
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;
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
569 while (1)
571 if (! StackWalk64_(machine, proc, GetCurrentThread(),
572 &stack_frame, &ctx, NULL,
573 SymFunctionTableAccess64_, SymGetModuleBase64_, NULL))
575 break;
578 if (i >= skip)
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)
585 write_function_detail(stack_frame, i, log_file);
588 i++;
590 #else
591 #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER
592 #endif
595 /* Check if a debugger is attached to this process */
596 static BOOL
597 is_debugger_present()
599 return IsDebuggerPresent();
602 /* Load the dbghelp.dll file, try to find a version that matches our
603 requirements. */
604 static BOOL
605 load_dbghelp_dll()
607 dbghelp_dll = LoadLibrary(DBGHELP_DLL);
608 if (dbghelp_dll != NULL)
610 DWORD opts;
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");
640 if (! (MiniDumpWriteDump_ &&
641 SymInitialize_ && SymSetOptions_ && SymGetOptions_ &&
642 SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr64_ &&
643 SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ &&
644 SymGetModuleBase64_ && StackWalk64_ &&
645 SymFunctionTableAccess64_))
646 goto cleanup;
648 /* initialize the symbol loading code */
649 opts = SymGetOptions_();
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);
656 /* Initialize the debughlp DLL with the default path and automatic
657 module enumeration (and loading of symbol tables) for this process.
659 SymInitialize_(GetCurrentProcess(), NULL, TRUE);
661 return TRUE;
664 cleanup:
665 if (dbghelp_dll)
666 FreeLibrary(dbghelp_dll);
668 return FALSE;
671 /* Cleanup the dbghelp.dll library */
672 static void
673 cleanup_debughlp()
675 SymCleanup_(GetCurrentProcess());
677 FreeLibrary(dbghelp_dll);
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
683 get_temp_filename(char *filename, const char *prefix, const char *ext)
685 char temp_dir[MAX_PATH - 64];
686 int i;
688 if (! GetTempPath(MAX_PATH - 64, temp_dir))
689 return FALSE;
691 for (i = 0;i < 3;i++)
693 HANDLE file;
694 time_t now;
695 char time_str[64];
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);
701 file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW,
702 FILE_ATTRIBUTE_NORMAL, NULL);
703 if (file != INVALID_HANDLE_VALUE)
705 CloseHandle(file);
706 return TRUE;
710 filename[0] = '\0';
711 return FALSE;
714 /* Unhandled exception callback set with SetUnhandledExceptionFilter() */
715 LONG WINAPI
716 svn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs)
718 char dmp_filename[MAX_PATH];
719 char log_filename[MAX_PATH];
720 FILE *log_file;
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;
727 /* don't log anything if we're running inside a debugger ... */
728 if (is_debugger_present())
729 return EXCEPTION_CONTINUE_SEARCH;
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;
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;
741 /* open log file */
742 log_file = fopen(log_filename, "w+");
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);
750 /* write the stacktrace, if available */
751 fprintf(log_file, "\nStacktrace:\n");
752 write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, log_file);
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);
760 fclose(log_file);
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);
776 if (getenv("SVN_DBG_STACKTRACES_TO_STDERR") != NULL)
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);
786 fflush(stderr);
787 fflush(stdout);
789 cleanup_debughlp();
791 /* terminate the application */
792 return EXCEPTION_EXECUTE_HANDLER;
794 #endif /* SVN_USE_WIN32_CRASHHANDLER */
795 #else /* !WIN32 */
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;
802 #endif /* WIN32 */