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