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