#!/bin/bash
#
# Copyright (C) - 2012 Christian Babeux <christian.babeux@efficios.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License, version 2 only, as
# published by the Free Software Foundation.
#
# This program 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 General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

TEST_DESC="Filtering - Valid filters"

CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../../..
LTTNG_BIN="lttng"
STATS_BIN="$TESTDIR/utils/babelstats.pl"
SESSION_NAME="valid_filter"
NR_ITER=100
NUM_GLOBAL_TESTS=2
NUM_UST_TESTS=330
NUM_KERNEL_TESTS=330
NUM_TESTS=$(($NUM_UST_TESTS+$NUM_KERNEL_TESTS+$NUM_GLOBAL_TESTS))

source $TESTDIR/utils/utils.sh

function enable_lttng_event_filter()
{
	domain="$1"
	sess_name="$2"
	event_name="$3"
	filter="$4"

	$TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $event_name \
			-s $sess_name $domain --filter "$filter" 2>&1 >/dev/null
	$TESTDIR/../src/bin/lttng/$LTTNG_BIN add-context \
			-s $sess_name $domain -t procname 2>&1 >/dev/null

	ok $? "Enable lttng event with filtering"
}

function run_ust
{
	./$CURDIR/$BIN_NAME $NR_ITER >/dev/null 2>&1
}

function run_kernel
{
	# Trigger the event for 100 iterations
	echo -n "100" > /proc/lttng-test-filter-event
}

function test_valid_filter
{
	domain_name="$1"
	domain="$2"
	event_name="$3"
	filter="$4"
	validator="$5"

	diag "Test valid $domain_name filter: $filter"

	trace_path=$(mktemp -d)

	# Create session
	create_lttng_session_ok $SESSION_NAME $trace_path

	# Enable filter
	enable_lttng_event_filter $domain $SESSION_NAME "$event_name" "$filter"

	# Trace apps
	start_lttng_tracing_ok $SESSION_NAME
	run_$domain_name
	stop_lttng_tracing_ok $SESSION_NAME

	# Destroy session
	destroy_lttng_session_ok $SESSION_NAME

	stats=`babeltrace $trace_path | $STATS_BIN --tracepoint $event_name`

	rm -rf $trace_path

	$validator "$stats"

	ok $? "Validate trace filter output"

	rm -rf $trace_path
}

function validate_min_max
{
	stats="$1"
	field=$2
	expected_min=$3
	expected_max=$4

	echo $stats | grep -q "$field $expected_min $expected_max"

	return $?
}

function validator_intfield
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "1" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "intfield2" "0x1" "0x63"
	status=$(($status|$?))

	validate_min_max "$stats" "longfield" "1" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "netintfield" "1" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "netintfieldhex" "0x1" "0x63"
	status=$(($status|$?))

	if [ $KERNEL_CHECK -eq 0 ]; then
		validate_min_max "$stats" "floatfield" "2222" "2222"
		status=$(($status|$?))

		validate_min_max "$stats" "doublefield" "2" "2"
		status=$(($status|$?))
	fi

	return $status
}

function validator_intfield_gt
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "2" "99"
	status=$(($status|$?))

	return $status
}

function validator_has_no_event
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "99"
	status=$(($status|$?))

	if [ $status -eq 0 ]; then
		return 1
	else
		return 0
	fi
}

function validator_has_events
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "99"
	status=$(($status|$?))

	return $status
}

function validator_intfield_ge
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "1" "99"
	status=$(($status|$?))

	return $status
}

function validator_intfield_lt
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "1"
	status=$(($status|$?))

	return $status
}

function validator_intfield_le
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "2"
	status=$(($status|$?))

	return $status
}

function validator_intfield_eq
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "1" "1"
	status=$(($status|$?))

	return $status
}

function validator_intfield_ne
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "98"
	status=$(($status|$?))

	return $status
}

function validator_intfield_not
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "0"
	status=$(($status|$?))

	return $status
}

function validator_intfield_gt_and_longfield_gt
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "43" "99"
	status=$(($status|$?))
	validate_min_max "$stats" "longfield" "43" "99"
	status=$(($status|$?))

	return $status
}

function validator_intfield_ge_and_longfield_le
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "42" "42"
	status=$(($status|$?))
	validate_min_max "$stats" "longfield" "42" "42"
	status=$(($status|$?))

	return $status
}

function validator_intfield_lt_or_longfield_gt
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "99"
	status=$(($status|$?))
	validate_min_max "$stats" "longfield" "0" "99"
	status=$(($status|$?))

	return $status
}

function validator_mixed_str_or_int_and_int
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "34" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "stringfield" "\"test\"" "\"test\""
	status=$(($status|$?))

	return $status
}

