gst: Use libunwind/libdw to generate backtraces if avalaible
authorThibault Saunier <thibault.saunier@osg.samsung.com>
Fri, 7 Oct 2016 09:38:27 +0000 (11:38 +0200)
committerThibault Saunier <thibault.saunier@osg.samsung.com>
Fri, 4 Nov 2016 17:22:05 +0000 (14:22 -0300)
Making the gst_debug_print_trace function more generally useful.

API:
  + gst_debug_get_trace

https://bugzilla.gnome.org/show_bug.cgi?id=772555

config.h.meson
configure.ac
docs/gst/gstreamer-sections.txt
gst/Makefile.am
gst/gstinfo.c
gst/gstinfo.h
gst/meson.build
meson.build
plugins/tracers/meson.build

index 15a5de7..5d01206 100644 (file)
 #mesondefine HAVE_DECL_STRSIGNAL
 #mesondefine HAVE_GETRUSAGE
 #mesondefine HAVE_SYS_RESOURCE_H
+#mesondefine HAVE_UNWIND
+#mesondefine HAVE_DW
+#mesondefine HAVE_BACKTRACE
index 17f0d59..350404a 100644 (file)
@@ -828,6 +828,15 @@ AM_CONDITIONAL(HAVE_GTK, test "x$HAVE_GTK" = "xyes")
 
 dnl libunwind is optionally used by the leaks tracer
 PKG_CHECK_MODULES(UNWIND, libunwind, HAVE_UNWIND=yes, HAVE_UNWIND=no)
+if test "x$HAVE_UNWIND" = "xyes"; then
+  AC_DEFINE(HAVE_UNWIND, 1, [libunwind available])
+fi
+
+dnl libdw is optionally used to add source lines and numbers to backtraces
+PKG_CHECK_MODULES(DW, libdw, HAVE_DW=yes, HAVE_DW=no)
+if test "x$HAVE_DW" = "xyes"; then
+  AC_DEFINE(HAVE_DW, 1, [libdw available])
+fi
 
 dnl Check for backtrace() from libc
 AC_CHECK_FUNC(backtrace, [
@@ -836,10 +845,6 @@ AC_CHECK_FUNC(backtrace, [
   ], [], [])
 ])
 
