Permalink
Browse files

Add -k option to print stack trace after each syscall

Print the stack trace of the traced process after each system call when
-k option is specified.  It is implemented using libunwind to unwind the
stack and to obtain the function name pointed by the IP.

Based on the code that was originally taken from strace-plus
of Philip J. Guo.

* configure.ac: Add --with-libunwind option.  Check libunwind support.
* Makefile.am: Add libunwind support.
* defs.h (struct tcb) [USE_LIBUNWIND]: Append libunwind specific fields.
[USE_LIBUNWIND] (stack_trace_enabled, alloc_mmap_cache,
delete_mmap_cache, print_stacktrace): New prototypes.
* mem.c (print_mmap, sys_munmap, sys_mprotect): Add libunwind support.
* process.c (sys_execve): Likewise.
* strace.c (usage, alloctcb, droptcb, init): Likewise.
* syscall.c (trace_syscall_exiting): Likewise.
* unwind.c: New file.
* strace.1: Document -k option.
  • Loading branch information...
lclementi authored and ldv-alt committed Jul 23, 2013
1 parent 6dbbe07 commit 327064b63722a4400058e0e7e9b39d9e34b14b57
Showing with 465 additions and 1 deletion.
  1. +9 −0 Makefile.am
  2. +89 −0 configure.ac
  3. +19 −0 defs.h
  4. +17 −0 mem.c
  5. +7 −0 process.c
  6. +4 −1 strace.1
  7. +33 −0 strace.c
  8. +5 −0 syscall.c
  9. +282 −0 unwind.c
