From: Kristian H. Kristensen Date: Wed, 29 Jul 2020 05:02:31 +0000 (-0700) Subject: util: Move stack debug functions to src/util X-Git-Url: https://git.libre-soc.org/?p=mesa.git;a=commitdiff_plain;h=848e7b947d0d505d54d27780b052e5532c721678 util: Move stack debug functions to src/util Reviewed-by: Eric Engestrom Part-of: --- diff --git a/meson.build b/meson.build index 4b57cf326ec..2dabd8188e2 100644 --- a/meson.build +++ b/meson.build @@ -841,10 +841,8 @@ if with_platform_android dependency('cutils'), dependency('hardware'), dependency('sync'), + dependency('backtrace') ] - if with_gallium - dep_android += dependency('backtrace') - endif if get_option('platform-sdk-version') >= 26 dep_android += dependency('nativewindow') endif diff --git a/src/gallium/auxiliary/meson.build b/src/gallium/auxiliary/meson.build index 76dd0e1f4a5..a1a1b5b64e6 100644 --- a/src/gallium/auxiliary/meson.build +++ b/src/gallium/auxiliary/meson.build @@ -249,9 +249,6 @@ files_libgallium = files( 'util/u_debug_image.h', 'util/u_debug_refcnt.c', 'util/u_debug_refcnt.h', - 'util/u_debug_stack.h', - 'util/u_debug_symbol.c', - 'util/u_debug_symbol.h', 'util/u_dirty_flags.h', 'util/u_dirty_surfaces.h', 'util/u_dl.c', @@ -341,16 +338,6 @@ files_libgallium = files( 'nir/nir_draw_helpers.h', ) -if with_platform_android - files_libgallium += files( - 'util/u_debug_stack_android.cpp', - ) -else - files_libgallium += files( - 'util/u_debug_stack.c', - ) -endif - if dep_libdrm.found() files_libgallium += files( 'renderonly/renderonly.c', @@ -522,7 +509,7 @@ libgallium = static_library( cpp_args : [cpp_msvc_compat_args], gnu_symbol_visibility : 'hidden', dependencies : [ - dep_libdrm, dep_llvm, dep_unwind, dep_dl, dep_m, dep_thread, dep_lmsensors, + dep_libdrm, dep_llvm, dep_dl, dep_m, dep_thread, dep_lmsensors, idep_nir, idep_nir_headers, idep_mesautil, ], build_by_default : false diff --git a/src/gallium/auxiliary/util/u_debug_stack.c b/src/gallium/auxiliary/util/u_debug_stack.c deleted file mode 100644 index 21f0371d3c1..00000000000 --- a/src/gallium/auxiliary/util/u_debug_stack.c +++ /dev/null @@ -1,332 +0,0 @@ -/************************************************************************** - * - * Copyright 2009 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ - -/** - * @file - * Stack backtracing. - * - * @author Jose Fonseca - */ - -#include "util/u_debug.h" -#include "u_debug_symbol.h" -#include "u_debug_stack.h" -#include "pipe/p_config.h" - -#if defined(HAVE_LIBUNWIND) - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include - -#include "os/os_thread.h" -#include "util/hash_table.h" - -static struct hash_table* symbols_hash; -static mtx_t symbols_mutex = _MTX_INITIALIZER_NP; - -/* TODO with some refactoring we might be able to re-use debug_symbol_name_cached() - * instead.. otoh if using libunwind I think u_debug_symbol could just be excluded - * from build? - */ -static const char * -symbol_name_cached(unw_cursor_t *cursor, unw_proc_info_t *pip) -{ - void *addr = (void *)(uintptr_t)pip->start_ip; - char *name; - - mtx_lock(&symbols_mutex); - if(!symbols_hash) - symbols_hash = _mesa_pointer_hash_table_create(NULL); - struct hash_entry *entry = _mesa_hash_table_search(symbols_hash, addr); - if (!entry) { - char procname[256]; - unw_word_t off; - int ret; - - ret = unw_get_proc_name(cursor, procname, sizeof(procname), &off); - if (ret && ret != -UNW_ENOMEM) { - procname[0] = '?'; - procname[1] = 0; - } - - if (asprintf(&name, "%s%s", procname, ret == -UNW_ENOMEM ? "..." : "") == -1) - name = "??"; - entry = _mesa_hash_table_insert(symbols_hash, addr, (void*)name); - } - mtx_unlock(&symbols_mutex); - - return entry->data; -} - -void -debug_backtrace_capture(struct debug_stack_frame *backtrace, - unsigned start_frame, - unsigned nr_frames) -{ - unw_cursor_t cursor; - unw_context_t context; - unw_proc_info_t pip; - unsigned i = 0; - - pip.unwind_info = NULL; - - unw_getcontext(&context); - unw_init_local(&cursor, &context); - - while ((start_frame > 0) && (unw_step(&cursor) > 0)) - start_frame--; - - while ((i < nr_frames) && (unw_step(&cursor) > 0)) { - unw_word_t ip; - - unw_get_reg(&cursor, UNW_REG_IP, &ip); - unw_get_proc_info(&cursor, &pip); - - backtrace[i].start_ip = pip.start_ip; - backtrace[i].off = ip - pip.start_ip; - backtrace[i].procname = symbol_name_cached(&cursor, &pip); - - i++; - } - - while (i < nr_frames) { - backtrace[i].start_ip = 0; - i++; - } -} - -static const void * -frame_ip(const struct debug_stack_frame *frame) -{ - return (void *)(uintptr_t)(frame->start_ip + frame->off); -} - -static const char * -frame_info(const struct debug_stack_frame *frame, unsigned *offset) -{ - Dl_info dlinfo; - const void *addr = frame_ip(frame); - - - if (dladdr(addr, &dlinfo) && dlinfo.dli_fname && - *dlinfo.dli_fname) { - *offset = (unsigned)((uintptr_t)addr - (uintptr_t)dlinfo.dli_fbase); - return dlinfo.dli_fname; - } - - *offset = 0; - return "?"; -} - -void -debug_backtrace_dump(const struct debug_stack_frame *backtrace, - unsigned nr_frames) -{ - unsigned i, offset; - const char *filename; - - for (i = 0; i < nr_frames; ++i) { - if (!backtrace[i].start_ip) - break; - filename = frame_info(&backtrace[i], &offset); - debug_printf("\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, - backtrace[i].procname, backtrace[i].off, - frame_ip(&backtrace[i])); - } -} - -void -debug_backtrace_print(FILE *f, - const struct debug_stack_frame *backtrace, - unsigned nr_frames) -{ - unsigned i, offset; - const char *filename; - - for (i = 0; i < nr_frames; ++i) { - if (!backtrace[i].start_ip) - break; - filename = frame_info(&backtrace[i], &offset); - fprintf(f, "\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, - backtrace[i].procname, backtrace[i].off, - frame_ip(&backtrace[i])); - } -} -#elif defined(ANDROID) - /* Not implemented here; see u_debug_stack_android.cpp */ -#else /* ! HAVE_LIBUNWIND */ - -#if defined(PIPE_OS_WINDOWS) -#include -#endif - - -/** - * Capture stack backtrace. - * - * NOTE: The implementation of this function is quite big, but it is important - * not to break it down in smaller functions to avoid adding new frames to the - * calling stack. - */ -void -debug_backtrace_capture(struct debug_stack_frame *backtrace, - unsigned start_frame, - unsigned nr_frames) -{ - const void **frame_pointer = NULL; - unsigned i = 0; - - if (!nr_frames) { - return; - } - - /* - * On Windows try obtaining the stack backtrace via CaptureStackBackTrace. - * - * It works reliably both for x86 for x86_64. - */ -#if defined(PIPE_OS_WINDOWS) - { - typedef USHORT (WINAPI *PFNCAPTURESTACKBACKTRACE)(ULONG, ULONG, - PVOID *, PULONG); - static PFNCAPTURESTACKBACKTRACE pfnCaptureStackBackTrace = NULL; - - if (!pfnCaptureStackBackTrace) { - static HMODULE hModule = NULL; - if (!hModule) { - hModule = LoadLibraryA("kernel32"); - assert(hModule); - } - if (hModule) { - pfnCaptureStackBackTrace = - (PFNCAPTURESTACKBACKTRACE)GetProcAddress(hModule, - "RtlCaptureStackBackTrace"); - } - } - if (pfnCaptureStackBackTrace) { - /* - * Skip this (debug_backtrace_capture) function's frame. - */ - - start_frame += 1; - - assert(start_frame + nr_frames < 63); - i = pfnCaptureStackBackTrace(start_frame, nr_frames, - (PVOID *) &backtrace->function, NULL); - - /* Pad remaing requested frames with NULL */ - while (i < nr_frames) { - backtrace[i++].function = NULL; - } - - return; - } - } -#endif - -#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION > 404) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wframe-address" - frame_pointer = ((const void **)__builtin_frame_address(1)); -#pragma GCC diagnostic pop -#elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86) - __asm { - mov frame_pointer, ebp - } - frame_pointer = (const void **)frame_pointer[0]; -#else - frame_pointer = NULL; -#endif - -#ifdef PIPE_ARCH_X86 - while (nr_frames) { - const void **next_frame_pointer; - - if (!frame_pointer) - break; - - if (start_frame) - --start_frame; - else { - backtrace[i++].function = frame_pointer[1]; - --nr_frames; - } - - next_frame_pointer = (const void **)frame_pointer[0]; - - /* Limit the stack walk to avoid referencing undefined memory */ - if ((uintptr_t)next_frame_pointer <= (uintptr_t)frame_pointer || - (uintptr_t)next_frame_pointer > (uintptr_t)frame_pointer + 64*1024) - break; - - frame_pointer = next_frame_pointer; - } -#else - (void) frame_pointer; -#endif - - while (nr_frames) { - backtrace[i++].function = NULL; - --nr_frames; - } -} - - -void -debug_backtrace_dump(const struct debug_stack_frame *backtrace, - unsigned nr_frames) -{ - unsigned i; - - for (i = 0; i < nr_frames; ++i) { - if (!backtrace[i].function) - break; - debug_symbol_print(backtrace[i].function); - } -} - - -void -debug_backtrace_print(FILE *f, - const struct debug_stack_frame *backtrace, - unsigned nr_frames) -{ - unsigned i; - - for (i = 0; i < nr_frames; ++i) { - const char *symbol; - if (!backtrace[i].function) - break; - symbol = debug_symbol_name_cached(backtrace[i].function); - if (symbol) - fprintf(f, "%s\n", symbol); - } -} - -#endif /* HAVE_LIBUNWIND */ diff --git a/src/gallium/auxiliary/util/u_debug_stack.h b/src/gallium/auxiliary/util/u_debug_stack.h deleted file mode 100644 index fff41a5a9ea..00000000000 --- a/src/gallium/auxiliary/util/u_debug_stack.h +++ /dev/null @@ -1,90 +0,0 @@ -/************************************************************************** - * - * Copyright 2009 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ - -#ifndef U_DEBUG_STACK_H_ -#define U_DEBUG_STACK_H_ - -#include - -#ifdef HAVE_LIBUNWIND -#define UNW_LOCAL_ONLY -#include -#endif - -/** - * @file - * Stack backtracing. - * - * @author Jose Fonseca - */ - - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * Represent a frame from a stack backtrace. - * -#if defined(PIPE_OS_WINDOWS) && !defined(HAVE_LIBUNWIND) - * XXX: Do not change this. (passed to Windows' CaptureStackBackTrace()) -#endif - * - * TODO: This should be refactored as a void * typedef. - */ -struct debug_stack_frame -{ -#ifdef HAVE_LIBUNWIND - unw_word_t start_ip; - unsigned int off; - const char *procname; -#else - const void *function; -#endif -}; - - -void -debug_backtrace_capture(struct debug_stack_frame *backtrace, - unsigned start_frame, - unsigned nr_frames); - -void -debug_backtrace_dump(const struct debug_stack_frame *backtrace, - unsigned nr_frames); - -void -debug_backtrace_print(FILE *f, - const struct debug_stack_frame *backtrace, - unsigned nr_frames); - -#ifdef __cplusplus -} -#endif - -#endif /* U_DEBUG_STACK_H_ */ diff --git a/src/gallium/auxiliary/util/u_debug_stack_android.cpp b/src/gallium/auxiliary/util/u_debug_stack_android.cpp deleted file mode 100644 index 879b0fb2e9d..00000000000 --- a/src/gallium/auxiliary/util/u_debug_stack_android.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2018 Stefan Schake - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include - -#include "util/u_debug.h" -#include "u_debug_stack.h" -#include "util/hash_table.h" -#include "os/os_thread.h" - -static hash_table *backtrace_table; -static mtx_t table_mutex = _MTX_INITIALIZER_NP; - -void -debug_backtrace_capture(debug_stack_frame *mesa_backtrace, - unsigned start_frame, - unsigned nr_frames) -{ - hash_entry *backtrace_entry; - Backtrace *backtrace; - pid_t tid = gettid(); - - if (!nr_frames) - return; - - /* We keep an Android Backtrace handler around for each thread */ - mtx_lock(&table_mutex); - if (!backtrace_table) - backtrace_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer, - _mesa_key_pointer_equal); - - backtrace_entry = _mesa_hash_table_search(backtrace_table, (void*) (uintptr_t)tid); - if (!backtrace_entry) { - backtrace = Backtrace::Create(getpid(), tid); - _mesa_hash_table_insert(backtrace_table, (void*) (uintptr_t)tid, backtrace); - } else { - backtrace = (Backtrace *) backtrace_entry->data; - } - mtx_unlock(&table_mutex); - - /* Add one to exclude this call. Unwind already ignores itself. */ - backtrace->Unwind(start_frame + 1); - - /* Store the Backtrace handler in the first mesa frame for reference. - * Unwind will generally return less frames than nr_frames specified - * but we have no good way of storing the real count otherwise. - * The Backtrace handler only stores the results until the next Unwind, - * but that is how u_debug_stack is used anyway. - */ - mesa_backtrace->function = backtrace; -} - -void -debug_backtrace_dump(const debug_stack_frame *mesa_backtrace, - unsigned nr_frames) -{ - Backtrace *backtrace = (Backtrace *) mesa_backtrace->function; - size_t i; - - if (!nr_frames) - return; - - if (nr_frames > backtrace->NumFrames()) - nr_frames = backtrace->NumFrames(); - for (i = 0; i < nr_frames; i++) { - /* There is no prescribed format and this isn't interpreted further, - * so we simply use the default Android format. - */ - const std::string& frame_line = backtrace->FormatFrameData(i); - debug_printf("%s\n", frame_line.c_str()); - } -} - -void -debug_backtrace_print(FILE *f, - const debug_stack_frame *mesa_backtrace, - unsigned nr_frames) -{ - Backtrace *backtrace = (Backtrace *) mesa_backtrace->function; - size_t i; - - if (!nr_frames) - return; - - if (nr_frames > backtrace->NumFrames()) - nr_frames = backtrace->NumFrames(); - for (i = 0; i < nr_frames; i++) { - const std::string& frame_line = backtrace->FormatFrameData(i); - fprintf(f, "%s\n", frame_line.c_str()); - } -} diff --git a/src/gallium/auxiliary/util/u_debug_symbol.c b/src/gallium/auxiliary/util/u_debug_symbol.c deleted file mode 100644 index 73225e9e495..00000000000 --- a/src/gallium/auxiliary/util/u_debug_symbol.c +++ /dev/null @@ -1,306 +0,0 @@ -/************************************************************************** - * - * Copyright 2009 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ - -/** - * @file - * Symbol lookup. - * - * @author Jose Fonseca - */ - -#include "pipe/p_compiler.h" -#include "os/os_thread.h" -#include "util/u_string.h" - -#include "util/u_debug.h" -#include "u_debug_symbol.h" -#include "util/hash_table.h" - - -#if defined(PIPE_OS_WINDOWS) - -#include -#include - -#include "dbghelp.h" - - -/** - * SymInitialize() must be called once for each process (in this case, the - * current process), before any of the other functions can be called. - */ -static BOOL g_bSymInitialized = FALSE; - - -/** - * Lookup the address of a DbgHelp function. - */ -static FARPROC WINAPI -getDbgHelpProcAddress(LPCSTR lpProcName) -{ - static HMODULE hModule = NULL; - - if (!hModule) { - static boolean bail = FALSE; - - if (bail) { - return NULL; - } - -#ifdef PIPE_CC_GCC - /* - * DbgHelp does not understand the debug information generated by MinGW toolchain. - * - * mgwhelp.dll is a dbghelp.dll look-alike replacement, which is able to - * understand MinGW symbols, including on 64-bit builds. - */ - if (!hModule) { - hModule = LoadLibraryA("mgwhelp.dll"); - if (!hModule) { - _debug_printf("warning: mgwhelp.dll not found: symbol names will not be resolved\n" - "warning: download it from https://github.com/jrfonseca/drmingw/#mgwhelp\n"); - } - } -#endif - - /* - * Fallback to the real DbgHelp. - */ - if (!hModule) { - hModule = LoadLibraryA("dbghelp.dll"); - } - - if (!hModule) { - bail = TRUE; - return NULL; - } - } - - return GetProcAddress(hModule, lpProcName); -} - - -/** - * Generic macro to dispatch a DbgHelp functions. - */ -#define DBGHELP_DISPATCH(_name, _ret_type, _ret_default, _arg_types, _arg_names) \ - static _ret_type WINAPI \ - j_##_name _arg_types \ - { \ - typedef BOOL (WINAPI *PFN) _arg_types; \ - static PFN pfn = NULL; \ - if (!pfn) { \ - pfn = (PFN) getDbgHelpProcAddress(#_name); \ - if (!pfn) { \ - return _ret_default; \ - } \ - } \ - return pfn _arg_names; \ - } - -DBGHELP_DISPATCH(SymInitialize, - BOOL, 0, - (HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess), - (hProcess, UserSearchPath, fInvadeProcess)) - -DBGHELP_DISPATCH(SymSetOptions, - DWORD, FALSE, - (DWORD SymOptions), - (SymOptions)) - -DBGHELP_DISPATCH(SymFromAddr, - BOOL, FALSE, - (HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol), - (hProcess, Address, Displacement, Symbol)) - -DBGHELP_DISPATCH(SymGetLineFromAddr64, - BOOL, FALSE, - (HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line), - (hProcess, dwAddr, pdwDisplacement, Line)) - - -#undef DBGHELP_DISPATCH - - -static inline boolean -debug_symbol_name_dbghelp(const void *addr, char* buf, unsigned size) -{ - DWORD64 dwAddr = (DWORD64)(uintptr_t)addr; - HANDLE hProcess = GetCurrentProcess(); - - /* General purpose buffer, to back pSymbol and other temporary stuff. - * Must not be too memory hungry here to avoid stack overflows. - */ - CHAR buffer[512]; - - PSYMBOL_INFO pSymbol = (PSYMBOL_INFO) buffer; - DWORD64 dwDisplacement = 0; /* Displacement of the input address, relative to the start of the symbol */ - DWORD dwLineDisplacement = 0; - IMAGEHLP_LINE64 Line; - - memset(pSymbol, 0, sizeof *pSymbol); - pSymbol->SizeOfStruct = sizeof buffer; - pSymbol->MaxNameLen = sizeof buffer - offsetof(SYMBOL_INFO, Name); - - if (!g_bSymInitialized) { - j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES); - if (j_SymInitialize(hProcess, NULL, TRUE)) { - g_bSymInitialized = TRUE; - } - } - - /* Lookup symbol name */ - if (!g_bSymInitialized || - !j_SymFromAddr(hProcess, dwAddr, &dwDisplacement, pSymbol)) { - /* - * We couldn't obtain symbol information. At least tell which module the address belongs. - */ - - HMODULE hModule = NULL; - - if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - (LPCTSTR)addr, - &hModule)) { - return FALSE; - } - - if (GetModuleFileNameA(hModule, buffer, sizeof buffer) == sizeof buffer) { - return FALSE; - } - snprintf(buf, size, "%p at %s+0x%lx", - addr, buffer, - (unsigned long)((uintptr_t)addr - (uintptr_t)hModule)); - - return TRUE; - } - - /* - * Try to get filename and line number. - */ - memset(&Line, 0, sizeof Line); - Line.SizeOfStruct = sizeof Line; - if (!j_SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &Line)) { - Line.FileName = NULL; - } - - if (Line.FileName) { - snprintf(buf, size, "%s at %s:%lu", pSymbol->Name, Line.FileName, Line.LineNumber); - } else { - snprintf(buf, size, "%s", pSymbol->Name); - } - - return TRUE; -} - -#endif /* PIPE_OS_WINDOWS */ - - -#if defined(HAVE_EXECINFO_H) - -#include - -/* This can only provide dynamic symbols, or binary offsets into a file. - * - * To fix this, post-process the output with tools/addr2line.sh - */ -static inline boolean -debug_symbol_name_glibc(const void *addr, char* buf, unsigned size) -{ - char** syms = backtrace_symbols((void**)&addr, 1); - if (!syms) { - return FALSE; - } - strncpy(buf, syms[0], size - 1); - buf[size - 1] = 0; - free(syms); - return TRUE; -} - -#endif /* defined(HAVE_EXECINFO_H) */ - - -void -debug_symbol_name(const void *addr, char* buf, unsigned size) -{ -#if defined(PIPE_OS_WINDOWS) - if (debug_symbol_name_dbghelp(addr, buf, size)) { - return; - } -#endif - -#if defined(HAVE_EXECINFO_H) - if (debug_symbol_name_glibc(addr, buf, size)) { - return; - } -#endif /* defined(HAVE_EXECINFO_H) */ - - snprintf(buf, size, "%p", addr); - buf[size - 1] = 0; -} - -void -debug_symbol_print(const void *addr) -{ - char buf[1024]; - debug_symbol_name(addr, buf, sizeof(buf)); - debug_printf("\t%s\n", buf); -} - -static struct hash_table* symbols_hash; -#ifdef PIPE_OS_WINDOWS -static mtx_t symbols_mutex; -#else -static mtx_t symbols_mutex = _MTX_INITIALIZER_NP; -#endif - -const char* -debug_symbol_name_cached(const void *addr) -{ - const char* name; -#ifdef PIPE_OS_WINDOWS - static boolean first = TRUE; - - if (first) { - (void) mtx_init(&symbols_mutex, mtx_plain); - first = FALSE; - } -#endif - - mtx_lock(&symbols_mutex); - if(!symbols_hash) - symbols_hash = _mesa_pointer_hash_table_create(NULL); - struct hash_entry *entry = _mesa_hash_table_search(symbols_hash, addr); - if (!entry) { - char buf[1024]; - debug_symbol_name(addr, buf, sizeof(buf)); - name = strdup(buf); - - entry = _mesa_hash_table_insert(symbols_hash, addr, (void*)name); - } - mtx_unlock(&symbols_mutex); - return entry->data; -} diff --git a/src/gallium/auxiliary/util/u_debug_symbol.h b/src/gallium/auxiliary/util/u_debug_symbol.h deleted file mode 100644 index b247706c2a0..00000000000 --- a/src/gallium/auxiliary/util/u_debug_symbol.h +++ /dev/null @@ -1,58 +0,0 @@ -/************************************************************************** - * - * Copyright 2009 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ - -#ifndef U_DEBUG_SYMBOL_H_ -#define U_DEBUG_SYMBOL_H_ - - -/** - * @file - * Symbol lookup. - * - * @author Jose Fonseca - */ - - -#ifdef __cplusplus -extern "C" { -#endif - - -void -debug_symbol_name(const void *addr, char* buf, unsigned size); - -const char* -debug_symbol_name_cached(const void *addr); - -void -debug_symbol_print(const void *addr); - -#ifdef __cplusplus -} -#endif - -#endif /* U_DEBUG_SYMBOL_H_ */ diff --git a/src/util/meson.build b/src/util/meson.build index 25c363ab95c..909b3e07468 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -158,15 +158,24 @@ deps_for_libmesa_util = [ dep_m, dep_valgrind, dep_zstd, + dep_dl, + dep_unwind, ] if with_platform_android deps_for_libmesa_util += dep_android + files_debug_stack = files('u_debug_stack_android.cpp') + else + files_debug_stack = files( + 'u_debug_stack.c', + 'u_debug_symbol.c', + 'u_debug_symbol.h', + ) endif _libmesa_util = static_library( 'mesa_util', - [files_mesa_util, format_srgb], + [files_mesa_util, files_debug_stack, format_srgb], include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, inc_gallium_aux], dependencies : deps_for_libmesa_util, link_with: libmesa_format, diff --git a/src/util/u_debug_stack.c b/src/util/u_debug_stack.c new file mode 100644 index 00000000000..21f0371d3c1 --- /dev/null +++ b/src/util/u_debug_stack.c @@ -0,0 +1,332 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Stack backtracing. + * + * @author Jose Fonseca + */ + +#include "util/u_debug.h" +#include "u_debug_symbol.h" +#include "u_debug_stack.h" +#include "pipe/p_config.h" + +#if defined(HAVE_LIBUNWIND) + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include + +#include "os/os_thread.h" +#include "util/hash_table.h" + +static struct hash_table* symbols_hash; +static mtx_t symbols_mutex = _MTX_INITIALIZER_NP; + +/* TODO with some refactoring we might be able to re-use debug_symbol_name_cached() + * instead.. otoh if using libunwind I think u_debug_symbol could just be excluded + * from build? + */ +static const char * +symbol_name_cached(unw_cursor_t *cursor, unw_proc_info_t *pip) +{ + void *addr = (void *)(uintptr_t)pip->start_ip; + char *name; + + mtx_lock(&symbols_mutex); + if(!symbols_hash) + symbols_hash = _mesa_pointer_hash_table_create(NULL); + struct hash_entry *entry = _mesa_hash_table_search(symbols_hash, addr); + if (!entry) { + char procname[256]; + unw_word_t off; + int ret; + + ret = unw_get_proc_name(cursor, procname, sizeof(procname), &off); + if (ret && ret != -UNW_ENOMEM) { + procname[0] = '?'; + procname[1] = 0; + } + + if (asprintf(&name, "%s%s", procname, ret == -UNW_ENOMEM ? "..." : "") == -1) + name = "??"; + entry = _mesa_hash_table_insert(symbols_hash, addr, (void*)name); + } + mtx_unlock(&symbols_mutex); + + return entry->data; +} + +void +debug_backtrace_capture(struct debug_stack_frame *backtrace, + unsigned start_frame, + unsigned nr_frames) +{ + unw_cursor_t cursor; + unw_context_t context; + unw_proc_info_t pip; + unsigned i = 0; + + pip.unwind_info = NULL; + + unw_getcontext(&context); + unw_init_local(&cursor, &context); + + while ((start_frame > 0) && (unw_step(&cursor) > 0)) + start_frame--; + + while ((i < nr_frames) && (unw_step(&cursor) > 0)) { + unw_word_t ip; + + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_proc_info(&cursor, &pip); + + backtrace[i].start_ip = pip.start_ip; + backtrace[i].off = ip - pip.start_ip; + backtrace[i].procname = symbol_name_cached(&cursor, &pip); + + i++; + } + + while (i < nr_frames) { + backtrace[i].start_ip = 0; + i++; + } +} + +static const void * +frame_ip(const struct debug_stack_frame *frame) +{ + return (void *)(uintptr_t)(frame->start_ip + frame->off); +} + +static const char * +frame_info(const struct debug_stack_frame *frame, unsigned *offset) +{ + Dl_info dlinfo; + const void *addr = frame_ip(frame); + + + if (dladdr(addr, &dlinfo) && dlinfo.dli_fname && + *dlinfo.dli_fname) { + *offset = (unsigned)((uintptr_t)addr - (uintptr_t)dlinfo.dli_fbase); + return dlinfo.dli_fname; + } + + *offset = 0; + return "?"; +} + +void +debug_backtrace_dump(const struct debug_stack_frame *backtrace, + unsigned nr_frames) +{ + unsigned i, offset; + const char *filename; + + for (i = 0; i < nr_frames; ++i) { + if (!backtrace[i].start_ip) + break; + filename = frame_info(&backtrace[i], &offset); + debug_printf("\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, + backtrace[i].procname, backtrace[i].off, + frame_ip(&backtrace[i])); + } +} + +void +debug_backtrace_print(FILE *f, + const struct debug_stack_frame *backtrace, + unsigned nr_frames) +{ + unsigned i, offset; + const char *filename; + + for (i = 0; i < nr_frames; ++i) { + if (!backtrace[i].start_ip) + break; + filename = frame_info(&backtrace[i], &offset); + fprintf(f, "\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, + backtrace[i].procname, backtrace[i].off, + frame_ip(&backtrace[i])); + } +} +#elif defined(ANDROID) + /* Not implemented here; see u_debug_stack_android.cpp */ +#else /* ! HAVE_LIBUNWIND */ + +#if defined(PIPE_OS_WINDOWS) +#include +#endif + + +/** + * Capture stack backtrace. + * + * NOTE: The implementation of this function is quite big, but it is important + * not to break it down in smaller functions to avoid adding new frames to the + * calling stack. + */ +void +debug_backtrace_capture(struct debug_stack_frame *backtrace, + unsigned start_frame, + unsigned nr_frames) +{ + const void **frame_pointer = NULL; + unsigned i = 0; + + if (!nr_frames) { + return; + } + + /* + * On Windows try obtaining the stack backtrace via CaptureStackBackTrace. + * + * It works reliably both for x86 for x86_64. + */ +#if defined(PIPE_OS_WINDOWS) + { + typedef USHORT (WINAPI *PFNCAPTURESTACKBACKTRACE)(ULONG, ULONG, + PVOID *, PULONG); + static PFNCAPTURESTACKBACKTRACE pfnCaptureStackBackTrace = NULL; + + if (!pfnCaptureStackBackTrace) { + static HMODULE hModule = NULL; + if (!hModule) { + hModule = LoadLibraryA("kernel32"); + assert(hModule); + } + if (hModule) { + pfnCaptureStackBackTrace = + (PFNCAPTURESTACKBACKTRACE)GetProcAddress(hModule, + "RtlCaptureStackBackTrace"); + } + } + if (pfnCaptureStackBackTrace) { + /* + * Skip this (debug_backtrace_capture) function's frame. + */ + + start_frame += 1; + + assert(start_frame + nr_frames < 63); + i = pfnCaptureStackBackTrace(start_frame, nr_frames, + (PVOID *) &backtrace->function, NULL); + + /* Pad remaing requested frames with NULL */ + while (i < nr_frames) { + backtrace[i++].function = NULL; + } + + return; + } + } +#endif + +#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION > 404) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wframe-address" + frame_pointer = ((const void **)__builtin_frame_address(1)); +#pragma GCC diagnostic pop +#elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86) + __asm { + mov frame_pointer, ebp + } + frame_pointer = (const void **)frame_pointer[0]; +#else + frame_pointer = NULL; +#endif + +#ifdef PIPE_ARCH_X86 + while (nr_frames) { + const void **next_frame_pointer; + + if (!frame_pointer) + break; + + if (start_frame) + --start_frame; + else { + backtrace[i++].function = frame_pointer[1]; + --nr_frames; + } + + next_frame_pointer = (const void **)frame_pointer[0]; + + /* Limit the stack walk to avoid referencing undefined memory */ + if ((uintptr_t)next_frame_pointer <= (uintptr_t)frame_pointer || + (uintptr_t)next_frame_pointer > (uintptr_t)frame_pointer + 64*1024) + break; + + frame_pointer = next_frame_pointer; + } +#else + (void) frame_pointer; +#endif + + while (nr_frames) { + backtrace[i++].function = NULL; + --nr_frames; + } +} + + +void +debug_backtrace_dump(const struct debug_stack_frame *backtrace, + unsigned nr_frames) +{ + unsigned i; + + for (i = 0; i < nr_frames; ++i) { + if (!backtrace[i].function) + break; + debug_symbol_print(backtrace[i].function); + } +} + + +void +debug_backtrace_print(FILE *f, + const struct debug_stack_frame *backtrace, + unsigned nr_frames) +{ + unsigned i; + + for (i = 0; i < nr_frames; ++i) { + const char *symbol; + if (!backtrace[i].function) + break; + symbol = debug_symbol_name_cached(backtrace[i].function); + if (symbol) + fprintf(f, "%s\n", symbol); + } +} + +#endif /* HAVE_LIBUNWIND */ diff --git a/src/util/u_debug_stack.h b/src/util/u_debug_stack.h new file mode 100644 index 00000000000..fff41a5a9ea --- /dev/null +++ b/src/util/u_debug_stack.h @@ -0,0 +1,90 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_DEBUG_STACK_H_ +#define U_DEBUG_STACK_H_ + +#include + +#ifdef HAVE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#endif + +/** + * @file + * Stack backtracing. + * + * @author Jose Fonseca + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Represent a frame from a stack backtrace. + * +#if defined(PIPE_OS_WINDOWS) && !defined(HAVE_LIBUNWIND) + * XXX: Do not change this. (passed to Windows' CaptureStackBackTrace()) +#endif + * + * TODO: This should be refactored as a void * typedef. + */ +struct debug_stack_frame +{ +#ifdef HAVE_LIBUNWIND + unw_word_t start_ip; + unsigned int off; + const char *procname; +#else + const void *function; +#endif +}; + + +void +debug_backtrace_capture(struct debug_stack_frame *backtrace, + unsigned start_frame, + unsigned nr_frames); + +void +debug_backtrace_dump(const struct debug_stack_frame *backtrace, + unsigned nr_frames); + +void +debug_backtrace_print(FILE *f, + const struct debug_stack_frame *backtrace, + unsigned nr_frames); + +#ifdef __cplusplus +} +#endif + +#endif /* U_DEBUG_STACK_H_ */ diff --git a/src/util/u_debug_stack_android.cpp b/src/util/u_debug_stack_android.cpp new file mode 100644 index 00000000000..879b0fb2e9d --- /dev/null +++ b/src/util/u_debug_stack_android.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 Stefan Schake + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "util/u_debug.h" +#include "u_debug_stack.h" +#include "util/hash_table.h" +#include "os/os_thread.h" + +static hash_table *backtrace_table; +static mtx_t table_mutex = _MTX_INITIALIZER_NP; + +void +debug_backtrace_capture(debug_stack_frame *mesa_backtrace, + unsigned start_frame, + unsigned nr_frames) +{ + hash_entry *backtrace_entry; + Backtrace *backtrace; + pid_t tid = gettid(); + + if (!nr_frames) + return; + + /* We keep an Android Backtrace handler around for each thread */ + mtx_lock(&table_mutex); + if (!backtrace_table) + backtrace_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer, + _mesa_key_pointer_equal); + + backtrace_entry = _mesa_hash_table_search(backtrace_table, (void*) (uintptr_t)tid); + if (!backtrace_entry) { + backtrace = Backtrace::Create(getpid(), tid); + _mesa_hash_table_insert(backtrace_table, (void*) (uintptr_t)tid, backtrace); + } else { + backtrace = (Backtrace *) backtrace_entry->data; + } + mtx_unlock(&table_mutex); + + /* Add one to exclude this call. Unwind already ignores itself. */ + backtrace->Unwind(start_frame + 1); + + /* Store the Backtrace handler in the first mesa frame for reference. + * Unwind will generally return less frames than nr_frames specified + * but we have no good way of storing the real count otherwise. + * The Backtrace handler only stores the results until the next Unwind, + * but that is how u_debug_stack is used anyway. + */ + mesa_backtrace->function = backtrace; +} + +void +debug_backtrace_dump(const debug_stack_frame *mesa_backtrace, + unsigned nr_frames) +{ + Backtrace *backtrace = (Backtrace *) mesa_backtrace->function; + size_t i; + + if (!nr_frames) + return; + + if (nr_frames > backtrace->NumFrames()) + nr_frames = backtrace->NumFrames(); + for (i = 0; i < nr_frames; i++) { + /* There is no prescribed format and this isn't interpreted further, + * so we simply use the default Android format. + */ + const std::string& frame_line = backtrace->FormatFrameData(i); + debug_printf("%s\n", frame_line.c_str()); + } +} + +void +debug_backtrace_print(FILE *f, + const debug_stack_frame *mesa_backtrace, + unsigned nr_frames) +{ + Backtrace *backtrace = (Backtrace *) mesa_backtrace->function; + size_t i; + + if (!nr_frames) + return; + + if (nr_frames > backtrace->NumFrames()) + nr_frames = backtrace->NumFrames(); + for (i = 0; i < nr_frames; i++) { + const std::string& frame_line = backtrace->FormatFrameData(i); + fprintf(f, "%s\n", frame_line.c_str()); + } +} diff --git a/src/util/u_debug_symbol.c b/src/util/u_debug_symbol.c new file mode 100644 index 00000000000..73225e9e495 --- /dev/null +++ b/src/util/u_debug_symbol.c @@ -0,0 +1,306 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Symbol lookup. + * + * @author Jose Fonseca + */ + +#include "pipe/p_compiler.h" +#include "os/os_thread.h" +#include "util/u_string.h" + +#include "util/u_debug.h" +#include "u_debug_symbol.h" +#include "util/hash_table.h" + + +#if defined(PIPE_OS_WINDOWS) + +#include +#include + +#include "dbghelp.h" + + +/** + * SymInitialize() must be called once for each process (in this case, the + * current process), before any of the other functions can be called. + */ +static BOOL g_bSymInitialized = FALSE; + + +/** + * Lookup the address of a DbgHelp function. + */ +static FARPROC WINAPI +getDbgHelpProcAddress(LPCSTR lpProcName) +{ + static HMODULE hModule = NULL; + + if (!hModule) { + static boolean bail = FALSE; + + if (bail) { + return NULL; + } + +#ifdef PIPE_CC_GCC + /* + * DbgHelp does not understand the debug information generated by MinGW toolchain. + * + * mgwhelp.dll is a dbghelp.dll look-alike replacement, which is able to + * understand MinGW symbols, including on 64-bit builds. + */ + if (!hModule) { + hModule = LoadLibraryA("mgwhelp.dll"); + if (!hModule) { + _debug_printf("warning: mgwhelp.dll not found: symbol names will not be resolved\n" + "warning: download it from https://github.com/jrfonseca/drmingw/#mgwhelp\n"); + } + } +#endif + + /* + * Fallback to the real DbgHelp. + */ + if (!hModule) { + hModule = LoadLibraryA("dbghelp.dll"); + } + + if (!hModule) { + bail = TRUE; + return NULL; + } + } + + return GetProcAddress(hModule, lpProcName); +} + + +/** + * Generic macro to dispatch a DbgHelp functions. + */ +#define DBGHELP_DISPATCH(_name, _ret_type, _ret_default, _arg_types, _arg_names) \ + static _ret_type WINAPI \ + j_##_name _arg_types \ + { \ + typedef BOOL (WINAPI *PFN) _arg_types; \ + static PFN pfn = NULL; \ + if (!pfn) { \ + pfn = (PFN) getDbgHelpProcAddress(#_name); \ + if (!pfn) { \ + return _ret_default; \ + } \ + } \ + return pfn _arg_names; \ + } + +DBGHELP_DISPATCH(SymInitialize, + BOOL, 0, + (HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess), + (hProcess, UserSearchPath, fInvadeProcess)) + +DBGHELP_DISPATCH(SymSetOptions, + DWORD, FALSE, + (DWORD SymOptions), + (SymOptions)) + +DBGHELP_DISPATCH(SymFromAddr, + BOOL, FALSE, + (HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol), + (hProcess, Address, Displacement, Symbol)) + +DBGHELP_DISPATCH(SymGetLineFromAddr64, + BOOL, FALSE, + (HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line), + (hProcess, dwAddr, pdwDisplacement, Line)) + + +#undef DBGHELP_DISPATCH + + +static inline boolean +debug_symbol_name_dbghelp(const void *addr, char* buf, unsigned size) +{ + DWORD64 dwAddr = (DWORD64)(uintptr_t)addr; + HANDLE hProcess = GetCurrentProcess(); + + /* General purpose buffer, to back pSymbol and other temporary stuff. + * Must not be too memory hungry here to avoid stack overflows. + */ + CHAR buffer[512]; + + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO) buffer; + DWORD64 dwDisplacement = 0; /* Displacement of the input address, relative to the start of the symbol */ + DWORD dwLineDisplacement = 0; + IMAGEHLP_LINE64 Line; + + memset(pSymbol, 0, sizeof *pSymbol); + pSymbol->SizeOfStruct = sizeof buffer; + pSymbol->MaxNameLen = sizeof buffer - offsetof(SYMBOL_INFO, Name); + + if (!g_bSymInitialized) { + j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES); + if (j_SymInitialize(hProcess, NULL, TRUE)) { + g_bSymInitialized = TRUE; + } + } + + /* Lookup symbol name */ + if (!g_bSymInitialized || + !j_SymFromAddr(hProcess, dwAddr, &dwDisplacement, pSymbol)) { + /* + * We couldn't obtain symbol information. At least tell which module the address belongs. + */ + + HMODULE hModule = NULL; + + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (LPCTSTR)addr, + &hModule)) { + return FALSE; + } + + if (GetModuleFileNameA(hModule, buffer, sizeof buffer) == sizeof buffer) { + return FALSE; + } + snprintf(buf, size, "%p at %s+0x%lx", + addr, buffer, + (unsigned long)((uintptr_t)addr - (uintptr_t)hModule)); + + return TRUE; + } + + /* + * Try to get filename and line number. + */ + memset(&Line, 0, sizeof Line); + Line.SizeOfStruct = sizeof Line; + if (!j_SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &Line)) { + Line.FileName = NULL; + } + + if (Line.FileName) { + snprintf(buf, size, "%s at %s:%lu", pSymbol->Name, Line.FileName, Line.LineNumber); + } else { + snprintf(buf, size, "%s", pSymbol->Name); + } + + return TRUE; +} + +#endif /* PIPE_OS_WINDOWS */ + + +#if defined(HAVE_EXECINFO_H) + +#include + +/* This can only provide dynamic symbols, or binary offsets into a file. + * + * To fix this, post-process the output with tools/addr2line.sh + */ +static inline boolean +debug_symbol_name_glibc(const void *addr, char* buf, unsigned size) +{ + char** syms = backtrace_symbols((void**)&addr, 1); + if (!syms) { + return FALSE; + } + strncpy(buf, syms[0], size - 1); + buf[size - 1] = 0; + free(syms); + return TRUE; +} + +#endif /* defined(HAVE_EXECINFO_H) */ + + +void +debug_symbol_name(const void *addr, char* buf, unsigned size) +{ +#if defined(PIPE_OS_WINDOWS) + if (debug_symbol_name_dbghelp(addr, buf, size)) { + return; + } +#endif + +#if defined(HAVE_EXECINFO_H) + if (debug_symbol_name_glibc(addr, buf, size)) { + return; + } +#endif /* defined(HAVE_EXECINFO_H) */ + + snprintf(buf, size, "%p", addr); + buf[size - 1] = 0; +} + +void +debug_symbol_print(const void *addr) +{ + char buf[1024]; + debug_symbol_name(addr, buf, sizeof(buf)); + debug_printf("\t%s\n", buf); +} + +static struct hash_table* symbols_hash; +#ifdef PIPE_OS_WINDOWS +static mtx_t symbols_mutex; +#else +static mtx_t symbols_mutex = _MTX_INITIALIZER_NP; +#endif + +const char* +debug_symbol_name_cached(const void *addr) +{ + const char* name; +#ifdef PIPE_OS_WINDOWS + static boolean first = TRUE; + + if (first) { + (void) mtx_init(&symbols_mutex, mtx_plain); + first = FALSE; + } +#endif + + mtx_lock(&symbols_mutex); + if(!symbols_hash) + symbols_hash = _mesa_pointer_hash_table_create(NULL); + struct hash_entry *entry = _mesa_hash_table_search(symbols_hash, addr); + if (!entry) { + char buf[1024]; + debug_symbol_name(addr, buf, sizeof(buf)); + name = strdup(buf); + + entry = _mesa_hash_table_insert(symbols_hash, addr, (void*)name); + } + mtx_unlock(&symbols_mutex); + return entry->data; +} diff --git a/src/util/u_debug_symbol.h b/src/util/u_debug_symbol.h new file mode 100644 index 00000000000..b247706c2a0 --- /dev/null +++ b/src/util/u_debug_symbol.h @@ -0,0 +1,58 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_DEBUG_SYMBOL_H_ +#define U_DEBUG_SYMBOL_H_ + + +/** + * @file + * Symbol lookup. + * + * @author Jose Fonseca + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +void +debug_symbol_name(const void *addr, char* buf, unsigned size); + +const char* +debug_symbol_name_cached(const void *addr); + +void +debug_symbol_print(const void *addr); + +#ifdef __cplusplus +} +#endif + +#endif /* U_DEBUG_SYMBOL_H_ */