mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			739 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			739 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2001, 2011, Oracle and/or its affiliates
 | 
						|
   Copyright (c) 2020, MariaDB Corporation.
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU General Public License as published by
 | 
						|
   the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program; if not, write to the Free Software
 | 
						|
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
#include "mysys_priv.h"
 | 
						|
#include <my_stacktrace.h>
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
#include <signal.h>
 | 
						|
#include <m_string.h>
 | 
						|
#ifdef HAVE_STACKTRACE
 | 
						|
#include <unistd.h>
 | 
						|
#include <strings.h>
 | 
						|
 | 
						|
#ifdef __linux__
 | 
						|
#include <ctype.h>          /* isprint */
 | 
						|
#include <sys/syscall.h>    /* SYS_gettid */
 | 
						|
#endif
 | 
						|
 | 
						|
#if HAVE_EXECINFO_H
 | 
						|
#include <execinfo.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_gcov
 | 
						|
#include <gcov.h>
 | 
						|
#endif
 | 
						|
/**
 | 
						|
   Default handler for printing stacktrace
 | 
						|
*/
 | 
						|
 | 
						|
static sig_handler default_handle_fatal_signal(int sig)
 | 
						|
{
 | 
						|
  my_safe_printf_stderr("%s: Got signal %d. Attempting backtrace\n",
 | 
						|
                        my_progname_short, sig);
 | 
						|
  my_print_stacktrace(0,0,1);
 | 
						|
#ifndef _WIN32
 | 
						|
  signal(sig, SIG_DFL);
 | 
						|
  kill(getpid(), sig);
 | 
						|
#endif /* _WIN32 */
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   Initialize printing of stacktrace at signal
 | 
						|
*/
 | 
						|
 | 
						|
void my_setup_stacktrace(void)
 | 
						|
{
 | 
						|
  struct sigaction sa;
 | 
						|
  sa.sa_flags = SA_RESETHAND | SA_NODEFER;
 | 
						|
  sigemptyset(&sa.sa_mask);
 | 
						|
  sa.sa_handler= default_handle_fatal_signal;
 | 
						|
  sigaction(SIGSEGV, &sa, NULL);
 | 
						|
  sigaction(SIGABRT, &sa, NULL);
 | 
						|
#ifdef SIGBUS
 | 
						|
  sigaction(SIGBUS, &sa, NULL);
 | 
						|
#endif
 | 
						|
  sigaction(SIGILL, &sa, NULL);
 | 
						|
  sigaction(SIGFPE, &sa, NULL);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Attempt to print a char * pointer as a string.
 | 
						|
 | 
						|
  SYNOPSIS
 | 
						|
    Prints either until the end of string ('\0'), or max_len characters have
 | 
						|
    been printed.
 | 
						|
 | 
						|
  RETURN VALUE
 | 
						|
    0  Pointer was within the heap address space.
 | 
						|
       The string was printed fully, or until the end of the heap address space.
 | 
						|
    1  Pointer is outside the heap address space. Printed as invalid.
 | 
						|
 | 
						|
  NOTE
 | 
						|
    On some systems, we can have valid pointers outside the heap address space.
 | 
						|
    This is through the use of mmap inside malloc calls. When this function
 | 
						|
    returns 1, it does not mean 100% that the pointer is corrupted.
 | 
						|
*/
 | 
						|
 | 
						|
int my_safe_print_str(const char* val, size_t max_len)
 | 
						|
{
 | 
						|
  const char *orig_val= val;
 | 
						|
  if (!val)
 | 
						|
  {
 | 
						|
    my_safe_printf_stderr("%s", "(null)");
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  for (; max_len; --max_len)
 | 
						|
  {
 | 
						|
    if (my_write_stderr((val++), 1) != 1)
 | 
						|
    {
 | 
						|
      if ((errno == EFAULT) &&(val == orig_val + 1))
 | 
						|
      {
 | 
						|
        // We can not read the address from very beginning
 | 
						|
        my_safe_printf_stderr("Can't access address %p", orig_val);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  my_safe_printf_stderr("%s", "\n");
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#if defined(HAVE_PRINTSTACK)
 | 
						|
 | 
						|
/* Use Solaris' symbolic stack trace routine. */
 | 
						|
#include <ucontext.h>
 | 
						|
 | 
						|
void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)), 
 | 
						|
                         ulong thread_stack __attribute__((unused)),
 | 
						|
                         my_bool silent)
 | 
						|
{
 | 
						|
  if (printstack(fileno(stderr)) == -1)
 | 
						|
    my_safe_printf_stderr("%s",
 | 
						|
      "Error when traversing the stack, stack appears corrupt.\n");
 | 
						|
  else if (!silent)
 | 
						|
    my_safe_printf_stderr("%s",
 | 
						|
      "Please read "
 | 
						|
      "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
 | 
						|
      "and follow instructions on how to resolve the stack trace.\n"
 | 
						|
      "Resolved stack trace is much more helpful in diagnosing the\n"
 | 
						|
      "problem, so please do resolve it\n");
 | 
						|
}
 | 
						|
 | 
						|
#elif HAVE_BACKTRACE && (HAVE_BACKTRACE_SYMBOLS || HAVE_BACKTRACE_SYMBOLS_FD)
 | 
						|
 | 
						|
#if BACKTRACE_DEMANGLE
 | 
						|
 | 
						|
char __attribute__ ((weak)) *
 | 
						|
my_demangle(const char *mangled_name __attribute__((unused)),
 | 
						|
            int *status __attribute__((unused)))
 | 
						|
{
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void my_demangle_symbols(char **addrs, int n)
 | 
						|
{
 | 
						|
  int status, i;
 | 
						|
  char *begin, *end, *demangled;
 | 
						|
 | 
						|
  for (i= 0; i < n; i++)
 | 
						|
  {
 | 
						|
    demangled= NULL;
 | 
						|
    begin= strchr(addrs[i], '(');
 | 
						|
    end= begin ? strchr(begin, '+') : NULL;
 | 
						|
 | 
						|
    if (begin && end)
 | 
						|
    {
 | 
						|
      *begin++= *end++= '\0';
 | 
						|
      demangled= my_demangle(begin, &status);
 | 
						|
      if (!demangled || status)
 | 
						|
      {
 | 
						|
        demangled= NULL;
 | 
						|
        begin[-1]= '(';
 | 
						|
        end[-1]= '+';
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (demangled)
 | 
						|
      my_safe_printf_stderr("%s(%s+%s\n", addrs[i], demangled, end);
 | 
						|
    else
 | 
						|
      my_safe_printf_stderr("%s\n", addrs[i]);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#endif /* BACKTRACE_DEMANGLE */
 | 
						|
 | 
						|
#if HAVE_MY_ADDR_RESOLVE
 | 
						|
static int print_with_addr_resolve(void **addrs, int n)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  const char *err;
 | 
						|
 | 
						|
  if ((err= my_addr_resolve_init()))
 | 
						|
  {
 | 
						|
    my_safe_printf_stderr("(my_addr_resolve failure: %s)\n", err);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  for (i= 0; i < n; i++)
 | 
						|
  {
 | 
						|
    my_addr_loc loc;
 | 
						|
    if (my_addr_resolve(addrs[i], &loc))
 | 
						|
      backtrace_symbols_fd(addrs+i, 1, fileno(stderr));
 | 
						|
    else
 | 
						|
      my_safe_printf_stderr("%s:%u(%s)[%p]\n",
 | 
						|
              loc.file, loc.line, loc.func, addrs[i]);
 | 
						|
  }
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack,
 | 
						|
                         my_bool silent __attribute__((unused)))
 | 
						|
{
 | 
						|
  void *addrs[128];
 | 
						|
  char **strings __attribute__((unused)) = NULL;
 | 
						|
  int n = backtrace(addrs, array_elements(addrs));
 | 
						|
  my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n",
 | 
						|
                        stack_bottom, thread_stack);
 | 
						|
#if HAVE_MY_ADDR_RESOLVE
 | 
						|
  if (print_with_addr_resolve(addrs, n))
 | 
						|
    return;
 | 
						|
#endif
 | 
						|
#if BACKTRACE_DEMANGLE
 | 
						|
  if ((strings= backtrace_symbols(addrs, n)))
 | 
						|
  {
 | 
						|
    my_demangle_symbols(strings, n);
 | 
						|
    free(strings);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
#if HAVE_BACKTRACE_SYMBOLS_FD
 | 
						|
  backtrace_symbols_fd(addrs, n, fileno(stderr));
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#elif defined(TARGET_OS_LINUX)
 | 
						|
 | 
						|
#ifdef __i386__
 | 
						|
#define SIGRETURN_FRAME_OFFSET 17
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __x86_64__
 | 
						|
#define SIGRETURN_FRAME_OFFSET 23
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(__alpha__) && defined(__GNUC__)
 | 
						|
/*
 | 
						|
  The only way to backtrace without a symbol table on alpha
 | 
						|
  is to find stq fp,N(sp), and the first byte
 | 
						|
  of the instruction opcode will give us the value of N. From this
 | 
						|
  we can find where the old value of fp is stored
 | 
						|
*/
 | 
						|
 | 
						|
#define MAX_INSTR_IN_FUNC  10000
 | 
						|
 | 
						|
inline uchar** find_prev_fp(uint32* pc, uchar** fp)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
 | 
						|
  {
 | 
						|
    uchar* p = (uchar*)pc;
 | 
						|
    if (p[2] == 222 &&  p[3] == 35)
 | 
						|
    {
 | 
						|
      return (uchar**)((uchar*)fp - *(short int*)p);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
inline uint32* find_prev_pc(uint32* pc, uchar** fp)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
 | 
						|
  {
 | 
						|
    char* p = (char*)pc;
 | 
						|
    if (p[1] == 0 && p[2] == 94 &&  p[3] == -73)
 | 
						|
    {
 | 
						|
      uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp)));
 | 
						|
      return prev_pc;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif /* defined(__alpha__) && defined(__GNUC__) */
 | 
						|
 | 
						|
void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack,
 | 
						|
                         my_bool silent)
 | 
						|
{
 | 
						|
  uchar** UNINIT_VAR(fp);
 | 
						|
  uint frame_count = 0, sigreturn_frame_count;
 | 
						|
#if defined(__alpha__) && defined(__GNUC__)
 | 
						|
  uint32* pc;
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifdef __i386__
 | 
						|
  __asm __volatile__ ("movl %%ebp,%0"
 | 
						|
		      :"=r"(fp)
 | 
						|
		      :"r"(fp));
 | 
						|
#endif
 | 
						|
#ifdef __x86_64__
 | 
						|
  __asm __volatile__ ("movq %%rbp,%0"
 | 
						|
		      :"=r"(fp)
 | 
						|
		      :"r"(fp));
 | 
						|
#endif
 | 
						|
#if defined(__alpha__) && defined(__GNUC__) 
 | 
						|
  __asm __volatile__ ("mov $30,%0"
 | 
						|
		      :"=r"(fp)
 | 
						|
		      :"r"(fp));
 | 
						|
#endif
 | 
						|
  if (!fp)
 | 
						|
  {
 | 
						|
    my_safe_printf_stderr("%s",
 | 
						|
      "frame pointer is NULL, did you compile with\n"
 | 
						|
      "-fomit-frame-pointer? Aborting backtrace!\n");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!stack_bottom || (uchar*) stack_bottom > (uchar*) &fp)
 | 
						|
  {
 | 
						|
    ulong tmp= MY_MIN(0x10000,thread_stack);
 | 
						|
    /* Assume that the stack starts at the previous even 65K */
 | 
						|
    stack_bottom= (uchar*) (((ulong) &fp + tmp) & ~(ulong) 0xFFFF);
 | 
						|
    my_safe_printf_stderr("Cannot determine thread, fp=%p, "
 | 
						|
                          "backtrace may not be correct.\n", fp);
 | 
						|
  }
 | 
						|
  if (fp > (uchar**) stack_bottom ||
 | 
						|
      fp < (uchar**) stack_bottom - thread_stack)
 | 
						|
  {
 | 
						|
    my_safe_printf_stderr("Bogus stack limit or frame pointer, "
 | 
						|
                          "fp=%p, stack_bottom=%p, thread_stack=%ld, "
 | 
						|
                          "aborting backtrace.\n",
 | 
						|
                          fp, stack_bottom, thread_stack);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  my_safe_printf_stderr("%s",
 | 
						|
    "Stack range sanity check OK, backtrace follows:\n");
 | 
						|
#if defined(__alpha__) && defined(__GNUC__)
 | 
						|
  my_safe_printf_stderr("%s",
 | 
						|
    "Warning: Alpha stacks are difficult -"
 | 
						|
    "will be taking some wild guesses, stack trace may be incorrect or "
 | 
						|
    "terminate abruptly\n");
 | 
						|
 | 
						|
  /* On Alpha, we need to get pc */
 | 
						|
  __asm __volatile__ ("bsr %0, do_next; do_next: "
 | 
						|
		      :"=r"(pc)
 | 
						|
		      :"r"(pc));
 | 
						|
#endif  /* __alpha__ */
 | 
						|
 | 
						|
  /* We are 1 frame above signal frame with NPTL */
 | 
						|
  sigreturn_frame_count = 1;
 | 
						|
 | 
						|
  while (fp < (uchar**) stack_bottom)
 | 
						|
  {
 | 
						|
#if defined(__i386__) || defined(__x86_64__)
 | 
						|
    uchar** new_fp = (uchar**)*fp;
 | 
						|
    my_safe_printf_stderr("%p\n",
 | 
						|
                          frame_count == sigreturn_frame_count ?
 | 
						|
                          *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1));
 | 
						|
#endif /* defined(__386__)  || defined(__x86_64__) */
 | 
						|
 | 
						|
#if defined(__alpha__) && defined(__GNUC__)
 | 
						|
    uchar** new_fp = find_prev_fp(pc, fp);
 | 
						|
    if (frame_count == sigreturn_frame_count - 1)
 | 
						|
    {
 | 
						|
      new_fp += 90;
 | 
						|
    }
 | 
						|
 | 
						|
    if (fp && pc)
 | 
						|
    {
 | 
						|
      pc = find_prev_pc(pc, fp);
 | 
						|
      if (pc)
 | 
						|
	my_safe_printf_stderr("%p\n", pc);
 | 
						|
      else
 | 
						|
      {
 | 
						|
        my_safe_printf_stderr("%s",
 | 
						|
          "Not smart enough to deal with the rest of this stack\n");
 | 
						|
	goto end;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      my_safe_printf_stderr("%s",
 | 
						|
        "Not smart enough to deal with the rest of this stack\n");
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
#endif /* defined(__alpha__) && defined(__GNUC__) */
 | 
						|
    if (new_fp <= fp )
 | 
						|
    {
 | 
						|
      my_safe_printf_stderr("New value of fp=%p failed sanity check, "
 | 
						|
                            "terminating stack trace!\n", new_fp);
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
    fp = new_fp;
 | 
						|
    ++frame_count;
 | 
						|
  }
 | 
						|
  my_safe_printf_stderr("%s",
 | 
						|
                        "Stack trace seems successful - bottom reached\n");
 | 
						|
 | 
						|
end:
 | 
						|
  if (!silent)
 | 
						|
    my_safe_printf_stderr("%s",
 | 
						|
    "Please read "
 | 
						|
    "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
 | 
						|
    "and follow instructions on how to resolve the stack trace.\n"
 | 
						|
    "Resolved stack trace is much more helpful in diagnosing the\n"
 | 
						|
    "problem, so please do resolve it\n");
 | 
						|
}
 | 
						|
#endif /* TARGET_OS_LINUX */
 | 
						|
#endif /* HAVE_STACKTRACE */
 | 
						|
 | 
						|
/* Produce a core for the thread */
 | 
						|
void my_write_core(int sig)
 | 
						|
{
 | 
						|
  signal(sig, SIG_DFL);
 | 
						|
#ifdef HAVE_gcov
 | 
						|
  /*
 | 
						|
    For GCOV build, crashing will prevent the writing of code coverage
 | 
						|
    information from this process, causing gcov output to be incomplete.
 | 
						|
    So we force the writing of coverage information here before terminating.
 | 
						|
  */
 | 
						|
  __gcov_dump();
 | 
						|
#endif
 | 
						|
  pthread_kill(pthread_self(), sig);
 | 
						|
#if defined(P_MYID) && !defined(SCO)
 | 
						|
  /* On Solaris, the above kill is not enough */
 | 
						|
  sigsend(P_PID,P_MYID,sig);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#else /* _WIN32*/
 | 
						|
 | 
						|
#ifdef _MSC_VER
 | 
						|
/* Silence warning in OS header dbghelp.h */
 | 
						|
#pragma warning(push)
 | 
						|
#pragma warning(disable : 4091)
 | 
						|
#endif
 | 
						|
 | 
						|
#include <dbghelp.h>
 | 
						|
 | 
						|
#ifdef _MSC_VER
 | 
						|
#pragma warning(pop)
 | 
						|
#endif
 | 
						|
 | 
						|
#include <tlhelp32.h>
 | 
						|
#include <my_sys.h>
 | 
						|
#if _MSC_VER
 | 
						|
#pragma comment(lib, "dbghelp")
 | 
						|
#endif
 | 
						|
 | 
						|
static EXCEPTION_POINTERS *exception_ptrs;
 | 
						|
 | 
						|
#define MODULE64_SIZE_WINXP 576
 | 
						|
#define STACKWALK_MAX_FRAMES 64
 | 
						|
 | 
						|
void my_set_exception_pointers(EXCEPTION_POINTERS *ep)
 | 
						|
{
 | 
						|
  exception_ptrs = ep;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Appends directory to symbol path.
 | 
						|
*/
 | 
						|
static void add_to_symbol_path(char *path, size_t path_buffer_size, 
 | 
						|
  char *dir, size_t dir_buffer_size)
 | 
						|
{
 | 
						|
  strcat_s(dir, dir_buffer_size, ";");
 | 
						|
  if (!strstr(path, dir))
 | 
						|
  {
 | 
						|
    strcat_s(path, path_buffer_size, dir);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Get symbol path - semicolon-separated list of directories to search
 | 
						|
  for debug symbols. We expect PDB in the same directory as
 | 
						|
  corresponding exe or dll, so the path is build from directories of
 | 
						|
  the loaded modules. If environment variable _NT_SYMBOL_PATH is set,
 | 
						|
  it's value appended to the symbol search path
 | 
						|
*/
 | 
						|
static void get_symbol_path(char *path, size_t size)
 | 
						|
{ 
 | 
						|
  HANDLE hSnap; 
 | 
						|
  char *envvar;
 | 
						|
  char *p;
 | 
						|
#ifndef DBUG_OFF
 | 
						|
  static char pdb_debug_dir[MAX_PATH + 7];
 | 
						|
#endif
 | 
						|
 | 
						|
  path[0]= '\0';
 | 
						|
 | 
						|
#ifndef DBUG_OFF
 | 
						|
  /* 
 | 
						|
    Add "debug" subdirectory of the application directory, sometimes PDB will 
 | 
						|
    placed here by installation.
 | 
						|
  */
 | 
						|
  GetModuleFileName(NULL, pdb_debug_dir, MAX_PATH);
 | 
						|
  p= strrchr(pdb_debug_dir, '\\');
 | 
						|
  if(p)
 | 
						|
  { 
 | 
						|
    *p= 0;
 | 
						|
    strcat_s(pdb_debug_dir, sizeof(pdb_debug_dir), "\\debug;");
 | 
						|
    add_to_symbol_path(path, size, pdb_debug_dir, sizeof(pdb_debug_dir));
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  /*
 | 
						|
    Enumerate all modules, and add their directories to the path.
 | 
						|
    Avoid duplicate entries.
 | 
						|
  */
 | 
						|
  hSnap= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
 | 
						|
  if (hSnap != INVALID_HANDLE_VALUE)
 | 
						|
  {
 | 
						|
    BOOL ret;
 | 
						|
    MODULEENTRY32 mod;
 | 
						|
    mod.dwSize= sizeof(MODULEENTRY32);
 | 
						|
    for (ret= Module32First(hSnap, &mod); ret; ret= Module32Next(hSnap, &mod))
 | 
						|
    {
 | 
						|
      char *module_dir= mod.szExePath;
 | 
						|
      p= strrchr(module_dir,'\\');
 | 
						|
      if (!p)
 | 
						|
      {
 | 
						|
        /*
 | 
						|
          Path separator was not found. Not known to happen, if ever happens,
 | 
						|
          will indicate current directory.
 | 
						|
        */
 | 
						|
        module_dir[0]= '.';
 | 
						|
        module_dir[1]= '\0';
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        *p= '\0';
 | 
						|
      }
 | 
						|
      add_to_symbol_path(path, size, module_dir,sizeof(mod.szExePath));
 | 
						|
    }
 | 
						|
    CloseHandle(hSnap);
 | 
						|
  }
 | 
						|
 | 
						|
  
 | 
						|
  /* Add _NT_SYMBOL_PATH, if present. */
 | 
						|
  envvar= getenv("_NT_SYMBOL_PATH");
 | 
						|
  if(envvar)
 | 
						|
  {
 | 
						|
    strcat_s(path, size, envvar);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#define MAX_SYMBOL_PATH 32768
 | 
						|
 | 
						|
/* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
 | 
						|
#ifndef SYMOPT_NO_PROMPTS
 | 
						|
#define SYMOPT_NO_PROMPTS 0
 | 
						|
#endif
 | 
						|
 | 
						|
void my_print_stacktrace(uchar* unused1, ulong unused2, my_bool silent)
 | 
						|
{
 | 
						|
  HANDLE  hProcess= GetCurrentProcess();
 | 
						|
  HANDLE  hThread= GetCurrentThread();
 | 
						|
  static  IMAGEHLP_MODULE64 module= {sizeof(module)};
 | 
						|
  static  IMAGEHLP_SYMBOL64_PACKAGE package;
 | 
						|
  DWORD64 addr;
 | 
						|
  DWORD   machine;
 | 
						|
  int     i;
 | 
						|
  CONTEXT context;
 | 
						|
  STACKFRAME64 frame={0};
 | 
						|
  static char symbol_path[MAX_SYMBOL_PATH];
 | 
						|
 | 
						|
  if(!exception_ptrs)
 | 
						|
    return;
 | 
						|
 | 
						|
  /* Copy context, as stackwalking on original will unwind the stack */
 | 
						|
  context = *(exception_ptrs->ContextRecord);
 | 
						|
  /*Initialize symbols.*/
 | 
						|
  SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
 | 
						|
  get_symbol_path(symbol_path, sizeof(symbol_path));
 | 
						|
  SymInitialize(hProcess, symbol_path, TRUE);
 | 
						|
 | 
						|
  /*Prepare stackframe for the first StackWalk64 call*/
 | 
						|
  frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat;
 | 
						|
#if (defined _M_IX86)
 | 
						|
  machine= IMAGE_FILE_MACHINE_I386;
 | 
						|
  frame.AddrFrame.Offset= context.Ebp;
 | 
						|
  frame.AddrPC.Offset=    context.Eip;
 | 
						|
  frame.AddrStack.Offset= context.Esp;
 | 
						|
#elif (defined _M_X64)
 | 
						|
  machine = IMAGE_FILE_MACHINE_AMD64;
 | 
						|
  frame.AddrFrame.Offset= context.Rbp;
 | 
						|
  frame.AddrPC.Offset=    context.Rip;
 | 
						|
  frame.AddrStack.Offset= context.Rsp;
 | 
						|
#elif defined(_M_ARM64)
 | 
						|
  machine= IMAGE_FILE_MACHINE_ARM64;
 | 
						|
  frame.AddrFrame.Offset= context.Fp;
 | 
						|
  frame.AddrPC.Offset=    context.Pc;
 | 
						|
  frame.AddrStack.Offset= context.Sp;
 | 
						|
#else
 | 
						|
#pragma error ("unsupported architecture")
 | 
						|
#endif
 | 
						|
 | 
						|
  package.sym.SizeOfStruct= sizeof(package.sym);
 | 
						|
  package.sym.MaxNameLength= sizeof(package.name);
 | 
						|
 | 
						|
  /*Walk the stack, output useful information*/ 
 | 
						|
  for(i= 0; i< STACKWALK_MAX_FRAMES;i++)
 | 
						|
  {
 | 
						|
    DWORD64 function_offset= 0;
 | 
						|
    DWORD line_offset= 0;
 | 
						|
    IMAGEHLP_LINE64 line= {sizeof(line)};
 | 
						|
    BOOL have_module= FALSE;
 | 
						|
    BOOL have_symbol= FALSE;
 | 
						|
    BOOL have_source= FALSE;
 | 
						|
 | 
						|
    if(!StackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0))
 | 
						|
      break;
 | 
						|
    addr= frame.AddrPC.Offset;
 | 
						|
 | 
						|
    have_module= SymGetModuleInfo64(hProcess,addr,&module);
 | 
						|
#ifdef _M_IX86
 | 
						|
    if(!have_module)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        ModuleInfo structure has been "compatibly" extended in
 | 
						|
        releases after XP, and its size was increased. To make XP
 | 
						|
        dbghelp.dll function happy, pretend passing the old structure.
 | 
						|
      */
 | 
						|
      module.SizeOfStruct= MODULE64_SIZE_WINXP;
 | 
						|
      have_module= SymGetModuleInfo64(hProcess, addr, &module);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    have_symbol= SymGetSymFromAddr64(hProcess, addr, &function_offset,
 | 
						|
      &(package.sym));
 | 
						|
    have_source= SymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
 | 
						|
 | 
						|
    if(have_module)
 | 
						|
    {
 | 
						|
      const char *base_image_name= my_basename(module.ImageName);
 | 
						|
      my_safe_printf_stderr("%s!", base_image_name);
 | 
						|
    }
 | 
						|
    if(have_symbol)
 | 
						|
      my_safe_printf_stderr("%s()", package.sym.Name);
 | 
						|
 | 
						|
    else if(have_module)
 | 
						|
      my_safe_printf_stderr("%s", "???");
 | 
						|
 | 
						|
    if(have_source)
 | 
						|
    {
 | 
						|
      const char *base_file_name= my_basename(line.FileName);
 | 
						|
      my_safe_printf_stderr("[%s:%lu]",
 | 
						|
                            base_file_name, line.LineNumber);
 | 
						|
    }
 | 
						|
    my_safe_printf_stderr("%s", "\n");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Write dump. The dump is created in current directory,
 | 
						|
  file name is constructed from executable name plus
 | 
						|
  ".dmp" extension
 | 
						|
*/
 | 
						|
void my_write_core(int unused)
 | 
						|
{
 | 
						|
  char path[MAX_PATH];
 | 
						|
  char dump_fname[MAX_PATH]= "core.dmp";
 | 
						|
  MINIDUMP_EXCEPTION_INFORMATION info;
 | 
						|
  HANDLE hFile;
 | 
						|
 | 
						|
  if(!exception_ptrs)
 | 
						|
    return;
 | 
						|
 | 
						|
  info.ExceptionPointers= exception_ptrs;
 | 
						|
  info.ClientPointers= FALSE;
 | 
						|
  info.ThreadId= GetCurrentThreadId();
 | 
						|
 | 
						|
  if(GetModuleFileName(NULL, path, sizeof(path)))
 | 
						|
  {
 | 
						|
    _splitpath(path, NULL, NULL,dump_fname,NULL);
 | 
						|
    strcat_s(dump_fname, sizeof(dump_fname), ".dmp");
 | 
						|
  }
 | 
						|
 | 
						|
  hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
 | 
						|
    FILE_ATTRIBUTE_NORMAL, 0);
 | 
						|
  if(hFile)
 | 
						|
  {
 | 
						|
    /* Create minidump */
 | 
						|
    if(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
 | 
						|
      hFile, MiniDumpNormal, &info, 0, 0))
 | 
						|
    {
 | 
						|
      my_safe_printf_stderr("Minidump written to %s\n",
 | 
						|
                            _fullpath(path, dump_fname, sizeof(path)) ?
 | 
						|
                            path : dump_fname);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %u\n",
 | 
						|
                            (uint) GetLastError());
 | 
						|
    }
 | 
						|
    CloseHandle(hFile);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    my_safe_printf_stderr("CreateFile(%s) failed, last error %u\n",
 | 
						|
                          dump_fname, (uint) GetLastError());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int my_safe_print_str(const char *val, size_t len)
 | 
						|
{
 | 
						|
  __try
 | 
						|
  {
 | 
						|
    my_write_stderr(val, len);
 | 
						|
  }
 | 
						|
  __except(EXCEPTION_EXECUTE_HANDLER)
 | 
						|
  {
 | 
						|
    my_safe_printf_stderr("%s", "is an invalid string pointer");
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif /*_WIN32*/
 | 
						|
 | 
						|
 | 
						|
size_t my_write_stderr(const void *buf, size_t count)
 | 
						|
{
 | 
						|
  return (size_t) write(fileno(stderr), buf, (uint)count);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
size_t my_safe_printf_stderr(const char* fmt, ...)
 | 
						|
{
 | 
						|
  char to[512];
 | 
						|
  size_t result;
 | 
						|
  va_list args;
 | 
						|
  va_start(args,fmt);
 | 
						|
  result= my_vsnprintf(to, sizeof(to), fmt, args);
 | 
						|
  va_end(args);
 | 
						|
  my_write_stderr(to, result);
 | 
						|
  return result;
 | 
						|
}
 |