1251881Speter/* 2251881Speter * win32_crashrpt.c : provides information after a crash 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* prevent "empty compilation unit" warning on e.g. UNIX */ 25251881Spetertypedef int win32_crashrpt__dummy; 26251881Speter 27251881Speter#ifdef WIN32 28251881Speter#ifdef SVN_USE_WIN32_CRASHHANDLER 29251881Speter 30251881Speter/*** Includes. ***/ 31251881Speter#include <apr.h> 32251881Speter#include <dbghelp.h> 33251881Speter#include <direct.h> 34251881Speter#include <stdio.h> 35251881Speter#include <stdlib.h> 36251881Speter#include <time.h> 37251881Speter 38251881Speter#include "svn_version.h" 39251881Speter 40251881Speter#include "win32_crashrpt.h" 41251881Speter#include "win32_crashrpt_dll.h" 42251881Speter 43251881Speter/*** Global variables ***/ 44251881SpeterHANDLE dbghelp_dll = INVALID_HANDLE_VALUE; 45251881Speter 46251881Speter/* Email address where the crash reports should be sent too. */ 47251881Speter#define CRASHREPORT_EMAIL "users@subversion.apache.org" 48251881Speter 49251881Speter#define DBGHELP_DLL "dbghelp.dll" 50251881Speter 51251881Speter#define LOGFILE_PREFIX "svn-crash-log" 52251881Speter 53251881Speter#if defined(_M_IX86) 54251881Speter#define FORMAT_PTR "0x%08x" 55251881Speter#elif defined(_M_X64) 56251881Speter#define FORMAT_PTR "0x%016I64x" 57251881Speter#endif 58251881Speter 59251881Speter/*** Code. ***/ 60251881Speter 61251881Speter/* Convert a wide-character string to utf-8. This function will create a buffer 62251881Speter * large enough to hold the result string, the caller should free this buffer. 63251881Speter * If the string can't be converted, NULL is returned. 64251881Speter */ 65251881Speterstatic char * 66251881Speterconvert_wbcs_to_utf8(const wchar_t *str) 67251881Speter{ 68251881Speter size_t len = wcslen(str); 69251881Speter char *utf8_str = malloc(sizeof(wchar_t) * len + 1); 70251881Speter len = wcstombs(utf8_str, str, len); 71251881Speter 72251881Speter if (len == -1) 73251881Speter return NULL; 74251881Speter 75251881Speter utf8_str[len] = '\0'; 76251881Speter 77251881Speter return utf8_str; 78251881Speter} 79251881Speter 80251881Speter/* Convert the exception code to a string */ 81251881Speterstatic const char * 82251881Speterexception_string(int exception) 83251881Speter{ 84251881Speter#define EXCEPTION(x) case EXCEPTION_##x: return (#x); 85251881Speter 86251881Speter switch (exception) 87251881Speter { 88251881Speter EXCEPTION(ACCESS_VIOLATION) 89251881Speter EXCEPTION(DATATYPE_MISALIGNMENT) 90251881Speter EXCEPTION(BREAKPOINT) 91251881Speter EXCEPTION(SINGLE_STEP) 92251881Speter EXCEPTION(ARRAY_BOUNDS_EXCEEDED) 93251881Speter EXCEPTION(FLT_DENORMAL_OPERAND) 94251881Speter EXCEPTION(FLT_DIVIDE_BY_ZERO) 95251881Speter EXCEPTION(FLT_INEXACT_RESULT) 96251881Speter EXCEPTION(FLT_INVALID_OPERATION) 97251881Speter EXCEPTION(FLT_OVERFLOW) 98251881Speter EXCEPTION(FLT_STACK_CHECK) 99251881Speter EXCEPTION(FLT_UNDERFLOW) 100251881Speter EXCEPTION(INT_DIVIDE_BY_ZERO) 101251881Speter EXCEPTION(INT_OVERFLOW) 102251881Speter EXCEPTION(PRIV_INSTRUCTION) 103251881Speter EXCEPTION(IN_PAGE_ERROR) 104251881Speter EXCEPTION(ILLEGAL_INSTRUCTION) 105251881Speter EXCEPTION(NONCONTINUABLE_EXCEPTION) 106251881Speter EXCEPTION(STACK_OVERFLOW) 107251881Speter EXCEPTION(INVALID_DISPOSITION) 108251881Speter EXCEPTION(GUARD_PAGE) 109251881Speter EXCEPTION(INVALID_HANDLE) 110251881Speter 111251881Speter default: 112251881Speter return "UNKNOWN_ERROR"; 113251881Speter } 114251881Speter#undef EXCEPTION 115251881Speter} 116251881Speter 117251881Speter/* Write the minidump to file. The callback function will at the same time 118251881Speter write the list of modules to the log file. */ 119251881Speterstatic BOOL 120251881Speterwrite_minidump_file(const char *file, PEXCEPTION_POINTERS ptrs, 121251881Speter MINIDUMP_CALLBACK_ROUTINE module_callback, 122251881Speter void *data) 123251881Speter{ 124251881Speter /* open minidump file */ 125251881Speter HANDLE minidump_file = CreateFile(file, GENERIC_WRITE, 0, NULL, 126251881Speter CREATE_ALWAYS, 127251881Speter FILE_ATTRIBUTE_NORMAL, 128251881Speter NULL); 129251881Speter 130251881Speter if (minidump_file != INVALID_HANDLE_VALUE) 131251881Speter { 132251881Speter MINIDUMP_EXCEPTION_INFORMATION expt_info; 133251881Speter MINIDUMP_CALLBACK_INFORMATION dump_cb_info; 134251881Speter 135251881Speter expt_info.ThreadId = GetCurrentThreadId(); 136251881Speter expt_info.ExceptionPointers = ptrs; 137251881Speter expt_info.ClientPointers = FALSE; 138251881Speter 139251881Speter dump_cb_info.CallbackRoutine = module_callback; 140251881Speter dump_cb_info.CallbackParam = data; 141251881Speter 142251881Speter MiniDumpWriteDump_(GetCurrentProcess(), 143251881Speter GetCurrentProcessId(), 144251881Speter minidump_file, 145251881Speter MiniDumpNormal, 146251881Speter ptrs ? &expt_info : NULL, 147251881Speter NULL, 148251881Speter &dump_cb_info); 149251881Speter 150251881Speter CloseHandle(minidump_file); 151251881Speter return TRUE; 152251881Speter } 153251881Speter 154251881Speter return FALSE; 155251881Speter} 156251881Speter 157251881Speter/* Write module information to the log file */ 158251881Speterstatic BOOL CALLBACK 159251881Speterwrite_module_info_callback(void *data, 160251881Speter CONST PMINIDUMP_CALLBACK_INPUT callback_input, 161251881Speter PMINIDUMP_CALLBACK_OUTPUT callback_output) 162251881Speter{ 163251881Speter if (data != NULL && 164251881Speter callback_input != NULL && 165251881Speter callback_input->CallbackType == ModuleCallback) 166251881Speter { 167251881Speter FILE *log_file = (FILE *)data; 168251881Speter MINIDUMP_MODULE_CALLBACK module = callback_input->Module; 169251881Speter 170251881Speter char *buf = convert_wbcs_to_utf8(module.FullPath); 171251881Speter fprintf(log_file, FORMAT_PTR, module.BaseOfImage); 172251881Speter fprintf(log_file, " %s", buf); 173251881Speter free(buf); 174251881Speter 175251881Speter fprintf(log_file, " (%d.%d.%d.%d, %d bytes)\n", 176251881Speter HIWORD(module.VersionInfo.dwFileVersionMS), 177251881Speter LOWORD(module.VersionInfo.dwFileVersionMS), 178251881Speter HIWORD(module.VersionInfo.dwFileVersionLS), 179251881Speter LOWORD(module.VersionInfo.dwFileVersionLS), 180251881Speter module.SizeOfImage); 181251881Speter } 182251881Speter 183251881Speter return TRUE; 184251881Speter} 185251881Speter 186251881Speter/* Write details about the current process, platform and the exception */ 187251881Speterstatic void 188251881Speterwrite_process_info(EXCEPTION_RECORD *exception, CONTEXT *context, 189251881Speter FILE *log_file) 190251881Speter{ 191251881Speter OSVERSIONINFO oi; 192251881Speter const char *cmd_line; 193251881Speter char workingdir[8192]; 194251881Speter 195251881Speter /* write the command line */ 196251881Speter cmd_line = GetCommandLine(); 197251881Speter fprintf(log_file, 198251881Speter "Cmd line: %s\n", cmd_line); 199251881Speter 200251881Speter _getcwd(workingdir, sizeof(workingdir)); 201251881Speter fprintf(log_file, 202251881Speter "Working Dir: %s\n", workingdir); 203251881Speter 204251881Speter /* write the svn version number info. */ 205251881Speter fprintf(log_file, 206251881Speter "Version: %s, compiled %s, %s\n", 207251881Speter SVN_VERSION, __DATE__, __TIME__); 208251881Speter 209251881Speter /* write information about the OS */ 210251881Speter oi.dwOSVersionInfoSize = sizeof(oi); 211251881Speter GetVersionEx(&oi); 212251881Speter 213251881Speter fprintf(log_file, 214251881Speter "Platform: Windows OS version %d.%d build %d %s\n\n", 215251881Speter oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, 216251881Speter oi.szCSDVersion); 217251881Speter 218251881Speter /* write the exception code */ 219251881Speter fprintf(log_file, 220251881Speter "Exception: %s\n\n", 221251881Speter exception_string(exception->ExceptionCode)); 222251881Speter 223251881Speter /* write the register info. */ 224251881Speter fprintf(log_file, 225251881Speter "Registers:\n"); 226251881Speter#if defined(_M_IX86) 227251881Speter fprintf(log_file, 228251881Speter "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n", 229251881Speter context->Eax, context->Ebx, context->Ecx, 230251881Speter context->Edx, context->Esi, context->Edi); 231251881Speter fprintf(log_file, 232251881Speter "eip=%08x esp=%08x ebp=%08x efl=%08x\n", 233251881Speter context->Eip, context->Esp, 234251881Speter context->Ebp, context->EFlags); 235251881Speter fprintf(log_file, 236251881Speter "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x\n", 237251881Speter context->SegCs, context->SegSs, context->SegDs, 238251881Speter context->SegEs, context->SegFs, context->SegGs); 239251881Speter#elif defined(_M_X64) 240251881Speter fprintf(log_file, 241251881Speter "Rax=%016I64x Rcx=%016I64x Rdx=%016I64x Rbx=%016I64x\n", 242251881Speter context->Rax, context->Rcx, context->Rdx, context->Rbx); 243251881Speter fprintf(log_file, 244251881Speter "Rsp=%016I64x Rbp=%016I64x Rsi=%016I64x Rdi=%016I64x\n", 245251881Speter context->Rsp, context->Rbp, context->Rsi, context->Rdi); 246251881Speter fprintf(log_file, 247251881Speter "R8= %016I64x R9= %016I64x R10= %016I64x R11=%016I64x\n", 248251881Speter context->R8, context->R9, context->R10, context->R11); 249251881Speter fprintf(log_file, 250251881Speter "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n", 251251881Speter context->R12, context->R13, context->R14, context->R15); 252251881Speter 253251881Speter fprintf(log_file, 254251881Speter "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\n", 255251881Speter context->SegCs, context->SegDs, context->SegEs, 256251881Speter context->SegFs, context->SegGs, context->SegSs); 257251881Speter#else 258251881Speter#error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER 259251881Speter#endif 260251881Speter} 261251881Speter 262251881Speter/* Formats the value at address based on the specified basic type 263251881Speter * (char, int, long ...). */ 264251881Speterstatic void 265251881Speterformat_basic_type(char *buf, DWORD basic_type, DWORD64 length, void *address) 266251881Speter{ 267251881Speter switch(length) 268251881Speter { 269251881Speter case 1: 270251881Speter sprintf(buf, "0x%02x", (int)*(unsigned char *)address); 271251881Speter break; 272251881Speter case 2: 273251881Speter sprintf(buf, "0x%04x", (int)*(unsigned short *)address); 274251881Speter break; 275251881Speter case 4: 276251881Speter switch(basic_type) 277251881Speter { 278251881Speter case 2: /* btChar */ 279251881Speter { 280251881Speter if (!IsBadStringPtr(*(PSTR*)address, 32)) 281251881Speter sprintf(buf, "\"%.31s\"", *(const char **)address); 282251881Speter else 283251881Speter sprintf(buf, FORMAT_PTR, *(DWORD_PTR *)address); 284251881Speter } 285251881Speter case 6: /* btInt */ 286251881Speter sprintf(buf, "%d", *(int *)address); 287251881Speter break; 288251881Speter case 8: /* btFloat */ 289251881Speter sprintf(buf, "%f", *(float *)address); 290251881Speter break; 291251881Speter default: 292251881Speter sprintf(buf, FORMAT_PTR, *(DWORD_PTR *)address); 293251881Speter break; 294251881Speter } 295251881Speter break; 296251881Speter case 8: 297251881Speter if (basic_type == 8) /* btFloat */ 298251881Speter sprintf(buf, "%lf", *(double *)address); 299251881Speter else 300251881Speter sprintf(buf, "0x%016I64X", *(unsigned __int64 *)address); 301251881Speter break; 302251881Speter default: 303251881Speter sprintf(buf, "[unhandled type 0x%08x of length " FORMAT_PTR "]", 304251881Speter basic_type, length); 305251881Speter break; 306251881Speter } 307251881Speter} 308251881Speter 309251881Speter/* Formats the value at address based on the type (pointer, user defined, 310251881Speter * basic type). */ 311251881Speterstatic void 312251881Speterformat_value(char *value_str, DWORD64 mod_base, DWORD type, void *value_addr) 313251881Speter{ 314251881Speter DWORD tag = 0; 315251881Speter int ptr = 0; 316251881Speter HANDLE proc = GetCurrentProcess(); 317251881Speter 318251881Speter while (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMTAG, &tag)) 319251881Speter { 320251881Speter /* SymTagPointerType */ 321251881Speter if (tag == 14) 322251881Speter { 323251881Speter ptr++; 324251881Speter SymGetTypeInfo_(proc, mod_base, type, TI_GET_TYPE, &type); 325251881Speter continue; 326251881Speter } 327251881Speter break; 328251881Speter } 329251881Speter 330251881Speter switch(tag) 331251881Speter { 332251881Speter case 11: /* SymTagUDT */ 333251881Speter { 334251881Speter WCHAR *type_name_wbcs; 335251881Speter if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMNAME, 336251881Speter &type_name_wbcs)) 337251881Speter { 338251881Speter char *type_name = convert_wbcs_to_utf8(type_name_wbcs); 339251881Speter LocalFree(type_name_wbcs); 340251881Speter 341251881Speter if (ptr == 0) 342251881Speter sprintf(value_str, "(%s) " FORMAT_PTR, 343251881Speter type_name, (DWORD_PTR *)value_addr); 344251881Speter else if (ptr == 1) 345251881Speter sprintf(value_str, "(%s *) " FORMAT_PTR, 346251881Speter type_name, *(DWORD_PTR *)value_addr); 347251881Speter else 348251881Speter sprintf(value_str, "(%s **) " FORMAT_PTR, 349251881Speter type_name, *(DWORD_PTR *)value_addr); 350251881Speter 351251881Speter free(type_name); 352251881Speter } 353251881Speter else 354251881Speter sprintf(value_str, "[no symbol tag]"); 355251881Speter } 356251881Speter break; 357251881Speter case 16: /* SymTagBaseType */ 358251881Speter { 359251881Speter DWORD bt; 360251881Speter ULONG64 length; 361251881Speter SymGetTypeInfo_(proc, mod_base, type, TI_GET_LENGTH, &length); 362251881Speter 363251881Speter /* print a char * as a string */ 364251881Speter if (ptr == 1 && length == 1) 365251881Speter { 366251881Speter sprintf(value_str, FORMAT_PTR " \"%s\"", 367251881Speter *(DWORD_PTR *)value_addr, *(const char **)value_addr); 368251881Speter } 369251881Speter else if (ptr >= 1) 370251881Speter { 371251881Speter sprintf(value_str, FORMAT_PTR, *(DWORD_PTR *)value_addr); 372251881Speter } 373251881Speter else if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_BASETYPE, &bt)) 374251881Speter { 375251881Speter format_basic_type(value_str, bt, length, value_addr); 376251881Speter } 377251881Speter } 378251881Speter break; 379251881Speter case 12: /* SymTagEnum */ 380251881Speter sprintf(value_str, "%d", *(DWORD_PTR *)value_addr); 381251881Speter break; 382251881Speter case 13: /* SymTagFunctionType */ 383251881Speter sprintf(value_str, FORMAT_PTR, *(DWORD_PTR *)value_addr); 384251881Speter break; 385251881Speter default: 386251881Speter sprintf(value_str, "[unhandled tag: %d]", tag); 387251881Speter break; 388251881Speter } 389251881Speter} 390251881Speter 391251881Speter/* Internal structure used to pass some data to the enumerate symbols 392251881Speter * callback */ 393251881Spetertypedef struct symbols_baton_t { 394251881Speter STACKFRAME64 *stack_frame; 395251881Speter FILE *log_file; 396251881Speter int nr_of_frame; 397251881Speter BOOL log_params; 398251881Speter} symbols_baton_t; 399251881Speter 400251881Speter/* Write the details of one parameter or local variable to the log file */ 401251881Speterstatic BOOL WINAPI 402251881Speterwrite_var_values(PSYMBOL_INFO sym_info, ULONG sym_size, void *baton) 403251881Speter{ 404251881Speter static int last_nr_of_frame = 0; 405251881Speter DWORD_PTR var_data = 0; /* Will point to the variable's data in memory */ 406251881Speter STACKFRAME64 *stack_frame = ((symbols_baton_t*)baton)->stack_frame; 407251881Speter FILE *log_file = ((symbols_baton_t*)baton)->log_file; 408251881Speter int nr_of_frame = ((symbols_baton_t*)baton)->nr_of_frame; 409251881Speter BOOL log_params = ((symbols_baton_t*)baton)->log_params; 410251881Speter char value_str[256] = ""; 411251881Speter 412251881Speter /* get the variable's data */ 413251881Speter if (sym_info->Flags & SYMFLAG_REGREL) 414251881Speter { 415251881Speter var_data = (DWORD_PTR)stack_frame->AddrFrame.Offset; 416251881Speter var_data += (DWORD_PTR)sym_info->Address; 417251881Speter } 418251881Speter else 419251881Speter return FALSE; 420251881Speter 421251881Speter if (log_params && sym_info->Flags & SYMFLAG_PARAMETER) 422251881Speter { 423251881Speter if (last_nr_of_frame == nr_of_frame) 424251881Speter fprintf(log_file, ", ", 2); 425251881Speter else 426251881Speter last_nr_of_frame = nr_of_frame; 427251881Speter 428251881Speter format_value(value_str, sym_info->ModBase, sym_info->TypeIndex, 429251881Speter (void *)var_data); 430251881Speter fprintf(log_file, "%s=%s", sym_info->Name, value_str); 431251881Speter } 432251881Speter if (!log_params && sym_info->Flags & SYMFLAG_LOCAL) 433251881Speter { 434251881Speter format_value(value_str, sym_info->ModBase, sym_info->TypeIndex, 435251881Speter (void *)var_data); 436251881Speter fprintf(log_file, " %s = %s\n", sym_info->Name, value_str); 437251881Speter } 438251881Speter 439251881Speter return TRUE; 440251881Speter} 441251881Speter 442251881Speter/* Write the details of one function to the log file */ 443251881Speterstatic void 444251881Speterwrite_function_detail(STACKFRAME64 stack_frame, int nr_of_frame, FILE *log_file) 445251881Speter{ 446251881Speter ULONG64 symbolBuffer[(sizeof(SYMBOL_INFO) + 447251881Speter MAX_SYM_NAME + 448251881Speter sizeof(ULONG64) - 1) / 449251881Speter sizeof(ULONG64)]; 450251881Speter PSYMBOL_INFO pIHS = (PSYMBOL_INFO)symbolBuffer; 451251881Speter DWORD64 func_disp=0; 452251881Speter 453251881Speter IMAGEHLP_STACK_FRAME ih_stack_frame; 454251881Speter IMAGEHLP_LINE64 ih_line; 455251881Speter DWORD line_disp=0; 456251881Speter 457251881Speter HANDLE proc = GetCurrentProcess(); 458251881Speter 459251881Speter symbols_baton_t ensym; 460251881Speter 461251881Speter nr_of_frame++; /* We need a 1 based index here */ 462251881Speter 463251881Speter /* log the function name */ 464251881Speter pIHS->SizeOfStruct = sizeof(SYMBOL_INFO); 465251881Speter pIHS->MaxNameLen = MAX_SYM_NAME; 466251881Speter if (SymFromAddr_(proc, stack_frame.AddrPC.Offset, &func_disp, pIHS)) 467251881Speter { 468251881Speter fprintf(log_file, 469251881Speter "#%d 0x%08I64x in %.200s(", 470251881Speter nr_of_frame, stack_frame.AddrPC.Offset, pIHS->Name); 471251881Speter 472251881Speter /* restrict symbol enumeration to this frame only */ 473251881Speter ih_stack_frame.InstructionOffset = stack_frame.AddrPC.Offset; 474251881Speter SymSetContext_(proc, &ih_stack_frame, 0); 475251881Speter 476251881Speter ensym.log_file = log_file; 477251881Speter ensym.stack_frame = &stack_frame; 478251881Speter ensym.nr_of_frame = nr_of_frame; 479251881Speter 480251881Speter /* log all function parameters */ 481251881Speter ensym.log_params = TRUE; 482251881Speter SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym); 483251881Speter 484251881Speter fprintf(log_file, ")"); 485251881Speter } 486251881Speter else 487251881Speter { 488251881Speter fprintf(log_file, 489251881Speter "#%d 0x%08I64x in (unknown function)", 490251881Speter nr_of_frame, stack_frame.AddrPC.Offset); 491251881Speter } 492251881Speter 493251881Speter /* find the source line for this function. */ 494251881Speter ih_line.SizeOfStruct = sizeof(IMAGEHLP_LINE); 495251881Speter if (SymGetLineFromAddr64_(proc, stack_frame.AddrPC.Offset, 496251881Speter &line_disp, &ih_line) != 0) 497251881Speter { 498251881Speter fprintf(log_file, 499251881Speter " at %s:%d\n", ih_line.FileName, ih_line.LineNumber); 500251881Speter } 501251881Speter else 502251881Speter { 503251881Speter fprintf(log_file, "\n"); 504251881Speter } 505251881Speter 506251881Speter /* log all function local variables */ 507251881Speter ensym.log_params = FALSE; 508251881Speter SymEnumSymbols_(proc, 0, 0, write_var_values, &ensym); 509251881Speter} 510251881Speter 511251881Speter/* Walk over the stack and log all relevant information to the log file */ 512251881Speterstatic void 513251881Speterwrite_stacktrace(CONTEXT *context, FILE *log_file) 514251881Speter{ 515251881Speter#if defined (_M_IX86) || defined(_M_X64) || defined(_M_IA64) 516251881Speter HANDLE proc = GetCurrentProcess(); 517251881Speter STACKFRAME64 stack_frame; 518251881Speter DWORD machine; 519251881Speter CONTEXT ctx; 520251881Speter int skip = 0, i = 0; 521251881Speter 522251881Speter /* The thread information - if not supplied. */ 523251881Speter if (context == NULL) 524251881Speter { 525251881Speter /* If no context is supplied, skip 1 frame */ 526251881Speter skip = 1; 527251881Speter 528251881Speter ctx.ContextFlags = CONTEXT_FULL; 529251881Speter if (!GetThreadContext(GetCurrentThread(), &ctx)) 530251881Speter return; 531251881Speter } 532251881Speter else 533251881Speter { 534251881Speter ctx = *context; 535251881Speter } 536251881Speter 537251881Speter if (context == NULL) 538251881Speter return; 539251881Speter 540251881Speter /* Write the stack trace */ 541251881Speter ZeroMemory(&stack_frame, sizeof(STACKFRAME64)); 542251881Speter stack_frame.AddrPC.Mode = AddrModeFlat; 543251881Speter stack_frame.AddrStack.Mode = AddrModeFlat; 544251881Speter stack_frame.AddrFrame.Mode = AddrModeFlat; 545251881Speter 546251881Speter#if defined(_M_IX86) 547251881Speter machine = IMAGE_FILE_MACHINE_I386; 548251881Speter stack_frame.AddrPC.Offset = context->Eip; 549251881Speter stack_frame.AddrStack.Offset = context->Esp; 550251881Speter stack_frame.AddrFrame.Offset = context->Ebp; 551251881Speter#elif defined(_M_X64) 552251881Speter machine = IMAGE_FILE_MACHINE_AMD64; 553251881Speter stack_frame.AddrPC.Offset = context->Rip; 554251881Speter stack_frame.AddrStack.Offset = context->Rsp; 555251881Speter stack_frame.AddrFrame.Offset = context->Rbp; 556251881Speter#elif defined(_M_IA64) 557251881Speter machine = IMAGE_FILE_MACHINE_IA64; 558251881Speter stack_frame.AddrPC.Offset = context->StIIP; 559251881Speter stack_frame.AddrStack.Offset = context->SP; 560251881Speter stack_frame.AddrBStore.Mode = AddrModeFlat; 561251881Speter stack_frame.AddrBStore.Offset = context->RsBSP; 562251881Speter#else 563251881Speter#error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER 564251881Speter#endif 565251881Speter 566251881Speter while (1) 567251881Speter { 568251881Speter if (! StackWalk64_(machine, proc, GetCurrentThread(), 569251881Speter &stack_frame, &ctx, NULL, 570251881Speter SymFunctionTableAccess64_, SymGetModuleBase64_, NULL)) 571251881Speter { 572251881Speter break; 573251881Speter } 574251881Speter 575251881Speter if (i >= skip) 576251881Speter { 577251881Speter /* Try to include symbolic information. 578251881Speter Also check that the address is not zero. Sometimes StackWalk 579251881Speter returns TRUE with a frame of zero. */ 580251881Speter if (stack_frame.AddrPC.Offset != 0) 581251881Speter { 582251881Speter write_function_detail(stack_frame, i, log_file); 583251881Speter } 584251881Speter } 585251881Speter i++; 586251881Speter } 587251881Speter#else 588251881Speter#error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER 589251881Speter#endif 590251881Speter} 591251881Speter 592251881Speter/* Check if a debugger is attached to this process */ 593251881Speterstatic BOOL 594251881Speteris_debugger_present() 595251881Speter{ 596251881Speter HANDLE kernel32_dll = LoadLibrary("kernel32.dll"); 597251881Speter BOOL result; 598251881Speter 599251881Speter ISDEBUGGERPRESENT IsDebuggerPresent_ = 600251881Speter (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent"); 601251881Speter 602251881Speter if (IsDebuggerPresent_ && IsDebuggerPresent_()) 603251881Speter result = TRUE; 604251881Speter else 605251881Speter result = FALSE; 606251881Speter 607251881Speter FreeLibrary(kernel32_dll); 608251881Speter 609251881Speter return result; 610251881Speter} 611251881Speter 612251881Speter/* Load the dbghelp.dll file, try to find a version that matches our 613251881Speter requirements. */ 614251881Speterstatic BOOL 615251881Speterload_dbghelp_dll() 616251881Speter{ 617251881Speter dbghelp_dll = LoadLibrary(DBGHELP_DLL); 618251881Speter if (dbghelp_dll != INVALID_HANDLE_VALUE) 619251881Speter { 620251881Speter DWORD opts; 621251881Speter 622251881Speter /* load the functions */ 623251881Speter MiniDumpWriteDump_ = 624251881Speter (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump"); 625251881Speter SymInitialize_ = 626251881Speter (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize"); 627251881Speter SymSetOptions_ = 628251881Speter (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions"); 629251881Speter SymGetOptions_ = 630251881Speter (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions"); 631251881Speter SymCleanup_ = 632251881Speter (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup"); 633251881Speter SymGetTypeInfo_ = 634251881Speter (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo"); 635251881Speter SymGetLineFromAddr64_ = 636251881Speter (SYMGETLINEFROMADDR64)GetProcAddress(dbghelp_dll, 637251881Speter "SymGetLineFromAddr64"); 638251881Speter SymEnumSymbols_ = 639251881Speter (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols"); 640251881Speter SymSetContext_ = 641251881Speter (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext"); 642251881Speter SymFromAddr_ = (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr"); 643251881Speter StackWalk64_ = (STACKWALK64)GetProcAddress(dbghelp_dll, "StackWalk64"); 644251881Speter SymFunctionTableAccess64_ = 645251881Speter (SYMFUNCTIONTABLEACCESS64)GetProcAddress(dbghelp_dll, 646251881Speter "SymFunctionTableAccess64"); 647251881Speter SymGetModuleBase64_ = 648251881Speter (SYMGETMODULEBASE64)GetProcAddress(dbghelp_dll, "SymGetModuleBase64"); 649251881Speter 650251881Speter if (! (MiniDumpWriteDump_ && 651251881Speter SymInitialize_ && SymSetOptions_ && SymGetOptions_ && 652251881Speter SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr64_ && 653251881Speter SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ && 654251881Speter SymGetModuleBase64_ && StackWalk64_ && 655251881Speter SymFunctionTableAccess64_)) 656251881Speter goto cleanup; 657251881Speter 658251881Speter /* initialize the symbol loading code */ 659251881Speter opts = SymGetOptions_(); 660251881Speter 661251881Speter /* Set the 'load lines' option to retrieve line number information; 662251881Speter set the Deferred Loads option to map the debug info in memory only 663251881Speter when needed. */ 664251881Speter SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS); 665251881Speter 666251881Speter /* Initialize the debughlp DLL with the default path and automatic 667251881Speter module enumeration (and loading of symbol tables) for this process. 668251881Speter */ 669251881Speter SymInitialize_(GetCurrentProcess(), NULL, TRUE); 670251881Speter 671251881Speter return TRUE; 672251881Speter } 673251881Speter 674251881Spetercleanup: 675251881Speter if (dbghelp_dll) 676251881Speter FreeLibrary(dbghelp_dll); 677251881Speter 678251881Speter return FALSE; 679251881Speter} 680251881Speter 681251881Speter/* Cleanup the dbghelp.dll library */ 682251881Speterstatic void 683251881Spetercleanup_debughlp() 684251881Speter{ 685251881Speter SymCleanup_(GetCurrentProcess()); 686251881Speter 687251881Speter FreeLibrary(dbghelp_dll); 688251881Speter} 689251881Speter 690251881Speter/* Create a filename based on a prefix, the timestamp and an extension. 691251881Speter check if the filename was already taken, retry 3 times. */ 692251881SpeterBOOL 693251881Speterget_temp_filename(char *filename, const char *prefix, const char *ext) 694251881Speter{ 695251881Speter char temp_dir[MAX_PATH - 64]; 696251881Speter int i; 697251881Speter 698251881Speter if (! GetTempPath(MAX_PATH - 64, temp_dir)) 699251881Speter return FALSE; 700251881Speter 701251881Speter for (i = 0;i < 3;i++) 702251881Speter { 703251881Speter HANDLE file; 704251881Speter time_t now; 705251881Speter char time_str[64]; 706251881Speter 707251881Speter time(&now); 708251881Speter strftime(time_str, 64, "%Y%m%d%H%M%S", localtime(&now)); 709251881Speter sprintf(filename, "%s%s%s.%s", temp_dir, prefix, time_str, ext); 710251881Speter 711251881Speter file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, 712251881Speter FILE_ATTRIBUTE_NORMAL, NULL); 713251881Speter if (file != INVALID_HANDLE_VALUE) 714251881Speter { 715251881Speter CloseHandle(file); 716251881Speter return TRUE; 717251881Speter } 718251881Speter } 719251881Speter 720251881Speter filename[0] = '\0'; 721251881Speter return FALSE; 722251881Speter} 723251881Speter 724251881Speter/* Unhandled exception callback set with SetUnhandledExceptionFilter() */ 725251881SpeterLONG WINAPI 726251881Spetersvn__unhandled_exception_filter(PEXCEPTION_POINTERS ptrs) 727251881Speter{ 728251881Speter char dmp_filename[MAX_PATH]; 729251881Speter char log_filename[MAX_PATH]; 730251881Speter FILE *log_file; 731251881Speter 732251881Speter /* Check if the crash handler was already loaded (crash while handling the 733251881Speter crash) */ 734251881Speter if (dbghelp_dll != INVALID_HANDLE_VALUE) 735251881Speter return EXCEPTION_CONTINUE_SEARCH; 736251881Speter 737251881Speter /* don't log anything if we're running inside a debugger ... */ 738251881Speter if (is_debugger_present()) 739251881Speter return EXCEPTION_CONTINUE_SEARCH; 740251881Speter 741251881Speter /* ... or if we can't create the log files ... */ 742251881Speter if (!get_temp_filename(dmp_filename, LOGFILE_PREFIX, "dmp") || 743251881Speter !get_temp_filename(log_filename, LOGFILE_PREFIX, "log")) 744251881Speter return EXCEPTION_CONTINUE_SEARCH; 745251881Speter 746251881Speter /* If we can't load a recent version of the dbghelp.dll, pass on this 747251881Speter exception */ 748251881Speter if (!load_dbghelp_dll()) 749251881Speter return EXCEPTION_CONTINUE_SEARCH; 750251881Speter 751251881Speter /* open log file */ 752251881Speter log_file = fopen(log_filename, "w+"); 753251881Speter 754251881Speter /* write information about the process */ 755251881Speter fprintf(log_file, "\nProcess info:\n"); 756251881Speter write_process_info(ptrs ? ptrs->ExceptionRecord : NULL, 757251881Speter ptrs ? ptrs->ContextRecord : NULL, 758251881Speter log_file); 759251881Speter 760251881Speter /* write the stacktrace, if available */ 761251881Speter fprintf(log_file, "\nStacktrace:\n"); 762251881Speter write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, log_file); 763251881Speter 764251881Speter /* write the minidump file and use the callback to write the list of modules 765251881Speter to the log file */ 766251881Speter fprintf(log_file, "\n\nLoaded modules:\n"); 767251881Speter write_minidump_file(dmp_filename, ptrs, 768251881Speter write_module_info_callback, (void *)log_file); 769251881Speter 770251881Speter fclose(log_file); 771251881Speter 772251881Speter /* inform the user */ 773251881Speter fprintf(stderr, "This application has halted due to an unexpected error.\n" 774251881Speter "A crash report and minidump file were saved to disk, you" 775251881Speter " can find them here:\n" 776251881Speter "%s\n%s\n" 777251881Speter "Please send the log file to %s to help us analyze\nand " 778251881Speter "solve this problem.\n\n" 779251881Speter "NOTE: The crash report and minidump files can contain some" 780251881Speter " sensitive information\n(filenames, partial file content, " 781251881Speter "usernames and passwords etc.)\n", 782251881Speter log_filename, 783251881Speter dmp_filename, 784251881Speter CRASHREPORT_EMAIL); 785251881Speter 786251881Speter if (getenv("SVN_DBG_STACKTRACES_TO_STDERR") != NULL) 787251881Speter { 788251881Speter fprintf(stderr, "\nProcess info:\n"); 789251881Speter write_process_info(ptrs ? ptrs->ExceptionRecord : NULL, 790251881Speter ptrs ? ptrs->ContextRecord : NULL, 791251881Speter stderr); 792251881Speter fprintf(stderr, "\nStacktrace:\n"); 793251881Speter write_stacktrace(ptrs ? ptrs->ContextRecord : NULL, stderr); 794251881Speter } 795251881Speter 796251881Speter fflush(stderr); 797251881Speter fflush(stdout); 798251881Speter 799251881Speter cleanup_debughlp(); 800251881Speter 801251881Speter /* terminate the application */ 802251881Speter return EXCEPTION_EXECUTE_HANDLER; 803251881Speter} 804251881Speter#endif /* SVN_USE_WIN32_CRASHHANDLER */ 805251881Speter#endif /* WIN32 */ 806