[PATCH 2/2] unwind: add libdw as a alternative unwinder back-end

Masatake YAMATO yamato at redhat.com
Sun Apr 8 18:06:16 UTC 2018


* unwind-libdw.c: New source file.

* configure.ac: New option --with-libdw.
  Accept only one of --with-libdw=yes or --with-libunwind=yes.
  Don't check libunwind if libdw is available.
  (USE_LIBDW): Define.
  (AC_SUBST): Add libdw_CFLAGS and libdw_LIBS.

* Makefile.am (USE_LIBDW): New condition.
[USE_LIBUNWIND] (strace_SOURCES): Add unwind-libdw.c.
[USE_LIBUNWIND] (strace_CPPFLAGS, strace_LDADD): Append libdw_CFLAGS,
and strace_LDADD respectively.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
Signed-off-by: Mark Wielaard <mjw at redhat.com>
---
 Makefile.am    |   6 ++
 configure.ac   |  65 +++++++++++++++++++++-
 unwind-libdw.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 241 insertions(+), 3 deletions(-)
 create mode 100644 unwind-libdw.c

diff --git a/Makefile.am b/Makefile.am
index b16c4afd..6b21116a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -358,6 +358,12 @@ strace_LDADD += $(libiberty_LIBS)
 endif
 endif
 
+if USE_LIBDW
+strace_SOURCES += unwind-libdw.c
+strace_CPPFLAGS += $(libdw_CFLAGS)
+strace_LDADD += $(libdw_LIBS)
+endif
+
 if USE_LIBUNWIND
 strace_SOURCES += unwind-libunwind.c
 strace_CPPFLAGS += $(libunwind_CPPFLAGS)
diff --git a/configure.ac b/configure.ac
index fa1c2360..9724125e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -844,7 +844,25 @@ AC_PATH_PROG([PERL], [perl])
 
 AC_CHECK_TOOL([READELF], [readelf])
 
-dnl stack trace with libunwind
+dnl stack trace
+can_unwind=no
+
+dnl option processing for libdw
+libdw_CFLAGS=
+libdw_LIBS=
+AC_ARG_WITH([libdw],
+	    [AS_HELP_STRING([--with-libdw],
+			    [use libdw to implement stack tracing support])],
+	    [case "${withval}" in
+	     yes|no|check) ;;
+	     *)
+	     AC_MSG_FAILURE([use pkg-config related environment variables instead of giving a path to --with-libdw option])
+	     ;;
+	     esac],
+	    [with_libdw=check]
+)
+
+dnl option processing for libunwind
 libunwind_CPPFLAGS=
 libunwind_LDFLAGS=
 libunwind_LIBS=
@@ -860,8 +878,49 @@ AC_ARG_WITH([libunwind],
 	    [with_libunwind=check]
 )
 
+if test x"${with_libunwind}" = xyes && test x"${with_libdw}" = xyes; then
+	AC_MSG_ERROR([Specify either libunwind or libdw])
+fi
+
+if test x"${with_libunwind}" = xyes; then
+	with_libdw=no
+elif test x"${with_libdw}" = xyes; then
+	with_libunwind=no
+fi
+
+dnl stack trace with libdw
+
+dnl check libdw
+use_libdw=no
+PKG_PROG_PKG_CONFIG
+AS_IF([test "x$with_libdw" != xno],
+      [PKG_CHECK_MODULES(libdw,libdw,
+       [use_libdw=yes],
+       [if test "x$with_libdw" == xyes; then
+         AC_MSG_FAILURE([failed to find libdw])
+        fi
+       ]
+      )]
+)
+
+dnl enable libdw
+AC_MSG_CHECKING([whether to enable stack tracing support using libdw])
+if test "x$use_libdw" = xyes; then
+	AC_DEFINE([USE_LIBDW], 1, [Compile stack tracing functionality with libdw])
+	AC_SUBST(libdw_CFLAGS)
+	AC_SUBST(libdw_LIBS)
+	can_unwind=yes
+fi
+AM_CONDITIONAL([USE_LIBDW], [test "x$use_libdw" = xyes])
+AC_MSG_RESULT([$use_libdw])
+
+dnl stack trace with libunwind
+
+dnl check libunwind
+dnl Don't check it if --with-libunwind=no is given nor
+dnl libdw is available.
 use_libunwind=no
