1 /* Copyright (c) 2001, 2021, Oracle and/or its affiliates.
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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 #include "my_stacktrace.h"
29 
30 #ifndef _WIN32
31 #include "my_thread.h"
32 #include "m_string.h"
33 #include <signal.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef HAVE_STACKTRACE
38 
39 #ifdef __linux__
40 #include <ctype.h>          /* isprint */
41 #include <sys/syscall.h>    /* SYS_gettid */
42 #endif
43 
44 #if HAVE_EXECINFO_H
45 #include <execinfo.h>
46 #endif
47 
48 #ifdef __linux__
49 /* __bss_start doesn't seem to work on FreeBSD and doesn't exist on OSX/Solaris. */
50 #define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)
51 static char *heap_start;
52 extern char *__bss_start;
53 #else
54 #define PTR_SANE(p) (p)
55 #endif /* __linux */
56 
my_init_stacktrace()57 void my_init_stacktrace()
58 {
59 #ifdef __linux__
60   heap_start = (char*) &__bss_start;
61 #endif /* __linux__ */
62 }
63 
64 #ifdef __linux__
65 
print_buffer(char * buffer,size_t count)66 static void print_buffer(char *buffer, size_t count)
67 {
68   const char s[]= " ";
69   for (; count && *buffer; --count)
70   {
71     my_write_stderr(isprint(*buffer) ? buffer : s, 1);
72     ++buffer;
73   }
74 }
75 
76 /**
77   Access the pages of this process through /proc/self/task/<tid>/mem
78   in order to safely print the contents of a memory address range.
79 
80   @param  addr      The address at the start of the memory region.
81   @param  max_len   The length of the memory region.
82 
83   @return Zero on success.
84 */
safe_print_str(const char * addr,int max_len)85 static int safe_print_str(const char *addr, int max_len)
86 {
87   int fd;
88   pid_t tid;
89   off_t offset;
90   ssize_t nbytes= 0;
91   size_t total, count;
92   char buf[256];
93 
94   tid= (pid_t) syscall(SYS_gettid);
95 
96   sprintf(buf, "/proc/self/task/%d/mem", tid);
97 
98   if ((fd= open(buf, O_RDONLY)) < 0)
99     return -1;
100 
101   /* Ensure that off_t can hold a pointer. */
102   compile_time_assert(sizeof(off_t) >= sizeof(intptr));
103 
104   total= max_len;
105   offset= (intptr) addr;
106 
107   /* Read up to the maximum number of bytes. */
108   while (total)
109   {
110     count= MY_MIN(sizeof(buf), total);
111 
112     if ((nbytes= pread(fd, buf, count, offset)) < 0)
113     {
114       /* Just in case... */
115       if (errno == EINTR)
116         continue;
117       else
118         break;
119     }
120 
121     /* Advance offset into memory. */
122     total-= nbytes;
123     offset+= nbytes;
124     addr+= nbytes;
125 
126     /* Output the printable characters. */
127     print_buffer(buf, nbytes);
128 
129     /* Break if less than requested... */
130     if ((count - nbytes))
131       break;
132   }
133 
134   /* Output a new line if something was printed. */
135   if (total != (size_t) max_len)
136     my_safe_printf_stderr("%s", "\n");
137 
138   if (nbytes == -1)
139     my_safe_printf_stderr("Can't read from address %p\n", addr);
140 
141   close(fd);
142 
143   return 0;
144 }
145 
146 #endif /* __linux __ */
147 
my_safe_puts_stderr(const char * val,size_t max_len)148 void my_safe_puts_stderr(const char* val, size_t max_len)
149 {
150 #ifdef __linux__
151 /* Only needed by the linux version of PTR_SANE */
152   char *heap_end;
153 
154   if (!safe_print_str(val, max_len))
155     return;
156 
157   heap_end= (char*) sbrk(0);
158 #endif
159 
160   if (!PTR_SANE(val))
161   {
162     my_safe_printf_stderr("%s", "is an invalid pointer\n");
163     return;
164   }
165 
166   for (; max_len && PTR_SANE(val) && *val; --max_len)
167     my_write_stderr((val++), 1);
168   my_safe_printf_stderr("%s", "\n");
169 }
170 
171 #if defined(HAVE_PRINTSTACK)
172 
173 /* Use Solaris' symbolic stack trace routine. */
174 #include <ucontext.h>
175 
my_print_stacktrace(uchar * stack_bottom MY_ATTRIBUTE ((unused)),ulong thread_stack MY_ATTRIBUTE ((unused)))176 void my_print_stacktrace(uchar* stack_bottom MY_ATTRIBUTE((unused)),
177                          ulong thread_stack MY_ATTRIBUTE((unused)))
178 {
179   if (printstack(fileno(stderr)) == -1)
180     my_safe_printf_stderr("%s",
181       "Error when traversing the stack, stack appears corrupt.\n");
182   else
183     my_safe_printf_stderr("Please read "
184       "http://dev.mysql.com/doc/refman/%u.%u/en/resolve-stack-dump.html\n"
185       "and follow instructions on how to resolve the stack trace.\n"
186       "Resolved stack trace is much more helpful in diagnosing the\n"
187       "problem, so please do resolve it\n",
188       MYSQL_VERSION_MAJOR, MYSQL_VERSION_MINOR);
189 }
190 
191 #elif HAVE_BACKTRACE
192 
193 #if HAVE_ABI_CXA_DEMANGLE
194 
195 char MY_ATTRIBUTE ((weak)) *
my_demangle(const char * mangled_name MY_ATTRIBUTE ((unused)),int * status MY_ATTRIBUTE ((unused)))196 my_demangle(const char *mangled_name MY_ATTRIBUTE((unused)),
197             int *status MY_ATTRIBUTE((unused)))
198 {
199   return NULL;
200 }
201 
my_demangle_symbols(char ** addrs,int n)202 static void my_demangle_symbols(char **addrs, int n)
203 {
204   int status, i;
205   char *begin, *end, *demangled;
206 
207   for (i= 0; i < n; i++)
208   {
209     demangled= NULL;
210     begin= strchr(addrs[i], '(');
211     end= begin ? strchr(begin, '+') : NULL;
212 
213     if (begin && end)
214     {
215       *begin++= *end++= '\0';
216       demangled= my_demangle(begin, &status);
217       if (!demangled || status)
218       {
219         demangled= NULL;
220         begin[-1]= '(';
221         end[-1]= '+';
222       }
223     }
224 
225     if (demangled)
226       my_safe_printf_stderr("%s(%s+%s\n", addrs[i], demangled, end);
227     else
228       my_safe_printf_stderr("%s\n", addrs[i]);
229   }
230 }
231 
232 #endif /* HAVE_ABI_CXA_DEMANGLE */
233 
my_print_stacktrace(uchar * stack_bottom,ulong thread_stack)234 void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
235 {
236   void *addrs[128];
237   char **strings= NULL;
238   int n = backtrace(addrs, array_elements(addrs));
239   my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n",
240                         stack_bottom, thread_stack);
241 #if HAVE_ABI_CXA_DEMANGLE
242   if ((strings= backtrace_symbols(addrs, n)))
243   {
244     my_demangle_symbols(strings, n);
245     free(strings);
246   }
247 #endif
248   if (!strings)
249   {
250     backtrace_symbols_fd(addrs, n, fileno(stderr));
251   }
252 }
253 
254 #endif /* HAVE_PRINTSTACK || HAVE_BACKTRACE */
255 #endif /* HAVE_STACKTRACE */
256 
257 /* Produce a core for the thread */
my_write_core(int sig)258 void my_write_core(int sig)
259 {
260   signal(sig, SIG_DFL);
261   pthread_kill(my_thread_self(), sig);
262 #if defined(P_MYID)
263   /* On Solaris, the above kill is not enough */
264   sigsend(P_PID,P_MYID,sig);
265 #endif
266 }
267 
268 #else /* _WIN32*/
269 
270 #include <dbghelp.h>
271 #include <tlhelp32.h>
272 #if _MSC_VER
273 #pragma comment(lib, "dbghelp")
274 #endif
275 
276 static EXCEPTION_POINTERS *exception_ptrs;
277 
278 #define MODULE64_SIZE_WINXP 576
279 #define STACKWALK_MAX_FRAMES 64
280 
my_init_stacktrace()281 void my_init_stacktrace()
282 {
283 }
284 
285 
my_set_exception_pointers(EXCEPTION_POINTERS * ep)286 void my_set_exception_pointers(EXCEPTION_POINTERS *ep)
287 {
288   exception_ptrs = ep;
289 }
290 
291 /*
292   Appends directory to symbol path.
293 */
add_to_symbol_path(char * path,size_t path_buffer_size,char * dir,size_t dir_buffer_size)294 static void add_to_symbol_path(char *path, size_t path_buffer_size,
295   char *dir, size_t dir_buffer_size)
296 {
297   strcat_s(dir, dir_buffer_size, ";");
298   if (!strstr(path, dir))
299   {
300     strcat_s(path, path_buffer_size, dir);
301   }
302 }
303 
304 /*
305   Get symbol path - semicolon-separated list of directories to search for debug
306   symbols. We expect PDB in the same directory as corresponding exe or dll,
307   so the path is build from directories of the loaded modules. If environment
308   variable _NT_SYMBOL_PATH is set, it's value appended to the symbol search path
309 */
get_symbol_path(char * path,size_t size)310 static void get_symbol_path(char *path, size_t size)
311 {
312   HANDLE hSnap;
313   char *envvar;
314   char *p;
315 #ifndef NDEBUG
316   static char pdb_debug_dir[MAX_PATH + 7];
317 #endif
318 
319   path[0]= '\0';
320 
321 #ifndef NDEBUG
322   /*
323     Add "debug" subdirectory of the application directory, sometimes PDB will
324     placed here by installation.
325   */
326   GetModuleFileName(NULL, pdb_debug_dir, MAX_PATH);
327   p= strrchr(pdb_debug_dir, '\\');
328   if(p)
329   {
330     *p= 0;
331     strcat_s(pdb_debug_dir, sizeof(pdb_debug_dir), "\\debug;");
332     add_to_symbol_path(path, size, pdb_debug_dir, sizeof(pdb_debug_dir));
333   }
334 #endif
335 
336   /*
337     Enumerate all modules, and add their directories to the path.
338     Avoid duplicate entries.
339   */
340   hSnap= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
341   if (hSnap != INVALID_HANDLE_VALUE)
342   {
343     BOOL ret;
344     MODULEENTRY32 mod;
345     mod.dwSize= sizeof(MODULEENTRY32);
346     for (ret= Module32First(hSnap, &mod); ret; ret= Module32Next(hSnap, &mod))
347     {
348       char *module_dir= mod.szExePath;
349       p= strrchr(module_dir,'\\');
350       if (!p)
351       {
352         /*
353           Path separator was not found. Not known to happen, if ever happens,
354           will indicate current directory.
355         */
356         module_dir[0]= '.';
357         module_dir[1]= '\0';
358       }
359       else
360       {
361         *p= '\0';
362       }
363       add_to_symbol_path(path, size, module_dir,sizeof(mod.szExePath));
364     }
365     CloseHandle(hSnap);
366   }
367 
368 
369   /* Add _NT_SYMBOL_PATH, if present. */
370   envvar= getenv("_NT_SYMBOL_PATH");
371   if(envvar)
372   {
373     strcat_s(path, size, envvar);
374   }
375 }
376 
377 #define MAX_SYMBOL_PATH 32768
378 
379 /* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
380 #ifndef SYMOPT_NO_PROMPTS
381 #define SYMOPT_NO_PROMPTS 0
382 #endif
383 
my_print_stacktrace(uchar * unused1,ulong unused2)384 void my_print_stacktrace(uchar* unused1, ulong unused2)
385 {
386   HANDLE  hProcess= GetCurrentProcess();
387   HANDLE  hThread= GetCurrentThread();
388   static  IMAGEHLP_MODULE64 module= {sizeof(module)};
389   static  IMAGEHLP_SYMBOL64_PACKAGE package;
390   DWORD64 addr;
391   DWORD   machine;
392   int     i;
393   CONTEXT context;
394   STACKFRAME64 frame={0};
395   static char symbol_path[MAX_SYMBOL_PATH];
396 
397   if(!exception_ptrs)
398     return;
399 
400   /* Copy context, as stackwalking on original will unwind the stack */
401   context = *(exception_ptrs->ContextRecord);
402   /*Initialize symbols.*/
403   SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
404   get_symbol_path(symbol_path, sizeof(symbol_path));
405   SymInitialize(hProcess, symbol_path, TRUE);
406 
407   /*Prepare stackframe for the first StackWalk64 call*/
408   frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat;
409 #if (defined _M_IX86)
410   machine= IMAGE_FILE_MACHINE_I386;
411   frame.AddrFrame.Offset= context.Ebp;
412   frame.AddrPC.Offset=    context.Eip;
413   frame.AddrStack.Offset= context.Esp;
414 #elif (defined _M_X64)
415   machine = IMAGE_FILE_MACHINE_AMD64;
416   frame.AddrFrame.Offset= context.Rbp;
417   frame.AddrPC.Offset=    context.Rip;
418   frame.AddrStack.Offset= context.Rsp;
419 #else
420   /*There is currently no need to support IA64*/
421 #pragma error ("unsupported architecture")
422 #endif
423 
424   package.sym.SizeOfStruct= sizeof(package.sym);
425   package.sym.MaxNameLength= sizeof(package.name);
426 
427   /*Walk the stack, output useful information*/
428   for(i= 0; i< STACKWALK_MAX_FRAMES;i++)
429   {
430     DWORD64 function_offset= 0;
431     DWORD line_offset= 0;
432     IMAGEHLP_LINE64 line= {sizeof(line)};
433     BOOL have_module= FALSE;
434     BOOL have_symbol= FALSE;
435     BOOL have_source= FALSE;
436 
437     if(!StackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0))
438       break;
439     addr= frame.AddrPC.Offset;
440 
441     have_module= SymGetModuleInfo64(hProcess,addr,&module);
442 #ifdef _M_IX86
443     if(!have_module)
444     {
445       /*
446         ModuleInfo structure has been "compatibly" extended in releases after XP,
447         and its size was increased. To make XP dbghelp.dll function
448         happy, pretend passing the old structure.
449       */
450       module.SizeOfStruct= MODULE64_SIZE_WINXP;
451       have_module= SymGetModuleInfo64(hProcess, addr, &module);
452     }
453 #endif
454 
455     have_symbol= SymGetSymFromAddr64(hProcess, addr, &function_offset,
456       &(package.sym));
457     have_source= SymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
458 
459     my_safe_printf_stderr("%p    ", addr);
460     if(have_module)
461     {
462       char *base_image_name= strrchr(module.ImageName, '\\');
463       if(base_image_name)
464         base_image_name++;
465       else
466         base_image_name= module.ImageName;
467       my_safe_printf_stderr("%s!", base_image_name);
468     }
469     if(have_symbol)
470       my_safe_printf_stderr("%s()", package.sym.Name);
471 
472     else if(have_module)
473       my_safe_printf_stderr("%s", "???");
474 
475     if(have_source)
476     {
477       char *base_file_name= strrchr(line.FileName, '\\');
478       if(base_file_name)
479         base_file_name++;
480       else
481         base_file_name= line.FileName;
482       my_safe_printf_stderr("[%s:%u]",
483                             base_file_name, line.LineNumber);
484     }
485     my_safe_printf_stderr("%s", "\n");
486   }
487 }
488 
489 
490 /*
491   Write dump. The dump is created in current directory,
492   file name is constructed from executable name plus
493   ".dmp" extension
494 */
my_write_core(int unused)495 void my_write_core(int unused)
496 {
497   char path[MAX_PATH];
498   char dump_fname[MAX_PATH]= "core.dmp";
499 
500   if(!exception_ptrs)
501     return;
502 
503   if(GetModuleFileName(NULL, path, sizeof(path)))
504   {
505     _splitpath(path, NULL, NULL,dump_fname,NULL);
506     strncat(dump_fname, ".dmp", sizeof(dump_fname));
507   }
508   my_create_minidump(dump_fname, 0, 0);
509 }
510 
511 
512 /** Create a minidump.
513   @param name    path of minidump file.
514   @param process HANDLE to process. (0 for own process).
515   @param pid     Process id.
516 */
517 
my_create_minidump(const char * name,HANDLE process,DWORD pid)518 void my_create_minidump(const char *name, HANDLE process, DWORD pid)
519 {
520   char path[MAX_PATH];
521   MINIDUMP_EXCEPTION_INFORMATION info;
522   HANDLE hFile;
523 
524   if (process == 0)
525   {
526     /* Does not need to CloseHandle() for the below. */
527     process= GetCurrentProcess();
528     pid= GetCurrentProcessId();
529     info.ExceptionPointers= exception_ptrs;
530     info.ClientPointers= FALSE;
531     info.ThreadId= GetCurrentThreadId();
532   }
533 
534   hFile= CreateFile(name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
535     FILE_ATTRIBUTE_NORMAL, 0);
536   if(hFile)
537   {
538     MINIDUMP_TYPE mdt= (MINIDUMP_TYPE) (MiniDumpNormal |
539                                         MiniDumpWithThreadInfo |
540                                         MiniDumpWithProcessThreadData);
541     /* Create minidump, use info only if same process. */
542     if(MiniDumpWriteDump(process, pid, hFile, mdt,
543                          process ? NULL : &info, 0, 0))
544     {
545       my_safe_printf_stderr("Minidump written to %s\n",
546                             _fullpath(path, name, sizeof(path)) ?
547                             path : name);
548     }
549     else
550     {
551       my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %d\n",
552                             GetLastError());
553     }
554     CloseHandle(hFile);
555   }
556   else
557   {
558     my_safe_printf_stderr("CreateFile(%s) failed, last error %d\n",
559                           name, GetLastError());
560   }
561 }
562 
563 
my_safe_puts_stderr(const char * val,size_t len)564 void my_safe_puts_stderr(const char *val, size_t len)
565 {
566   __try
567   {
568     my_write_stderr(val, len);
569     my_safe_printf_stderr("%s", "\n");
570   }
571   __except(EXCEPTION_EXECUTE_HANDLER)
572   {
573     my_safe_printf_stderr("%s", "is an invalid string pointer\n");
574   }
575 }
576 #endif /* _WIN32 */
577 
578 
579 #ifdef _WIN32
my_write_stderr(const void * buf,size_t count)580 size_t my_write_stderr(const void *buf, size_t count)
581 {
582   DWORD bytes_written;
583   SetFilePointer(GetStdHandle(STD_ERROR_HANDLE), 0, NULL, FILE_END);
584   WriteFile(GetStdHandle(STD_ERROR_HANDLE), buf, (DWORD)count, &bytes_written, NULL);
585   return bytes_written;
586 }
587 #else
my_write_stderr(const void * buf,size_t count)588 size_t my_write_stderr(const void *buf, size_t count)
589 {
590   return (size_t) write(STDERR_FILENO, buf, count);
591 }
592 #endif
593 
594 
595 static const char digits[]= "0123456789abcdef";
596 
my_safe_utoa(int base,ulonglong val,char * buf)597 char *my_safe_utoa(int base, ulonglong val, char *buf)
598 {
599   *buf--= 0;
600   do {
601     *buf--= digits[val % base];
602   } while ((val /= base) != 0);
603   return buf + 1;
604 }
605 
606 
my_safe_itoa(int base,longlong val,char * buf)607 char *my_safe_itoa(int base, longlong val, char *buf)
608 {
609   char *orig_buf= buf;
610   const my_bool is_neg= (val < 0);
611   *buf--= 0;
612 
613   if (is_neg)
614     val= -val;
615   if (is_neg && base == 16)
616   {
617     int ix;
618     val-= 1;
619     for (ix= 0; ix < 16; ++ix)
620       buf[-ix]= '0';
621   }
622 
623   do {
624     *buf--= digits[val % base];
625   } while ((val /= base) != 0);
626 
627   if (is_neg && base == 10)
628     *buf--= '-';
629 
630   if (is_neg && base == 16)
631   {
632     int ix;
633     buf= orig_buf - 1;
634     for (ix= 0; ix < 16; ++ix, --buf)
635     {
636       switch (*buf)
637       {
638       case '0': *buf= 'f'; break;
639       case '1': *buf= 'e'; break;
640       case '2': *buf= 'd'; break;
641       case '3': *buf= 'c'; break;
642       case '4': *buf= 'b'; break;
643       case '5': *buf= 'a'; break;
644       case '6': *buf= '9'; break;
645       case '7': *buf= '8'; break;
646       case '8': *buf= '7'; break;
647       case '9': *buf= '6'; break;
648       case 'a': *buf= '5'; break;
649       case 'b': *buf= '4'; break;
650       case 'c': *buf= '3'; break;
651       case 'd': *buf= '2'; break;
652       case 'e': *buf= '1'; break;
653       case 'f': *buf= '0'; break;
654       }
655     }
656   }
657   return buf+1;
658 }
659 
660 
check_longlong(const char * fmt,my_bool * have_longlong)661 static const char *check_longlong(const char *fmt, my_bool *have_longlong)
662 {
663   *have_longlong= FALSE;
664   if (*fmt == 'l')
665   {
666     fmt++;
667     if (*fmt != 'l')
668       *have_longlong= (sizeof(long) == sizeof(longlong));
669     else
670     {
671       fmt++;
672       *have_longlong= TRUE;
673     }
674   }
675   return fmt;
676 }
677 
my_safe_vsnprintf(char * to,size_t size,const char * format,va_list ap)678 static size_t my_safe_vsnprintf(char *to, size_t size,
679                                 const char* format, va_list ap)
680 {
681   char *start= to;
682   char *end= start + size - 1;
683   for (; *format; ++format)
684   {
685     my_bool have_longlong = FALSE;
686     if (*format != '%')
687     {
688       if (to == end)                            /* end of buffer */
689         break;
690       *to++= *format;                           /* copy ordinary char */
691       continue;
692     }
693     ++format;                                   /* skip '%' */
694 
695     format= check_longlong(format, &have_longlong);
696 
697     switch (*format)
698     {
699     case 'd':
700     case 'i':
701     case 'u':
702     case 'x':
703     case 'p':
704       {
705         longlong ival= 0;
706         ulonglong uval = 0;
707         if (*format == 'p')
708           have_longlong= (sizeof(void *) == sizeof(longlong));
709         if (have_longlong)
710         {
711           if (*format == 'u')
712             uval= va_arg(ap, ulonglong);
713           else
714             ival= va_arg(ap, longlong);
715         }
716         else
717         {
718           if (*format == 'u')
719             uval= va_arg(ap, unsigned int);
720           else
721             ival= va_arg(ap, int);
722         }
723 
724         {
725           char buff[22];
726           const int base= (*format == 'x' || *format == 'p') ? 16 : 10;
727           char *val_as_str= (*format == 'u') ?
728             my_safe_utoa(base, uval, &buff[sizeof(buff)-1]) :
729             my_safe_itoa(base, ival, &buff[sizeof(buff)-1]);
730 
731           /*
732             Strip off "ffffffff" if we have 'x' format without 'll'
733             Similarly for 'p' format on 32bit systems.
734           */
735           if (base == 16 && !have_longlong && ival < 0)
736             val_as_str+= 8;
737 
738           while (*val_as_str && to < end)
739             *to++= *val_as_str++;
740           continue;
741         }
742       }
743     case 's':
744       {
745         const char *val= va_arg(ap, char*);
746         if (!val)
747           val= "(null)";
748         while (*val && to < end)
749           *to++= *val++;
750         continue;
751       }
752     }
753   }
754   *to= 0;
755   return to - start;
756 }
757 
758 
my_safe_snprintf(char * to,size_t n,const char * fmt,...)759 size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...)
760 {
761   size_t result;
762   va_list args;
763   va_start(args,fmt);
764   result= my_safe_vsnprintf(to, n, fmt, args);
765   va_end(args);
766   return result;
767 }
768 
769 
my_safe_printf_stderr(const char * fmt,...)770 size_t my_safe_printf_stderr(const char* fmt, ...)
771 {
772   char to[512];
773   size_t result;
774   va_list args;
775   va_start(args,fmt);
776   result= my_safe_vsnprintf(to, sizeof(to), fmt, args);
777   va_end(args);
778   my_write_stderr(to, result);
779   return result;
780 }
781 
782 
my_safe_print_system_time()783 void my_safe_print_system_time()
784 {
785   char hrs_buf[3]= "00";
786   char mins_buf[3]= "00";
787   char secs_buf[3]= "00";
788   int base= 10;
789 #ifdef _WIN32
790   SYSTEMTIME utc_time;
791   long hrs, mins, secs;
792   GetSystemTime(&utc_time);
793   hrs=  utc_time.wHour;
794   mins= utc_time.wMinute;
795   secs= utc_time.wSecond;
796 #else
797   /* Using time() instead of my_time() to avoid looping */
798   const time_t curr_time= time(NULL);
799   /* Calculate time of day */
800   const long tmins = curr_time / 60;
801   const long thrs  = tmins / 60;
802   const long hrs   = thrs  % 24;
803   const long mins  = tmins % 60;
804   const long secs  = curr_time % 60;
805 #endif
806 
807   my_safe_itoa(base, hrs, &hrs_buf[2]);
808   my_safe_itoa(base, mins, &mins_buf[2]);
809   my_safe_itoa(base, secs, &secs_buf[2]);
810 
811   my_safe_printf_stderr("---------- %s:%s:%s UTC - ",
812                         hrs_buf, mins_buf, secs_buf);
813 }
814 
815