#!/bin/bash
#
# Copyright (C) - 2012 David Goulet <dgoulet@efficios.com>
#
# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; version 2.1 of the License.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
TEST_DESC="UST tracer - Tracing with per UID buffers and periodical flush"

CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../../..
NR_ITER=100
NR_USEC_WAIT=100000
TESTAPP_PATH="$TESTDIR/utils/testapp"
TESTAPP_NAME="gen-ust-events"
TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
SESSION_NAME="periodical-flush"
EVENT_NAME="tp:tptest"
BIN_NAME="gen-nevents"
NUM_TESTS=38
APP_TMP_FILE="/tmp/lttng_test_ust.42.file"

source $TESTDIR/utils/utils.sh

if [ ! -x "$TESTAPP_BIN" ]; then
	BAIL_OUT "No UST events binary detected."
fi

# MUST set TESTDIR before calling those functions

function enable_channel_per_uid()
{
	local sess_name=$1
	local channel_name=$2

	$TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-uid -u $channel_name -s $sess_name --switch-timer 100000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per UID for session $sess_name"
}

function enable_channel_per_pid()
{
	local sess_name=$1
	local channel_name=$2

	$TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-pid -u $channel_name -s $sess_name --switch-timer 100000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per UID for session $sess_name"
}

function enable_metadata_per_uid()
{
	local sess_name=$1
	local channel_name="metadata"

	$TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-uid -u $channel_name -s $sess_name --switch-timer 200000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per UID for session $sess_name"
}

function enable_metadata_per_pid()
{
	local sess_name=$1
	local channel_name="metadata"

	$TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-pid -u $channel_name -s $sess_name --switch-timer 200000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per PID for session $sess_name"
}

function wait_apps
{
	diag "Waiting for applications to end..."
	while [ -n "$(pidof $TESTAPP_NAME)" ]; do
		sleep 1
	done
}

function validate_trace()
{
	local out

	out=$(babeltrace $TRACE_PATH | grep $EVENT_NAME | wc -l)
	if [ $out -eq 0 ]; then
		fail "Trace validation"
		diag "No event(s) found. We are supposed to have at least one."
		out=1
	else
		pass "Trace validation"
		diag "Found $out event(s). Coherent."
		out=0
	fi

	return $out
}

function check_app_tmp_file()
{
	# Wait for the application file to appear indicating that at least one
	# tracepoint has been fired.
	while [ ! -f "$APP_TMP_FILE" ]; do
		sleep 0.5
	done
	diag "Removing test app temporary file $APP_TMP_FILE"
	rm -f $APP_TMP_FILE
}

function start_trace_app()
{
	# Start application with a temporary file.
	$TESTAPP_BIN $NR_ITER $NR_USEC_WAIT $APP_TMP_FILE &
	ok $? "Start application to trace"
}

function start_check_trace_app()
{
	start_trace_app
	check_app_tmp_file
}

test_after_app_pid() {
	local out

	diag "Start application AFTER tracing is started"

	create_lttng_session $SESSION_NAME $TRACE_PATH
	enable_metadata_per_pid $SESSION_NAME
	enable_channel_per_pid $SESSION_NAME "channel0"
	enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing $SESSION_NAME

	# Start application after tracing
	start_check_trace_app
	# After this point we are sure that at least one event has been hit.

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	killall -SIGSTOP -q $TESTAPP_NAME

	# Give time to the consumer to write inflight data.
	sleep 2

	validate_trace
	out=$?

	stop_lttng_tracing $SESSION_NAME
	destroy_lttng_session $SESSION_NAME

	killall -SIGKILL -q $TESTAPP_NAME
	wait_apps

	return $out
}

test_before_app_pid() {
	local out
	local tmp_file="/tmp/lttng_test_ust.42.file"

	diag "Start application BEFORE tracing is started"

	start_trace_app

	# Start application before tracing
	create_lttng_session $SESSION_NAME $TRACE_PATH
	enable_metadata_per_pid $SESSION_NAME
	enable_channel_per_pid $SESSION_NAME "channel0"
	enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing $SESSION_NAME

	check_app_tmp_file
	# Let the application at least perform a flush!
	sleep 2

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	killall -SIGSTOP -q $TESTAPP_NAME

	# Give time to the consumer to write inflight data.
	sleep 2

	validate_trace
	out=$?

	stop_lttng_tracing $SESSION_NAME
	destroy_lttng_session $SESSION_NAME

	killall -SIGKILL -q $TESTAPP_NAME
	wait_apps

	return $out
}

test_after_app_uid() {
	local out

	diag "Start application AFTER tracing is started"

	create_lttng_session $SESSION_NAME $TRACE_PATH
	enable_metadata_per_uid $SESSION_NAME
	enable_channel_per_uid $SESSION_NAME "channel0"
	enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing $SESSION_NAME

	# Start application after tracing
	start_check_trace_app
	# After this point we are sure that at least one event has been hit.

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	killall -SIGSTOP -q $TESTAPP_NAME

	# Give time to the consumer to write inflight data.
	sleep 2

	validate_trace
	out=$?

	stop_lttng_tracing $SESSION_NAME
	destroy_lttng_session $SESSION_NAME

	killall -SIGKILL -q $TESTAPP_NAME
	wait_apps

	return $out
}

test_before_app_uid() {
	local out

	diag "Start application BEFORE tracing is started"

	# Start application before tracing
	start_trace_app

	create_lttng_session $SESSION_NAME $TRACE_PATH
	enable_metadata_per_uid $SESSION_NAME
	enable_channel_per_uid $SESSION_NAME "channel0"
	enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing $SESSION_NAME

	check_app_tmp_file
	# Let the application at least perform a flush!
	sleep 2

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	killall -SIGSTOP -q $TESTAPP_NAME

	# Give time to the consumer to write inflight data.
	sleep 2

	validate_trace
	out=$?

	stop_lttng_tracing $SESSION_NAME
	destroy_lttng_session $SESSION_NAME

	killall -SIGKILL -q $TESTAPP_NAME
	wait_apps

	return $out
}

# MUST set TESTDIR before calling those functions
plan_tests $NUM_TESTS

print_test_banner "$TEST_DESC"

TESTS=(
	"test_before_app_uid"
	"test_after_app_uid"
	"test_before_app_pid"
	"test_after_app_pid"
)

TEST_COUNT=${#TESTS[@]}
i=0

start_lttng_sessiond

while [ $i -lt $TEST_COUNT ]; do
	TRACE_PATH=$(mktemp -d)
	${TESTS[$i]}
	rm -rf $TRACE_PATH
	let "i++"
done

stop_lttng_sessiond
