#!/bin/sh # usttrace by Pierre-Marc Fournier 2009 # Distributed under the GPLv2. error() { echo "$0: error: $1" 1>&2 } sighandler() { echo "Caught Ctrl-C" if [ -z "${UST_CONSUMERD_PID}" ]; then UST_CONSUMERD_PID=`cat $pidfilepath` fi # Tell the daemon to die kill -TERM "${UST_CONSUMERD_PID}" echo "Waiting for ust-consumerd to shutdown..." wait "${UST_CONSUMERD_PID}" rm "$pidfilepath" exit 0; } USTTRACE_DIR="$(dirname $0)" if [ -x "${USTTRACE_DIR}/ust-consumerd/ust-consumerd" ] ; then # Use the not installed libraries instead UST_CONSUMERD="${USTTRACE_DIR}/ust-consumerd/ust-consumerd" LIBINTERFORK_PATH="${USTTRACE_DIR}/libustfork/.libs/libustfork.so" LIBMALLOCWRAP_PATH="${USTTRACE_DIR}/libustinstr-malloc/.libs/libustinstr-malloc.so" LIBUST_PATH="${USTTRACE_DIR}/libust/.libs/libust.so" else # Use the libraries that the dynamic link finds UST_CONSUMERD="ust-consumerd" if [ ! -x "$(which ust-consumerd 2>/dev/null)" ]; then error "cannot find an executable ust-consumerd; make sure its location is in the PATH" exit 1 fi LIBINTERFORK_PATH="libustfork.so" LIBMALLOCWRAP_PATH="libustinstr-malloc.so" LIBUST_PATH="libust.so.0" fi BASE_TRACE_DIR="${HOME}/.usttraces" usage() { echo "usage: $0 OPTIONS COMMAND" 1>&2 echo "" 1>&2 echo "Options:" 1>&2 echo " -l Runtime link with UST library." 1>&2 echo " (Needed only if program was not linked at compile time with libust.)" 1>&2 echo " -L Add path to ust libraries to LD_LIBRARY_PATH." 1>&2 echo " -m Instrument malloc calls." 1>&2 echo " -f Also trace forked processes." 1>&2 echo " -s Use system-wide daemon instead of creating one for this session." 1>&2 echo " -S Specify the subbuffer size." 1>&2 echo " -N Specify the number of subbuffers." 1>&2 echo " -o Output directory of the trace." 1>&2 } while getopts ":hlLmfsWS:N:o:" 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;; S) export UST_SUBBUF_SIZE=$OPTARG;; N) export UST_SUBBUF_NUM=$OPTARG;; o) OUTPUT_DIR=$OPTARG;; 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 # Validate input if [ -z "$HOME" ]; then error "no home specified" fi if [ -z "$*" ]; then error "no command specified" usage; exit 1 fi # Create directory for trace output if [ -n "$OUTPUT_DIR" ]; then OUTDIR=$OUTPUT_DIR else DATESTRING="$(hostname)-$(date +%Y%m%d%H%M%S%N)" OUTDIR="$BASE_TRACE_DIR/$DATESTRING" fi # Check if directory exist if [ ! -d "$OUTDIR" ]; then mkdir -p $OUTDIR if [ $? -eq 1 ]; then exit 1 fi fi # Choose ust-consumerd socket path UST_CONSUMERD_SOCKPATH="/tmp/ust-consumerd-sock-$$" if [ "$arg_syswide_daemon" != "1" ]; then pidfilepath="/tmp/usttrace-$USER-$(date +%Y%m%d%H%M%S%N)-ust-consumerd-pid" trap "sighandler $pidfilepath" INT mkfifo -m 0600 "$pidfilepath" # Start daemon ${UST_CONSUMERD} --pidfile "$pidfilepath" -s "${UST_CONSUMERD_SOCKPATH}" -o "$OUTDIR" >"$OUTDIR/ust-consumerd.log" 2>&1 & # ust-consumerd sets up its server socket # ust-consumerd opens the pidfile, blocks because no one has opened it # we open pidfile # we block reading pidfile # ust-consumerd writes to pidfile # ust-consumerd closes pidfile # we unblock reading pidfile UST_CONSUMERD_PID=`cat $pidfilepath` export UST_DAEMON_SOCKET="${UST_CONSUMERD_SOCKPATH}" 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 if [ -n "$LD_LIBRARY_PATH" ]; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${LIBUST_PATH%libust.so}" else export LD_LIBRARY_PATH="${LIBUST_PATH%libust.so}" fi fi if [ -n "$LIBUST_PATH" ]; then if [ -n "$LD_PRELOAD" ]; then export LD_PRELOAD="$LD_PRELOAD:$LIBUST_PATH" else export LD_PRELOAD="$LIBUST_PATH" fi fi fi if [ "$arg_ld_std_ust" = "1" ] && [ -n "${LIBUST_PATH%libust.so}" ]; then if [ -n "$LD_LIBRARY_PATH" ]; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${LIBUST_PATH%libust.so}" else export LD_LIBRARY_PATH="${LIBUST_PATH%libust.so}" fi fi if [ "$arg_preload_malloc" = "1" ] && [ -n "$LIBMALLOCWRAP_PATH" ]; then if [ -n "$LD_PRELOAD" ]; then export LD_PRELOAD="$LD_PRELOAD:$LIBMALLOCWRAP_PATH" else export LD_PRELOAD="$LIBMALLOCWRAP_PATH" fi fi if [ "$arg_preload_fork" = "1" ] && [ -n "$LIBINTERFORK_PATH" ]; then if [ -n "$LD_PRELOAD" ]; then export LD_PRELOAD="$LD_PRELOAD:$LIBINTERFORK_PATH" else export LD_PRELOAD="$LIBINTERFORK_PATH" fi fi # Execute the command "$@" 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 -TERM "${UST_CONSUMERD_PID}" echo "Waiting for ust-consumerd to shutdown..." wait "${UST_CONSUMERD_PID}" rm "$pidfilepath" fi echo "Trace was output in: " $OUTDIR