123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807 |
- /* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 2.0,
- as published by the Free Software Foundation.
- This program is also distributed with certain software (including
- but not limited to OpenSSL) that is licensed under separate terms,
- as designated in a particular file or component or in included license
- documentation. The authors of MySQL hereby grant you an additional
- permission to link the program and your derivative works with the
- separately licensed software that they have included with MySQL.
- Without limiting anything contained in the foregoing, this file,
- which is part of C Driver for MySQL (Connector/C), is also subject to the
- Universal FOSS Exception, version 1.0, a copy of which can be found at
- http://oss.oracle.com/licenses/universal-foss-exception.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License, version 2.0, for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
- /**
- @file mysys/stacktrace.cc
- */
- #include "my_config.h"
- #include <errno.h>
- #include <fcntl.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #ifdef __linux__
- #include <syscall.h>
- #endif
- #include <time.h>
- #include "my_inttypes.h"
- #include "my_macros.h"
- #include "my_stacktrace.h"
- #ifndef _WIN32
- #include <signal.h>
- #include "my_thread.h"
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef HAVE_STACKTRACE
- #ifdef __linux__
- #include <ctype.h> /* isprint */
- #endif
- #ifdef HAVE_EXECINFO_H
- #include <execinfo.h>
- #endif
- #ifdef __FreeBSD__
- #define UNW_LOCAL_ONLY
- #error #include <libunwind.h>
- #endif
- #ifdef __linux__
- /* __bss_start doesn't seem to work on FreeBSD and doesn't exist on OSX/Solaris.
- */
- static const char *heap_start;
- extern char *__bss_start;
- #endif /* __linux */
- static inline bool ptr_sane(const char *p MY_ATTRIBUTE((unused)),
- const char *heap_end MY_ATTRIBUTE((unused))) {
- #ifdef __linux__
- return p && p >= heap_start && p <= heap_end;
- #else
- return true;
- #endif
- }
- void my_init_stacktrace() {
- #ifdef __linux__
- heap_start = (char *)&__bss_start;
- #endif /* __linux__ */
- }
- #ifdef __linux__
- static void print_buffer(char *buffer, size_t count) {
- const char s[] = " ";
- for (; count && *buffer; --count) {
- my_write_stderr(isprint(*buffer) ? buffer : s, 1);
- ++buffer;
- }
- }
- /**
- Access the pages of this process through /proc/self/task/<tid>/mem
- in order to safely print the contents of a memory address range.
- @param addr The address at the start of the memory region.
- @param max_len The length of the memory region.
- @return Zero on success.
- */
- static int safe_print_str(const char *addr, int max_len) {
- int fd;
- pid_t tid;
- off_t offset;
- ssize_t nbytes = 0;
- size_t total, count;
- char buf[256];
- tid = (pid_t)syscall(SYS_gettid);
- sprintf(buf, "/proc/self/task/%d/mem", tid);
- if ((fd = open(buf, O_RDONLY)) < 0) return -1;
- static_assert(sizeof(off_t) >= sizeof(intptr),
- "off_t needs to be able to hold a pointer.");
- total = max_len;
- offset = (intptr)addr;
- /* Read up to the maximum number of bytes. */
- while (total) {
- count = MY_MIN(sizeof(buf), total);
- if ((nbytes = pread(fd, buf, count, offset)) < 0) {
- /* Just in case... */
- if (errno == EINTR)
- continue;
- else
- break;
- }
- /* Advance offset into memory. */
- total -= nbytes;
- offset += nbytes;
- addr += nbytes;
- /* Output the printable characters. */
- print_buffer(buf, nbytes);
- /* Break if less than requested... */
- if ((count - nbytes)) break;
- }
- /* Output a new line if something was printed. */
- if (total != (size_t)max_len) my_safe_printf_stderr("%s", "\n");
- if (nbytes == -1) my_safe_printf_stderr("Can't read from address %p\n", addr);
- close(fd);
- return 0;
- }
- #endif /* __linux __ */
- void my_safe_puts_stderr(const char *val, size_t max_len) {
- const char *heap_end = nullptr;
- #ifdef __linux__
- if (!safe_print_str(val, max_len)) return;
- /* Only needed by the linux version of ptr_sane() */
- heap_end = static_cast<const char *>(sbrk(0));
- #endif
- if (!ptr_sane(val, heap_end)) {
- my_safe_printf_stderr("%s", "is an invalid pointer\n");
- return;
- }
- for (; max_len && ptr_sane(val, heap_end) && *val; --max_len)
- my_write_stderr((val++), 1);
- my_safe_printf_stderr("%s", "\n");
- }
- #if defined(HAVE_BACKTRACE)
- #ifdef HAVE_ABI_CXA_DEMANGLE
- #include <cxxabi.h>
- static char *my_demangle(const char *mangled_name, int *status) {
- return abi::__cxa_demangle(mangled_name, NULL, NULL, status);
- }
- static bool my_demangle_symbol(char *line) {
- char *demangled = NULL;
- #ifdef __APPLE__ // OS X formatting of stacktraces is different from Linux
- char *begin = strstr(line, "_Z");
- char *end = begin ? strchr(begin, ' ') : NULL;
- if (begin && end) {
- begin[-1] = '\0';
- *end = '\0';
- int status;
- demangled = my_demangle(begin, &status);
- if (!demangled || status) {
- demangled = NULL;
- begin[-1] = '_';
- *end = ' ';
- }
- }
- if (demangled) my_safe_printf_stderr("%s %s %s\n", line, demangled, end + 1);
- #elif defined(__SUNPRO_CC) // Solaris has different formatting .....
- char *begin = strchr(line, '\'');
- char *end = begin ? strchr(begin, '+') : NULL;
- if (begin && end) {
- *begin++ = *end++ = '\0';
- int status = 0;
- demangled = my_demangle(begin, &status);
- if (!demangled || status) {
- demangled = NULL;
- begin[-1] = ' ';
- end[-1] = '+';
- }
- }
- if (demangled) my_safe_printf_stderr("%s %s+%s\n", line, demangled, end);
- #else // !__APPLE__ and !__SUNPRO_CC
- char *begin = strchr(line, '(');
- char *end = begin ? strchr(begin, '+') : NULL;
- if (begin && end) {
- *begin++ = *end++ = '\0';
- int status;
- demangled = my_demangle(begin, &status);
- if (!demangled || status) {
- demangled = NULL;
- begin[-1] = '(';
- end[-1] = '+';
- }
- }
- if (demangled) my_safe_printf_stderr("%s(%s+%s\n", line, demangled, end);
- #endif
- bool ret = (demangled == NULL);
- free(demangled);
- return (ret);
- }
- // If it does not start with "_Z" it is a C function, and demangling fails.
- // Print the original line, with modifications done by my_demangle_symbol().
- static void my_demangle_symbols(char **addrs, int n) {
- for (int i = 0; i < n; i++) {
- if (my_demangle_symbol(addrs[i])) // demangling failed
- my_safe_printf_stderr("%s\n", addrs[i]);
- }
- }
- #endif /* HAVE_ABI_CXA_DEMANGLE */
- void my_print_stacktrace(uchar *stack_bottom, ulong thread_stack) {
- #if defined(__FreeBSD__)
- static char procname_buffer[2048];
- unw_cursor_t cursor;
- unw_context_t uc;
- unw_word_t ip;
- unw_getcontext(&uc);
- unw_init_local(&cursor, &uc);
- unw_word_t offp;
- while (unw_step(&cursor) > 0) {
- unw_get_reg(&cursor, UNW_REG_IP, &ip);
- unw_get_proc_name(&cursor, procname_buffer, sizeof(procname_buffer), &offp);
- int status;
- char *demangled = my_demangle(procname_buffer, &status);
- my_safe_printf_stderr("[0x%lx] %s+0x%lx\n", ip,
- demangled ? demangled : procname_buffer, offp);
- if (demangled) free(demangled);
- }
- #endif
- void *addrs[128];
- char **strings = NULL;
- int n = backtrace(addrs, array_elements(addrs));
- my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n", stack_bottom,
- thread_stack);
- #ifdef HAVE_ABI_CXA_DEMANGLE
- if ((strings = backtrace_symbols(addrs, n))) {
- my_demangle_symbols(strings, n);
- free(strings);
- }
- #endif
- if (!strings) {
- backtrace_symbols_fd(addrs, n, fileno(stderr));
- }
- }
- #endif /* HAVE_BACKTRACE */
- #endif /* HAVE_STACKTRACE */
- /* Produce a core for the thread */
- void my_write_core(int sig) {
- signal(sig, SIG_DFL);
- pthread_kill(my_thread_self(), sig);
- #if defined(P_MYID)
- /* On Solaris, the above kill is not enough */
- sigsend(P_PID, P_MYID, sig);
- #endif
- }
- #else /* _WIN32*/
- #include <dbghelp.h>
- #include <tlhelp32.h>
- #if _MSC_VER
- #pragma comment(lib, "dbghelp")
- #endif
- static EXCEPTION_POINTERS *exception_ptrs;
- #define MODULE64_SIZE_WINXP 576
- #define STACKWALK_MAX_FRAMES 64
- void my_init_stacktrace() {}
- void my_set_exception_pointers(EXCEPTION_POINTERS *ep) { exception_ptrs = ep; }
- /*
- Appends directory to symbol path.
- */
- static void add_to_symbol_path(char *path, size_t path_buffer_size, char *dir,
- size_t dir_buffer_size) {
- strcat_s(dir, dir_buffer_size, ";");
- if (!strstr(path, dir)) {
- strcat_s(path, path_buffer_size, dir);
- }
- }
- /*
- Get symbol path - semicolon-separated list of directories to search for debug
- symbols. We expect PDB in the same directory as corresponding exe or dll,
- so the path is build from directories of the loaded modules. If environment
- variable _NT_SYMBOL_PATH is set, it's value appended to the symbol search path
- */
- static void get_symbol_path(char *path, size_t size) {
- HANDLE hSnap;
- char *envvar;
- char *p;
- #ifndef DBUG_OFF
- static char pdb_debug_dir[MAX_PATH + 7];
- #endif
- path[0] = '\0';
- #ifndef DBUG_OFF
- /*
- Add "debug" subdirectory of the application directory, sometimes PDB will
- placed here by installation.
- */
- GetModuleFileName(NULL, pdb_debug_dir, MAX_PATH);
- p = strrchr(pdb_debug_dir, '\\');
- if (p) {
- *p = 0;
- strcat_s(pdb_debug_dir, sizeof(pdb_debug_dir), "\\debug;");
- add_to_symbol_path(path, size, pdb_debug_dir, sizeof(pdb_debug_dir));
- }
- #endif
- /*
- Enumerate all modules, and add their directories to the path.
- Avoid duplicate entries.
- */
- hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
- if (hSnap != INVALID_HANDLE_VALUE) {
- BOOL ret;
- MODULEENTRY32 mod;
- mod.dwSize = sizeof(MODULEENTRY32);
- for (ret = Module32First(hSnap, &mod); ret;
- ret = Module32Next(hSnap, &mod)) {
- char *module_dir = mod.szExePath;
- p = strrchr(module_dir, '\\');
- if (!p) {
- /*
- Path separator was not found. Not known to happen, if ever happens,
- will indicate current directory.
- */
- module_dir[0] = '.';
- module_dir[1] = '\0';
- } else {
- *p = '\0';
- }
- add_to_symbol_path(path, size, module_dir, sizeof(mod.szExePath));
- }
- CloseHandle(hSnap);
- }
- /* Add _NT_SYMBOL_PATH, if present. */
- envvar = getenv("_NT_SYMBOL_PATH");
- if (envvar) {
- strcat_s(path, size, envvar);
- }
- }
- #define MAX_SYMBOL_PATH 32768
- /* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
- #ifndef SYMOPT_NO_PROMPTS
- #define SYMOPT_NO_PROMPTS 0
- #endif
- void my_print_stacktrace(uchar *unused1, ulong unused2) {
- HANDLE hProcess = GetCurrentProcess();
- HANDLE hThread = GetCurrentThread();
- static IMAGEHLP_MODULE64 module = {sizeof(module)};
- static IMAGEHLP_SYMBOL64_PACKAGE package;
- DWORD64 addr;
- DWORD machine;
- int i;
- CONTEXT context;
- STACKFRAME64 frame = {0};
- static char symbol_path[MAX_SYMBOL_PATH];
- if (!exception_ptrs) return;
- /* Copy context, as stackwalking on original will unwind the stack */
- context = *(exception_ptrs->ContextRecord);
- /*Initialize symbols.*/
- SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_NO_PROMPTS | SYMOPT_DEFERRED_LOADS |
- SYMOPT_DEBUG);
- get_symbol_path(symbol_path, sizeof(symbol_path));
- SymInitialize(hProcess, symbol_path, true);
- /*Prepare stackframe for the first StackWalk64 call*/
- frame.AddrFrame.Mode = frame.AddrPC.Mode = frame.AddrStack.Mode =
- AddrModeFlat;
- #if (defined _M_X64)
- machine = IMAGE_FILE_MACHINE_AMD64;
- frame.AddrFrame.Offset = context.Rbp;
- frame.AddrPC.Offset = context.Rip;
- frame.AddrStack.Offset = context.Rsp;
- #else
- /*There is currently no need to support IA64*/
- #pragma error("unsupported architecture")
- #endif
- package.sym.SizeOfStruct = sizeof(package.sym);
- package.sym.MaxNameLength = sizeof(package.name);
- /*Walk the stack, output useful information*/
- for (i = 0; i < STACKWALK_MAX_FRAMES; i++) {
- DWORD64 function_offset = 0;
- DWORD line_offset = 0;
- IMAGEHLP_LINE64 line = {sizeof(line)};
- BOOL have_module = false;
- BOOL have_symbol = false;
- BOOL have_source = false;
- if (!StackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0, 0))
- break;
- addr = frame.AddrPC.Offset;
- have_module = SymGetModuleInfo64(hProcess, addr, &module);
- have_symbol =
- SymGetSymFromAddr64(hProcess, addr, &function_offset, &(package.sym));
- have_source = SymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
- my_safe_printf_stderr("%p ", addr);
- if (have_module) {
- char *base_image_name = strrchr(module.ImageName, '\\');
- if (base_image_name)
- base_image_name++;
- else
- base_image_name = module.ImageName;
- my_safe_printf_stderr("%s!", base_image_name);
- }
- if (have_symbol)
- my_safe_printf_stderr("%s()", package.sym.Name);
- else if (have_module)
- my_safe_printf_stderr("%s", "???");
- if (have_source) {
- char *base_file_name = strrchr(line.FileName, '\\');
- if (base_file_name)
- base_file_name++;
- else
- base_file_name = line.FileName;
- my_safe_printf_stderr("[%s:%u]", base_file_name, line.LineNumber);
- }
- my_safe_printf_stderr("%s", "\n");
- }
- }
- /*
- Write dump. The dump is created in current directory,
- file name is constructed from executable name plus
- ".dmp" extension
- */
- void my_write_core(int unused) {
- char path[MAX_PATH];
- // See comment below for clarification about size of dump_fname
- char dump_fname[MAX_PATH + 1 + 10 + 4 + 1] = "core.dmp";
- if (!exception_ptrs) return;
- if (GetModuleFileName(NULL, path, sizeof(path))) {
- char module_name[MAX_PATH];
- _splitpath(path, NULL, NULL, module_name, NULL);
- // max length of a value being placed to dump_fname is
- // MAX_PATH + 1 byte for '.' + up to 10 bytes for string
- // representation of DWORD value + 4 bytes for .dmp suffix +
- // 1 byte for termitated \0. Such size of output buffer guarantees
- // that there is enough space to place a result of string formatting
- // performed by snprintf().
- snprintf(dump_fname, sizeof(dump_fname), "%s.%u.dmp", module_name,
- GetCurrentProcessId());
- }
- my_create_minidump(dump_fname, 0, 0);
- }
- /** Create a minidump.
- @param name path of minidump file.
- @param process HANDLE to process. (0 for own process).
- @param pid Process id.
- */
- void my_create_minidump(const char *name, HANDLE process, DWORD pid) {
- char path[MAX_PATH];
- MINIDUMP_EXCEPTION_INFORMATION info;
- PMINIDUMP_EXCEPTION_INFORMATION info_ptr = NULL;
- HANDLE hFile;
- if (process == 0) {
- /* Does not need to CloseHandle() for the below. */
- process = GetCurrentProcess();
- pid = GetCurrentProcessId();
- info.ExceptionPointers = exception_ptrs;
- info.ClientPointers = false;
- info.ThreadId = GetCurrentThreadId();
- info_ptr = &info;
- }
- hFile = CreateFile(name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL, 0);
- if (hFile) {
- MINIDUMP_TYPE mdt =
- (MINIDUMP_TYPE)(MiniDumpNormal | MiniDumpWithThreadInfo |
- MiniDumpWithProcessThreadData);
- /* Create minidump, use info only if same process. */
- if (MiniDumpWriteDump(process, pid, hFile, mdt, info_ptr, 0, 0)) {
- my_safe_printf_stderr("Minidump written to %s\n",
- _fullpath(path, name, sizeof(path)) ? path : name);
- } else {
- my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %d\n",
- GetLastError());
- }
- CloseHandle(hFile);
- } else {
- my_safe_printf_stderr("CreateFile(%s) failed, last error %d\n", name,
- GetLastError());
- }
- }
- void my_safe_puts_stderr(const char *val, size_t len) {
- __try {
- my_write_stderr(val, len);
- my_safe_printf_stderr("%s", "\n");
- } __except (EXCEPTION_EXECUTE_HANDLER) {
- my_safe_printf_stderr("%s", "is an invalid string pointer\n");
- }
- }
- #endif /* _WIN32 */
- #ifdef _WIN32
- size_t my_write_stderr(const void *buf, size_t count) {
- DWORD bytes_written;
- SetFilePointer(GetStdHandle(STD_ERROR_HANDLE), 0, NULL, FILE_END);
- WriteFile(GetStdHandle(STD_ERROR_HANDLE), buf, (DWORD)count, &bytes_written,
- NULL);
- return bytes_written;
- }
- #else
- size_t my_write_stderr(const void *buf, size_t count) {
- return (size_t)write(STDERR_FILENO, buf, count);
- }
- #endif
- static const char digits[] = "0123456789abcdef";
- char *my_safe_utoa(int base, ulonglong val, char *buf) {
- *buf-- = 0;
- do {
- *buf-- = digits[val % base];
- } while ((val /= base) != 0);
- return buf + 1;
- }
- char *my_safe_itoa(int base, longlong val, char *buf) {
- char *orig_buf = buf;
- const bool is_neg = (val < 0);
- *buf-- = 0;
- if (is_neg) val = -val;
- if (is_neg && base == 16) {
- int ix;
- val -= 1;
- for (ix = 0; ix < 16; ++ix) buf[-ix] = '0';
- }
- do {
- *buf-- = digits[val % base];
- } while ((val /= base) != 0);
- if (is_neg && base == 10) *buf-- = '-';
- if (is_neg && base == 16) {
- int ix;
- buf = orig_buf - 1;
- for (ix = 0; ix < 16; ++ix, --buf) {
- switch (*buf) {
- case '0':
- *buf = 'f';
- break;
- case '1':
- *buf = 'e';
- break;
- case '2':
- *buf = 'd';
- break;
- case '3':
- *buf = 'c';
- break;
- case '4':
- *buf = 'b';
- break;
- case '5':
- *buf = 'a';
- break;
- case '6':
- *buf = '9';
- break;
- case '7':
- *buf = '8';
- break;
- case '8':
- *buf = '7';
- break;
- case '9':
- *buf = '6';
- break;
- case 'a':
- *buf = '5';
- break;
- case 'b':
- *buf = '4';
- break;
- case 'c':
- *buf = '3';
- break;
- case 'd':
- *buf = '2';
- break;
- case 'e':
- *buf = '1';
- break;
- case 'f':
- *buf = '0';
- break;
- }
- }
- }
- return buf + 1;
- }
- static const char *check_longlong(const char *fmt, bool *have_longlong) {
- *have_longlong = false;
- if (*fmt == 'l') {
- fmt++;
- if (*fmt != 'l')
- *have_longlong = (sizeof(long) == sizeof(longlong));
- else {
- fmt++;
- *have_longlong = true;
- }
- }
- return fmt;
- }
- static size_t my_safe_vsnprintf(char *to, size_t size, const char *format,
- va_list ap) {
- char *start = to;
- char *end = start + size - 1;
- for (; *format; ++format) {
- bool have_longlong = false;
- if (*format != '%') {
- if (to == end) /* end of buffer */
- break;
- *to++ = *format; /* copy ordinary char */
- continue;
- }
- ++format; /* skip '%' */
- format = check_longlong(format, &have_longlong);
- switch (*format) {
- case 'd':
- case 'i':
- case 'u':
- case 'x':
- case 'p': {
- longlong ival = 0;
- ulonglong uval = 0;
- if (*format == 'p')
- have_longlong = (sizeof(void *) == sizeof(longlong));
- if (have_longlong) {
- if (*format == 'u')
- uval = va_arg(ap, ulonglong);
- else
- ival = va_arg(ap, longlong);
- } else {
- if (*format == 'u')
- uval = va_arg(ap, unsigned int);
- else
- ival = va_arg(ap, int);
- }
- {
- char buff[22];
- const int base = (*format == 'x' || *format == 'p') ? 16 : 10;
- char *val_as_str =
- (*format == 'u')
- ? my_safe_utoa(base, uval, &buff[sizeof(buff) - 1])
- : my_safe_itoa(base, ival, &buff[sizeof(buff) - 1]);
- /*
- Strip off "ffffffff" if we have 'x' format without 'll'
- Similarly for 'p' format on 32bit systems.
- */
- if (base == 16 && !have_longlong && ival < 0) val_as_str += 8;
- while (*val_as_str && to < end) *to++ = *val_as_str++;
- continue;
- }
- }
- case 's': {
- const char *val = va_arg(ap, char *);
- if (!val) val = "(null)";
- while (*val && to < end) *to++ = *val++;
- continue;
- }
- }
- }
- *to = 0;
- return to - start;
- }
- size_t my_safe_snprintf(char *to, size_t n, const char *fmt, ...) {
- size_t result;
- va_list args;
- va_start(args, fmt);
- result = my_safe_vsnprintf(to, n, fmt, args);
- va_end(args);
- return result;
- }
- size_t my_safe_printf_stderr(const char *fmt, ...) {
- char to[512];
- size_t result;
- va_list args;
- va_start(args, fmt);
- result = my_safe_vsnprintf(to, sizeof(to), fmt, args);
- va_end(args);
- my_write_stderr(to, result);
- return result;
- }
- void my_safe_print_system_time() {
- char hrs_buf[3] = "00";
- char mins_buf[3] = "00";
- char secs_buf[3] = "00";
- int base = 10;
- #ifdef _WIN32
- SYSTEMTIME utc_time;
- long hrs, mins, secs;
- GetSystemTime(&utc_time);
- hrs = utc_time.wHour;
- mins = utc_time.wMinute;
- secs = utc_time.wSecond;
- #else
- /* Using time() instead of my_time() to avoid looping */
- const time_t curr_time = time(NULL);
- /* Calculate time of day */
- const long tmins = curr_time / 60;
- const long thrs = tmins / 60;
- const long hrs = thrs % 24;
- const long mins = tmins % 60;
- const long secs = curr_time % 60;
- #endif
- my_safe_itoa(base, hrs, &hrs_buf[2]);
- my_safe_itoa(base, mins, &mins_buf[2]);
- my_safe_itoa(base, secs, &secs_buf[2]);
- my_safe_printf_stderr("---------- %s:%s:%s UTC - ", hrs_buf, mins_buf,
- secs_buf);
- }
|