[PATCH] print stack trace after each syscall

Luca Clementi luca.clementi at gmail.com
Mon Jul 8 01:52:52 UTC 2013


This patch prints the stack trace of the traced process after
each system call when using -k flag. It uses libunwind to
unwind the stack and to obtain the function name pointed by
the IP.

Tested on Ubuntu 12.04 64 with the distribution version of
libunwind (0.99-0.3ubuntu1) and on CentOS 6.3 with libunwind
form the source code. On Ubuntu you need the libunwind and
libunwind-dev package to compile the stack trace feature in
the code.

Some of this code was originally taken from strace-plus of
Philip J. Guo.

 v2:
   fixed several identation issues
   removed a strlen in alloc_mmap_cache (Denys)
   reload mmap_cache after execv
   fixed a bug in libunwind_backtrace binary search (Masatake)
   check return value of _UPT_create
   use die_out_of_memory after Xalloc
   use exiting instead of !entering
   created a new separate unwind.c file
   removed include from defs.h
   added --with-libunwind to autoconf script
   cleaned up defs.h from several inclusions
---
 Makefile.am  |   10 +++++++++-
 configure.ac |   57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 defs.h       |   45 +++++++++++++++++++++++++++++++++++++++++++++
 mem.c        |    7 +++++++
 process.c    |    8 ++++++++
 strace.c     |   43 +++++++++++++++++++++++++++++++++++++++++++
 syscall.c    |    9 +++++++++
 7 files changed, 178 insertions(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index 9d611f3..c8ad026 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,7 +12,7 @@ ARCH		= @arch@
 
 ACLOCAL_AMFLAGS = -I m4
 AM_CFLAGS = $(WARN_CFLAGS)
-AM_CPPFLAGS = -I$(srcdir)/$(OS)/$(ARCH) -I$(srcdir)/$(OS) -I$(builddir)/$(OS)
+AM_CPPFLAGS = -I$(srcdir)/$(OS)/$(ARCH) -I$(srcdir)/$(OS) -I$(builddir)/$(OS) $(libunwind_CPPFLAGS)
 
 strace_SOURCES =	\
 	bjm.c		\
@@ -43,6 +43,14 @@ strace_SOURCES =	\
 	util.c		\
 	vsprintf.c
 
+
+if LIB_UNWIND
+strace_SOURCES += unwind.c
+strace_LDADD = $(libunwind_LIBS)
+AM_LDFLAGS = $(libunwind_LDFLAGS)
+endif
+
+
 noinst_HEADERS = defs.h
 # Enable this to get link map generated
 #strace_CFLAGS = $(AM_CFLAGS) -Wl,-Map=strace.mapfile
diff --git a/configure.ac b/configure.ac
index 03e49fe..526fbda 100644
--- a/configure.ac
+++ b/configure.ac
@@ -261,6 +261,63 @@ AC_CHECK_MEMBERS([struct sigcontext.sc_hi2],,, [#include <signal.h>
 # include <asm/sigcontext.h>
 #endif])
 
+
+dnl stack trace with libunwind
+AC_ARG_WITH([libunwind],
+	    [AS_HELP_STRING([--with-libunwind],
+			    [libunwind is used to display system call stacktrace])],
+	    [case "${withval}" in
+	     (yes|no) enable_libunwind=$withval;;
+	     (*)      enable_libunwind=yes
+		      libunwind_CPPFLAGS="${AM_CPPFLAGS} -I${withval}/include"
+		      libunwind_LDFLAGS="-L${withval}/lib" ;;
+	     esac],
+	    [enable_libunwind=maybe])
+
+AS_IF([test "x$enable_libunwind" != xno],
+	[saved_CPPFLAGS="${CPPFLAGS}"
+	CPPFLAGS="${CPPFLAGS} ${libunwind_CPPFLAGS}"
+	AC_CHECK_HEADERS([libunwind-ptrace.h libunwind.h])
+	CPPFLAGS="${saved_CPPFLAGS}" ])
+
+if test "x$ac_cv_header_libunwind_ptrace_h" = "xyes" &&
+	test "x$ac_cv_header_libunwind_h" = "xyes"; then
+
+	dnl code is taken from ltrace
+	case "${host_cpu}" in
+		arm*|sa110)         UNWIND_ARCH="arm" ;;
+		i?86)               UNWIND_ARCH="x86" ;;
+		powerpc)            UNWIND_ARCH="ppc32" ;;
+		powerpc64)          UNWIND_ARCH="ppc64" ;;
+		mips*)              UNWIND_ARCH="mips" ;;
+		*)                  UNWIND_ARCH="${host_cpu}" ;;
+	esac
+
+	saved_LDFLAGS="${LDFLAGS}"
+	LDFLAGS="${LDFLAGS} ${libunwind_LDFLAGS}"
+	AC_CHECK_LIB([unwind], [backtrace],
+		     [libunwind_LIBS="-lunwind"],
+		     [AC_MSG_FAILURE([Unable to find libunwind])])
+        AC_CHECK_LIB([unwind-generic], [_U${UNWIND_ARCH}_create_addr_space],
+		     [libunwind_LIBS="-lunwind-generic $libunwind_LIBS"],
+		     [AC_MSG_FAILURE([Unable to find libunwind-generic])],
+		     [$libunwind_LIBS])
+	AC_CHECK_LIB([unwind-ptrace], [_UPT_create],
+		     [libunwind_LIBS="-lunwind-ptrace $libunwind_LIBS"],
+		     [AC_MSG_FAILURE([Unable to find libunwind-ptrace])],
+		     [$libunwind_LIBS])
+	LDFLAGS="${saved_LDFLAGS}"
+
+	dnl we have all the dependencies we need we can activate stack tracing
+	AC_SUBST(libunwind_LIBS)
+	AC_SUBST(libunwind_LDFLAGS)
+	AC_SUBST(libunwind_CPPFLAGS)
+	AC_DEFINE([LIB_UNWIND], 1, [Compile stack tracing functionality])
+	AC_MSG_NOTICE([Enabled stack tracing])
+fi
+AM_CONDITIONAL([LIB_UNWIND], [test "x$ac_cv_lib_unwind_ptrace__UPT_create" == "xyes"])
+
+
 AC_CHECK_MEMBERS([struct utsname.domainname],,, [#include <sys/utsname.h>])
 
 AC_CHECK_DECLS([sys_errlist])
diff --git a/defs.h b/defs.h
index 64cfc8d..b314622 100644
--- a/defs.h
+++ b/defs.h
@@ -405,6 +405,33 @@ typedef struct ioctlent {
 	unsigned long code;
 } struct_ioctlent;
 
+
+#ifdef LIB_UNWIND
+
+/* keep a sorted array of cache entries, so that we can binary search
+ * through it
+ */
+struct mmap_cache_t {
+	/**
+	 * example entry:
+	 * 7fabbb09b000-7fabbb09f000 r--p 00179000 fc:00 1180246 /lib/libc-2.11.1.so
+	 *
+	 * start_addr  is 0x7fabbb09b000
+	 * end_addr    is 0x7fabbb09f000
+	 * mmap_offset is 0x179000
+	 * binary_filename is "/lib/libc-2.11.1.so"
+	 */
+	unsigned long start_addr;
+	unsigned long end_addr;
+	unsigned long mmap_offset;
+	char* binary_filename;
+};
+
+
+/* if this is true do the stack trace for every system call */
+extern bool use_libunwind;
+#endif
+
 /* Trace Control Block */
 struct tcb {
 	int flags;		/* See below for TCB_ values */
@@ -430,6 +457,12 @@ struct tcb {
 	struct timeval etime;	/* Syscall entry time */
 				/* Support for tracing forked processes: */
 	long inst[2];		/* Saved clone args (badly named) */
+
+#ifdef LIB_UNWIND
+	struct mmap_cache_t* mmap_cache;
+	int mmap_cache_size;
+	struct UPT_info* libunwind_ui;
+#endif
 };
 
 /* TCB flags */
@@ -721,6 +754,18 @@ extern void tv_sub(struct timeval *, struct timeval *, struct timeval *);
 extern void tv_mul(struct timeval *, struct timeval *, int);
 extern void tv_div(struct timeval *, struct timeval *, int);
 
+#ifdef LIB_UNWIND
+/**
+ * print stack (-k flag) memory allocation and deallocation
+ */
+extern void alloc_mmap_cache(struct tcb* tcp);
+extern void delete_mmap_cache(struct tcb* tcp);
+extern void print_stacktrace(struct tcb* tcp);
+#else
+# define alloc_mmap_cache(tcp) ((void)0)
+# define delete_mmap_cache(tcp) ((void)0)
+#endif
+
 /* Strace log generation machinery.
  *
  * printing_tcp: tcb which has incomplete line being printed right now.
diff --git a/mem.c b/mem.c
index ef273c7..a82b747 100644
--- a/mem.c
+++ b/mem.c
@@ -175,6 +175,9 @@ static int
 print_mmap(struct tcb *tcp, long *u_arg, unsigned long long offset)
 {
 	if (entering(tcp)) {
+		if (use_libunwind)
+			delete_mmap_cache(tcp);
+
 		/* addr */
 		if (!u_arg[0])
 			tprints("NULL, ");
@@ -304,6 +307,8 @@ sys_munmap(struct tcb *tcp)
 		tprintf("%#lx, %lu",
 			tcp->u_arg[0], tcp->u_arg[1]);
 	}
+	if (exiting(tcp) && use_libunwind)
+		delete_mmap_cache(tcp);
 	return 0;
 }
 
@@ -315,6 +320,8 @@ sys_mprotect(struct tcb *tcp)
 			tcp->u_arg[0], tcp->u_arg[1]);
 		printflags(mmap_prot, tcp->u_arg[2], "PROT_???");
 	}
