#!/bin/bash
#
# SPDX-FileCopyrightText: 2012 Christian Babeux <christian.babeux@efficios.com>
#
# SPDX-License-Identifier: GPL-2.0-only
#

TEST_DESC="Event exclusion"

CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../../..
BT2_PLUGINS_DIR="${TESTDIR}/utils/bt2_plugins"

SESSION_NAME="test-exclusion"
TESTAPP_PATH="$TESTDIR/utils/testapp"
TESTAPP_NAME="gen-ust-nevents"
TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
NR_ITER=100
NR_USEC_WAIT=1
NUM_TESTS=185

source $TESTDIR/utils/utils.sh

function enable_ust_lttng_all_event_exclusion()
{
	sess_name="$1"
	exclusion="$2"

	_run_lttng_cmd "$(lttng_client_log_file)" "$(lttng_client_err_file)" \
		enable-event --userspace "tp:*" --session "$sess_name" \
		--exclude "$exclusion"
}

function run_apps
{
	$TESTAPP_BIN --iter $NR_ITER --wait $NR_USEC_WAIT >/dev/null 2>&1
	ok $? "Running test application"
}

# Testing for the absence of an event when testing exclusion is tricky. An
# event could be absent because our exclusion mechanism works but also because
# the event was not generate in the first place. This function test the ability
# of our test suite to generate events.
function dry_run
{
	local trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")

	# Create session
	create_lttng_session_ok $SESSION_NAME $trace_path

	_run_lttng_cmd "$(lttng_client_log_file)" "$(lttng_client_err_file)" \
		enable-event --userspace "tp:*" --session "$SESSION_NAME"
	ok $? "Enabling events without exclusion"

	# Trace apps
	start_lttng_tracing_ok $SESSION_NAME
	run_apps
	stop_lttng_tracing_ok $SESSION_NAME

	nb_events=$("_run_babeltrace_cmd" $trace_path | wc -l)
	if [ "$nb_events" -ne "0" ]; then
		ok 0 "Events were found during the dry run without exclusion"
	else
		fail "No events were found during the dry run without exclusion"
	fi

	rm -rf $trace_path

	# Destroy session
	destroy_lttng_session_ok $SESSION_NAME
}

function test_exclusion
{
	local exclusions="$1"
	local event_name_expected_to_be_missing="$2"
	local trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")

	# Create session
	create_lttng_session_ok $SESSION_NAME $trace_path

	enable_ust_lttng_all_event_exclusion $SESSION_NAME "$exclusions"
	ok $? "Enable lttng event with event \"$exclusions\" excluded"

	# Trace apps
	start_lttng_tracing_ok $SESSION_NAME
	run_apps
	stop_lttng_tracing_ok $SESSION_NAME

	# Destroy session
	destroy_lttng_session_ok $SESSION_NAME

	stats=$("_run_babeltrace_cmd" --plugin-path "${BT2_PLUGINS_DIR}" "${trace_path}" -c filter.lttngtest.event_name -p "names=[\"${event_name_expected_to_be_missing}\"]" -c sink.lttngtest.field_stats | grep -v index)
	if [ ! -z "$stats" ]; then
		fail "Excluded event \"$event_name_expected_to_be_missing\" was found in trace!"
	else
		ok 0 "Validate trace exclusion output"
		rm -rf $trace_path
	fi
}

