From c7fa4a5be885c13689c021bce364041a47ccd890 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 24 Apr 2024 17:56:19 +0300 Subject: Release version 8.0.15+16 Add support for building with MinGW GCC. --- libmysqlclient/mysql/mysys/stacktrace.cc | 784 +++++++++++++++++++++++++++++++ 1 file changed, 784 insertions(+) create mode 100644 libmysqlclient/mysql/mysys/stacktrace.cc (limited to 'libmysqlclient/mysql/mysys/stacktrace.cc') diff --git a/libmysqlclient/mysql/mysys/stacktrace.cc b/libmysqlclient/mysql/mysys/stacktrace.cc new file mode 100644 index 0000000..9f7e443 --- /dev/null +++ b/libmysqlclient/mysql/mysys/stacktrace.cc @@ -0,0 +1,784 @@ +/* Copyright (c) 2001, 2018, 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 +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include + +#include "my_inttypes.h" +#include "my_macros.h" +#include "my_stacktrace.h" + +#ifndef _WIN32 +#include + +#include "my_thread.h" +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STACKTRACE + +#ifdef __linux__ +#include /* isprint */ +#endif + +#ifdef HAVE_EXECINFO_H +#include +#endif + +#ifdef __linux__ +/* __bss_start doesn't seem to work on FreeBSD and doesn't exist on OSX/Solaris. + */ +#define PTR_SANE(p) \ + ((p) && (char *)(p) >= heap_start && (char *)(p) <= heap_end) +static char *heap_start; +extern char *__bss_start; +#else +#define PTR_SANE(p) (p) +#endif /* __linux */ + +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//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) { +#ifdef __linux__ + if (!safe_print_str(val, max_len)) return; + + /* Only needed by the linux version of PTR_SANE */ + char *heap_end = (char *)sbrk(0); +#endif + + if (!PTR_SANE(val)) { + my_safe_printf_stderr("%s", "is an invalid pointer\n"); + return; + } + + for (; max_len && PTR_SANE(val) && *val; --max_len) + my_write_stderr((val++), 1); + my_safe_printf_stderr("%s", "\n"); +} + +#if defined(HAVE_PRINTSTACK) + +/* Use Solaris' symbolic stack trace routine. */ +#include + +void my_print_stacktrace(uchar *stack_bottom MY_ATTRIBUTE((unused)), + ulong thread_stack MY_ATTRIBUTE((unused))) { + if (printstack(fileno(stderr)) == -1) + my_safe_printf_stderr( + "%s", "Error when traversing the stack, stack appears corrupt.\n"); + else + my_safe_printf_stderr( + "Please read " + "http://dev.mysql.com/doc/refman/%u.%u/en/resolve-stack-dump.html\n" + "and follow instructions on how to resolve the stack trace.\n" + "Resolved stack trace is much more helpful in diagnosing the\n" + "problem, so please do resolve it\n", + MYSQL_VERSION_MAJOR, MYSQL_VERSION_MINOR); +} + +#elif defined(HAVE_BACKTRACE) + +#ifdef HAVE_ABI_CXA_DEMANGLE + +#include + +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); +#else // !__APPLE__ + 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 // !__APPLE__ + bool ret = (demangled == NULL); + free(demangled); + return (ret); +} + +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) { + 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_PRINTSTACK || 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 +#include +#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) { +#ifndef __MINGW32__ + __try { +#endif + my_write_stderr(val, len); + my_safe_printf_stderr("%s", "\n"); +#ifndef __MINGW32__ + } __except (EXCEPTION_EXECUTE_HANDLER) { + my_safe_printf_stderr("%s", "is an invalid string pointer\n"); + } +#endif +} +#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); +} -- cgit v1.1