-if test "x$HAVE_UNWIND" = "xyes"; then
-  AC_DEFINE(HAVE_UNWIND, 1, [libunwind available])
-fi
-
 dnl building of unit test libraries
 AC_ARG_ENABLE(check,
   AS_HELP_STRING([--disable-check],[disable building unit test libraries]),
@@ -1161,6 +1166,7 @@ Configuration
        Unit testing support       : ${BUILD_CHECK}
        PTP clock support          : ${HAVE_PTP}
        libunwind support          : ${HAVE_UNWIND}
+       libdw support              : ${HAVE_DW}
 
        Debug                      : ${USE_DEBUG}
        Profiling                  : ${USE_PROFILING}
index 9355155..1f6a52a 100644 (file)
@@ -1379,6 +1379,7 @@ GST_DEBUG_FUNCPTR_NAME
 GST_DEBUG_BIN_TO_DOT_FILE
 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS
 gst_debug_print_stack_trace
+gst_debug_get_stack_trace
 GST_TIME_FORMAT
 GST_TIME_ARGS
 GST_STIME_FORMAT
index ffc10cc..91e0e70 100644 (file)
@@ -141,6 +141,8 @@ libgstreamer_@GST_API_VERSION@_la_CFLAGS =          \
        -DGST_API_VERSION=\""$(GST_API_VERSION)"\"      \
        -DGST_DISABLE_DEPRECATED                        \
        $(VALGRIND_CFLAGS)                              \
+       $(UNWIND_CFLAGS)                                \
+       $(DW_CFLAGS)                            \
        $(GST_ALL_CFLAGS)
 
 libgstreamer_@GST_API_VERSION@_la_LIBADD =             \
@@ -149,6 +151,8 @@ libgstreamer_@GST_API_VERSION@_la_LIBADD =          \
        $(GST_ALL_LIBS)                                 \
        $(WIN32_LIBS)                                   \
        $(SOCKET_LIBS)                                  \
+       $(UNWIND_LIBS)                                  \
+       $(DW_LIBS)                                      \
        $(LIBM)
 
 libgstreamer_@GST_API_VERSION@_la_LDFLAGS =            \
index ba2c407..ea1c41b 100644 (file)
@@ -90,7 +90,6 @@
 #undef gst_debug_add_log_function
 
 #ifndef GST_DISABLE_GST_DEBUG
-
 #ifdef HAVE_DLFCN_H
 #  include <dlfcn.h>
 #endif
 
 static char *gst_info_printf_pointer_extension_func (const char *format,
     void *ptr);
+#else /* GST_DISABLE_GST_DEBUG */
 
+#include <glib/gprintf.h>
 #endif /* !GST_DISABLE_GST_DEBUG */
 
+#ifdef HAVE_UNWIND
+/* No need for remote debugging so turn on the 'local only' optimizations in
+ * libunwind */
+#define UNW_LOCAL_ONLY
+
+#include <libunwind.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_DW
+#include <elfutils/libdwfl.h>
+#endif /* HAVE_DW */
+#endif /* HAVE_UNWIND */
+
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#define BT_BUF_SIZE 100
+#endif /* HAVE_BACKTRACE */
+
 extern gboolean gst_is_initialized (void);
 
 /* we want these symbols exported even if debug is disabled, to maintain
@@ -2253,7 +2277,6 @@ _gst_debug_dump_mem (GstDebugCategory * cat, const gchar * file,
  * fallback function that cleans up the format string and replaces all pointer
  * extension formats with plain %p. */
 #ifdef GST_DISABLE_GST_DEBUG
-#include <glib/gprintf.h>
 int
 __gst_info_fallback_vasprintf (char **result, char const *format, va_list args)
 {
@@ -2412,15 +2435,16 @@ __cyg_profile_func_exit (void *this_fn, void *call_site)
 }
 
 /**
- * gst_debug_print_stack_trace:
+ * gst_debug_get_stack_trace:
  *
- * If GST_ENABLE_FUNC_INSTRUMENTATION is defined a stacktrace is available for
- * gstreamer code, which can be printed with this function.
+ * If GST_ENABLE_FUNC_INSTRUMENTATION is defined or libunwind or
+ * glibc backtrace are present, a stack trace is return.
  */
-void
-gst_debug_print_stack_trace (void)
+gchar *
+gst_debug_get_stack_trace (void)
 {
   GSList *walk = stack_trace;
+  GString *trace = g_string_new (NULL);
   gint count = 0;
 
   if (walk)
@@ -2429,16 +2453,162 @@ gst_debug_print_stack_trace (void)
   while (walk) {
     gchar *name = (gchar *) walk->data;
 
-    g_print ("#%-2d %s\n", count++, name);
+    g_string_append (trace, "#%-2d %s\n", count++, name);
 
     walk = g_slist_next (walk);
   }
+
+  return g_string_free (trace, FALSE);
 }
+
 #else
-void
-gst_debug_print_stack_trace (void)
+
+#ifdef HAVE_UNWIND
+#ifdef HAVE_DW
+static gboolean
+append_debug_info (GString * trace, const void *ip)
+{
+  Dwfl *dwfl;
+  Dwfl_Line *line;
+  Dwarf_Addr addr;
+  Dwfl_Module *module;
+  const gchar *function_name;
+  gchar *debuginfo_path = NULL;
+  Dwfl_Callbacks callbacks = {
+    .find_elf = dwfl_linux_proc_find_elf,
+    .find_debuginfo = dwfl_standard_find_debuginfo,
+    .debuginfo_path = &debuginfo_path,
+  };
+
+  dwfl = dwfl_begin (&callbacks);
+  if (!dwfl)
+    return FALSE;
+
+  if (dwfl_linux_proc_report (dwfl, getpid ()) != 0)
+    return FALSE;
+
+  if (dwfl_report_end (dwfl, NULL, NULL))
+    return FALSE;
+
+  addr = (uintptr_t) ip;
+  module = dwfl_addrmodule (dwfl, addr);
+  function_name = dwfl_module_addrname (module, addr);
+
+  g_string_append_printf (trace, "%s(", function_name ? function_name : "??");
+
+  line = dwfl_getsrc (dwfl, addr);
+  if (line != NULL) {
+    gint nline;
+    Dwarf_Addr addr;
+    const gchar *filename = dwfl_lineinfo (line, &addr,
+        &nline, NULL, NULL, NULL);
+
+    g_string_append_printf (trace, "%s:%d", strrchr (filename,
+            G_DIR_SEPARATOR) + 1, nline);
+  } else {
+    const gchar *eflfile = NULL;
+
+    dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, &eflfile, NULL);
+    g_string_append_printf (trace, "%s:%p", eflfile ? eflfile : "??", ip);
+  }
+
+  return TRUE;
+}
+#endif /* HAVE_DW */
+
+static gchar *
+generate_unwind_trace (void)
 {
-  /* nothing because it's compiled out */
+  unw_context_t uc;
+  unw_cursor_t cursor;
+  gboolean use_libunwind = TRUE;
+  GString *trace = g_string_new (NULL);
+
+  unw_getcontext (&uc);
+  unw_init_local (&cursor, &uc);
+
+  while (unw_step (&cursor) > 0) {
+#ifdef HAVE_DW
+    unw_word_t ip;
+
+    unw_get_reg (&cursor, UNW_REG_IP, &ip);
+    if (append_debug_info (trace, (void *) (ip - 4))) {
+      use_libunwind = FALSE;
+      g_string_append (trace, ")\n");
+    }
+#endif /* HAVE_DW */
+
+    if (use_libunwind) {
+      char name[32];
+
+      unw_word_t offset = 0;
+      unw_get_proc_name (&cursor, name, sizeof (name), &offset);
+      g_string_append_printf (trace, "%s (0x%" G_GSIZE_FORMAT ")\n", name,
+          (gsize) offset);
+    }
+  }
+
+  return g_string_free (trace, FALSE);
+}
+
+#endif /* HAVE_UNWIND */
+
+#ifdef HAVE_BACKTRACE
+static gchar *
+generate_backtrace_trace (void)
+{
+  int j, nptrs;
+  void *buffer[BT_BUF_SIZE];
+  char **strings;
+  GString *trace;
+
+  trace = g_string_new (NULL);
+  nptrs = backtrace (buffer, BT_BUF_SIZE);
+
+  strings = backtrace_symbols (buffer, nptrs);
+
+  if (!strings)
+    return NULL;
+
+  for (j = 0; j < nptrs; j++)
+    g_string_append_printf (trace, "%s\n", strings[j]);
+
+  return g_string_free (trace, FALSE);
 }
