MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
stacktrace.c
1 /* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include <my_global.h>
17 #include <my_stacktrace.h>
18 
19 #ifndef __WIN__
20 #include <signal.h>
21 #include <my_pthread.h>
22 #include <m_string.h>
23 #ifdef HAVE_STACKTRACE
24 #include <unistd.h>
25 #include <strings.h>
26 
27 #ifdef __linux__
28 #include <ctype.h> /* isprint */
29 #include <sys/syscall.h> /* SYS_gettid */
30 #endif
31 
32 #if HAVE_EXECINFO_H
33 #include <execinfo.h>
34 #endif
35 
36 #define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)
37 
38 static char *heap_start;
39 
40 #ifdef HAVE_BSS_START
41 extern char *__bss_start;
42 #endif
43 
44 void my_init_stacktrace()
45 {
46 #ifdef HAVE_BSS_START
47  heap_start = (char*) &__bss_start;
48 #endif
49 }
50 
51 #ifdef __linux__
52 
53 static void print_buffer(char *buffer, size_t count)
54 {
55  const char s[]= " ";
56  for (; count && *buffer; --count)
57  {
58  my_write_stderr(isprint(*buffer) ? buffer : s, 1);
59  ++buffer;
60  }
61 }
62 
72 static int safe_print_str(const char *addr, int max_len)
73 {
74  int fd;
75  pid_t tid;
76  off_t offset;
77  ssize_t nbytes= 0;
78  size_t total, count;
79  char buf[256];
80 
81  tid= (pid_t) syscall(SYS_gettid);
82 
83  sprintf(buf, "/proc/self/task/%d/mem", tid);
84 
85  if ((fd= open(buf, O_RDONLY)) < 0)
86  return -1;
87 
88  /* Ensure that off_t can hold a pointer. */
89  compile_time_assert(sizeof(off_t) >= sizeof(intptr));
90 
91  total= max_len;
92  offset= (intptr) addr;
93 
94  /* Read up to the maximum number of bytes. */
95  while (total)
96  {
97  count= MY_MIN(sizeof(buf), total);
98 
99  if ((nbytes= pread(fd, buf, count, offset)) < 0)
100  {
101  /* Just in case... */
102  if (errno == EINTR)
103  continue;
104  else
105  break;
106  }
107 
108  /* Advance offset into memory. */
109  total-= nbytes;
110  offset+= nbytes;
111  addr+= nbytes;
112 
113  /* Output the printable characters. */
114  print_buffer(buf, nbytes);
115 
116  /* Break if less than requested... */
117  if ((count - nbytes))
118  break;
119  }
120 
121  /* Output a new line if something was printed. */
122  if (total != (size_t) max_len)
123  my_safe_printf_stderr("%s", "\n");
124 
125  if (nbytes == -1)
126  my_safe_printf_stderr("Can't read from address %p\n", addr);
127 
128  close(fd);
129 
130  return 0;
131 }
132 
133 #endif
134 
135 void my_safe_print_str(const char* val, int max_len)
136 {
137  char *heap_end;
138 
139 #ifdef __linux__
140  if (!safe_print_str(val, max_len))
141  return;
142 #endif
143 
144  heap_end= (char*) sbrk(0);
145 
146  if (!PTR_SANE(val))
147  {
148  my_safe_printf_stderr("%s", "is an invalid pointer\n");
149  return;
150  }
151 
152  for (; max_len && PTR_SANE(val) && *val; --max_len)
153  my_write_stderr((val++), 1);
154  my_safe_printf_stderr("%s", "\n");
155 }
156 
157 #if defined(HAVE_PRINTSTACK)
158 
159 /* Use Solaris' symbolic stack trace routine. */
160 #include <ucontext.h>
161 
162 void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)),
163  ulong thread_stack __attribute__((unused)))
164 {
165  if (printstack(fileno(stderr)) == -1)
166  my_safe_printf_stderr("%s",
167  "Error when traversing the stack, stack appears corrupt.\n");
168  else
169  my_safe_printf_stderr("%s",
170  "Please read "
171  "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
172  "and follow instructions on how to resolve the stack trace.\n"
173  "Resolved stack trace is much more helpful in diagnosing the\n"
174  "problem, so please do resolve it\n");
175 }
176 
177 #elif HAVE_BACKTRACE && (HAVE_BACKTRACE_SYMBOLS || HAVE_BACKTRACE_SYMBOLS_FD)
178 
179 #if BACKTRACE_DEMANGLE
180 
181 char __attribute__ ((weak)) *
182 my_demangle(const char *mangled_name __attribute__((unused)),
183  int *status __attribute__((unused)))
184 {
185  return NULL;
186 }
187 
188 static void my_demangle_symbols(char **addrs, int n)
189 {
190  int status, i;
191  char *begin, *end, *demangled;
192 
193  for (i= 0; i < n; i++)
194  {
195  demangled= NULL;
196  begin= strchr(addrs[i], '(');
197  end= begin ? strchr(begin, '+') : NULL;
198 
199  if (begin && end)
200  {
201  *begin++= *end++= '\0';
202  demangled= my_demangle(begin, &status);
203  if (!demangled || status)
204  {
205  demangled= NULL;
206  begin[-1]= '(';
207  end[-1]= '+';
208  }
209  }
210 
211  if (demangled)
212  my_safe_printf_stderr("%s(%s+%s\n", addrs[i], demangled, end);
213  else
214  my_safe_printf_stderr("%s\n", addrs[i]);
215  }
216 }
217 
218 #endif /* BACKTRACE_DEMANGLE */
219 
220 void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
221 {
222  void *addrs[128];
223  char **strings= NULL;
224  int n = backtrace(addrs, array_elements(addrs));
225  my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n",
226  stack_bottom, thread_stack);
227 #if BACKTRACE_DEMANGLE
228  if ((strings= backtrace_symbols(addrs, n)))
229  {
230  my_demangle_symbols(strings, n);
231  free(strings);
232  }
233 #endif
234 #if HAVE_BACKTRACE_SYMBOLS_FD
235  if (!strings)
236  {
237  backtrace_symbols_fd(addrs, n, fileno(stderr));
238  }
239 #endif
240 }
241 
242 #elif defined(TARGET_OS_LINUX)
243 
244 #ifdef __i386__
245 #define SIGRETURN_FRAME_OFFSET 17
246 #endif
247 
248 #ifdef __x86_64__
249 #define SIGRETURN_FRAME_OFFSET 23
250 #endif
251 
252 #if defined(__alpha__) && defined(__GNUC__)
253 /*
254  The only way to backtrace without a symbol table on alpha
255  is to find stq fp,N(sp), and the first byte
256  of the instruction opcode will give us the value of N. From this
257  we can find where the old value of fp is stored
258 */
259 
260 #define MAX_INSTR_IN_FUNC 10000
261 
262 inline uchar** find_prev_fp(uint32* pc, uchar** fp)
263 {
264  int i;
265  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
266  {
267  uchar* p = (uchar*)pc;
268  if (p[2] == 222 && p[3] == 35)
269  {
270  return (uchar**)((uchar*)fp - *(short int*)p);
271  }
272  }
273  return 0;
274 }
275 
276 inline uint32* find_prev_pc(uint32* pc, uchar** fp)
277 {
278  int i;
279  for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
280  {
281  char* p = (char*)pc;
282  if (p[1] == 0 && p[2] == 94 && p[3] == -73)
283  {
284  uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp)));
285  return prev_pc;
286  }
287  }
288  return 0;
289 }
290 #endif /* defined(__alpha__) && defined(__GNUC__) */
291 
292 void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
293 {
294  uchar** fp;
295  uint frame_count = 0, sigreturn_frame_count;
296 #if defined(__alpha__) && defined(__GNUC__)
297  uint32* pc;
298 #endif
299  LINT_INIT(fp);
300 
301 
302 #ifdef __i386__
303  __asm __volatile__ ("movl %%ebp,%0"
304  :"=r"(fp)
305  :"r"(fp));
306 #endif
307 #ifdef __x86_64__
308  __asm __volatile__ ("movq %%rbp,%0"
309  :"=r"(fp)
310  :"r"(fp));
311 #endif
312 #if defined(__alpha__) && defined(__GNUC__)
313  __asm __volatile__ ("mov $30,%0"
314  :"=r"(fp)
315  :"r"(fp));
316 #endif
317  if (!fp)
318  {
319  my_safe_printf_stderr("%s",
320  "frame pointer is NULL, did you compile with\n"
321  "-fomit-frame-pointer? Aborting backtrace!\n");
322  return;
323  }
324 
325  if (!stack_bottom || (uchar*) stack_bottom > (uchar*) &fp)
326  {
327  ulong tmp= MY_MIN(0x10000, thread_stack);
328  /* Assume that the stack starts at the previous even 65K */
329  stack_bottom= (uchar*) (((ulong) &fp + tmp) & ~(ulong) 0xFFFF);
330  my_safe_printf_stderr("Cannot determine thread, fp=%p, "
331  "backtrace may not be correct.\n", fp);
332  }
333  if (fp > (uchar**) stack_bottom ||
334  fp < (uchar**) stack_bottom - thread_stack)
335  {
336  my_safe_printf_stderr("Bogus stack limit or frame pointer, "
337  "fp=%p, stack_bottom=%p, thread_stack=%ld, "
338  "aborting backtrace.\n",
339  fp, stack_bottom, thread_stack);
340  return;
341  }
342 
343  my_safe_printf_stderr("%s",
344  "Stack range sanity check OK, backtrace follows:\n");
345 #if defined(__alpha__) && defined(__GNUC__)
346  my_safe_printf_stderr("%s",
347  "Warning: Alpha stacks are difficult -"
348  "will be taking some wild guesses, stack trace may be incorrect or "
349  "terminate abruptly\n");
350 
351  /* On Alpha, we need to get pc */
352  __asm __volatile__ ("bsr %0, do_next; do_next: "
353  :"=r"(pc)
354  :"r"(pc));
355 #endif /* __alpha__ */
356 
357  /* We are 1 frame above signal frame with NPTL and 2 frames above with LT */
358  sigreturn_frame_count = thd_lib_detected == THD_LIB_LT ? 2 : 1;
359 
360  while (fp < (uchar**) stack_bottom)
361  {
362 #if defined(__i386__) || defined(__x86_64__)
363  uchar** new_fp = (uchar**)*fp;
364  my_safe_printf_stderr("%p\n",
365  frame_count == sigreturn_frame_count ?
366  *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1));
367 #endif /* defined(__386__) || defined(__x86_64__) */
368 
369 #if defined(__alpha__) && defined(__GNUC__)
370  uchar** new_fp = find_prev_fp(pc, fp);
371  if (frame_count == sigreturn_frame_count - 1)
372  {
373  new_fp += 90;
374  }
375 
376  if (fp && pc)
377  {
378  pc = find_prev_pc(pc, fp);
379  if (pc)
380  my_safe_printf_stderr("%p\n", pc);
381  else
382  {
383  my_safe_printf_stderr("%s",
384  "Not smart enough to deal with the rest of this stack\n");
385  goto end;
386  }
387  }
388  else
389  {
390  my_safe_printf_stderr("%s",
391  "Not smart enough to deal with the rest of this stack\n");
392  goto end;
393  }
394 #endif /* defined(__alpha__) && defined(__GNUC__) */
395  if (new_fp <= fp )
396  {
397  my_safe_printf_stderr("New value of fp=%p failed sanity check, "
398  "terminating stack trace!\n", new_fp);
399  goto end;
400  }
401  fp = new_fp;
402  ++frame_count;
403  }
404  my_safe_printf_stderr("%s",
405  "Stack trace seems successful - bottom reached\n");
406 
407 end:
408  my_safe_printf_stderr("%s",
409  "Please read "
410  "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
411  "and follow instructions on how to resolve the stack trace.\n"
412  "Resolved stack trace is much more helpful in diagnosing the\n"
413  "problem, so please do resolve it\n");
414 }
415 #endif /* TARGET_OS_LINUX */
416 #endif /* HAVE_STACKTRACE */
417 
418 /* Produce a core for the thread */
419 void my_write_core(int sig)
420 {
421  signal(sig, SIG_DFL);
422  pthread_kill(pthread_self(), sig);
423 #if defined(P_MYID) && !defined(SCO)
424  /* On Solaris, the above kill is not enough */
425  sigsend(P_PID,P_MYID,sig);
426 #endif
427 }
428 
429 #else /* __WIN__*/
430 
431 #include <dbghelp.h>
432 #include <tlhelp32.h>
433 #if _MSC_VER
434 #pragma comment(lib, "dbghelp")
435 #endif
436 
437 static EXCEPTION_POINTERS *exception_ptrs;
438 
439 #define MODULE64_SIZE_WINXP 576
440 #define STACKWALK_MAX_FRAMES 64
441 
442 void my_init_stacktrace()
443 {
444 }
445 
446 
447 void my_set_exception_pointers(EXCEPTION_POINTERS *ep)
448 {
449  exception_ptrs = ep;
450 }
451 
452 /*
453  Appends directory to symbol path.
454 */
455 static void add_to_symbol_path(char *path, size_t path_buffer_size,
456  char *dir, size_t dir_buffer_size)
457 {
458  strcat_s(dir, dir_buffer_size, ";");
459  if (!strstr(path, dir))
460  {
461  strcat_s(path, path_buffer_size, dir);
462  }
463 }
464 
465 /*
466  Get symbol path - semicolon-separated list of directories to search for debug
467  symbols. We expect PDB in the same directory as corresponding exe or dll,
468  so the path is build from directories of the loaded modules. If environment
469  variable _NT_SYMBOL_PATH is set, it's value appended to the symbol search path
470 */
471 static void get_symbol_path(char *path, size_t size)
472 {
473  HANDLE hSnap;
474  char *envvar;
475  char *p;
476 #ifndef DBUG_OFF
477  static char pdb_debug_dir[MAX_PATH + 7];
478 #endif
479 
480  path[0]= '\0';
481 
482 #ifndef DBUG_OFF
483  /*
484  Add "debug" subdirectory of the application directory, sometimes PDB will
485  placed here by installation.
486  */
487  GetModuleFileName(NULL, pdb_debug_dir, MAX_PATH);
488  p= strrchr(pdb_debug_dir, '\\');
489  if(p)
490  {
491  *p= 0;
492  strcat_s(pdb_debug_dir, sizeof(pdb_debug_dir), "\\debug;");
493  add_to_symbol_path(path, size, pdb_debug_dir, sizeof(pdb_debug_dir));
494  }
495 #endif
496 
497  /*
498  Enumerate all modules, and add their directories to the path.
499  Avoid duplicate entries.
500  */
501  hSnap= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
502  if (hSnap != INVALID_HANDLE_VALUE)
503  {
504  BOOL ret;
505  MODULEENTRY32 mod;
506  mod.dwSize= sizeof(MODULEENTRY32);
507  for (ret= Module32First(hSnap, &mod); ret; ret= Module32Next(hSnap, &mod))
508  {
509  char *module_dir= mod.szExePath;
510  p= strrchr(module_dir,'\\');
511  if (!p)
512  {
513  /*
514  Path separator was not found. Not known to happen, if ever happens,
515  will indicate current directory.
516  */
517  module_dir[0]= '.';
518  module_dir[1]= '\0';
519  }
520  else
521  {
522  *p= '\0';
523  }
524  add_to_symbol_path(path, size, module_dir,sizeof(mod.szExePath));
525  }
526  CloseHandle(hSnap);
527  }
528 
529 
530  /* Add _NT_SYMBOL_PATH, if present. */
531  envvar= getenv("_NT_SYMBOL_PATH");
532  if(envvar)
533  {
534  strcat_s(path, size, envvar);
535  }
536 }
537 
538 #define MAX_SYMBOL_PATH 32768
539 
540 /* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
541 #ifndef SYMOPT_NO_PROMPTS
542 #define SYMOPT_NO_PROMPTS 0
543 #endif
544 
545 void my_print_stacktrace(uchar* unused1, ulong unused2)
546 {
547  HANDLE hProcess= GetCurrentProcess();
548  HANDLE hThread= GetCurrentThread();
549  static IMAGEHLP_MODULE64 module= {sizeof(module)};
550  static IMAGEHLP_SYMBOL64_PACKAGE package;
551  DWORD64 addr;
552  DWORD machine;
553  int i;
554  CONTEXT context;
555  STACKFRAME64 frame={0};
556  static char symbol_path[MAX_SYMBOL_PATH];
557 
558  if(!exception_ptrs)
559  return;
560 
561  /* Copy context, as stackwalking on original will unwind the stack */
562  context = *(exception_ptrs->ContextRecord);
563  /*Initialize symbols.*/
564  SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
565  get_symbol_path(symbol_path, sizeof(symbol_path));
566  SymInitialize(hProcess, symbol_path, TRUE);
567 
568  /*Prepare stackframe for the first StackWalk64 call*/
569  frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat;
570 #if (defined _M_IX86)
571  machine= IMAGE_FILE_MACHINE_I386;
572  frame.AddrFrame.Offset= context.Ebp;
573  frame.AddrPC.Offset= context.Eip;
574  frame.AddrStack.Offset= context.Esp;
575 #elif (defined _M_X64)
576  machine = IMAGE_FILE_MACHINE_AMD64;
577  frame.AddrFrame.Offset= context.Rbp;
578  frame.AddrPC.Offset= context.Rip;
579  frame.AddrStack.Offset= context.Rsp;
580 #else
581  /*There is currently no need to support IA64*/
582 #pragma error ("unsupported architecture")
583 #endif
584 
585  package.sym.SizeOfStruct= sizeof(package.sym);
586  package.sym.MaxNameLength= sizeof(package.name);
587 
588  /*Walk the stack, output useful information*/
589  for(i= 0; i< STACKWALK_MAX_FRAMES;i++)
590  {
591  DWORD64 function_offset= 0;
592  DWORD line_offset= 0;
593  IMAGEHLP_LINE64 line= {sizeof(line)};
594  BOOL have_module= FALSE;
595  BOOL have_symbol= FALSE;
596  BOOL have_source= FALSE;
597 
598  if(!StackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0))
599  break;
600  addr= frame.AddrPC.Offset;
601 
602  have_module= SymGetModuleInfo64(hProcess,addr,&module);
603 #ifdef _M_IX86
604  if(!have_module)
605  {
606  /*
607  ModuleInfo structure has been "compatibly" extended in releases after XP,
608  and its size was increased. To make XP dbghelp.dll function
609  happy, pretend passing the old structure.
610  */
611  module.SizeOfStruct= MODULE64_SIZE_WINXP;
612  have_module= SymGetModuleInfo64(hProcess, addr, &module);
613  }
614 #endif
615 
616  have_symbol= SymGetSymFromAddr64(hProcess, addr, &function_offset,
617  &(package.sym));
618  have_source= SymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
619 
620  my_safe_printf_stderr("%p ", addr);
621  if(have_module)
622  {
623  char *base_image_name= strrchr(module.ImageName, '\\');
624  if(base_image_name)
625  base_image_name++;
626  else
627  base_image_name= module.ImageName;
628  my_safe_printf_stderr("%s!", base_image_name);
629  }
630  if(have_symbol)
631  my_safe_printf_stderr("%s()", package.sym.Name);
632 
633  else if(have_module)
634  my_safe_printf_stderr("%s", "???");
635 
636  if(have_source)
637  {
638  char *base_file_name= strrchr(line.FileName, '\\');
639  if(base_file_name)
640  base_file_name++;
641  else
642  base_file_name= line.FileName;
643  my_safe_printf_stderr("[%s:%u]",
644  base_file_name, line.LineNumber);
645  }
646  my_safe_printf_stderr("%s", "\n");
647  }
648 }
649 
650 
651 /*
652  Write dump. The dump is created in current directory,
653  file name is constructed from executable name plus
654  ".dmp" extension
655 */
656 void my_write_core(int unused)
657 {
658  char path[MAX_PATH];
659  char dump_fname[MAX_PATH]= "core.dmp";
660  MINIDUMP_EXCEPTION_INFORMATION info;
661  HANDLE hFile;
662 
663  if(!exception_ptrs)
664  return;
665 
666  info.ExceptionPointers= exception_ptrs;
667  info.ClientPointers= FALSE;
668  info.ThreadId= GetCurrentThreadId();
669 
670  if(GetModuleFileName(NULL, path, sizeof(path)))
671  {
672  _splitpath(path, NULL, NULL,dump_fname,NULL);
673  strncat(dump_fname, ".dmp", sizeof(dump_fname));
674  }
675 
676  hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
677  FILE_ATTRIBUTE_NORMAL, 0);
678  if(hFile)
679  {
680  /* Create minidump */
681  if(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
682  hFile, MiniDumpNormal, &info, 0, 0))
683  {
684  my_safe_printf_stderr("Minidump written to %s\n",
685  _fullpath(path, dump_fname, sizeof(path)) ?
686  path : dump_fname);
687  }
688  else
689  {
690  my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %u\n",
691  (uint) GetLastError());
692  }
693  CloseHandle(hFile);
694  }
695  else
696  {
697  my_safe_printf_stderr("CreateFile(%s) failed, last error %u\n",
698  dump_fname, (uint) GetLastError());
699  }
700 }
701 
702 
703 void my_safe_print_str(const char *val, int len)
704 {
705  __try
706  {
707  my_write_stderr(val, len);
708  }
709  __except(EXCEPTION_EXECUTE_HANDLER)
710  {
711  my_safe_printf_stderr("%s", "is an invalid string pointer\n");
712  }
713 }
714 #endif /*__WIN__*/
715 
716 
717 #ifdef __WIN__
718 size_t my_write_stderr(const void *buf, size_t count)
719 {
720  DWORD bytes_written;
721  SetFilePointer(GetStdHandle(STD_ERROR_HANDLE), 0, NULL, FILE_END);
722  WriteFile(GetStdHandle(STD_ERROR_HANDLE), buf, count, &bytes_written, NULL);
723  return bytes_written;
724 }
725 #else
726 size_t my_write_stderr(const void *buf, size_t count)
727 {
728  return (size_t) write(STDERR_FILENO, buf, count);
729 }
730 #endif
731 
732 
733 static const char digits[]= "0123456789abcdef";
734 
735 char *my_safe_utoa(int base, ulonglong val, char *buf)
736 {
737  *buf--= 0;
738  do {
739  *buf--= digits[val % base];
740  } while ((val /= base) != 0);
741  return buf + 1;
742 }
743 
744 
745 char *my_safe_itoa(int base, longlong val, char *buf)
746 {
747  char *orig_buf= buf;
748  const my_bool is_neg= (val < 0);
749  *buf--= 0;
750 
751  if (is_neg)
752  val= -val;
753  if (is_neg && base == 16)
754  {
755  int ix;
756  val-= 1;
757  for (ix= 0; ix < 16; ++ix)
758  buf[-ix]= '0';
759  }
760 
761  do {
762  *buf--= digits[val % base];
763  } while ((val /= base) != 0);
764 
765  if (is_neg && base == 10)
766  *buf--= '-';
767 
768  if (is_neg && base == 16)
769  {
770  int ix;
771  buf= orig_buf - 1;
772  for (ix= 0; ix < 16; ++ix, --buf)
773  {
774  switch (*buf)
775  {
776  case '0': *buf= 'f'; break;
777  case '1': *buf= 'e'; break;
778  case '2': *buf= 'd'; break;
779  case '3': *buf= 'c'; break;
780  case '4': *buf= 'b'; break;
781  case '5': *buf= 'a'; break;
782  case '6': *buf= '9'; break;
783  case '7': *buf= '8'; break;
784  case '8': *buf= '7'; break;
785  case '9': *buf= '6'; break;
786  case 'a': *buf= '5'; break;
787  case 'b': *buf= '4'; break;
788  case 'c': *buf= '3'; break;
789  case 'd': *buf= '2'; break;
790  case 'e': *buf= '1'; break;
791  case 'f': *buf= '0'; break;
792  }
793  }
794  }
795  return buf+1;
796 }
797 
798 
799 static const char *check_longlong(const char *fmt, my_bool *have_longlong)
800 {
801  *have_longlong= FALSE;
802  if (*fmt == 'l')
803  {
804  fmt++;
805  if (*fmt != 'l')
806  *have_longlong= (sizeof(long) == sizeof(longlong));
807  else
808  {
809  fmt++;
810  *have_longlong= TRUE;
811  }
812  }
813  return fmt;
814 }
815 
816 static size_t my_safe_vsnprintf(char *to, size_t size,
817  const char* format, va_list ap)
818 {
819  char *start= to;
820  char *end= start + size - 1;
821  for (; *format; ++format)
822  {
823  my_bool have_longlong = FALSE;
824  if (*format != '%')
825  {
826  if (to == end) /* end of buffer */
827  break;
828  *to++= *format; /* copy ordinary char */
829  continue;
830  }
831  ++format; /* skip '%' */
832 
833  format= check_longlong(format, &have_longlong);
834 
835  switch (*format)
836  {
837  case 'd':
838  case 'i':
839  case 'u':
840  case 'x':
841  case 'p':
842  {
843  longlong ival= 0;
844  ulonglong uval = 0;
845  if (*format == 'p')
846  have_longlong= (sizeof(void *) == sizeof(longlong));
847  if (have_longlong)
848  {
849  if (*format == 'u')
850  uval= va_arg(ap, ulonglong);
851  else
852  ival= va_arg(ap, longlong);
853  }
854  else
855  {
856  if (*format == 'u')
857  uval= va_arg(ap, unsigned int);
858  else
859  ival= va_arg(ap, int);
860  }
861 
862  {
863  char buff[22];
864  const int base= (*format == 'x' || *format == 'p') ? 16 : 10;
865  char *val_as_str= (*format == 'u') ?
866  my_safe_utoa(base, uval, &buff[sizeof(buff)-1]) :
867  my_safe_itoa(base, ival, &buff[sizeof(buff)-1]);
868 
869  /* Strip off "ffffffff" if we have 'x' format without 'll' */
870  if (*format == 'x' && !have_longlong && ival < 0)
871  val_as_str+= 8;
872 
873  while (*val_as_str && to < end)
874  *to++= *val_as_str++;
875  continue;
876  }
877  }
878  case 's':
879  {
880  const char *val= va_arg(ap, char*);
881  if (!val)
882  val= "(null)";
883  while (*val && to < end)
884  *to++= *val++;
885  continue;
886  }
887  }
888  }
889  *to= 0;
890  return to - start;
891 }
892 
893 
894 size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...)
895 {
896  size_t result;
897  va_list args;
898  va_start(args,fmt);
899  result= my_safe_vsnprintf(to, n, fmt, args);
900  va_end(args);
901  return result;
902 }
903 
904 
905 size_t my_safe_printf_stderr(const char* fmt, ...)
906 {
907  char to[512];
908  size_t result;
909  va_list args;
910  va_start(args,fmt);
911  result= my_safe_vsnprintf(to, sizeof(to), fmt, args);
912  va_end(args);
913  my_write_stderr(to, result);
914  return result;
915 }