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