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