+	if (exiting(tcp) && use_libunwind)
+		delete_mmap_cache(tcp);
 	return 0;
 }
 
diff --git a/process.c b/process.c
index e2fa25b..306aa23 100644
--- a/process.c
+++ b/process.c
@@ -992,6 +992,10 @@ sys_execve(struct tcb *tcp)
 			tprints("]");
 		}
 	}
+
+	if (exiting(tcp) && use_libunwind)
+		delete_mmap_cache(tcp);
+
 	return 0;
 }
 
@@ -1209,6 +1213,10 @@ sys_waitid(struct tcb *tcp)
 				printrusage(tcp, tcp->u_arg[4]);
 		}
 	}
+
+	if (exiting(tcp) && use_libunwind)
+		delete_mmap_cache(tcp);
+
 	return 0;
 }
 
diff --git a/strace.c b/strace.c
index a489db4..5956da6 100644
--- a/strace.c
+++ b/strace.c
@@ -50,6 +50,13 @@ extern char **environ;
 extern int optind;
 extern char *optarg;
 
+#ifdef LIB_UNWIND
+# include <libunwind-ptrace.h>
+
+/* if this is true do the stack trace for every system call */
+bool use_libunwind = false;
+unw_addr_space_t libunwind_as;
+#endif
 
 #if defined __NR_tkill
 # define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig))
