[Mesa-dev] [PATCH] util: Add backtrace support based on gallium u_debug_stack

Jordan Justen jordan.l.justen at intel.com
Fri Dec 1 23:08:39 UTC 2017


Only the libunwind support is retained from u_debug_stack.

Signed-off-by: Jordan Justen <jordan.l.justen at intel.com>
---
 src/util/Makefile.am      |   4 +-
 src/util/Makefile.sources |   2 +
 src/util/backtrace.c      | 223 ++++++++++++++++++++++++++++++++++++++++++++++
 src/util/backtrace.h      |  55 ++++++++++++
 src/util/meson.build      |   2 +
 5 files changed, 285 insertions(+), 1 deletion(-)
 create mode 100644 src/util/backtrace.c
 create mode 100644 src/util/backtrace.h

diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index a5241ad27ba..25ed47ab248 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -42,6 +42,7 @@ libmesautil_la_CPPFLAGS = \
 	-I$(top_srcdir)/src/gallium/include \
 	-I$(top_srcdir)/src/gallium/auxiliary \
 	$(VISIBILITY_CFLAGS) \
+	$(LIBUNWIND_CFLAGS) \
 	$(MSVC2013_COMPAT_CFLAGS) \
 	$(ZLIB_CFLAGS)
 
@@ -52,7 +53,8 @@ libmesautil_la_SOURCES = \
 libmesautil_la_LIBADD = \
 	$(CLOCK_LIB) \
 	$(ZLIB_LIBS) \
-	$(LIBATOMIC_LIBS)
+	$(LIBATOMIC_LIBS) \
+	$(LIBUNWIND_LIBS)
 
 libxmlconfig_la_SOURCES = $(XMLCONFIG_FILES)
 libxmlconfig_la_CFLAGS = \
diff --git a/src/util/Makefile.sources b/src/util/Makefile.sources
index 104ecae8ed3..39e67bd6f44 100644
--- a/src/util/Makefile.sources
+++ b/src/util/Makefile.sources
@@ -1,4 +1,6 @@
 MESA_UTIL_FILES := \
+	backtrace.c \
+	backtrace.h \
 	bitscan.c \
 	bitscan.h \
 	bitset.h \
diff --git a/src/util/backtrace.c b/src/util/backtrace.c
new file mode 100644
index 00000000000..df629c877ac
--- /dev/null
+++ b/src/util/backtrace.c
@@ -0,0 +1,223 @@
+/*
+ * 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 at vmware.com>
+ */
+
+#include "backtrace.h"
+
+#if defined(HAVE_LIBUNWIND)
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+/**
+ * Represent a frame from a stack backtrace.
+ */
+#ifdef HAVE_LIBUNWIND
+struct debug_stack_frame
+{
+   unw_word_t start_ip;
+   unsigned int off;
+   const char *procname;
+};
+#else
+struct debug_stack_frame;
+#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);
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <dlfcn.h>
+
+#include "u_thread.h"
+#include "hash_table.h"
+
+static struct hash_table* symbols_hash = NULL;;
+static mtx_t symbols_mutex = _MTX_INITIALIZER_NP;
+
+static const char *
+symbol_name_cached(unw_cursor_t *cursor, unw_proc_info_t *pip)
+{
+   void *addr = (void *)(uintptr_t)pip->start_ip;
+   struct hash_entry *entry;
+   char *name;
+
+   mtx_lock(&symbols_mutex);
+   if(!symbols_hash)
+      symbols_hash = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
+                                             _mesa_key_pointer_equal);
+   entry = _mesa_hash_table_search(symbols_hash, addr);
+   if(entry) {
+      name = entry->data;
+   } else {
+      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;
+      }
+
+      asprintf(&name, "%s%s", procname, ret == -UNW_ENOMEM ? "..." : "");
+
+      _mesa_hash_table_insert(symbols_hash, addr, name);
+   }
+   mtx_unlock(&symbols_mutex);
+
+   return name;
+}
+
+static 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 "?";
+}
+
+static 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]));
+   }
+}
+
+bool
+backtrace_available(void)
+{
+   return true;
+}
+
+void
+fput_backtrace(FILE *f, unsigned max_frames)
+{
+   if (max_frames == 0)
+      max_frames = 100;
+
+   struct debug_stack_frame *frames = calloc(max_frames, sizeof(*frames));
+   debug_backtrace_capture(frames, 1, max_frames);
+   debug_backtrace_print(f, frames, max_frames);
+   free(frames);
+}
+
+#else /* ! HAVE_LIBUNWIND */
+
+bool
+backtrace_available(void)
+{
+   return false;
+}
+
+void
+fput_backtrace(FILE *f, unsigned max_frames)
+{
+}
+
+#endif /* HAVE_LIBUNWIND */
diff --git a/src/util/backtrace.h b/src/util/backtrace.h
new file mode 100644
index 00000000000..7b8b6a3bb00
--- /dev/null
+++ b/src/util/backtrace.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef BACKTRACE_H_INCLUDED
+#define BACKTRACE_H_INCLUDED
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Returns `true` if a backtrace can be captured.
+ */
+bool
+backtrace_available(void);
+
+/**
+ * Write a maximum of `max_frames` backtrace frames to file `f`.
+ *
+ * If `max_frames` is 0, then all frames will be written.
+ *
+ * If `backtrace_available` returns false, this function will have no effect
+ * if called.
+ */
+void
+fput_backtrace(FILE *f, unsigned max_frames);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BACKTRACE_H_INCLUDED */
diff --git a/src/util/meson.build b/src/util/meson.build
index b54c20796ff..c59cd964735 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -23,6 +23,8 @@ inc_util = include_directories('.')
 subdir('xmlpool')
 
 files_mesa_util = files(
+  'backtrace.c',
+  'backtrace.h',
   'bitscan.c',
   'bitscan.h',
   'bitset.h',
-- 
2.15.0



More information about the mesa-dev mailing list