-AS_IF([test "x$with_libunwind" != xno],
+AS_IF([test "x$with_libunwind" != xno && test "x$use_libdw" != xyes],
       [saved_CPPFLAGS="$CPPFLAGS"
        CPPFLAGS="$CPPFLAGS $libunwind_CPPFLAGS"
 
@@ -934,7 +993,7 @@ AM_CONDITIONAL([USE_LIBUNWIND], [test "x$use_libunwind" = xyes])
 AC_MSG_RESULT([$use_libunwind])
 
 can_unwind=no
-if test "x$use_libunwind" = xyes; then
+if test "x$use_libunwind" = xyes || test "x$use_libdw" = xyes; then
 	can_unwind=yes
 	AC_DEFINE([CAN_UNWIND], 1, [Compile stack tracing functionality])
 fi
diff --git a/unwind-libdw.c b/unwind-libdw.c
new file mode 100644
index 00000000..651206da
--- /dev/null
+++ b/unwind-libdw.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2018 Mark Wielaard
+ * Copyright (c) 2018 Masatake YAMATO
+ * Copyright (c) 2018 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* This file is for using libdw as a backend unwinder.
+ * Do note that elfutils libdw itself is distributed under the GPL/LGPL.
+ */
+
+#include "defs.h"
+#include "unwind.h"
+
+#include "mmap_cache.h"
+#include <elfutils/libdwfl.h>
+
+static void *
+tcb_init(struct tcb *tcp)
+{
+	int r;
+	const char *msg = NULL;
+	static const Dwfl_Callbacks proc_callbacks = {
+		.find_elf = dwfl_linux_proc_find_elf,
+		.find_debuginfo = dwfl_standard_find_debuginfo
+	};
+
+	Dwfl *dwfl = dwfl_begin(&proc_callbacks);
+	if (dwfl == NULL)
+		error_msg_and_die("Couldn't initialize libdwfl unwinding: %s\n",
+				  dwfl_errmsg(-1));
+
+	r = dwfl_linux_proc_attach(dwfl, tcp->pid, true);
+
+	if (r < 0)
+		msg = dwfl_errmsg(-1);
+	else if (r > 0)
+		msg = strerror(r);
+
+	if (msg)
+		error_msg("Couldn't initialize libdwfl unwinding "
+			  "for process %d: %s\n", tcp->pid, msg);
+
+	return dwfl;
+}
+
+static void
+tcb_fin(struct tcb *tcp)
+{
+	dwfl_end(tcp->unwind_ctx);
+}
+
+struct frame_user_data {
+	unwind_call_action_fn call_action;
+	unwind_error_action_fn error_action;
+	void *data;
+	int stack_depth;
+};
+
+static int
+frame_callback(Dwfl_Frame *state, void *arg)
+{
+	struct frame_user_data *user_data = arg;
+	Dwarf_Addr pc;
+	bool isactivation;
+
+	int *frames = &(user_data->stack_depth);
+
+	if (!dwfl_frame_pc(state, &pc, &isactivation))
+		/* Propagate the error to the caller. */
+		return -1;
+
+	if (!isactivation)
+		pc--;
+
+	Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state));
+	Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc);
+	GElf_Off off = 0;
+
+	if (mod != NULL) {
+		const char *modname = NULL;
+		const char *symname = NULL;
+		GElf_Sym sym;
+		Dwarf_Addr true_offset = pc;
+
+		modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL,
+					   NULL, NULL, NULL);
+		symname = dwfl_module_addrinfo(mod, pc, &off, &sym,
+					       NULL, NULL, NULL);
+		dwfl_module_relocate_address(mod, &true_offset);
+		user_data->call_action(user_data->data, modname, symname,
+				       off, true_offset);
+	}
+	/* Max number of frames to print reached? */
+	if ((*frames)-- == 0)
+		return DWARF_CB_ABORT;
+
+	return DWARF_CB_OK;
+}
+
+static void
+tcb_walk(struct tcb *tcp,
+	 unwind_call_action_fn call_action,
+	 unwind_error_action_fn error_action,
+	 void *data)
+{
+	struct frame_user_data user_data = {
+		.call_action = call_action,
+		.error_action = error_action,
+		.data = data,
+		.stack_depth = 256,
+	};
+	int r = dwfl_getthread_frames(tcp->unwind_ctx, tcp->pid,
+				      frame_callback, &user_data);
+
+	if (r < 0)
+		error_action(data, dwfl_errmsg(-1), 0);
+	else if (r == DWARF_CB_ABORT)
+		error_action(data, "too many stack frames", 0);
+
+}
+
+static void
+tcb_flush_cache(struct tcb *tcp)
+{
+	int r;
+	Dwfl *dwfl = tcp->unwind_ctx;
+
+	r = dwfl_linux_proc_report(dwfl, tcp->pid);
+	if (r < 0) {
+		error_msg("Error at dwfl_linux_proc_report pid %lld: %s",
+			  (long long)tcp->pid, dwfl_errmsg(-1));
+		return;
+	}
+	else if (r > 0) {
+		error_msg("Error at dwfl_linux_proc_report pid %lld",
+				  (long long)tcp->pid);
+		return;
+	}
+
+	if (dwfl_report_end(dwfl, NULL, NULL) != 0)
+		error_msg("dwfl_report_end: %s", dwfl_errmsg(-1));
+}
+
+const struct unwind_unwinder_t unwinder = {
+	.name = "libdw",
+	.init = NULL,
+	.tcb_init = tcb_init,
+	.tcb_fin = tcb_fin,
+	.tcb_walk = tcb_walk,
+	.tcb_flush_cache = tcb_flush_cache,
+};
-- 
2.14.3



More information about the Strace-devel mailing list