#!/bin/bash
#
# Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
#
# SPDX-License-Identifier: LGPL-2.1-only

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=$(mktemp --tmpdir -u tmp.test_periodical_metadata_flush_ust_app_tmp_file.XXXXXX)
APPS_PID=

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 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 -i $NR_ITER -w $NR_USEC_WAIT --sync-after-first-event $APP_TMP_FILE &
	ret=$?
	APPS_PID="${APPS_PID} ${!}"
	ok $ret "Start application to trace"
}

function start_check_trace_app()
{
	start_trace_app
	check_app_tmp_file
}


function wait_trace_apps()
{
	for p in ${APPS_PID}; do
		wait ${p} 2>/dev/null
	done
	APPS_PID=
}

test_after_app_pid() {
	local out

	APPS_PID=

	diag "Start application AFTER tracing is started"

	create_lttng_session_ok $SESSION_NAME $TRACE_PATH
	enable_metadata_per_pid $SESSION_NAME
	enable_channel_per_pid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $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.
	for p in ${APPS_PID}; do
		kill -s SIGSTOP ${p}
	done

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

	validate_trace
	out=$?

	for p in ${APPS_PID}; do
		kill -s SIGKILL ${p}
		wait ${p} 2>/dev/null
	done
	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

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

	APPS_PID=

	diag "Start application BEFORE tracing is started"

	start_trace_app

	# Start application before tracing
	create_lttng_session_ok $SESSION_NAME $TRACE_PATH
	enable_metadata_per_pid $SESSION_NAME
	enable_channel_per_pid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $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.
	for p in ${APPS_PID}; do
		kill -s SIGSTOP ${p}
	done

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

	validate_trace
	out=$?

	for p in ${APPS_PID}; do
		kill -s SIGKILL ${p}
		wait ${p} 2>/dev/null
	done

	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

test_after_app_uid() {
	local out

	APPS_PID=

	diag "Start application AFTER tracing is started"

	create_lttng_session_ok $SESSION_NAME $TRACE_PATH
	enable_metadata_per_uid $SESSION_NAME
	enable_channel_per_uid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $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.
	for p in ${APPS_PID}; do
		kill -s SIGSTOP ${p}
	done


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

	validate_trace
	out=$?

	for p in ${APPS_PID}; do
		kill -s SIGKILL ${p}
		wait ${p} 2>/dev/null
	done
	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

test_before_app_uid() {
	local out

	APPS_PID=

	diag "Start application BEFORE tracing is started"

	# Start application before tracing
	start_trace_app

	create_lttng_session_ok $SESSION_NAME $TRACE_PATH
	enable_metadata_per_uid $SESSION_NAME
	enable_channel_per_uid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $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.
	for p in ${APPS_PID}; do
		kill -s SIGSTOP ${p}
	done

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

	validate_trace
	out=$?

	for p in ${APPS_PID}; do
		kill -s SIGKILL ${p}
		wait ${p} 2>/dev/null
	done
	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_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 --tmpdir -d tmp.test_periodical_metadata_flush_ust_trace_path.XXXXXX)
	${TESTS[$i]}
	rm -rf $TRACE_PATH
	let "i++"
done

stop_lttng_sessiond
