From a8d4857555ec649a7b50d46fa5fefcb66ae65d14 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 7 Oct 2016 11:38:27 +0200 Subject: [PATCH] gst: Use libunwind/libdw to generate backtraces if avalaible 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 | 3 + configure.ac | 14 ++- docs/gst/gstreamer-sections.txt | 1 + gst/Makefile.am | 4 + gst/gstinfo.c | 192 +++++++++++++++++++++++++++++++++++++--- gst/gstinfo.h | 1 + gst/meson.build | 4 +- meson.build | 21 +++++ plugins/tracers/meson.build | 9 -- 9 files changed, 223 insertions(+), 26 deletions(-) diff --git a/config.h.meson b/config.h.meson index 15a5de7..5d01206 100644 --- a/config.h.meson +++ b/config.h.meson @@ -438,3 +438,6 @@ #mesondefine HAVE_DECL_STRSIGNAL #mesondefine HAVE_GETRUSAGE #mesondefine HAVE_SYS_RESOURCE_H +#mesondefine HAVE_UNWIND +#mesondefine HAVE_DW +#mesondefine HAVE_BACKTRACE diff --git a/configure.ac b/configure.ac index 17f0d59..350404a 100644 --- a/configure.ac +++ b/configure.ac @@ -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} diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index 9355155..1f6a52a 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -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 diff --git a/gst/Makefile.am b/gst/Makefile.am index ffc10cc..91e0e70 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -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 = \ diff --git a/gst/gstinfo.c b/gst/gstinfo.c index ba2c407..ea1c41b 100644 --- a/gst/gstinfo.c +++ b/gst/gstinfo.c @@ -90,7 +90,6 @@ #undef gst_debug_add_log_function #ifndef GST_DISABLE_GST_DEBUG - #ifdef HAVE_DLFCN_H # include #endif @@ -127,9 +126,34 @@ static char *gst_info_printf_pointer_extension_func (const char *format, void *ptr); +#else /* GST_DISABLE_GST_DEBUG */ +#include #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 +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DW +#include +#endif /* HAVE_DW */ +#endif /* HAVE_UNWIND */ + +#ifdef HAVE_BACKTRACE +#include +#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 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); +} diff --git a/gst/gstinfo.h b/gst/gstinfo.h index d2fd7e4..d274fe0 100644 --- a/gst/gstinfo.h +++ b/gst/gstinfo.h @@ -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 diff --git a/gst/meson.build b/gst/meson.build index 11b4c06..697163e 100644 --- a/gst/meson.build +++ b/gst/meson.build @@ -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 diff --git a/meson.build b/meson.build index c9d9e90..055d905 100644 --- a/meson.build +++ b/meson.build @@ -260,6 +260,27 @@ if cc.has_function('strsignal', prefix : '#include ') 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 ') + cdata.set('HAVE_BACKTRACE', 1) +endif + configure_file(input : 'config.h.meson', output : 'config.h', configuration : cdata) diff --git a/plugins/tracers/meson.build b/plugins/tracers/meson.build index 4ca2733..0cc9a57 100644 --- a/plugins/tracers/meson.build +++ b/plugins/tracers/meson.build @@ -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 ') - tracers_args += ['-DHAVE_BACKTRACE'] -endif - gst_tracers = library('gstcoretracers', gst_tracers_sources, c_args : tracers_args, -- 2.7.4