[PATCH] print stack trace after each syscall

Masatake YAMATO yamato at redhat.com
Mon Jul 8 05:32:38 UTC 2013


How can I try this v2 patch?

As far as my seeing, I cannot find the definition of print_stacktrace
function. Should I apply both v1 and v2 patches?

Masatake YAMATO



> 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