[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