@@ -231,6 +238,10 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\
 -E var -- remove var from the environment for command\n\
 -P path -- trace accesses to path\n\
 "
+#ifdef LIB_UNWIND
+"-k obtain stack trace between each syscall\n\
+"
+#endif
 /* ancient, no one should use it
 -F -- attempt to follow vforks (deprecated, use -f)\n\
  */
@@ -685,6 +696,15 @@ alloctcb(int pid)
 #if SUPPORTED_PERSONALITIES > 1
 			tcp->currpers = current_personality;
 #endif
+
+#ifdef LIB_UNWIND
+			if (use_libunwind) {
+				tcp->libunwind_ui = _UPT_create(tcp->pid);
+				if (!tcp->libunwind_ui)
+				    die_out_of_memory();
+			}
+#endif
+
 			nprocs++;
 			if (debug_flag)
 				fprintf(stderr, "new tcb for pid %d, active tcbs:%d\n", tcp->pid, nprocs);
@@ -721,6 +741,12 @@ droptcb(struct tcb *tcp)
 	if (printing_tcp == tcp)
 		printing_tcp = NULL;
 
+#ifdef LIB_UNWIND
+	if (use_libunwind) {
+		delete_mmap_cache(tcp);
+		_UPT_destroy(tcp->libunwind_ui);
+	}
+#endif
 	memset(tcp, 0, sizeof(*tcp));
 }
 
@@ -1642,6 +1668,9 @@ init(int argc, char *argv[])
 	qualify("signal=all");
 	while ((c = getopt(argc, argv,
 		"+b:cCdfFhiqrtTvVxyz"
+#ifdef LIB_UNWIND
+		"k"
+#endif
 		"D"
 		"a:e:o:O:p:s:S:u:E:P:I:")) != EOF) {
 		switch (c) {
@@ -1744,6 +1773,11 @@ init(int argc, char *argv[])
 		case 'u':
 			username = strdup(optarg);
 			break;
+#ifdef LIB_UNWIND
+		case 'k':
+			use_libunwind = true;
+			break;
+#endif
 		case 'E':
 			if (putenv(optarg) < 0)
 				die_out_of_memory();
@@ -1775,6 +1809,15 @@ init(int argc, char *argv[])
 		error_msg_and_die("-D and -p are mutually exclusive");
 	}
 
+#ifdef LIB_UNWIND
+	if (use_libunwind) {
+		libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
+		if (!libunwind_as) {
+			error_msg_and_die("Fatal error: unable to create address space for stack tracing\n");
+		}
+	}
+#endif
+
 	if (!followfork)
 		followfork = optF;
 
diff --git a/syscall.c b/syscall.c
index f524b13..dee4c61 100644
--- a/syscall.c
+++ b/syscall.c
@@ -2684,6 +2684,15 @@ trace_syscall_exiting(struct tcb *tcp)
 	dumpio(tcp);
 	line_ended();
 
+#ifdef LIB_UNWIND
+	if (use_libunwind) {
+		if (!tcp->mmap_cache) {
+			alloc_mmap_cache(tcp);
+		}
+		print_stacktrace(tcp);
+	}
+#endif
+
  ret:
 	tcp->flags &= ~TCB_INSYSCALL;
 	return 0;
-- 
1.7.9.5





More information about the Strace-devel mailing list