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