function test_exclusion_tracing_started
{
	local exclusions="$1"
	local event_name_expected_to_be_missing="$2"
	local trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
	local file_wait_before_first=$(mktemp -u -t "tmp.${FUNCNAME[0]}_sync_before_first.XXXXXX")
	local file_create_in_main=$(mktemp -u -t "tmp.${FUNCNAME[0]}_sync_create_in_main.XXXXXX")

	# Create session
	create_lttng_session_ok $SESSION_NAME $trace_path

	# Enable a dummy event so that the session is active after we start the
	# session.
	enable_ust_lttng_event_ok $SESSION_NAME "non-existent-event"

	# Start the tracing
	start_lttng_tracing_ok $SESSION_NAME

	# Launch the test app and make it create a sync file once it's in the
	# main function.
	$TESTAPP_BIN -i 1 -w 10000 \
		--create-in-main ${file_create_in_main} \
		--wait-before-first-event ${file_wait_before_first} 2>&1 &
	tracee_pids+=("${!}")

	while [ ! -f "${file_create_in_main}" ]; do
		sleep 0.5
	done

	# Enable an event with an exclusion once the tracing is active in the
	# UST app.
	enable_ust_lttng_all_event_exclusion $SESSION_NAME "$exclusions"
	ok $? "Enable lttng event with event \"$exclusions\" excluded"

	# Create the sync file so that the test app starts generating events.
	touch ${file_wait_before_first}

	# Wait for the testapp to finish.
	wait "${tracee_pids[@]}"
	tracee_pids=()

	stop_lttng_tracing_ok $SESSION_NAME

	# Destroy session
	destroy_lttng_session_ok $SESSION_NAME

	stats=$("_run_babeltrace_cmd" --plugin-path "${BT2_PLUGINS_DIR}" "${trace_path}" -c filter.lttngtest.event_name -p "names=[\"${event_name_expected_to_be_missing}\"]" -c sink.lttngtest.field_stats | grep -v index)
	if [ ! -z "$stats" ]; then
		fail "Excluded event \"$event_name_expected_to_be_missing\" was found in trace!"
	else
		ok 0 "Validate trace exclusion output"
		rm -rf $trace_path
	fi

	rm -f $file_wait_before_first
	rm -f $file_create_in_main
}

function test_exclusion_fail
{
	event_name="$1"
	exclusions="$2"

	create_lttng_session_ok $SESSION_NAME $trace_path
	_run_lttng_cmd "$(lttng_client_log_file)" "$(lttng_client_err_file)" \
		enable-event --userspace "$event_name" --session "$sess_name" \
		--exclude "$exclusions"
	res=$?
	destroy_lttng_session_ok $SESSION_NAME

	if [ $res -eq 0 ]; then
		fail "Enable LTTng event \"$event_name\" with exclusion \"$exclusions\" passes"
		return 1
	else
		pass "Enable LTTng event \"$event_name\" with exclusion \"$exclusions\" fails"
		return 0
	fi
}

plan_tests $NUM_TESTS

print_test_banner "$TEST_DESC"

bail_out_if_no_babeltrace

start_lttng_sessiond
tracee_pids=()

diag "Enable event without exclusion"
dry_run

diag "Enable event with exclusion"
test_exclusion 'tp:tptest2' 'tp:tptest2'
test_exclusion 'tp:tptest3' 'tp:tptest3'
test_exclusion 'tp:tptest*' 'tp:tptest1'
test_exclusion 'tp:tptest*' 'tp:tptest2'
test_exclusion 'tp:tptest*' 'tp:tptest3'
test_exclusion 'tp:tptest*' 'tp:tptest4'
test_exclusion 'tp:tptest*' 'tp:tptest5'
test_exclusion 'tp*tptest*' 'tp:tptest1'
test_exclusion 'tp*tptest*' 'tp:tptest2'
test_exclusion 'tp*tptest*' 'tp:tptest3'
test_exclusion 'tp*tptest*' 'tp:tptest4'
test_exclusion 'tp*tptest*' 'tp:tptest5'
test_exclusion '*test2' 'tp:tptest2'
test_exclusion '*test5' 'tp:tptest5'
test_exclusion '*p*test*' 'tp:tptest1'
test_exclusion '*p*test*' 'tp:tptest2'
test_exclusion '*p*test*' 'tp:tptest3'
test_exclusion '*p***test*' 'tp:tptest4'
test_exclusion '*p*test*' 'tp:tptest5'
test_exclusion '*3' 'tp:tptest3'
test_exclusion 'tp*test3,*2' 'tp:tptest2'
test_exclusion '**tp*test3,*2' 'tp:tptest3'

test_exclusion_tracing_started 'tp:tptest1' 'tp:tptest1'

diag "Cannot use exclusions with non-globbing event name"
test_exclusion_fail "allo" "lol"
test_exclusion_fail "allo" "meow,lol"
test_exclusion_fail "allo" "z*em"

diag "Exclusion name excludes all possible event names"
test_exclusion_fail "allo*" "all*"
test_exclusion_fail "allo*" "ze,all*,yes"

stop_lttng_sessiond
