util: Move stack debug functions to src/util
authorKristian H. Kristensen <hoegsberg@google.com>
Wed, 29 Jul 2020 05:02:31 +0000 (22:02 -0700)
committerMarge Bot <eric+marge@anholt.net>
Wed, 5 Aug 2020 18:08:06 +0000 (18:08 +0000)
Reviewed-by: Eric Engestrom <eric@engestrom.ch>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6112>

13 files changed:
meson.build
src/gallium/auxiliary/meson.build
src/gallium/auxiliary/util/u_debug_stack.c [deleted file]
src/gallium/auxiliary/util/u_debug_stack.h [deleted file]
src/gallium/auxiliary/util/u_debug_stack_android.cpp [deleted file]
src/gallium/auxiliary/util/u_debug_symbol.c [deleted file]
src/gallium/auxiliary/util/u_debug_symbol.h [deleted file]
src/util/meson.build
src/util/u_debug_stack.c [new file with mode: 0644]
src/util/u_debug_stack.h [new file with mode: 0644]
src/util/u_debug_stack_android.cpp [new file with mode: 0644]
src/util/u_debug_symbol.c [new file with mode: 0644]
src/util/u_debug_symbol.h [new file with mode: 0644]

index 4b57cf326ec4ec811125bd90a4d1e4acea071e84..2dabd8188e2dc9a3dbb3a35d54efca6f499bafe6 100644 (file)
@@ -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
index 76dd0e1f4a5a65db24812ec24a4b301332d8b712..a1a1b5b64e6cea54ccf4e7526b32826398f0edb2 100644 (file)
@@ -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 (file)
index 21f0371..0000000
+++ /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 <jfonseca@vmware.com>
- */
-
-#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 <dlfcn.h>
-
-#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 <windows.h>
-#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 (file)
index fff41a5..0000000
+++ /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 <stdio.h>
-
-#ifdef HAVE_LIBUNWIND
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-#endif
-
-/**
- * @file
- * Stack backtracing.
- * 
- * @author Jose Fonseca <jfonseca@vmware.com>
- */
-
-
-#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 (file)
index 879b0fb..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2018 Stefan Schake <stschake@gmail.com>
- *
- * 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 <backtrace/Backtrace.h>
-
-#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 (file)
index 73225e9..0000000
+++ /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 <jfonseca@vmware.com>
- */
-
-#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 <windows.h>
-#include <stddef.h>
-
-#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 <execinfo.h>
-
-/* 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 (file)
index b247706..0000000
+++ /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 <jfonseca@vmware.com>
- */
-
-
-#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_ */
index 25c363ab95c6986592d61699c72f9cec92f5e33b..909b3e074683325be78c01689d83abd7bc9ed707 100644 (file)
@@ -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 (file)
index 0000000..21f0371
--- /dev/null
@@ -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 <jfonseca@vmware.com>
+ */
+
+#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 <dlfcn.h>
+
+#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 <windows.h>
+#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 (file)
index 0000000..fff41a5
--- /dev/null
@@ -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 <stdio.h>
+
+#ifdef HAVE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
+/**
+ * @file
+ * Stack backtracing.
+ * 
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ */
+
+
+#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 (file)
index 0000000..879b0fb
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 Stefan Schake <stschake@gmail.com>
+ *
+ * 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 <backtrace/Backtrace.h>
+
+#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 (file)
index 0000000..73225e9
--- /dev/null
@@ -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 <jfonseca@vmware.com>
+ */
+
+#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 <windows.h>
+#include <stddef.h>
+
+#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 <execinfo.h>
+
+/* 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 (file)
index 0000000..b247706
--- /dev/null
@@ -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 <jfonseca@vmware.com>
+ */
+
+
+#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_ */