[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