#!/bin/bash # usttrace by Pierre-Marc Fournier 2009 # Distributed under the GPLv2. function error() { echo "$0: error: $1" 2>/dev/stderr } function sighandler() { echo "Caught Ctrl-C" if [ -z "$USTDPID" ]; then USTDPID="$(<$pidfilepath)" fi # Tell the daemon to die kill -SIGTERM "$USTDPID" echo "Waiting for ustd to shutdown..." wait "$USTDPID" rm "$pidfilepath" exit 0; } USTTRACE_DIR="$(dirname $0)" if [ -x "${USTTRACE_DIR}/ustd/ustd" ] ; then # Use the not installed libraries instead USTD="${USTTRACE_DIR}/ustd/ustd" LIBINTERFORK_PATH="${USTTRACE_DIR}/libinterfork/.libs/libinterfork.so" LIBMALLOCWRAP_PATH="${USTTRACE_DIR}/libmallocwrap/.libs/libmallocwrap.so" LIBUST_PATH="${USTTRACE_DIR}/libust/.libs/libust.so" else # Use the libraries that the dynamic link finds USTD="ustd" if [ ! -x "$(which ustd 2>/dev/null)" ]; then error "cannot find an executable ustd; make sure its location is in the PATH" exit 1 fi LIBINTERFORK_PATH="libinterfork.so" LIBMALLOCWRAP_PATH="libmallocwrap.so" LIBUST_PATH="libust.so.0" fi BASE_TRACE_DIR="${HOME}/.usttraces" function usage () { echo "usage: $0 OPTIONS COMMAND" 2>/dev/stderr echo "" 2>/dev/stderr echo "Options:" 2>/dev/stderr echo " -l Runtime link with UST library." 2>/dev/stderr echo " (Needed only if program was not linked at compile time with libust.)" 2>/dev/stderr echo " -L Add path to ust libraries to LD_LIBRARY_PATH." 2>/dev/stderr echo " -m Instrument malloc calls." 2>/dev/stderr echo " -f Also trace forked processes." 2>/dev/stderr echo " -s Use system-wide daemon instead of creating one for this session." 2>/dev/stderr } while getopts ":hlLmfsW" options; do case $options in l) arg_preload_libust=1;; L) arg_ld_std_ust=1;; m) arg_preload_malloc=1;; f) arg_preload_fork=1;; s) arg_syswide_daemon=1;; W) where=1;; h) usage; exit 0;; \?) usage exit 1;; *) usage exit 1;; esac done shift $(($OPTIND - 1)) if [ -n "$where" ]; then echo $BASE_TRACE_DIR/$(ls "$BASE_TRACE_DIR" | tail -n 1) exit 0 fi # Prepare vars CMD=$* # Validate input if [ -z "$HOME" ]; then error "no home specified" fi if [ -z "$CMD" ]; then error "no command specified" usage; exit 1 fi # Create directory for trace output DATESTRING="$(hostname)-$(date +%Y%m%d%H%M%S%N)" OUTDIR="$BASE_TRACE_DIR/$DATESTRING" mkdir -p "$OUTDIR" # Choose ustd socket path USTDSOCKPATH="/tmp/ustd-sock-$$" if [ "$arg_syswide_daemon" != "1" ]; then pidfilepath="/tmp/usttrace-$USER-$(date +%Y%m%d%H%M%S%N)-ustd-pid" trap "sighandler $pidfilepath" SIGINT mkfifo -m 0600 "$pidfilepath" # Start daemon $USTD --pidfile "$pidfilepath" -s "$USTDSOCKPATH" -o "$OUTDIR" >"$OUTDIR/ustd.log" 2>&1 & # ustd sets up its server socket # ustd opens the pidfile, blocks because no one has opened it # we open pidfile # we block reading pidfile # ustd writes to pidfile # ustd closes pidfile # we unblock reading pidfile USTDPID="$(<$pidfilepath)" export UST_DAEMON_SOCKET="$USTDSOCKPATH" fi # Establish the environment for the command ( export UST_TRACE=1 export UST_AUTOPROBE=1 if [ "$arg_preload_libust" = "1" ]; then if [ -n "${LIBUST_PATH%libust.so}" ] ; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${LIBUST_PATH%libust.so}" fi export LD_PRELOAD="$LD_PRELOAD:$LIBUST_PATH" fi if [ "$arg_ld_std_ust" = "1" ]; then if [ -n "$${LIBUST_PATH%libust.so}" ] ; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${LIBUST_PATH%libust.so}" fi fi if [ "$arg_preload_malloc" = "1" ]; then export LD_PRELOAD="$LD_PRELOAD:$LIBMALLOCWRAP_PATH" fi if [ "$arg_preload_fork" = "1" ]; then export LD_PRELOAD="$LD_PRELOAD:$LIBINTERFORK_PATH" fi # Execute the command $CMD 2>&1 ) | tee "$OUTDIR/app.log" ## Because of the keepalive mechanism, we're sure that by the time ## we get here, the daemon is connected to all the buffers that still exist. ## Therefore we can politely ask it to die when it's done. if [ "$arg_syswide_daemon" != "1" ]; then # Tell the daemon to die kill -SIGTERM "$USTDPID" echo "Waiting for ustd to shutdown..." wait "$USTDPID" rm "$pidfilepath" fi echo "Trace was output in: " $OUTDIR