function validator_mixed_int_double
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "42"
	status=$(($status|$?))

	return $status
}

function validator_true_statement
{
	stats="$1"
	status=0

	validate_min_max "$stats" "intfield" "0" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "intfield2" "0x0" "0x63"
	status=$(($status|$?))

	validate_min_max "$stats" "longfield" "0" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "netintfield" "0" "99"
	status=$(($status|$?))

	validate_min_max "$stats" "netintfieldhex" "0x0" "0x63"
	status=$(($status|$?))

	if [ $KERNEL_CHECK -eq 0 ]; then
		validate_min_max "$stats" "floatfield" "2222" "2222"
		status=$(($status|$?))

		validate_min_max "$stats" "doublefield" "2" "2"
		status=$(($status|$?))
	fi

	validate_min_max "$stats" "stringfield" "\"test\"" "\"test\""
	status=$(($status|$?))

	validate_min_max "$stats" "stringfield2" ""\*"" ""\*""
	status=$(($status|$?))

	return $status
}

plan_tests $NUM_TESTS

print_test_banner "$TEST_DESC"

issue_356_filter="intfield > 0 && intfield > 1 && "
issue_356_filter+="intfield > 2 && intfield > 3 && "
issue_356_filter+="intfield > 4 && intfield > 5 && "
issue_356_filter+="intfield > 6 && intfield > 7 && "
issue_356_filter+="intfield > 8 || intfield > 0"

start_lttng_sessiond

### UST TESTS

BIN_NAME="gen-ust-events"

# One to one mapping between filters and validators

# Set IFS to newline to facilitate array declaration
OLDIFS="$IFS"
IFS=$'\n'
UST_FILTERS=("intfield"                                                 #1
	 "intfield > 1"                                                 #2
	 "intfield >= 1"                                                #3
	 "intfield < 2"                                                 #4
	 "intfield <= 2"                                                #5
	 "intfield == 1"                                                #6
	 "intfield != 99"                                               #7
	 "!intfield"                                                    #8
	 "-intfield"                                                    #9
	 "--intfield"                                                   #10
	 "+intfield"                                                    #11
	 "++intfield"                                                   #12
	 "intfield > 1 && longfield > 42"                               #13
	 "intfield >= 42 && longfield <= 42"                            #14
	 "intfield < 1 || longfield > 98"                               #15
	 "(stringfield == \"test\" || intfield != 10) && intfield > 33" #16
	 "intfield < 42.4242424242"                                     #17
	 "\"test\" == \"test\""                                         #18 #Issue #342
	 "stringfield == \"test\""                                      #19
	 "stringfield == \"t*\""                                        #20
	 "stringfield == \"*\""                                         #21
	 $issue_356_filter                                              #22 #Issue #356
	 "intfield < 0xDEADBEEF"                                        #23
	 "intfield < 0x2"                                               #24
	 "intfield < 02"                                                #25
	 "stringfield2 == \"\\\*\""                                     #26
	 "1.0 || intfield || 1.0"                                       #27
	 "1 < intfield"                                                 #28
	 "\$ctx.vtid == 0"                                              #29
	 "\$ctx.vtid != 0"                                              #30
	 "0 == \$ctx.vtid"                                              #31
	 "0 != \$ctx.vtid"                                              #32
	 "\$ctx.vpid == 0"                                              #33
	 "\$ctx.vpid != 0"                                              #34
	 "0 == \$ctx.vpid"                                              #35
	 "0 != \$ctx.vpid"                                              #36
	 "\$ctx.procname != \"$BIN_NAME\""                              #37
	 "\$ctx.procname == \"$BIN_NAME\""                              #38
	 "\"$BIN_NAME\" != \$ctx.procname"                              #39
	 "\"$BIN_NAME\" == \$ctx.procname"                              #40
	 "\$ctx.procname != \"$BIN_NAME*\""                             #41
	 "\$ctx.procname == \"$BIN_NAME*\""                             #42
	 "\"$BIN_NAME*\" != \$ctx.procname"                             #43
	 "\"$BIN_NAME*\" == \$ctx.procname"                             #44
	 "\$ctx.procname != \"*\""                                      #45
	 "\$ctx.procname == \"*\""                                      #46
	 "\"*\" != \$ctx.procname"                                      #47
	 "\"*\" == \$ctx.procname"                                      #48
	 "!a.f.d"							#49
	 "a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a"			#50
	 "\$ctx.vtid.blah == 0"						#51
	 "asdf.asdfsd.sadf < 4"						#52
	 "0 == \$ctx.vtid.blah"						#53
	 "\$ctx.44 == 0"						#54
	 "0 == \$ctx.44"						#55
)

