mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			353 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (C) 2011 Monty Program Ab
 | 
						|
 | 
						|
   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 <m_string.h>
 | 
						|
#include <my_sys.h>
 | 
						|
#include <my_stacktrace.h>
 | 
						|
 | 
						|
/**
 | 
						|
  strip the path, leave the file name and the last dirname
 | 
						|
*/
 | 
						|
static const char *strip_path(const char *s) __attribute__((unused));
 | 
						|
static const char *strip_path(const char *s)
 | 
						|
{
 | 
						|
  const char *prev, *last;
 | 
						|
  for(prev= last= s; *s; s++)
 | 
						|
    if (*s == '/' || *s == '\\')
 | 
						|
    {
 | 
						|
      prev= last;
 | 
						|
      last= s + 1;
 | 
						|
    }
 | 
						|
  return prev;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  The following is very much single-threaded code and it's only supposed
 | 
						|
  to be used on shutdown or for a crash report
 | 
						|
  Or the caller should take care and use mutexes.
 | 
						|
 | 
						|
  Also it does not free any its memory. For the same reason -
 | 
						|
  it's only used for crash reports or on shutdown when we already
 | 
						|
  have a memory leak.
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef HAVE_BFD_H
 | 
						|
#include <bfd.h>
 | 
						|
static bfd *bfdh= 0;
 | 
						|
static asymbol **symtable= 0;
 | 
						|
 | 
						|
#if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN)
 | 
						|
#include <link.h>
 | 
						|
static ElfW(Addr) offset= 0;
 | 
						|
#else
 | 
						|
#define offset 0
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef bfd_get_section_flags
 | 
						|
#define bfd_get_section_flags(H, S) bfd_section_flags(S)
 | 
						|
#endif /* bfd_get_section_flags */
 | 
						|
 | 
						|
#ifndef bfd_get_section_size
 | 
						|
#define bfd_get_section_size(S) bfd_section_size(S)
 | 
						|
#endif /* bfd_get_section_size */
 | 
						|
 | 
						|
#ifndef bfd_get_section_vma
 | 
						|
#define bfd_get_section_vma(H, S) bfd_section_vma(S)
 | 
						|
#endif /* bfd_get_section_vma */
 | 
						|
 | 
						|
/**
 | 
						|
  finds a file name, a line number, and a function name corresponding to addr.
 | 
						|
 | 
						|
  the function name is demangled.
 | 
						|
  the file name is stripped of its path, only the two last components are kept
 | 
						|
  the resolving logic is mostly based on addr2line of binutils-2.17
 | 
						|
 | 
						|
  @return 0 on success, 1 on failure
 | 
						|
*/
 | 
						|
int my_addr_resolve(void *ptr, my_addr_loc *loc)
 | 
						|
{
 | 
						|
  bfd_vma addr= (intptr)ptr - offset;
 | 
						|
  asection *sec;
 | 
						|
 | 
						|
  for (sec= bfdh->sections; sec; sec= sec->next)
 | 
						|
  {
 | 
						|
    bfd_vma start;
 | 
						|
 | 
						|
    if ((bfd_get_section_flags(bfdh, sec) & SEC_ALLOC) == 0)
 | 
						|
      continue;
 | 
						|
 | 
						|
    start = bfd_get_section_vma(bfdh, sec);
 | 
						|
    if (addr < start || addr >= start + bfd_get_section_size(sec))
 | 
						|
      continue;
 | 
						|
 | 
						|
    if (bfd_find_nearest_line(bfdh, sec, symtable, addr - start,
 | 
						|
                              &loc->file, &loc->func, &loc->line))
 | 
						|
    {
 | 
						|
      if (loc->file)
 | 
						|
        loc->file= strip_path(loc->file);
 | 
						|
      else
 | 
						|
        loc->file= "";
 | 
						|
 | 
						|
      if (loc->func)
 | 
						|
      {
 | 
						|
        const char *str= bfd_demangle(bfdh, loc->func, 3);
 | 
						|
        if (str)
 | 
						|
          loc->func= str;
 | 
						|
      }
 | 
						|
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
const char *my_addr_resolve_init()
 | 
						|
{
 | 
						|
  if (!bfdh)
 | 
						|
  {
 | 
						|
    uint unused;
 | 
						|
    char **matching;
 | 
						|
 | 
						|
#if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN)
 | 
						|
    struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW);
 | 
						|
    if (lm)
 | 
						|
      offset= lm->l_addr;
 | 
						|
#endif
 | 
						|
 | 
						|
    bfdh= bfd_openr(my_progname, NULL);
 | 
						|
    if (!bfdh)
 | 
						|
      goto err;
 | 
						|
 | 
						|
    if (bfd_check_format(bfdh, bfd_archive))
 | 
						|
      goto err;
 | 
						|
    if (!bfd_check_format_matches (bfdh, bfd_object, &matching))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    if (bfd_read_minisymbols(bfdh, FALSE, (void *)&symtable, &unused) < 0)
 | 
						|
      goto err;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
 | 
						|
err:
 | 
						|
  return bfd_errmsg(bfd_get_error());
 | 
						|
}
 | 
						|
#elif defined(HAVE_LIBELF_H)
 | 
						|
/*
 | 
						|
  another possible implementation.
 | 
						|
*/
 | 
						|
#elif defined(MY_ADDR_RESOLVE_FORK)
 | 
						|
/*
 | 
						|
  yet another - just execute addr2line pipe the addresses to it, and parse the
 | 
						|
  output
 | 
						|
*/
 | 
						|
 | 
						|
#include <m_string.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
 | 
						|
#if defined(HAVE_POLL_H)
 | 
						|
#include <poll.h>
 | 
						|
#elif defined(HAVE_SYS_POLL_H)
 | 
						|
#include <sys/poll.h>
 | 
						|
#endif /* defined(HAVE_POLL_H) */
 | 
						|
 | 
						|
static int in[2], out[2];
 | 
						|
static pid_t pid;
 | 
						|
static char addr2line_binary[1024];
 | 
						|
static char output[1024];
 | 
						|
static struct pollfd poll_fds;
 | 
						|
static void *addr_offset;
 | 
						|
 | 
						|
int start_addr2line_fork(const char *binary_path)
 | 
						|
{
 | 
						|
 | 
						|
  if (pid > 0)
 | 
						|
  {
 | 
						|
    /* Don't leak FDs */
 | 
						|
    close(in[1]);
 | 
						|
    close(out[0]);
 | 
						|
    /* Don't create zombie processes. */
 | 
						|
    waitpid(pid, NULL, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (pipe(in) < 0)
 | 
						|
    return 1;
 | 
						|
  if (pipe(out) < 0)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  pid = fork();
 | 
						|
  if (pid == -1)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  if (!pid) /* child */
 | 
						|
  {
 | 
						|
    dup2(in[0], 0);
 | 
						|
    dup2(out[1], 1);
 | 
						|
    close(in[0]);
 | 
						|
    close(in[1]);
 | 
						|
    close(out[0]);
 | 
						|
    close(out[1]);
 | 
						|
    execlp("addr2line", "addr2line", "-C", "-f", "-e", binary_path, NULL);
 | 
						|
    _exit(1);
 | 
						|
  }
 | 
						|
 | 
						|
  close(in[0]);
 | 
						|
  close(out[1]);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int first_error= 0;
 | 
						|
 | 
						|
static int addr_resolve(void *ptr, my_addr_loc *loc)
 | 
						|
{
 | 
						|
  char input[32];
 | 
						|
  size_t len;
 | 
						|
 | 
						|
  ssize_t total_bytes_read = 0;
 | 
						|
  ssize_t extra_bytes_read = 0;
 | 
						|
  ssize_t parsed = 0;
 | 
						|
 | 
						|
  int ret;
 | 
						|
 | 
						|
  int filename_start = -1;
 | 
						|
  int line_number_start = -1;
 | 
						|
 | 
						|
  poll_fds.fd = out[0];
 | 
						|
  poll_fds.events = POLLIN | POLLRDBAND;
 | 
						|
 | 
						|
  len= my_snprintf(input, sizeof(input), "%p\n", ptr);
 | 
						|
  if (write(in[1], input, len) <= 0)
 | 
						|
  {
 | 
						|
    if (!first_error++)
 | 
						|
      fputs("Printing to addr2line failed\n", stderr);
 | 
						|
    return 3;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* 5000 ms should be plenty of time for addr2line to issue a response. */
 | 
						|
  /* Read in a loop till all the output from addr2line is complete. */
 | 
						|
  while (parsed == total_bytes_read &&
 | 
						|
         (ret= poll(&poll_fds, 1, 5000)))
 | 
						|
  {
 | 
						|
    /* error during poll */
 | 
						|
    if (ret < 0)
 | 
						|
      return 1;
 | 
						|
 | 
						|
    extra_bytes_read= read(out[0], output + total_bytes_read,
 | 
						|
                           sizeof(output) - total_bytes_read);
 | 
						|
    if (extra_bytes_read < 0)
 | 
						|
      return 4;
 | 
						|
    /* Timeout or max bytes read. */
 | 
						|
    if (extra_bytes_read == 0)
 | 
						|
      break;
 | 
						|
 | 
						|
    total_bytes_read += extra_bytes_read;
 | 
						|
 | 
						|
    /* Go through the addr2line response and get the required data.
 | 
						|
       The response is structured in 2 lines. The first line contains the function
 | 
						|
       name, while the second one contains <filename>:<line number> */
 | 
						|
    for (; parsed < total_bytes_read; parsed++)
 | 
						|
    {
 | 
						|
      if (output[parsed] == '\n')
 | 
						|
      {
 | 
						|
        filename_start = parsed + 1;
 | 
						|
        output[parsed] = '\0';
 | 
						|
      }
 | 
						|
      if (filename_start != -1 && output[parsed] == ':')
 | 
						|
      {
 | 
						|
        line_number_start = parsed + 1;
 | 
						|
        output[parsed] = '\0';
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Response is malformed. */
 | 
						|
  if (filename_start == -1 || line_number_start == -1)
 | 
						|
   return 5;
 | 
						|
 | 
						|
  loc->func= output;
 | 
						|
  loc->file= output + filename_start;
 | 
						|
  loc->line= atoi(output + line_number_start);
 | 
						|
 | 
						|
  /* Addr2line was unable to extract any meaningful information. */
 | 
						|
  if ((strcmp(loc->file, "??") == 0 || strcmp(loc->file, "") == 0) &&
 | 
						|
      (loc->func[0] == '?' || loc->line == 0))
 | 
						|
    return 6;
 | 
						|
 | 
						|
  loc->file= strip_path(loc->file);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int my_addr_resolve(void *ptr, my_addr_loc *loc)
 | 
						|
{
 | 
						|
  Dl_info info;
 | 
						|
 | 
						|
  if (!dladdr(ptr, &info))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  if (strcmp(addr2line_binary, info.dli_fname))
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      We use dli_fname in case the path is longer than the length of
 | 
						|
      our static string. We don't want to allocate anything
 | 
						|
      dynamically here as we are in a "crashed" state.
 | 
						|
    */
 | 
						|
    if (start_addr2line_fork(info.dli_fname))
 | 
						|
    {
 | 
						|
      if (!first_error++)
 | 
						|
        fputs("Can't start addr2line\n", stderr);
 | 
						|
      addr2line_binary[0] = '\0';
 | 
						|
      return 2;
 | 
						|
    }
 | 
						|
    /* Save result for future comparisons. */
 | 
						|
    strnmov(addr2line_binary, info.dli_fname, sizeof(addr2line_binary));
 | 
						|
 | 
						|
#ifdef _AIX
 | 
						|
    /*
 | 
						|
      info.dli_fbase is a char on AIX and casting it doesn't fool gcc.
 | 
						|
      leave backtracing broken on AIX until a real solution can be found.
 | 
						|
    */
 | 
						|
    addr_offset= NULL;
 | 
						|
#else
 | 
						|
    /*
 | 
						|
      Check if we should use info.dli_fbase as an offset or not
 | 
						|
      for the base program. This is depending on if the compilation is
 | 
						|
      done with PIE or not.
 | 
						|
    */
 | 
						|
    addr_offset= info.dli_fbase;
 | 
						|
#endif
 | 
						|
#ifndef __PIE__
 | 
						|
    if (strcmp(info.dli_fname, my_progname) == 0 &&
 | 
						|
        addr_resolve((void*) my_addr_resolve, loc) == 0 &&
 | 
						|
	strcmp(loc->func, "my_addr_resolve") == 0)
 | 
						|
      addr_offset= 0;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
  return addr_resolve((void*) (ptr - addr_offset), loc);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const char *my_addr_resolve_init()
 | 
						|
{
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 |