[PATCH 7/9] unwind: add libdw as a unwinder

Masatake YAMATO yamato at redhat.com
Tue Mar 13 17:28:12 UTC 2018


* configure.ac: Add --with-libdw option.
(USE_LIBDW): New definition of config.h and also
new condition of Makefile.am
(libdw_CFLAGS, libdw_LIBS): New substations in Makefile.am.

* Makefile.am (USE_LIBDW): New condition. libdw related compiler
flags are set here.
(strace_SOURCES): add unwind-libdw.c in USE_LIBDW condition.

* unwind-libdw.c: New source file to use libdw as unwinder.

* unwind.c (unwinders): Add libdw as unwinder.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 Makefile.am        |   6 ++
 configure.ac       |  39 ++++++++++
 stacktrace-libdw.c |   0
 unwind-libdw.c     | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 unwind.c           |   7 ++
 5 files changed, 255 insertions(+)
 create mode 100644 stacktrace-libdw.c
 create mode 100644 unwind-libdw.c

diff --git a/Makefile.am b/Makefile.am
index a4d274a5..1d5b39c2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -356,6 +356,12 @@ strace_LDFLAGS += $(libunwind_LDFLAGS)
 strace_LDADD += $(libunwind_LIBS)
 endif
 
+if USE_LIBDW
+strace_SOURCES += unwind-libdw.c
+strace_CPPFLAGS += $(libdw_CFLAGS)
+strace_LDADD += $(libdw_LIBS)
+endif
+
 @CODE_COVERAGE_RULES@
 CODE_COVERAGE_BRANCH_COVERAGE = 1
 CODE_COVERAGE_GENHTML_OPTIONS = $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \
diff --git a/configure.ac b/configure.ac
index a1f15444..cc974753 100644
--- a/configure.ac
+++ b/configure.ac
@@ -816,6 +816,45 @@ AC_CHECK_TOOL([READELF], [readelf])
 dnl stack trace
 can_unwind=no
 
+dnl stack trace with 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 env vars instead of giving path to --with-libdw])
+	     ;;
+	     esac],
+	    [with_libdw=check]
+)
+
+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, [Use libdw for stack tracing])
+	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
 libunwind_CPPFLAGS=
 libunwind_LDFLAGS=
diff --git a/stacktrace-libdw.c b/stacktrace-libdw.c
new file mode 100644
index 00000000..e69de29b
diff --git a/unwind-libdw.c b/unwind-libdw.c
new file mode 100644
index 00000000..f46f5ca1
--- /dev/null
+++ b/unwind-libdw.c
@@ -0,0 +1,203 @@
+/*
+ * This file is mostly based on git commit
+ * dfefa9f057857735a073ea655f5cb34351032c8e
+ * of ltrace submitted by Mark Wielaard <mjw at redhat.com>
+ *
+ * https://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e
+ *
+ * The commit changes proc.c and output.c files of ltrace
+ */
+
+/*
+ * Copyright notice of output.c
+ */
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012,2013,2014 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Paul Gilliam, IBM Corporation
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/*
+ * Copyright notice of proc.c
+ */
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012,2013,2014 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1998,2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#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 = dwfl_begin(&proc_callbacks);
+	if (dwfl == NULL)
+		error_msg_and_die("Couldn't initialize libdwfl unwinding "
+				  "for process %d: %s\n", tcp->pid,
+				  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_and_die("Couldn't initialize libdwfl unwinding "
+				  "for process %d: %s\n", tcp->pid, msg);
+
+	return dwfl;
+}
+
+static void
+tcb_fin(struct tcb *tcp)
+{
+	Dwfl *dwfl = tcp->unwind_ctx;
+
+	dwfl_end(dwfl);
+}
+
+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))
+		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 1;
+
+	return 0;
+}
+
+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,
+	};
+	if (dwfl_getthread_frames(tcp->unwind_ctx, tcp->pid,
+				  frame_callback, &user_data) < 0) {
+		if (user_data.stack_depth == 256)
+			error_action(data, "too many stack frames", 0);
+		else
+			perror_msg("Can't walk the stack of process %d: %s",
+				   tcp->pid, dwfl_errmsg(-1));
+
+	}
+}
+
+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_and_die("Error at dwfl_linux_proc_report pid %lld: %s",
+				  (long long)tcp->pid, dwfl_errmsg(-1));
+	else if (r > 0)
+		error_msg_and_die("Error at dwfl_linux_proc_report pid %lld",
+				  (long long)tcp->pid);
+
+	if (dwfl_report_end(dwfl, NULL, NULL) != 0)
+		error_msg_and_die("dwfl_report_end: %s", dwfl_errmsg(-1));
+}
+
+const struct unwind_unwinder_t unwinder_libdw = {
+	.name = "libdw",
+	.init = NULL,
+	.tcb_init = tcb_init,
+	.tcb_fin = tcb_fin,
+	.tcb_walk = tcb_walk,
+	.tcb_flush_cache = tcb_flush_cache,
+};
diff --git a/unwind.c b/unwind.c
index 845e2d20..f3eb8ef6 100644
--- a/unwind.c
+++ b/unwind.c
@@ -58,10 +58,17 @@ static const char asprintf_error_str[] = "???";
 extern const struct unwind_unwinder_t unwinder_libunwind;
 #endif
 
+#ifdef USE_LIBDW
+extern const struct unwind_unwinder_t unwinder_libdw;
+#endif
+
 static const struct unwind_unwinder_t *unwinders[] = {
 #ifdef USE_LIBUNWIND
 	&unwinder_libunwind,
 #endif
+#ifdef USE_LIBDW
+	&unwinder_libdw,
+#endif
 };
 
 static const struct unwind_unwinder_t *unwinder;
-- 
2.14.3



More information about the Strace-devel mailing list