View
@@ -54,6 +54,15 @@ strace_SOURCES = \
util.c \
vsprintf.c
if USE_LIBUNWIND
strace_SOURCES += unwind.c
strace_CPPFLAGS = $(AM_CPPFLAGS) $(libunwind_CPPFLAGS)
strace_LDFLAGS = $(libunwind_LDFLAGS)
strace_LDADD = $(libunwind_LIBS)
else
strace_CPPFLAGS = $(AM_CPPFLAGS)
endif
noinst_HEADERS = defs.h
# Enable this to get link map generated
#strace_CFLAGS = $(AM_CFLAGS) -Wl,-Map=strace.mapfile
View
@@ -649,5 +649,94 @@ fi
AC_PATH_PROG([PERL], [perl])
dnl stack trace with libunwind
libunwind_CPPFLAGS=
libunwind_LDFLAGS=
libunwind_LIBS=
AC_ARG_WITH([libunwind],
[AS_HELP_STRING([--with-libunwind],
[use libunwind to implement stack tracing support])],
[case "${withval}" in
yes|no|check) ;;
*) with_libunwind=yes
libunwind_CPPFLAGS="-I${withval}/include"
libunwind_LDFLAGS="-L${withval}/lib" ;;
esac],
[with_libunwind=check]
)
use_libunwind=no
AS_IF([test "x$with_libunwind" != xno],
[saved_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $libunwind_CPPFLAGS"
AC_CHECK_HEADERS([libunwind-ptrace.h],
[saved_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $libunwind_LDFLAGS"
AC_CHECK_LIB([unwind], [backtrace],
[libunwind_LIBS="-lunwind $libunwind_LIBS"
AC_MSG_CHECKING([for unw_create_addr_space in libunwind-generic])
saved_LIBS="$LIBS"
LIBS="-lunwind-generic $libunwind_LIBS $LIBS"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[#include <libunwind-ptrace.h>]],
[[return !unw_create_addr_space(0, 0)]])
],
[AC_MSG_RESULT([yes])
libunwind_LIBS="-lunwind-generic $libunwind_LIBS"
AC_CHECK_LIB([unwind-ptrace], [_UPT_create],
[libunwind_LIBS="-lunwind-ptrace $libunwind_LIBS"
use_libunwind=yes
],
[if test "x$with_libunwind" != xcheck; then
AC_MSG_FAILURE([failed to find _UPT_create in libunwind-ptrace])
fi
],
[$libunwind_LIBS]
)
],
[AC_MSG_RESULT([no])
if test "x$with_libunwind" != xcheck; then
AC_MSG_FAILURE([failed to find unw_create_addr_space in libunwind-generic])
fi
]
)
LIBS="$saved_LIBS"
],
[if test "x$with_libunwind" != xcheck; then
AC_MSG_FAILURE([failed to find libunwind])
fi
],
[$libunwind_LIBS]
)
LDFLAGS="$saved_LDFLAGS"
],
[if test "x$with_libunwind" != xcheck; then
AC_MSG_FAILURE([failed to find libunwind-ptrace.h])
fi
]
)
CPPFLAGS="$saved_CPPFLAGS"
]
)
dnl enable libunwind
AC_MSG_CHECKING([whether to enable stack tracing support using libunwind])
if test "x$use_libunwind" = xyes; then
AC_DEFINE([USE_LIBUNWIND], 1, [Compile stack tracing functionality])
AC_SUBST(libunwind_LIBS)
AC_SUBST(libunwind_LDFLAGS)
AC_SUBST(libunwind_CPPFLAGS)
fi
AM_CONDITIONAL([USE_LIBUNWIND], [test "x$use_libunwind" = xyes])
AC_MSG_RESULT([$use_libunwind])
AC_CONFIG_FILES([Makefile tests/Makefile])
AC_OUTPUT
View
19 defs.h
@@ -425,6 +425,12 @@ struct tcb {
struct timeval etime; /* Syscall entry time */
/* Support for tracing forked processes: */
long inst[2]; /* Saved clone args (badly named) */
#ifdef USE_LIBUNWIND
struct UPT_info* libunwind_ui;
struct mmap_cache_t* mmap_cache;
unsigned int mmap_cache_size;
#endif
};
/* TCB flags */
@@ -559,6 +565,10 @@ extern const char **paths_selected;
extern bool need_fork_exec_workarounds;
extern unsigned xflag;
extern unsigned followfork;
#ifdef USE_LIBUNWIND
/* if this is true do the stack trace for every system call */
extern bool stack_trace_enabled;
#endif
extern unsigned ptrace_setoptions;
extern unsigned max_strlen;
extern unsigned os_release;
@@ -721,6 +731,15 @@ extern void tv_sub(struct timeval *, const struct timeval *, const struct timeva
extern void tv_mul(struct timeval *, const struct timeval *, int);
extern void tv_div(struct timeval *, const struct timeval *, int);
#ifdef USE_LIBUNWIND
extern void init_unwind_addr_space(void);
extern void init_libunwind_ui(struct tcb *tcp);
extern void free_libunwind_ui(struct tcb *tcp);
extern void alloc_mmap_cache(struct tcb* tcp);
extern void delete_mmap_cache(struct tcb* tcp);
extern void print_stacktrace(struct tcb* tcp);
#endif
/* Strace log generation machinery.
*
* printing_tcp: tcb which has incomplete line being printed right now.
View
17 mem.c
@@ -60,6 +60,11 @@ static int
print_mmap(struct tcb *tcp, long *u_arg, unsigned long long offset)
{
if (entering(tcp)) {
#ifdef USE_LIBUNWIND
if (stack_trace_enabled)
delete_mmap_cache(tcp);
#endif
/* addr */
if (!u_arg[0])
tprints("NULL, ");
@@ -189,6 +194,12 @@ sys_munmap(struct tcb *tcp)
tprintf("%#lx, %lu",
tcp->u_arg[0], tcp->u_arg[1]);
}
#ifdef USE_LIBUNWIND
else {
if (stack_trace_enabled)
delete_mmap_cache(tcp);
}
#endif
return 0;
}
@@ -200,6 +211,12 @@ sys_mprotect(struct tcb *tcp)
tcp->u_arg[0], tcp->u_arg[1]);
printflags(mmap_prot, tcp->u_arg[2], "PROT_???");
}
#ifdef USE_LIBUNWIND
else {
if (stack_trace_enabled)
delete_mmap_cache(tcp);
}
#endif
return 0;
}
View
@@ -799,6 +799,13 @@ sys_execve(struct tcb *tcp)
tprints("]");
}
}
#ifdef USE_LIBUNWIND
else {
if (stack_trace_enabled)
delete_mmap_cache(tcp);
}
#endif
return 0;
}
View
@@ -39,7 +39,7 @@
strace \- trace system calls and signals
.SH SYNOPSIS
.B strace
[\fB-CdffhiqrtttTvVxxy\fR]
[\fB-CdffhikqrtttTvVxxy\fR]
[\fB-I\fIn\fR]
[\fB-b\fIexecve\fR]
[\fB-e\fIexpr\fR]...
@@ -262,6 +262,9 @@ Print the help summary.
.B \-i
Print the instruction pointer at the time of the system call.
.TP
.B \-k
Print the execution stack trace of the traced processes after each system call.
.TP
.B \-q
Suppress messages about attaching, detaching etc. This happens
automatically when output is redirected to a file and the command
View
@@ -50,6 +50,10 @@ extern char **environ;
extern int optind;
extern char *optarg;
#ifdef USE_LIBUNWIND
/* if this is true do the stack trace for every system call */
bool stack_trace_enabled = false;
#endif
#if defined __NR_tkill
# define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig))
@@ -233,6 +237,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 USE_LIBUNWIND
"-k obtain stack trace between each syscall\n\
"
#endif
/* ancient, no one should use it
-F -- attempt to follow vforks (deprecated, use -f)\n\
*/
@@ -695,6 +703,12 @@ alloctcb(int pid)
#if SUPPORTED_PERSONALITIES > 1
tcp->currpers = current_personality;
#endif
#ifdef USE_LIBUNWIND
if (stack_trace_enabled)
init_libunwind_ui(tcp);
#endif
nprocs++;
if (debug_flag)
fprintf(stderr, "new tcb for pid %d, active tcbs:%d\n", tcp->pid, nprocs);
@@ -731,6 +745,12 @@ droptcb(struct tcb *tcp)
if (printing_tcp == tcp)
printing_tcp = NULL;
#ifdef USE_LIBUNWIND
if (stack_trace_enabled) {
delete_mmap_cache(tcp);
free_libunwind_ui(tcp);
}
#endif
memset(tcp, 0, sizeof(*tcp));
}
@@ -1653,6 +1673,9 @@ init(int argc, char *argv[])
qualify("signal=all");
while ((c = getopt(argc, argv,
"+b:cCdfFhiqrtTvVwxyz"
#ifdef USE_LIBUNWIND
"k"
#endif
"D"
"a:e:o:O:p:s:S:u:E:P:I:")) != EOF) {
switch (c) {
@@ -1758,6 +1781,11 @@ init(int argc, char *argv[])
case 'u':
username = strdup(optarg);
break;
#ifdef USE_LIBUNWIND
case 'k':
stack_trace_enabled = true;
break;
#endif
case 'E':
if (putenv(optarg) < 0)
die_out_of_memory();
@@ -1789,6 +1817,11 @@ init(int argc, char *argv[])
error_msg_and_die("-D and -p are mutually exclusive");
}
#ifdef USE_LIBUNWIND
if (stack_trace_enabled)
init_unwind_addr_space();
#endif
if (!followfork)
followfork = optF;
View
@@ -2706,6 +2706,11 @@ trace_syscall_exiting(struct tcb *tcp)
dumpio(tcp);
line_ended();
#ifdef USE_LIBUNWIND
if (stack_trace_enabled)
print_stacktrace(tcp);
#endif
ret:
tcp->flags &= ~TCB_INSYSCALL;
return 0;
Oops, something went wrong.

0 comments on commit 327064b

Please sign in to comment.