+#endif /* HAVE_BACKTRACE */
+
+gchar *
+gst_debug_get_stack_trace (void)
+{
+  gchar *trace = NULL;
+
+#ifdef HAVE_UNWIND
+  trace = generate_unwind_trace ();
+  if (trace)
+    return trace;
+#endif /* HAVE_UNWIND */
 
+#ifdef HAVE_BACKTRACE
+  trace = generate_backtrace_trace ();
+#endif /* HAVE_BACKTRACE */
+
+  return trace;
+}
 #endif /* GST_ENABLE_FUNC_INSTRUMENTATION */
+
+/**
+ * gst_debug_print_stack_trace:
+ *
+ * If GST_ENABLE_FUNC_INSTRUMENTATION is defined or libunwind or
+ * glibc backtrace are present, a stack trace is printed.
+ */
+void
+gst_debug_print_stack_trace (void)
+{
+  gchar *trace = gst_debug_get_stack_trace ();
+
+  if (trace)
+    g_print ("%s\n", trace);
+
+  g_free (trace);
+}
index d2fd7e4..d274fe0 100644 (file)
@@ -1564,6 +1564,7 @@ GST_TRACE (const char *format, ...)
 
 
 void gst_debug_print_stack_trace (void);
+gchar * gst_debug_get_stack_trace (void);
 
 G_END_DECLS
 
index 11b4c06..697163e 100644 (file)
@@ -202,7 +202,7 @@ if libtype != 'shared'
       include_directories('parse')],
     install : true,
     link_with : printf_lib,
-    dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib] + platform_deps,
+    dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib, unwind_dep, dw_dep] + platform_deps,
   )
   libgst = libgst_static
 endif
@@ -221,7 +221,7 @@ if libtype != 'static'
       include_directories('parse')],
     link_with : printf_lib,
     install : true,
-    dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib] + platform_deps,
+    dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib, unwind_dep, dw_dep] + platform_deps,
     vs_module_defs: vs_module_defs_dir + 'libgstreamer.def',
   )
   libgst = libgst_shared
index c9d9e90..055d905 100644 (file)
@@ -260,6 +260,27 @@ if cc.has_function('strsignal', prefix : '#include <string.h>')
   cdata.set('HAVE_DECL_STRSIGNAL', 1)
 endif
 
+unwind_dep = dependency('libunwind', required : false)
+dw_dep = dependency('libdw', required: false)
+if unwind_dep.found()
+  cdata.set('HAVE_UNWIND', 1)
+  if dw_dep.found()
+    cdata.set('HAVE_DW', 1)
+  else
+    message('Support for backtraces is partial only.')
+  endif
+else
+  if cc.has_function('backtrace')
+    cdata.set('HAVE_BACKTRACE', 1)
+  else
+      message('NO backtraces support.')
+  endif
+endif
+
+if cc.has_header('execinfo.h') and cc.has_function('backtrace', prefix : '#include <execinfo.h>')
+  cdata.set('HAVE_BACKTRACE', 1)
+endif
+
 configure_file(input : 'config.h.meson',
   output : 'config.h',
   configuration : cdata)
index 4ca2733..0cc9a57 100644 (file)
@@ -12,15 +12,6 @@ endif
 
 tracers_args = gst_c_args + ['-DGST_USE_UNSTABLE_API']
 
-unwind_dep = dependency('libunwind', required : false)
-if unwind_dep.found()
-  tracers_args += ['-DHAVE_UNWIND']
-endif
-
-if cc.has_header('execinfo.h') and cc.has_function('backtrace', prefix : '#include <execinfo.h>')
-  tracers_args += ['-DHAVE_BACKTRACE']
-endif
-
 gst_tracers = library('gstcoretracers',
   gst_tracers_sources,
   c_args : tracers_args,