stacktrace.c 6.49 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* Copyright (C) 2000 MySQL 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; either version 2 of the License, or
   (at your option) any later version.

   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

17
#include <my_global.h>
18 19
#include "stacktrace.h"
#include <signal.h>
20
#include <my_pthread.h>
21 22 23

#ifdef HAVE_STACKTRACE
#include <unistd.h>
24
#include <strings.h>
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)

char *heap_start;

void safe_print_str(const char* name, const char* val, int max_len)
{
  char *heap_end= (char*) sbrk(0);
  fprintf(stderr, "%s at %p ", name, val);

  if (!PTR_SANE(val))
  {
    fprintf(stderr, " is invalid pointer\n");
    return;
  }

  fprintf(stderr, "= ");
42
  for (; max_len && PTR_SANE(val) && *val; --max_len)
43 44 45 46
    fputc(*val++, stderr);
  fputc('\n', stderr);
}

47
#ifdef TARGET_OS_LINUX
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

#ifdef __i386__
#define SIGRETURN_FRAME_OFFSET 17
#endif

#ifdef __x86_64__
#define SIGRETURN_FRAME_OFFSET 23
#endif

static my_bool is_nptl;

/* Check if we are using NPTL or LinuxThreads on Linux */
void check_thread_lib(void)
{
  char buf[5];

64
#ifdef _CS_GNU_LIBPTHREAD_VERSION
65 66
  confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf));
  is_nptl = !strncasecmp(buf, "NPTL", sizeof(buf));
67 68 69
#else
  is_nptl = 0;
#endif
70
}
71 72 73 74 75 76 77 78 79 80 81 82 83 84

#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;
85
  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
86 87 88 89 90 91 92 93 94 95 96 97 98
  {
    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;
99
  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
  {
    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  print_stacktrace(gptr stack_bottom, ulong thread_stack)
{
  uchar** fp;
116
  uint frame_count = 0, sigreturn_frame_count;
117 118 119 120 121 122 123 124 125
#if defined(__alpha__) && defined(__GNUC__)
  uint32* pc;
#endif
  LINT_INIT(fp);

  fprintf(stderr,"\
Attempting backtrace. You can use the following information to find out\n\
where mysqld died. If you see no messages after this, something went\n\
terribly wrong...\n");
126
#ifdef __i386__
127 128 129
  __asm __volatile__ ("movl %%ebp,%0"
		      :"=r"(fp)
		      :"r"(fp));
130 131 132 133 134
#endif
#ifdef __x86_64__
  __asm __volatile__ ("movq %%rbp,%0"
		      :"=r"(fp)
		      :"r"(fp));
135 136
#endif
#if defined(__alpha__) && defined(__GNUC__) 
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
137
  __asm __volatile__ ("mov $30,%0"
138 139
		      :"=r"(fp)
		      :"r"(fp));
140
#endif
141 142
  if (!fp)
  {
143
    fprintf(stderr, "frame pointer is NULL, did you compile with\n\
144 145 146
-fomit-frame-pointer? Aborting backtrace!\n");
    return;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
147

148
  if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp)
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  {
    ulong tmp= min(0x10000,thread_stack);
    /* Assume that the stack starts at the previous even 65K */
    stack_bottom= (gptr) (((ulong) &fp + tmp) &
			  ~(ulong) 0xFFFF);
    fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp);
  }
  if (fp > (uchar**) stack_bottom ||
      fp < (uchar**) stack_bottom - thread_stack)
  {
    fprintf(stderr, "Bogus stack limit or frame pointer,\
 fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n",
	    fp, stack_bottom, thread_stack);
    return;
  }

  fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n");
#if defined(__alpha__) && defined(__GNUC__)
  fprintf(stderr, "Warning: Alpha stacks are difficult -\
 will be taking some wild guesses, stack trace may be incorrect or \
 terminate abruptly\n");
salle@geopard.online.bg's avatar
salle@geopard.online.bg committed
170
  /* On Alpha, we need to get pc */
171 172 173 174
  __asm __volatile__ ("bsr %0, do_next; do_next: "
		      :"=r"(pc)
		      :"r"(pc));
#endif  /* __alpha__ */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
175

176 177 178
  /* We are 1 frame above signal frame with NPTL and 2 frames above with LT */
  sigreturn_frame_count = is_nptl ? 1 : 2;
  
179 180
  while (fp < (uchar**) stack_bottom)
  {
181
#if defined(__i386__) || defined(__x86_64__)
182
    uchar** new_fp = (uchar**)*fp;
183 184 185
    fprintf(stderr, "%p\n", frame_count == sigreturn_frame_count ?
	    *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1));
#endif /* defined(__386__)  || defined(__x86_64__) */
186 187 188 189 190 191 192

#if defined(__alpha__) && defined(__GNUC__)
    uchar** new_fp = find_prev_fp(pc, fp);
    if (frame_count == SIGRETURN_FRAME_COUNT - 1)
    {
      new_fp += 90;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
193

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    if (fp && pc)
    {
      pc = find_prev_pc(pc, fp);
      if (pc)
	fprintf(stderr, "%p\n", pc);
      else
      {
	fprintf(stderr, "Not smart enough to deal with the rest\
 of this stack\n");
	goto end;
      }
    }
    else
    {
      fprintf(stderr, "Not smart enough to deal with the rest of this stack\n");
      goto end;
    }
#endif /* defined(__alpha__) && defined(__GNUC__) */
    if (new_fp <= fp )
    {
      fprintf(stderr, "New value of fp=%p failed sanity check,\
 terminating stack trace!\n", new_fp);
      goto end;
    }
    fp = new_fp;
    ++frame_count;
  }

  fprintf(stderr, "Stack trace seems successful - bottom reached\n");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
223

224
end:
225
  fprintf(stderr, "Please read http://dev.mysql.com/doc/mysql/en/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\
226 227 228
stack trace is much more helpful in diagnosing the problem, so please do \n\
resolve it\n");
}
229
#endif /* TARGET_OS_LINUX */
230 231 232 233
#endif /* HAVE_STACKTRACE */

/* Produce a core for the thread */

234
#ifdef NOT_USED /* HAVE_LINUXTHREADS */
235 236 237
void write_core(int sig)
{
  signal(sig, SIG_DFL);
salle@geopard.online.bg's avatar
salle@geopard.online.bg committed
238 239
  if (fork() != 0) exit(1);			/* Abort main program */
  /* Core will be written at exit */
240
}
241 242 243 244 245
#else
void write_core(int sig)
{
  signal(sig, SIG_DFL);
  pthread_kill(pthread_self(), sig);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
246
#if defined(P_MYID) && !defined(SCO)
247 248
  /* On Solaris, the above kill is not enough */
  sigsend(P_PID,P_MYID,sig);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
249
#endif
250 251
}
#endif