diff -u ltrace-0.7.91/configure.ac ltrace-0.7.91-pm/configure.ac --- ltrace-0.7.91/configure.ac 2015-01-09 00:38:17.977190726 +0100 +++ ltrace-0.7.91-pm/configure.ac 2015-01-09 00:37:40.261910548 +0100 @@ -128,6 +128,51 @@ AC_CHECK_HEADERS(selinux/selinux.h) AC_CHECK_LIB(selinux, security_get_boolean_active) +dnl Whether (and which) elfutils libdw.so to use for unwinding. +AC_ARG_WITH(elfutils, + AS_HELP_STRING([--with-elfutils], [Use elfutils libdwfl unwinding support]), + [case "${withval}" in + (yes|no) enable_elfutils=$withval;; + (*) enable_elfutils=yes + AM_CPPFLAGS="${AM_CPPFLAGS} -I${withval}/include" + AM_LDFLAGS="${AM_LDFLAGS} -L${withval}/lib" + elfutils_LD_LIBRARY_PATH="${withval}/lib:${withval}/lib/elfutils" + ;; +esac],[enable_elfutils=maybe]) + +dnl Check whether we have the elfutils libdwfl.h header installed. +saved_CPPFLAGS="${CPPFLAGS}" +CPPFLAGS="${CPPFLAGS} ${AM_CPPFLAGS}" +AC_CHECK_HEADERS([elfutils/libdwfl.h],[have_libdwfl_h=yes]) +CPPFLAGS="${saved_CPPFLAGS}" + +dnl And whether libdw.so provides the unwinding functions. +saved_LDFLAGS="${LDFLAGS}" +LDFLAGS="${LDFLAGS} ${AM_LDFLAGS}" +AC_CHECK_LIB([dw], [dwfl_getthread_frames], [have_libdw_dwfl_frames=yes]) +LDFLAGS="${saved_LDFLAGS}" + +AC_MSG_CHECKING([whether to use elfutils libdwfl unwinding support]) +case "${enable_elfutils}" in +(yes|maybe) + if test x$have_libdwfl_h = xyes -a x$have_libdw_dwfl_frames = xyes; then + enable_elfutils=yes + elif test $enable_elfutils = maybe; then + enable_elfutils=no + else + AC_MSG_RESULT([$enable_elfutils]) + AC_MSG_ERROR([Missing elfutils/libdwfl.h or dwfl_getthread_frames not in libdw.so]) + fi + ;; +(*) ;; +esac +AC_MSG_RESULT([$enable_elfutils]) + +if test x"$enable_elfutils" = xyes; then + libdw_LIBS=-ldw + AC_SUBST(libdw_LIBS) + AC_DEFINE([HAVE_LIBDW], [1], [we have elfutils libdw]) +fi # HAVE_LIBUNWIND AC_ARG_WITH(libunwind, @@ -193,6 +238,13 @@ LDFLAGS="${saved_LDFLAGS}" fi +if test x"$enable_elfutils" = xyes -a x"$enable_libunwind" = xyes; then + AC_MSG_ERROR([Cannot enable both --with-libunwind and --with-elfutils]) +fi + +if test x"$enable_elfutils" = xyes -o x"$enable_libunwind" = xyes; then + AC_DEFINE([HAVE_UNWINDER], [1], [we have an unwinder available]) +fi saved_CPPFLAGS="${CPPFLAGS}" saved_LDFLAGS="${LDFLAGS}" @@ -340,6 +392,7 @@ AC_SUBST(AM_CFLAGS) AC_SUBST(AM_LDFLAGS) AC_SUBST(libelf_LD_LIBRARY_PATH) +AC_SUBST(elfutils_LD_LIBRARY_PATH) AC_SUBST(libunwind_LD_LIBRARY_PATH) AC_CONFIG_FILES([ diff -u ltrace-0.7.91/ltrace.1 ltrace-0.7.91-pm/ltrace.1 --- ltrace-0.7.91/ltrace.1 2015-01-09 00:38:17.975190764 +0100 +++ ltrace-0.7.91-pm/ltrace.1 2015-01-09 00:37:40.261910548 +0100 @@ -196,7 +196,8 @@ correct execution of setuid and/or setgid binaries. .IP "\-w, --where \fInr" Show backtrace of \fInr\fR stack frames for each traced function. This -option enabled only if libunwind support was enabled at compile time. +option enabled only if elfutils or libunwind support was enabled at compile +time. .IP "\-x \fIfilter" A qualifying expression which modifies which symbol table entry points to trace. The format of the filter expression is described in the diff -u ltrace-0.7.91/Makefile.am ltrace-0.7.91-pm/Makefile.am --- ltrace-0.7.91/Makefile.am 2015-01-09 00:38:17.965190955 +0100 +++ ltrace-0.7.91-pm/Makefile.am 2015-01-09 00:37:40.260910568 +0100 @@ -40,6 +40,7 @@ $(liberty_LIBS) \ $(libsupcxx_LIBS) \ $(libstdcxx_LIBS) \ + $(libdw_LIBS) \ $(libunwind_LIBS) \ sysdeps/libos.la diff -u ltrace-0.7.91/options.c ltrace-0.7.91-pm/options.c --- ltrace-0.7.91/options.c 2015-01-09 00:38:17.974190783 +0100 +++ ltrace-0.7.91-pm/options.c 2015-01-09 00:37:40.261910548 +0100 @@ -107,9 +107,9 @@ " -T show the time spent inside each call.\n" " -u USERNAME run command with the userid, groupid of username.\n" " -V, --version output version information and exit.\n" -#if defined(HAVE_LIBUNWIND) +#if defined(HAVE_UNWINDER) " -w, --where=NR print backtrace showing NR stack frames at most.\n" -#endif /* defined(HAVE_LIBUNWIND) */ +#endif /* defined(HAVE_UNWINDER) */ " -x FILTER modify which static functions to trace.\n" "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", progname); @@ -519,9 +519,9 @@ progname = argv[0]; options.output = stderr; options.no_signals = 0; -#if defined(HAVE_LIBUNWIND) +#if defined(HAVE_UNWINDER) options.bt_depth = -1; -#endif /* defined(HAVE_LIBUNWIND) */ +#endif /* defined(HAVE_UNWINDER) */ guess_cols(); @@ -545,9 +545,9 @@ {"output", 1, 0, 'o'}, {"version", 0, 0, 'V'}, {"no-signals", 0, 0, 'b'}, -# if defined(HAVE_LIBUNWIND) +# if defined(HAVE_UNWINDER) {"where", 1, 0, 'w'}, -# endif /* defined(HAVE_LIBUNWIND) */ +# endif /* defined(HAVE_UNWINDER) */ {0, 0, 0, 0} }; #endif @@ -556,7 +556,7 @@ #ifdef USE_DEMANGLE "C" #endif -#if defined(HAVE_LIBUNWIND) +#if defined(HAVE_UNWINDER) "w:" #endif "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:X:"; @@ -681,11 +681,11 @@ "There is NO WARRANTY, to the extent permitted by law.\n"); exit(0); break; -#if defined(HAVE_LIBUNWIND) +#if defined(HAVE_UNWINDER) case 'w': options.bt_depth = parse_int(optarg, 'w', 1, 0); break; -#endif /* defined(HAVE_LIBUNWIND) */ +#endif /* defined(HAVE_UNWINDER) */ case 'x': parse_filter_chain(optarg, &options.static_filter); diff -u ltrace-0.7.91/options.h ltrace-0.7.91-pm/options.h --- ltrace-0.7.91/options.h 2015-01-09 00:38:17.966190936 +0100 +++ ltrace-0.7.91-pm/options.h 2015-01-09 00:37:40.261910548 +0100 @@ -44,9 +44,9 @@ size_t strlen; /* default maximum # of bytes printed in strings */ int follow; /* trace child processes */ int no_signals; /* don't print signals */ -#if defined(HAVE_LIBUNWIND) +#if defined(HAVE_UNWINDER) int bt_depth; /* how may levels of stack frames to show */ -#endif /* defined(HAVE_LIBUNWIND) */ +#endif /* defined(HAVE_UNWINDER) */ struct filter *plt_filter; struct filter *static_filter; diff -u ltrace-0.7.91/output.c ltrace-0.7.91-pm/output.c --- ltrace-0.7.91/output.c 2015-01-09 00:38:17.966190936 +0100 +++ ltrace-0.7.91-pm/output.c 2015-01-09 00:37:40.261910548 +0100 @@ -33,6 +33,7 @@ #include #include #include +#include #include "output.h" #include "demangle.h" @@ -567,6 +568,73 @@ stel->out.need_delim = need_delim; } +#if defined(HAVE_LIBDW) +/* Prints information about one frame of a thread. Called by + dwfl_getthread_frames in output_right. Returns 1 when done (max + number of frames reached). Returns -1 on error. Returns 0 on + success (if there are more frames in the thread, call us again). */ +static int +frame_callback (Dwfl_Frame *state, void *arg) +{ + Dwarf_Addr pc; + bool isactivation; + + int *frames = (int *) arg; + + 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); + const char *modname = NULL; + const char *symname = NULL; + GElf_Off off = 0; + if (mod != NULL) { + GElf_Sym sym; + modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, + NULL, NULL, NULL); + symname = dwfl_module_addrinfo(mod, pc, &off, &sym, + NULL, NULL, NULL); + } + + /* This mimics the output produced by libunwind below. */ + fprintf(options.output, " > %s(%s+0x%" PRIx64 ") [%" PRIx64 "]\n", + modname, symname, off, pc); + + /* See if we can extract the source line too and print it on + the next line if we can find it. */ + if (mod != NULL) { + Dwfl_Line *l = dwfl_module_getsrc(mod, pc); + if (l != NULL) { + int line, col; + line = col = -1; + const char *src = dwfl_lineinfo(l, NULL, &line, &col, + NULL, NULL); + if (src != NULL) { + fprintf(options.output, "\t%s", src); + if (line > 0) { + fprintf(options.output, ":%d", line); + if (col > 0) + fprintf(options.output, + ":%d", col); + } + fprintf(options.output, "\n"); + } + + } + } + + /* Max number of frames to print reached? */ + if ((*frames)-- == 0) + return 1; + + return 0; +} +#endif /* defined(HAVE_LIBDW) */ + void output_right(enum tof type, struct process *proc, struct library_symbol *libsym, struct timedelta *spent) @@ -694,6 +762,24 @@ } #endif /* defined(HAVE_LIBUNWIND) */ +#if defined(HAVE_LIBDW) + if (options.bt_depth > 0 && proc->leader->dwfl != NULL) { + int frames = options.bt_depth; + if (dwfl_getthread_frames(proc->leader->dwfl, proc->pid, + frame_callback, &frames) < 0) { + // Only print an error if we couldn't show anything. + // Otherwise just show there might be more... + if (frames == options.bt_depth) + fprintf(stderr, + "dwfl_getthread_frames tid %d: %s\n", + proc->pid, dwfl_errmsg(-1)); + else + fprintf(options.output, " > [...]\n"); + } + fprintf(options.output, "\n"); + } +#endif /* defined(HAVE_LIBDW) */ + current_proc = NULL; current_column = 0; } diff -u ltrace-0.7.91/proc.c ltrace-0.7.91-pm/proc.c --- ltrace-0.7.91/proc.c 2015-01-09 00:38:17.981190650 +0100 +++ ltrace-0.7.91-pm/proc.c 2015-01-09 00:37:40.261910548 +0100 @@ -111,6 +111,11 @@ if (proc->unwind_as != NULL) unw_destroy_addr_space(proc->unwind_as); #endif /* defined(HAVE_LIBUNWIND) */ + +#if defined(HAVE_LIBDW) + if (proc->dwfl != NULL) + dwfl_end(proc->dwfl); +#endif /* defined(HAVE_LIBDW) */ } static int @@ -172,6 +177,10 @@ } #endif /* defined(HAVE_LIBUNWIND) */ +#if defined(HAVE_LIBDW) + proc->dwfl = NULL; /* Initialize for leader only on first library. */ +#endif /* defined(HAVE_LIBDW) */ + return 0; } @@ -887,6 +896,59 @@ debug(DEBUG_PROCESS, "added library %s@%p (%s) to %d", lib->soname, lib->base, lib->pathname, proc->pid); +#if defined(HAVE_LIBDW) + if (options.bt_depth > 0) { + /* Setup module tracking for libdwfl unwinding. */ + struct process *leader = proc->leader; + Dwfl *dwfl = leader->dwfl; + if (dwfl == NULL) { + static const Dwfl_Callbacks proc_callbacks = { + .find_elf = dwfl_linux_proc_find_elf, + .find_debuginfo = dwfl_standard_find_debuginfo + }; + dwfl = dwfl_begin(&proc_callbacks); + if (dwfl == NULL) + fprintf(stderr, + "Couldn't initialize libdwfl unwinding " + "for process %d: %s\n", leader->pid, + dwfl_errmsg (-1)); + } + + if (dwfl != NULL) { + dwfl_report_begin_add(dwfl); + if (dwfl_report_elf(dwfl, lib->soname, + lib->pathname, -1, + (GElf_Addr) lib->base, + false) == NULL) + fprintf(stderr, + "dwfl_report_elf %s@%p (%s) %d: %s\n", + lib->soname, lib->base, lib->pathname, + proc->pid, dwfl_errmsg (-1)); + dwfl_report_end(dwfl, NULL, NULL); + + if (leader->dwfl == NULL) { + int r = dwfl_linux_proc_attach(dwfl, + leader->pid, + true); + if (r == 0) + leader->dwfl = dwfl; + else { + const char *msg; + dwfl_end(dwfl); + if (r < 0) + msg = dwfl_errmsg(-1); + else + msg = strerror(r); + fprintf(stderr, "Couldn't initialize " + "libdwfl unwinding for " + "process %d: %s\n", + leader->pid, msg); + } + } + } + } +#endif /* defined(HAVE_LIBDW) */ + /* Insert breakpoints for all active (non-latent) symbols. */ struct library_symbol *libsym = NULL; while ((libsym = library_each_symbol(lib, libsym, diff -u ltrace-0.7.91/proc.c.orig ltrace-0.7.91-pm/proc.c.orig --- ltrace-0.7.91/proc.h 2015-01-09 00:38:17.966190936 +0100 +++ ltrace-0.7.91-pm/proc.h 2015-01-09 00:37:40.261910548 +0100 @@ -28,6 +28,10 @@ #include #include +#if defined(HAVE_LIBDW) +# include +#endif + #if defined(HAVE_LIBUNWIND) # include #endif /* defined(HAVE_LIBUNWIND) */ @@ -113,6 +117,11 @@ short e_machine; char e_class; +#if defined(HAVE_LIBDW) + /* Unwind info for leader, NULL for non-leader procs. */ + Dwfl *dwfl; +#endif /* defined(HAVE_LIBDW) */ + #if defined(HAVE_LIBUNWIND) /* libunwind address space */ unw_addr_space_t unwind_as; diff -up ltrace-0.7.91/proc.c\~ ltrace-0.7.91/proc.c --- ltrace-0.7.91/proc.c~ 2015-01-09 01:55:38.289864078 +0100 +++ ltrace-0.7.91/proc.c 2015-01-09 01:56:29.818881935 +0100 @@ -918,7 +918,8 @@ proc_add_library(struct process *proc, s dwfl_report_begin_add(dwfl); if (dwfl_report_elf(dwfl, lib->soname, lib->pathname, -1, - (GElf_Addr) lib->base, + /* XXX double cast */ + (GElf_Addr) (uintptr_t) lib->base, false) == NULL) fprintf(stderr, "dwfl_report_elf %s@%p (%s) %d: %s\n", diff -up a/configure b/configure --- a/configure 2018-04-06 19:48:06.543593689 -0400 +++ b/configure 2018-04-06 19:47:52.039859482 -0400 @@ -637,6 +637,7 @@ am__EXEEXT_TRUE LTLIBOBJS LIBOBJS libunwind_LD_LIBRARY_PATH +elfutils_LD_LIBRARY_PATH libelf_LD_LIBRARY_PATH AM_LDFLAGS AM_CFLAGS @@ -645,6 +646,7 @@ USE_VALGRIND_FALSE USE_VALGRIND_TRUE HAVE_VALGRIND libunwind_LIBS +libdw_LIBS libstdcxx_LIBS libsupcxx_LIBS liberty_LIBS @@ -780,6 +782,7 @@ enable_silent_rules enable_maintainer_mode enable_largefile with_libelf +with_elfutils with_libunwind enable_debug enable_werror @@ -1440,6 +1443,7 @@ Optional Packages: --with-sysroot=DIR Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-libelf Prefix of libelf headers/library + --with-elfutils Use elfutils libdwfl unwinding support --with-libunwind Use libunwind frame unwinding support Some influential environment variables: @@ -12115,6 +12119,108 @@ fi +# Check whether --with-elfutils was given. +if test "${with_elfutils+set}" = set; then : + withval=$with_elfutils; case "${withval}" in + (yes|no) enable_elfutils=$withval;; + (*) enable_elfutils=yes + AM_CPPFLAGS="${AM_CPPFLAGS} -I${withval}/include" + AM_LDFLAGS="${AM_LDFLAGS} -L${withval}/lib" + elfutils_LD_LIBRARY_PATH="${withval}/lib:${withval}/lib/elfutils" + ;; +esac +else + enable_elfutils=maybe +fi + + +saved_CPPFLAGS="${CPPFLAGS}" +CPPFLAGS="${CPPFLAGS} ${AM_CPPFLAGS}" +for ac_header in elfutils/libdwfl.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "elfutils/libdwfl.h" "ac_cv_header_elfutils_libdwfl_h" "$ac_includes_default" +if test "x$ac_cv_header_elfutils_libdwfl_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_ELFUTILS_LIBDWFL_H 1 +_ACEOF + have_libdwfl_h=yes +fi + +done + +CPPFLAGS="${saved_CPPFLAGS}" + +saved_LDFLAGS="${LDFLAGS}" +LDFLAGS="${LDFLAGS} ${AM_LDFLAGS}" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dwfl_getthread_frames in -ldw" >&5 +$as_echo_n "checking for dwfl_getthread_frames in -ldw... " >&6; } +if ${ac_cv_lib_dw_dwfl_getthread_frames+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldw $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dwfl_getthread_frames (); +int +main () +{ +return dwfl_getthread_frames (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dw_dwfl_getthread_frames=yes +else + ac_cv_lib_dw_dwfl_getthread_frames=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dw_dwfl_getthread_frames" >&5 +$as_echo "$ac_cv_lib_dw_dwfl_getthread_frames" >&6; } +if test "x$ac_cv_lib_dw_dwfl_getthread_frames" = xyes; then : + have_libdw_dwfl_frames=yes +fi + +LDFLAGS="${saved_LDFLAGS}" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use elfutils libdwfl unwinding support" >&5 +$as_echo_n "checking whether to use elfutils libdwfl unwinding support... " >&6; } +case "${enable_elfutils}" in +(yes|maybe) + if test x$have_libdwfl_h = xyes -a x$have_libdw_dwfl_frames = xyes; then + enable_elfutils=yes + elif test $enable_elfutils = maybe; then + enable_elfutils=no + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_elfutils" >&5 +$as_echo "$enable_elfutils" >&6; } + as_fn_error $? "Missing elfutils/libdwfl.h or dwfl_getthread_frames not in libdw.so" "$LINENO" 5 + fi + ;; +(*) ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_elfutils" >&5 +$as_echo "$enable_elfutils" >&6; } + +if test x"$enable_elfutils" = xyes; then + libdw_LIBS=-ldw + + +$as_echo "#define HAVE_LIBDW 1" >>confdefs.h + +fi + # HAVE_LIBUNWIND # Check whether --with-libunwind was given. @@ -12329,6 +12435,15 @@ $as_echo "#define HAVE_LIBUNWIND 1" >>co LDFLAGS="${saved_LDFLAGS}" fi +if test x"$enable_elfutils" = xyes -a x"$enable_libunwind" = xyes; then + as_fn_error $? "Cannot enable both --with-libunwind and --with-elfutils" "$LINENO" 5 +fi + +if test x"$enable_elfutils" = xyes -o x"$enable_libunwind" = xyes; then + +$as_echo "#define HAVE_UNWINDER 1" >>confdefs.h + +fi saved_CPPFLAGS="${CPPFLAGS}" saved_LDFLAGS="${LDFLAGS}" diff a/config.h.in b/config.h.in --- a/config.h.in 2013-03-22 19:12:35.000000000 -0400 +++ b/config.h.in 2018-04-06 21:49:42.000000000 -0400 @@ -15,6 +15,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H +/* Define to 1 if you have the header file. */ +#undef HAVE_ELFUTILS_LIBDWFL_H + /* we have read mmap support */ #undef HAVE_ELF_C_READ_MMAP @@ -39,6 +42,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* we have elfutils libdw */ +#undef HAVE_LIBDW + /* Define to 1 if you have the `elf' library (-lelf). */ #undef HAVE_LIBELF @@ -129,6 +135,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* we have an unwinder available */ +#undef HAVE_UNWINDER + /* Define to 1 if you have the `vfork' function. */ #undef HAVE_VFORK @@ -175,6 +184,11 @@ /* Version number of package */ #undef VERSION +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS diff a/Makefile.in b/Makefile.in --- a/Makefile.in 2013-11-04 20:22:47.000000000 -0500 +++ b/Makefile.in 2018-04-06 23:29:16.919673155 -0400 @@ -297,6 +297,7 @@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ +libdw_LIBS = @libdw_LIBS@ libelf_LD_LIBRARY_PATH = @libelf_LD_LIBRARY_PATH@ liberty_LIBS = @liberty_LIBS@ libexecdir = @libexecdir@ @@ -342,6 +343,7 @@ libltrace_la_LIBADD = \ $(liberty_LIBS) \ $(libsupcxx_LIBS) \ $(libstdcxx_LIBS) \ + $(libdw_LIBS) \ $(libunwind_LIBS) \ sysdeps/libos.la