#!/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

# 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
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
    $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 -TERM "${UST_CONSUMERD_PID}"

	echo "Waiting for ust-consumerd to shutdown..."
	wait "${UST_CONSUMERD_PID}"

	rm "$pidfilepath"
fi

echo "Trace was output in: " $OUTDIR
