win32_crashrpt.c revision 262253
11590Srgrimes/* 21590Srgrimes * win32_crashrpt.c : provides information after a crash 31590Srgrimes * 41590Srgrimes * ==================================================================== 51590Srgrimes * Licensed to the Apache Software Foundation (ASF) under one 61590Srgrimes * or more contributor license agreements. See the NOTICE file 71590Srgrimes * distributed with this work for additional information 81590Srgrimes * regarding copyright ownership. The ASF licenses this file 91590Srgrimes * to you under the Apache License, Version 2.0 (the 101590Srgrimes * "License"); you may not use this file except in compliance 111590Srgrimes * with the License. You may obtain a copy of the License at 121590Srgrimes * 131590Srgrimes * http://www.apache.org/licenses/LICENSE-2.0 141590Srgrimes * 151590Srgrimes * Unless required by applicable law or agreed to in writing, 161590Srgrimes * software distributed under the License is distributed on an 171590Srgrimes * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 181590Srgrimes * KIND, either express or implied. See the License for the 191590Srgrimes * specific language governing permissions and limitations 201590Srgrimes * under the License. 211590Srgrimes * ==================================================================== 221590Srgrimes */ 231590Srgrimes 241590Srgrimes/* prevent "empty compilation unit" warning on e.g. UNIX */ 251590Srgrimestypedef int win32_crashrpt__dummy; 261590Srgrimes 271590Srgrimes#ifdef WIN32 281590Srgrimes#ifdef SVN_USE_WIN32_CRASHHANDLER 2950477Speter 301590Srgrimes/*** Includes. ***/ 31240506Seadler#include <apr.h> 321590Srgrimes#include <dbghelp.h> 3379535Sru#include <direct.h> 341590Srgrimes#include <stdio.h> 351590Srgrimes#include <stdlib.h> 361590Srgrimes#include <time.h> 371590Srgrimes 3868963Sru#include "svn_version.h" 39119024Stjr 4077291Sdd#include "win32_crashrpt.h" 4177291Sdd#include "win32_crashrpt_dll.h" 4277291Sdd 4377291Sdd/*** Global variables ***/ 4477291SddHANDLE dbghelp_dll = INVALID_HANDLE_VALUE; 4577291Sdd 4677291Sdd/* Email address where the crash reports should be sent too. */ 4777291Sdd#define CRASHREPORT_EMAIL "users@subversion.apache.org" 4877291Sdd 491590Srgrimes#define DBGHELP_DLL "dbghelp.dll" 501590Srgrimes 51118077Stjr#define LOGFILE_PREFIX "svn-crash-log" 521590Srgrimes 5379275Sru#if defined(_M_IX86) 541590Srgrimes#define FORMAT_PTR "0x%08x" 5595124Scharnier#elif defined(_M_X64) 5695124Scharnier#define FORMAT_PTR "0x%016I64x" 5795124Scharnier#endif 581590Srgrimes 591590Srgrimes/*** Code. ***/ 601590Srgrimes 611590Srgrimes/* Convert a wide-character string to utf-8. This function will create a buffer 6277291Sdd * large enough to hold the result string, the caller should free this buffer. 6377291Sdd * If the string can't be converted, NULL is returned. 6476014Sdd */ 651590Srgrimesstatic char * 661590Srgrimesconvert_wbcs_to_utf8(const wchar_t *str) 6776014Sdd{ 6876014Sdd size_t len = wcslen(str); 691590Srgrimes char *utf8_str = malloc(sizeof(wchar_t) * len + 1); 7068963Sru len = wcstombs(utf8_str, str, len); 711590Srgrimes 721590Srgrimes if (len == -1) 7395124Scharnier return NULL; 741590Srgrimes 7577291Sdd utf8_str[len] = '\0'; 7677291Sdd 7777291Sdd return utf8_str; 7877291Sdd} 7977291Sdd 8077291Sdd/* Convert the exception code to a string */ 81100440Scharnierstatic const char * 82202756Sedexception_string(int exception) 83100440Scharnier{ 8477291Sdd#define EXCEPTION(x) case EXCEPTION_##x: return (#x); 8577291Sdd 8677291Sdd switch (exception) 87118077Stjr { 8877291Sdd EXCEPTION(ACCESS_VIOLATION) 8977291Sdd EXCEPTION(DATATYPE_MISALIGNMENT) 9077291Sdd EXCEPTION(BREAKPOINT) 9177291Sdd EXCEPTION(SINGLE_STEP) 9277291Sdd EXCEPTION(ARRAY_BOUNDS_EXCEEDED) 9377291Sdd EXCEPTION(FLT_DENORMAL_OPERAND) 9477291Sdd EXCEPTION(FLT_DIVIDE_BY_ZERO) 9577291Sdd EXCEPTION(FLT_INEXACT_RESULT) 9677291Sdd EXCEPTION(FLT_INVALID_OPERATION) 9777291Sdd EXCEPTION(FLT_OVERFLOW) 9877291Sdd EXCEPTION(FLT_STACK_CHECK) 9977291Sdd EXCEPTION(FLT_UNDERFLOW) 10077291Sdd EXCEPTION(INT_DIVIDE_BY_ZERO) 10177291Sdd EXCEPTION(INT_OVERFLOW) 10277291Sdd EXCEPTION(PRIV_INSTRUCTION) 10377291Sdd EXCEPTION(IN_PAGE_ERROR) 10477291Sdd EXCEPTION(ILLEGAL_INSTRUCTION) 10577291Sdd EXCEPTION(NONCONTINUABLE_EXCEPTION) 10677291Sdd EXCEPTION(STACK_OVERFLOW) 10777291Sdd EXCEPTION(INVALID_DISPOSITION) 10877291Sdd EXCEPTION(GUARD_PAGE) 10977291Sdd EXCEPTION(INVALID_HANDLE) 11077291Sdd 11177291Sdd default: 11277291Sdd return "UNKNOWN_ERROR"; 11377291Sdd } 11477291Sdd#undef EXCEPTION 11577291Sdd} 11677291Sdd 11777291Sdd/* Write the minidump to file. The callback function will at the same time 11877291Sdd write the list of modules to the log file. */ 11977291Sddstatic BOOL 12077291Sddwrite_minidump_file(const char *file, PEXCEPTION_POINTERS ptrs, 12177291Sdd MINIDUMP_CALLBACK_ROUTINE module_callback, 12277291Sdd void *data) 12377291Sdd{ 12477291Sdd /* open minidump file */ 12577291Sdd HANDLE minidump_file = CreateFile(file, GENERIC_WRITE, 0, NULL, 12677291Sdd CREATE_ALWAYS, 12777291Sdd FILE_ATTRIBUTE_NORMAL, 12877291Sdd NULL); 12977291Sdd 13077291Sdd if (minidump_file != INVALID_HANDLE_VALUE) 13177291Sdd { 13277291Sdd MINIDUMP_EXCEPTION_INFORMATION expt_info; 13377291Sdd MINIDUMP_CALLBACK_INFORMATION dump_cb_info; 13477291Sdd 13577291Sdd expt_info.ThreadId = GetCurrentThreadId(); 1361590Srgrimes expt_info.ExceptionPointers = ptrs; 13795124Scharnier expt_info.ClientPointers = FALSE; 1381590Srgrimes 1391590Srgrimes dump_cb_info.CallbackRoutine = module_callback; 140202756Sed dump_cb_info.CallbackParam = data; 14136434Sdanny 14236434Sdanny MiniDumpWriteDump_(GetCurrentProcess(), 14336434Sdanny GetCurrentProcessId(), 144118077Stjr minidump_file, 145118077Stjr MiniDumpNormal, 146118077Stjr ptrs ? &expt_info : NULL, 147118077Stjr NULL, 14836434Sdanny &dump_cb_info); 14936434Sdanny 15036434Sdanny CloseHandle(minidump_file); 1511590Srgrimes return TRUE; 1521590Srgrimes } 1531590Srgrimes 1541590Srgrimes return FALSE; 1551590Srgrimes} 1561590Srgrimes 1571590Srgrimes/* Write module information to the log file */ 1581590Srgrimesstatic BOOL CALLBACK 15936434Sdannywrite_module_info_callback(void *data, 16036434Sdanny CONST PMINIDUMP_CALLBACK_INPUT callback_input, 16136434Sdanny PMINIDUMP_CALLBACK_OUTPUT callback_output) 16291541Siedowse{ 16391541Siedowse if (data != NULL && 1641590Srgrimes callback_input != NULL && 1651590Srgrimes callback_input->CallbackType == ModuleCallback) 16677291Sdd { 16777291Sdd FILE *log_file = (FILE *)data; 16877291Sdd MINIDUMP_MODULE_CALLBACK module = callback_input->Module; 1691590Srgrimes 1701590Srgrimes char *buf = convert_wbcs_to_utf8(module.FullPath); 1711590Srgrimes fprintf(log_file, FORMAT_PTR, module.BaseOfImage); 1721590Srgrimes fprintf(log_file, " %s", buf); 17357670Ssheldonh free(buf); 17457670Ssheldonh 1751590Srgrimes fprintf(log_file, " (%d.%d.%d.%d, %d bytes)\n", 17668963Sru HIWORD(module.VersionInfo.dwFileVersionMS), 1771590Srgrimes LOWORD(module.VersionInfo.dwFileVersionMS), 1781590Srgrimes HIWORD(module.VersionInfo.dwFileVersionLS), 1791590Srgrimes LOWORD(module.VersionInfo.dwFileVersionLS), 1801590Srgrimes module.SizeOfImage); 1811590Srgrimes } 1821590Srgrimes 1831590Srgrimes return TRUE; 1841590Srgrimes} 1851590Srgrimes 1861590Srgrimes/* Write details about the current process, platform and the exception */ 18768963Srustatic void 1881590Srgrimeswrite_process_info(EXCEPTION_RECORD *exception, CONTEXT *context, 18976014Sdd FILE *log_file) 19076014Sdd{ 19168963Sru OSVERSIONINFO oi; 1921590Srgrimes const char *cmd_line; 1931590Srgrimes char workingdir[8192]; 1941590Srgrimes 195202756Sed /* write the command line */ 196202756Sed cmd_line = GetCommandLine(); 1971590Srgrimes fprintf(log_file, 1981590Srgrimes "Cmd line: %s\n", cmd_line); 1991590Srgrimes 2001590Srgrimes _getcwd(workingdir, sizeof(workingdir)); 201230458Shrs fprintf(log_file, 202202756Sed "Working Dir: %s\n", workingdir); 2031590Srgrimes 204140420Sru /* write the svn version number info. */ 205140420Sru fprintf(log_file, 206140420Sru "Version: %s, compiled %s, %s\n", 207140420Sru SVN_VERSION, __DATE__, __TIME__); 208140420Sru 20911547Sdg /* write information about the OS */ 21011547Sdg oi.dwOSVersionInfoSize = sizeof(oi); 211141846Sru GetVersionEx(&oi); 212202756Sed 213100440Scharnier fprintf(log_file, 21457670Ssheldonh "Platform: Windows OS version %d.%d build %d %s\n\n", 21568963Sru oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, 21611547Sdg oi.szCSDVersion); 217 218 /* write the exception code */ 219 fprintf(log_file, 220 "Exception: %s\n\n", 221 exception_string(exception->ExceptionCode)); 222 223 /* write the register info. */ 224 fprintf(log_file, 225 "Registers:\n"); 226#if defined(_M_IX86) 227 fprintf(log_file, 228 "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n", 229 context->Eax, context->Ebx, context->Ecx, 230 context->Edx, context->Esi, context->Edi); 231 fprintf(log_file, 232 "eip=%08x esp=%08x ebp=%08x efl=%08x\n", 233 context->Eip, context->Esp, 234 context->Ebp, context->EFlags); 235 fprintf(log_file, 236 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x\n", 237 context->SegCs, context->SegSs, context->SegDs, 238 context->SegEs, context->SegFs, context->SegGs); 239#elif defined(_M_X64) 240 fprintf(log_file, 241 "Rax=%016I64x Rcx=%016I64x Rdx=%016I64x Rbx=%016I64x\n", 242 context->Rax, context->Rcx, context->Rdx, context->Rbx); 243 fprintf(log_file, 244 "Rsp=%016I64x Rbp=%016I64x Rsi=%016I64x Rdi=%016I64x\n", 245 context->Rsp, context->Rbp, context->Rsi, context->Rdi); 246 fprintf(log_file, 247 "R8= %016I64x R9= %016I64x R10= %016I64x R11=%016I64x\n", 248 context->R8, context->R9, context->R10, context->R11); 249 fprintf(log_file, 250 "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n", 251 context->R12, context->R13, context->R14, context->R15); 252 253 fprintf(log_file, 254 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\n", 255 context->SegCs, context->SegDs, context->SegEs, 256 context->SegFs, context->SegGs, context->SegSs); 257#else 258#error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER 259#endif 260} 261 262/* Formats the value at address based on the specified basic type 263 * (char, int, long ...). */ 264static void 265format_basic_type(char *buf, DWORD basic_type, DWORD64 length, void *address) 266{ 267 switch(length) 268 { 269 case 1: 270 sprintf(buf, "0x%02x", (int)*(unsigned char *)address); 271 break; 272 case 2: 273 sprintf(buf, "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 sprintf(buf, "\"%.31s\"", *(const char **)address); 282 else 283 sprintf(buf, FORMAT_PTR, *(DWORD_PTR *)address); 284 } 285 case 6: /* btInt */ 286 sprintf(buf, "%d", *(int *)address); 287 break; 288 case 8: /* btFloat */ 289 sprintf(buf, "%f", *(float *)address); 290 break; 291 default: 292 sprintf(buf, FORMAT_PTR, *(DWORD_PTR *)address); 293 break; 294 } 295 break; 296 case 8: 297 if (basic_type == 8) /* btFloat */ 298 sprintf(buf, "%lf", *(double *)address); 299 else 300 sprintf(buf, "0x%016I64X", *(unsigned __int64 *)address); 301 break; 302 default: 303 sprintf(buf, "[unhandled type 0x%08x of length " FORMAT_PTR "]", 304 basic_type, length); 305 break; 306 } 307} 308 309/* Formats the value at address based on the type (pointer, user defined, 310 * basic type). */ 311static void 312format_value(char *value_str, 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_utf8(type_name_wbcs); 339 LocalFree(type_name_wbcs); 340 341 if (ptr == 0) 342 sprintf(value_str, "(%s) " FORMAT_PTR, 343 type_name, (DWORD_PTR *)value_addr); 344 else if (ptr == 1) 345 sprintf(value_str, "(%s *) " FORMAT_PTR, 346 type_name, *(DWORD_PTR *)value_addr); 347 else 348 sprintf(value_str, "(%s **) " FORMAT_PTR, 349 type_name, *(DWORD_PTR *)value_addr); 350 351 free(type_name); 352 } 353 else 354 sprintf(value_str, "[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 sprintf(value_str, FORMAT_PTR " \"%s\"", 367 *(DWORD_PTR *)value_addr, *(const char **)value_addr); 368 } 369 else if (ptr >= 1) 370 { 371 sprintf(value_str, FORMAT_PTR, *(DWORD_PTR *)value_addr); 372 } 373 else if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_BASETYPE, &bt)) 374 { 375 format_basic_type(value_str, bt, length, value_addr); 376 } 377 } 378 break; 379 case 12: /* SymTagEnum */ 380 sprintf(value_str, "%d", *(DWORD_PTR *)value_addr); 381 break; 382 case 13: /* SymTagFunctionType */ 383 sprintf(value_str, FORMAT_PTR, *(DWORD_PTR *)value_addr); 384 break; 385 default: 386 sprintf(value_str, "[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 char value_str[256] = ""; 411 412 /* get the variable's data */ 413 if (sym_info->Flags & SYMFLAG_REGREL) 414 { 415 var_data = (DWORD_PTR)stack_frame->AddrFrame.Offset; 416 var_data += (DWORD_PTR)sym_info->Address; 417 } 418 else 419 return FALSE; 420 421 if (log_params && sym_info->Flags & SYMFLAG_PARAMETER) 422 { 423 if (last_nr_of_frame == nr_of_frame) 424 fprintf(log_file, ", ", 2); 425 else 426 last_nr_of_frame = nr_of_frame; 427 428 format_value(value_str, sym_info->ModBase, sym_info->TypeIndex, 429 (void *)var_data); 430 fprintf(log_file, "%.*s=%s", (int)sym_info->NameLen, sym_info->Name, 431 value_str); 432 } 433 if (!log_params && sym_info->Flags & SYMFLAG_LOCAL) 434 { 435 format_value(value_str, sym_info->ModBase, sym_info->TypeIndex, 436 (void *)var_data); 437 fprintf(log_file, " %.*s = %s\n", (int)sym_info->NameLen, 438 sym_info->Name, value_str); 439 } 440 441 return TRUE; 442} 443 444/* Write the details of one function to the log file */ 445static void 446write_function_detail(STACKFRAME64 stack_frame, int nr_of_frame, FILE *log_file) 447{ 448 ULONG64 symbolBuffer[(sizeof(SYMBOL_INFO) + 449 MAX_SYM_NAME + 450 sizeof(ULONG64) - 1) / 451 sizeof(ULONG64)]; 452 PSYMBOL_INFO pIHS = (PSYMBOL_INFO)symbolBuffer; 453 DWORD64 func_disp=0; 454 455 IMAGEHLP_STACK_FRAME ih_stack_frame; 456 IMAGEHLP_LINE64 ih_line; 457 DWORD line_disp=0; 458 459 HANDLE proc = GetCurrentProcess(); 460 461 symbols_baton_t ensym; 462 463 nr_of_frame++; /* We need a 1 based index here */ 464 465 /* log the function name */ 466 pIHS->SizeOfStruct = sizeof(SYMBOL_INFO); 467 pIHS->MaxNameLen = MAX_SYM_NAME; 468 if (SymFromAddr_(proc, stack_frame.AddrPC.Offset, &func_disp, pIHS)) 469 { 470 fprintf(log_file, 471 "#%d 0x%08I64x in %.*s(", 472 nr_of_frame, stack_frame.AddrPC.Offset, 473 pIHS->NameLen > 200 ? 200 : (int)pIHS->NameLen, 474 pIHS->Name); 475 476 /* restrict symbol enumeration to this frame only */ 477 ih_stack_frame.InstructionOffset = stack_frame.AddrPC.Offset; 478 SymSetContext_(proc, &ih_stack_frame, 0); 479 480 ensym.log_file = log_file; 481 ensym.stack_frame = &stack_frame; 482 ensym.nr_of_frame = nr_of_frame; 483 484 /* log all function parameters */ 485 ensym.log_params = TRUE; 486 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym); 487 488 fprintf(log_file, ")"); 489 } 490 else 491 { 492 fprintf(log_file, 493 "#%d 0x%08I64x in (unknown function)", 494 nr_of_frame, stack_frame.AddrPC.Offset); 495 } 496 497 /* find the source line for this function. */ 498 ih_line.SizeOfStruct = sizeof(IMAGEHLP_LINE); 499 if (SymGetLineFromAddr64_(proc, stack_frame.AddrPC.Offset, 500 &line_disp, &ih_line) != 0) 501 { 502 fprintf(log_file, 503 " at %s:%d\n", ih_line.FileName, ih_line.LineNumber); 504 } 505 else 506 { 507 fprintf(log_file, "\n"); 508 } 509 510 /* log all function local variables */ 511 ensym.log_params = FALSE; 512 SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym); 513} 514 515/* Walk over the stack and log all relevant information to the log file */ 516static void 517write_stacktrace(CONTEXT *context, FILE *log_file) 518{ 519#if defined (_M_IX86) || defined(_M_X64) || defined(_M_IA64) 520 HANDLE proc = GetCurrentProcess(); 521 STACKFRAME64 stack_frame; 522 DWORD machine; 523 CONTEXT ctx; 524 int skip = 0, i = 0; 525 526 /* The thread information - if not supplied. */ 527 if (context == NULL) 528 { 529 /* If no context is supplied, skip 1 frame */ 530 skip = 1; 531 532 ctx.ContextFlags = CONTEXT_FULL; 533 if (!GetThreadContext(GetCurrentThread(), &ctx)) 534 return; 535 } 536 else 537 { 538 ctx = *context; 539 } 540 541 if (context == NULL) 542 return; 543 544 /* Write the stack trace */ 545 ZeroMemory(&stack_frame, sizeof(STACKFRAME64)); 546 stack_frame.AddrPC.Mode = AddrModeFlat; 547 stack_frame.AddrStack.Mode = AddrModeFlat; 548 stack_frame.AddrFrame.Mode = AddrModeFlat; 549 550#if defined(_M_IX86) 551 machine = IMAGE_FILE_MACHINE_I386; 552 stack_frame.AddrPC.Offset = context->Eip; 553 stack_frame.AddrStack.Offset = context->Esp; 554 stack_frame.AddrFrame.Offset = context->Ebp; 555#elif defined(_M_X64) 556 machine = IMAGE_FILE_MACHINE_AMD64; 557 stack_frame.AddrPC.Offset = context->Rip; 558 stack_frame.AddrStack.Offset = context->Rsp; 559 stack_frame.AddrFrame.Offset = context->Rbp; 560#elif defined(_M_IA64) 561 machine = IMAGE_FILE_MACHINE_IA64; 562 stack_frame.AddrPC.Offset = context->StIIP; 563 stack_frame.AddrStack.Offset = context->SP; 564 stack_frame.AddrBStore.Mode = AddrModeFlat; 565 stack_frame.AddrBStore.Offset = context->RsBSP; 566#else 567#error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER 568#endif 569 570 while (1) 571 { 572 if (! StackWalk64_(machine, proc, GetCurrentThread(), 573 &stack_frame, &ctx, NULL, 574 SymFunctionTableAccess64_, SymGetModuleBase64_, NULL)) 575 { 576 break; 577 } 578 579 if (i >= skip) 580 { 581 /* Try to include symbolic information. 582 Also check that the address is not zero. Sometimes StackWalk 583 returns TRUE with a frame of zero. */ 584 if (stack_frame.AddrPC.Offset != 0) 585 { 586 write_function_detail(stack_frame, i, log_file); 587 } 588 } 589 i++; 590 } 591#else 592#error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER 593#endif 594} 595 596/* Check if a debugger is attached to this process */ 597static BOOL 598is_debugger_present() 599{ 600 HANDLE kernel32_dll = LoadLibrary("kernel32.dll"); 601 BOOL result; 602 603 ISDEBUGGERPRESENT IsDebuggerPresent_ = 604 (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent"); 605 606 if (IsDebuggerPresent_ && IsDebuggerPresent_()) 607 result = TRUE; 608 else 609 result = FALSE; 610 611 FreeLibrary(kernel32_dll); 612 613 return result; 614} 615 616/* Load the dbghelp.dll file, try to find a version that matches our 617 requirements. */ 618static BOOL 619load_dbghelp_dll() 620{ 621 dbghelp_dll = LoadLibrary(DBGHELP_DLL); 622 if (dbghelp_dll != INVALID_HANDLE_VALUE) 623 { 624 DWORD opts; 625 626 /* load the functions */ 627 MiniDumpWriteDump_ = 628 (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump"); 629 SymInitialize_ = 630 (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize"); 631 SymSetOptions_ = 632 (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions"); 633 SymGetOptions_ = 634 (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions"); 635 SymCleanup_ = 636 (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup"); 637 SymGetTypeInfo_ = 638 (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo"); 639 SymGetLineFromAddr64_ = 640 (SYMGETLINEFROMADDR64)GetProcAddress(dbghelp_dll, 641 "SymGetLineFromAddr64"); 642 SymEnumSymbols_ = 643 (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols"); 644 SymSetContext_ = 645 (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext"); 646 SymFromAddr_ = (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr"); 647 StackWalk64_ = (STACKWALK64)GetProcAddress(dbghelp_dll, "StackWalk64"); 648 SymFunctionTableAccess64_ = 649 (SYMFUNCTIONTABLEACCESS64)GetProcAddress(dbghelp_dll, 650 "SymFunctionTableAccess64"); 651 SymGetModuleBase64_ = 652 (SYMGETMODULEBASE64)GetProcAddress(dbghelp_dll, "SymGetModuleBase64"); 653 654 if (! (MiniDumpWriteDump_ && 655 SymInitialize_ && SymSetOptions_ && SymGetOptions_ && 656 SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr64_ && 657 SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ && 658 SymGetModuleBase64_ && StackWalk64_ && 659 SymFunctionTableAccess64_)) 660 goto cleanup; 661 662 /* initialize the symbol loading code */ 663 opts = SymGetOptions_(); 664 665 /* Set the 'load lines' option to retrieve line number information; 666 set the Deferred Loads option to map the debug info in memory only 667 when needed. */ 668 SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS); 669 670 /* Initialize the debughlp DLL with the default path and automatic 671 module enumeration (and loading of symbol tables) for this process. 672 */ 673 SymInitialize_(GetCurrentProcess(), NULL, TRUE); 674 675 return TRUE; 676 } 677 678cleanup: 679 if (dbghelp_dll) 680 FreeLibrary(dbghelp_dll); 681 682 return FALSE; 683} 684 685/* Cleanup the dbghelp.dll library */ 686static void 687cleanup_debughlp() 688{ 689 SymCleanup_(GetCurrentProcess()); 690 691 FreeLibrary(dbghelp_dll); 692} 693 694/* Create a filename based on a prefix, the timestamp and an extension. 695 check if the filename was already taken, retry 3 times. */ 696BOOL 697get_temp_filename(char *filename, const char *prefix, const char *ext) 698{ 699 char temp_dir[MAX_PATH - 64]; 700 int i; 701 702 if (! GetTempPath(MAX_PATH - 64, temp_dir)) 703 return FALSE; 704 705 for (i = 0;i < 3;i++) 706 { 707 HANDLE file; 708 time_t now; 709 char time_str[64]; 710 711 time(&now); 712 strftime(time_str, 64, "%Y%m%d%H%M%S", localtime(&now)); 713 sprintf(filename, "%s%s%s.%s", temp_dir, prefix, time_str, ext); 714 715 file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, 716 FILE_ATTRIBUTE_NORMAL, NULL); 717 if (file != INVALID_HANDLE_VALUE) 718 { 719 CloseHandle(file); 720 return TRUE; 721 } 722 } 723 724 filename[0] = '\0'; 725 return FALSE; 726} 727 728/* Unhandled exception callback set with SetUnhandledExceptionFilter() */ 729LONG WINAPI 730svn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs) 731{ 732 char dmp_filename[MAX_PATH]; 733 char log_filename[MAX_PATH]; 734 FILE *log_file; 735 736 /* Check if the crash handler was already loaded (crash while handling the 737 crash) */ 738 if (dbghelp_dll != INVALID_HANDLE_VALUE) 739 return EXCEPTION_CONTINUE_SEARCH; 740 741 /* don't log anything if we're running inside a debugger ... */ 742 if (is_debugger_present()) 743 return EXCEPTION_CONTINUE_SEARCH; 744 745 /* ... or if we can't create the log files ... */ 746 if (!get_temp_filename(dmp_filename, LOGFILE_PREFIX, "dmp") || 747 !get_temp_filename(log_filename, LOGFILE_PREFIX, "log")) 748 return EXCEPTION_CONTINUE_SEARCH; 749 750 /* If we can't load a recent version of the dbghelp.dll, pass on this 751 exception */ 752 if (!load_dbghelp_dll()) 753 return EXCEPTION_CONTINUE_SEARCH; 754 755 /* open log file */ 756 log_file = fopen(log_filename, "w+"); 757 758 /* write information about the process */ 759 fprintf(log_file, "\nProcess info:\n"); 760 write_process_info(ptrs ? ptrs->ExceptionRecord : NULL, 761 ptrs ? ptrs->ContextRecord : NULL, 762 log_file); 763 764 /* write the stacktrace, if available */ 765 fprintf(log_file, "\nStacktrace:\n"); 766 write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, log_file); 767 768 /* write the minidump file and use the callback to write the list of modules 769 to the log file */ 770 fprintf(log_file, "\n\nLoaded modules:\n"); 771 write_minidump_file(dmp_filename, ptrs, 772 write_module_info_callback, (void *)log_file); 773 774 fclose(log_file); 775 776 /* inform the user */ 777 fprintf(stderr, "This application has halted due to an unexpected error.\n" 778 "A crash report and minidump file were saved to disk, you" 779 " can find them here:\n" 780 "%s\n%s\n" 781 "Please send the log file to %s to help us analyze\nand " 782 "solve this problem.\n\n" 783 "NOTE: The crash report and minidump files can contain some" 784 " sensitive information\n(filenames, partial file content, " 785 "usernames and passwords etc.)\n", 786 log_filename, 787 dmp_filename, 788 CRASHREPORT_EMAIL); 789 790 if (getenv("SVN_DBG_STACKTRACES_TO_STDERR") != NULL) 791 { 792 fprintf(stderr, "\nProcess info:\n"); 793 write_process_info(ptrs ? ptrs->ExceptionRecord : NULL, 794 ptrs ? ptrs->ContextRecord : NULL, 795 stderr); 796 fprintf(stderr, "\nStacktrace:\n"); 797 write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, stderr); 798 } 799 800 fflush(stderr); 801 fflush(stdout); 802 803 cleanup_debughlp(); 804 805 /* terminate the application */ 806 return EXCEPTION_EXECUTE_HANDLER; 807} 808#endif /* SVN_USE_WIN32_CRASHHANDLER */ 809#endif /* WIN32 */ 810