stacktrace.c 6.49 KB
Newer Older
unknown's avatar
unknown committed
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 */

unknown's avatar
unknown committed
17
#include <my_global.h>
unknown's avatar
unknown committed
18 19
#include "stacktrace.h"
#include <signal.h>
unknown's avatar
unknown committed
20
#include <my_pthread.h>
unknown's avatar
unknown committed
21 22 23

#ifdef HAVE_STACKTRACE
#include <unistd.h>
24
#include <strings.h>
unknown's avatar
unknown committed
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, "= ");
unknown's avatar
unknown committed
42
  for (; max_len && PTR_SANE(val) && *val; --max_len)
unknown's avatar
unknown committed
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];

unknown's avatar
unknown committed
64
#ifdef _CS_GNU_LIBPTHREAD_VERSION
65 66
  confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf));
  is_nptl = !strncasecmp(buf, "NPTL", sizeof(buf));
unknown's avatar
unknown committed
67 68 69
#else
  is_nptl = 0;
#endif
70
}
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
85
  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
99
  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
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__
unknown's avatar
unknown committed
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));
unknown's avatar
unknown committed
135 136
#endif
#if defined(__alpha__) && defined(__GNUC__) 
unknown's avatar
unknown committed
137
  __asm __volatile__ ("mov $30,%0"
unknown's avatar
unknown committed
138 139
		      :"=r"(fp)
		      :"r"(fp));
140
#endif
unknown's avatar
unknown committed
141 142
  if (!fp)
  {
143
    fprintf(stderr, "frame pointer is NULL, did you compile with\n\
unknown's avatar
unknown committed
144 145 146
-fomit-frame-pointer? Aborting backtrace!\n");
    return;
  }
unknown's avatar
unknown committed
147

148
  if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp)
unknown's avatar
unknown committed
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");
unknown's avatar
unknown committed
170
  /* On Alpha, we need to get pc */
unknown's avatar
unknown committed
171 172 173 174
  __asm __volatile__ ("bsr %0, do_next; do_next: "
		      :"=r"(pc)
		      :"r"(pc));
#endif  /* __alpha__ */
unknown's avatar
unknown 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;
  
unknown's avatar
unknown committed
179 180
  while (fp < (uchar**) stack_bottom)
  {
181
#if defined(__i386__) || defined(__x86_64__)
unknown's avatar
unknown committed
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__) */
unknown's avatar
unknown committed
186 187 188

#if defined(__alpha__) && defined(__GNUC__)
    uchar** new_fp = find_prev_fp(pc, fp);
189
    if (frame_count == sigreturn_frame_count - 1)
unknown's avatar
unknown committed
190 191 192
    {
      new_fp += 90;
    }
unknown's avatar
unknown committed
193

unknown's avatar
unknown committed
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");
unknown's avatar
unknown committed
223

unknown's avatar
unknown committed
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\
unknown's avatar
unknown committed
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 */
unknown's avatar
unknown committed
230 231 232 233
#endif /* HAVE_STACKTRACE */

/* Produce a core for the thread */

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