UST_FILTER_COUNT=${#UST_FILTERS[@]}

if [ ! -x "$CURDIR/$BIN_NAME" ]; then
	BAIL_OUT "No UST nevents binary detected."
fi

UST_VALIDATOR=("validator_intfield"                 #1
	   "validator_intfield_gt"                  #2
	   "validator_intfield_ge"                  #3
	   "validator_intfield_lt"                  #4
	   "validator_intfield_le"                  #5
	   "validator_intfield_eq"                  #6
	   "validator_intfield_ne"                  #7
	   "validator_intfield_not"                 #8
	   "validator_intfield"                     #9
	   "validator_intfield"                     #10
	   "validator_intfield"                     #11
	   "validator_intfield"                     #12
	   "validator_intfield_gt_and_longfield_gt" #13
	   "validator_intfield_ge_and_longfield_le" #14
	   "validator_intfield_lt_or_longfield_gt"  #15
	   "validator_mixed_str_or_int_and_int"     #16
	   "validator_mixed_int_double"             #17
	   "validator_true_statement"               #18
	   "validator_true_statement"               #19
	   "validator_true_statement"               #20
	   "validator_true_statement"               #21
	   "validator_intfield"                     #22
	   "validator_true_statement"               #23
	   "validator_intfield_lt"                  #24
	   "validator_intfield_lt"                  #25
	   "validator_true_statement"               #26
	   "validator_true_statement"               #27
	   "validator_intfield_gt"                  #28
	   "validator_has_no_event"                 #29
	   "validator_has_events"                   #30
	   "validator_has_no_event"                 #31
	   "validator_has_events"                   #32
	   "validator_has_no_event"                 #33
	   "validator_has_events"                   #34
	   "validator_has_no_event"                 #35
	   "validator_has_events"                   #36
	   "validator_has_no_event"                 #36
	   "validator_has_events"                   #37
	   "validator_has_no_event"                 #38
	   "validator_has_events"                   #39
	   "validator_has_no_event"                 #41
	   "validator_has_events"                   #42
	   "validator_has_no_event"                 #43
	   "validator_has_events"                   #44
	   "validator_has_no_event"                 #45
	   "validator_has_events"                   #46
	   "validator_has_no_event"                 #47
	   "validator_has_events"                   #48
	   "validator_has_no_event"                 #49
	   "validator_has_no_event"                 #50
	   "validator_has_no_event"                 #51
	   "validator_has_no_event"                 #52
	   "validator_has_no_event"                 #53
	   "validator_has_no_event"                 #54
	   "validator_has_no_event"                 #55
)

IFS="$OLDIFS"

diag "Test UST valid filters"

KERNEL_CHECK=0
i=0
while [ "$i" -lt "$UST_FILTER_COUNT" ]; do

	test_valid_filter ust -u "tp:tptest" "${UST_FILTERS[$i]}" "${UST_VALIDATOR[$i]}"

	if [ $? -eq 1 ]; then
		stop_lttng_sessiond
		exit 1
	fi

	let "i++"
done


### KERNEL TESTS

BIN_NAME="test_valid_filt"	# Current script name truncated by kernel

# One to one mapping between filters and validators
# Set IFS to newline to facilitate array declaration
OLDIFS="$IFS"
IFS=$'\n'
KERNEL_FILTERS=("intfield"                                              #1
	 "intfield > 1"                                                 #2
	 "intfield >= 1"                                                #3
	 "intfield < 2"                                                 #4
	 "intfield <= 2"                                                #5
	 "intfield == 1"                                                #6
	 "intfield != 99"                                               #7
	 "!intfield"                                                    #8
	 "-intfield"                                                    #9
	 "--intfield"                                                   #10
	 "+intfield"                                                    #11
	 "++intfield"                                                   #12
	 "intfield > 1 && longfield > 42"                               #13
	 "intfield >= 42 && longfield <= 42"                            #14
	 "intfield < 1 || longfield > 98"                               #15
	 "(stringfield == \"test\" || intfield != 10) && intfield > 33" #16
	 "intfield < 42.4242424242"                                     #17
	 "\"test\" == \"test\""                                         #18 #Issue #342
	 "stringfield == \"test\""                                      #19
	 "stringfield == \"t*\""                                        #20
	 "stringfield == \"*\""                                         #21
	 $issue_356_filter                                              #22 #Issue #356
	 "intfield < 0xDEADBEEF"                                        #23
	 "intfield < 0x2"                                               #24
	 "intfield < 02"                                                #25
	 "stringfield2 == \"\\\*\""                                     #26
	 "1.0 || intfield || 1.0"                                       #27
	 "1 < intfield"                                                 #28
	 "\$ctx.vtid == 0"                                              #29
	 "\$ctx.vtid != 0"                                              #30
	 "0 == \$ctx.vtid"                                              #31
	 "0 != \$ctx.vtid"                                              #32
	 "\$ctx.vpid == 0"                                              #33
	 "\$ctx.vpid != 0"                                              #34
	 "0 == \$ctx.vpid"                                              #35
	 "0 != \$ctx.vpid"                                              #36
	 "\$ctx.procname != \"$BIN_NAME\""                              #37
	 "\$ctx.procname == \"$BIN_NAME\""                              #38
	 "\"$BIN_NAME\" != \$ctx.procname"                              #39
	 "\"$BIN_NAME\" == \$ctx.procname"                              #40
	 "\$ctx.procname != \"$BIN_NAME*\""                             #41
	 "\$ctx.procname == \"$BIN_NAME*\""                             #42
	 "\"$BIN_NAME*\" != \$ctx.procname"                             #43
	 "\"$BIN_NAME*\" == \$ctx.procname"                             #44
	 "\$ctx.procname != \"*\""                                      #45
	 "\$ctx.procname == \"*\""                                      #46
	 "\"*\" != \$ctx.procname"                                      #47
	 "\"*\" == \$ctx.procname"                                      #48
	 "!a.f.d"							#49
	 "a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a"			#50
	 "\$ctx.vtid.blah == 0"						#51
	 "asdf.asdfsd.sadf < 4"						#52
	 "0 == \$ctx.vtid.blah"						#53
	 "\$ctx.44 == 0"						#54
	 "0 == \$ctx.44"						#55
)

KERNEL_FILTER_COUNT=${#KERNEL_FILTERS[@]}

KERNEL_VALIDATOR=("validator_intfield"              #1
	   "validator_intfield_gt"                  #2
	   "validator_intfield_ge"                  #3
	   "validator_intfield_lt"                  #4
	   "validator_intfield_le"                  #5
	   "validator_intfield_eq"                  #6
	   "validator_intfield_ne"                  #7
	   "validator_intfield_not"                 #8
	   "validator_intfield"                     #9
	   "validator_intfield"                     #10
	   "validator_intfield"                     #11
	   "validator_intfield"                     #12
	   "validator_intfield_gt_and_longfield_gt" #13
	   "validator_intfield_ge_and_longfield_le" #14
	   "validator_intfield_lt_or_longfield_gt"  #15
	   "validator_mixed_str_or_int_and_int"     #16
	   "validator_has_no_event"                 #17 #Unsupported by kernel
	   "validator_true_statement"               #18
	   "validator_true_statement"               #19
	   "validator_true_statement"               #20
	   "validator_true_statement"               #21
	   "validator_intfield"                     #22
	   "validator_true_statement"               #23
	   "validator_intfield_lt"                  #24
	   "validator_intfield_lt"                  #25
	   "validator_true_statement"               #26
	   "validator_has_no_event"                 #27 #Unsupported by kernel
	   "validator_intfield_gt"                  #28
	   "validator_has_no_event"                 #29
	   "validator_has_events"                   #30
	   "validator_has_no_event"                 #31
	   "validator_has_events"                   #32
	   "validator_has_no_event"                 #33
	   "validator_has_events"                   #34
	   "validator_has_no_event"                 #35
	   "validator_has_events"                   #36
	   "validator_has_no_event"                 #36
	   "validator_has_events"                   #37
	   "validator_has_no_event"                 #38
	   "validator_has_events"                   #39
	   "validator_has_no_event"                 #41
	   "validator_has_events"                   #42
	   "validator_has_no_event"                 #43
	   "validator_has_events"                   #44
	   "validator_has_no_event"                 #45
	   "validator_has_events"                   #46
	   "validator_has_no_event"                 #47
	   "validator_has_events"                   #48
	   "validator_has_no_event"                 #49
	   "validator_has_no_event"                 #50
	   "validator_has_no_event"                 #51
	   "validator_has_no_event"                 #52
	   "validator_has_no_event"                 #53
	   "validator_has_no_event"                 #54
	   "validator_has_no_event"                 #55
)

IFS=$OLDIFS

if [ "$(id -u)" == "0" ]; then
	isroot=1
else
	isroot=0
fi

skip $isroot "Root access is needed. Skipping all kernel valid filter tests." $NUM_KERNEL_TESTS ||
{
	diag "Test kernel valid filters"

	KERNEL_CHECK=1
	modprobe lttng-test
	i=0
	while [ "$i" -lt "$KERNEL_FILTER_COUNT" ]; do

		test_valid_filter kernel -k "lttng_test_filter_event" \
				"${KERNEL_FILTERS[$i]}" "${KERNEL_VALIDATOR[$i]}"

		if [ $? -eq 1 ]; then
			stop_lttng_sessiond
			exit 1
		fi

		let "i++"
	done
	rmmod lttng-test
}

stop_lttng_sessiond
