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