diff mbox series

[3/3] btrfs-progs: add extent buffer leak detection to make test

Message ID 4df1b25365287e0fa3e7b4c8d1400ad5d576d992.1693945163.git.josef@toxicpanda.com (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: add eb leak detection and fixes | expand

Commit Message

Josef Bacik Sept. 5, 2023, 8:21 p.m. UTC
I introduced a regression where we were leaking extent buffers, and this
resulted in the CI failing because we were spewing these errors.

Instead of waiting for fstests to catch my mistakes, check every command
output for leak messages, and fail the test if we detect any of these
messages.  I've made this generic enough that we could check for other
debug messages in the future.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 tests/common | 108 +++++++++++++++++++++++++++++----------------------
 1 file changed, 61 insertions(+), 47 deletions(-)

Comments

David Sterba Sept. 5, 2023, 8:49 p.m. UTC | #1
On Tue, Sep 05, 2023 at 04:21:53PM -0400, Josef Bacik wrote:
> I introduced a regression where we were leaking extent buffers, and this
> resulted in the CI failing because we were spewing these errors.
> 
> Instead of waiting for fstests to catch my mistakes, check every command
> output for leak messages, and fail the test if we detect any of these
> messages.  I've made this generic enough that we could check for other
> debug messages in the future.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>

There already is a script to verify more than just the error leaks, I've
added it to all the test type running scripts so it would fail.
> ---
>  tests/common | 108 +++++++++++++++++++++++++++++----------------------
>  1 file changed, 61 insertions(+), 47 deletions(-)
> 
> diff --git a/tests/common b/tests/common
> index 602a4122..607ad747 100644
> --- a/tests/common
> +++ b/tests/common
> @@ -160,6 +160,18 @@ _is_target_command()
>  	return 1
>  }
>  
> +# Check to see if there's any debug messages that may mean we have a problem.
> +_check_output()
> +{
> +	local results="$1"
> +
> +	if grep -q "extent buffer leak" "$results"; then
> +		_fail "extent buffer leak reported"

There's more than that we'd like to catch, see tests/scan-results.sh.

> +		return 1
> +	fi
> +	return 0
> +}
> +
>  # Expanding extra commands/options for current command string
>  # This function is responsible for inserting the following things:
>  # - @INSTRUMENT before 'btrfs'  commands
Qu Wenruo Sept. 5, 2023, 10:57 p.m. UTC | #2
On 2023/9/6 04:21, Josef Bacik wrote:
> I introduced a regression where we were leaking extent buffers, and this
> resulted in the CI failing because we were spewing these errors.
>
> Instead of waiting for fstests to catch my mistakes, check every command
> output for leak messages, and fail the test if we detect any of these
> messages.  I've made this generic enough that we could check for other
> debug messages in the future.
>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>

Another solution is to make debug build of btrfs-progs more noisy when
eb leak is detected.

Instead of a graceful exit (which is suitable for release build), a
noisy BUG_ON()/ASSERT() would definitely catch our attention, and
requires less work in the test framework.

Thanks,
Qu

> ---
>   tests/common | 108 +++++++++++++++++++++++++++++----------------------
>   1 file changed, 61 insertions(+), 47 deletions(-)
>
> diff --git a/tests/common b/tests/common
> index 602a4122..607ad747 100644
> --- a/tests/common
> +++ b/tests/common
> @@ -160,6 +160,18 @@ _is_target_command()
>   	return 1
>   }
>
> +# Check to see if there's any debug messages that may mean we have a problem.
> +_check_output()
> +{
> +	local results="$1"
> +
> +	if grep -q "extent buffer leak" "$results"; then
> +		_fail "extent buffer leak reported"
> +		return 1
> +	fi
> +	return 0
> +}
> +
>   # Expanding extra commands/options for current command string
>   # This function is responsible for inserting the following things:
>   # - @INSTRUMENT before 'btrfs'  commands
> @@ -206,6 +218,48 @@ expand_command()
>   	done
>   }
>
> +# This is the helper for the run_check variants.
> +# The first argument is the run_check type
> +# The second argument is the run_check type that will get logged to tty
> +# The third argument is wether we want the output echo'ed
> +# The rest of the arguments are the command
> +_run_check()
> +{
> +	local header_type
> +	local test_log_type
> +	local do_stdout
> +	local tmp_output
> +
> +	run_type="$1"
> +	shift
> +
> +	test_log_type="$1"
> +	shift
> +
> +	do_stdout="$1"
> +	shift
> +
> +	tmp_output=$(mktemp --tmpdir btrfs-progs-leak-detect.XXXXXX)
> +
> +	expand_command "$@"
> +	echo "====== RUN $run_type ${cmd_array[@]}" >> "$RESULTS" 2>&1
> +	if [[ $TEST_LOG =~ tty ]]; then echo "$test_log_type: ${cmd_array[@]}" \
> +		> /dev/tty; fi
> +	"${cmd_array[@]}" > "$tmp_output" 2>&1
> +	ret=$?
> +
> +	cat "$tmp_output" >> "$RESULTS"
> +	[ "$do_stdout" = true ] && cat "$tmp_output"
> +
> +	if ! _check_output "$tmp_output"; then
> +		_fail "bad output"
> +		rm "$tmp_output"
> +		return 1
> +	fi
> +	rm "$tmp_output"
> +	return "$ret"
> +}
> +
>   # Argument passing magic:
>   # the command passed to run_* helpers is inspected, if there's 'btrfs command'
>   # found and there are defined additional arguments, they're inserted just after
> @@ -216,11 +270,7 @@ expand_command()
>
>   run_check()
>   {
> -	expand_command "$@"
> -	echo "====== RUN CHECK ${cmd_array[@]}" >> "$RESULTS" 2>&1
> -	if [[ $TEST_LOG =~ tty ]]; then echo "CMD: ${cmd_array[@]}" > /dev/tty; fi
> -
> -	"${cmd_array[@]}" >> "$RESULTS" 2>&1 || _fail "failed: ${cmd_array[@]}"
> +	_run_check "CHECK" "CMD" "false" "$@" || _fail "failed: ${cmd_array[@]}"
>   }
>
>   # same as run_check but the stderr+stdout output is duplicated on stdout and
> @@ -230,12 +280,8 @@ run_check()
>   #	filter the output, as INSTRUMENT can easily pollute the output.
>   run_check_stdout()
>   {
> -	expand_command "$@"
> -	echo "====== RUN CHECK ${cmd_array[@]}" >> "$RESULTS" 2>&1
> -	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(stdout): ${cmd_array[@]}" \
> -		> /dev/tty; fi
> -	"${cmd_array[@]}" 2>&1 | tee -a "$RESULTS"
> -	if [ ${PIPESTATUS[0]} -ne 0 ]; then
> +	_run_check "CHECK" "CMD(stdout)" "true" "$@"
> +	if [ $? -ne 0 ]; then
>   		_fail "failed: $@"
>   	fi
>   }
> @@ -245,11 +291,7 @@ run_check_stdout()
>   # output is logged
>   run_mayfail()
>   {
> -	expand_command "$@"
> -	echo "====== RUN MAYFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
> -	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): ${cmd_array[@]}" \
> -		> /dev/tty; fi
> -	"${cmd_array[@]}" >> "$RESULTS" 2>&1
> +	_run_check "MAYFAIL" "CMD(mayfail)" "false" "$@"
>   	ret=$?
>   	if [ $ret != 0 ]; then
>   		echo "failed (ignored, ret=$ret): $@" >> "$RESULTS"
> @@ -271,19 +313,8 @@ run_mayfail()
>   # store the output to a variable for further processing.
>   run_mayfail_stdout()
>   {
> -	tmp_output=$(mktemp --tmpdir btrfs-progs-mayfail-stdout.XXXXXX)
> -
> -	expand_command "$@"
> -	echo "====== RUN MAYFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
> -	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): ${cmd_array[@]}" \
> -		> /dev/tty; fi
> -	"${cmd_array[@]}" 2>&1 > "$tmp_output"
> +	_run_check "MAYFAIL" "CMD(mayfail)" "true" "$@"
>   	ret=$?
> -
> -	cat "$tmp_output" >> "$RESULTS"
> -	cat "$tmp_output"
> -	rm -- "$tmp_output"
> -
>   	if [ "$ret" != 0 ]; then
>   		echo "failed (ignored, ret=$ret): $@" >> "$RESULTS"
>   		if [ "$ret" == 139 ]; then
> @@ -312,12 +343,7 @@ run_mustfail()
>   		exit 1
>   	fi
>
> -
> -	expand_command "$@"
> -	echo "====== RUN MUSTFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
> -	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): ${cmd_array[@]}" \
> -		> /dev/tty; fi
> -	"${cmd_array[@]}" >> "$RESULTS" 2>&1
> +	_run_check "MUSTFAIL" "CMD(mustfail)" "false" "$@"
>   	if [ $? != 0 ]; then
>   		echo "failed (expected): $@" >> "$RESULTS"
>   		return 0
> @@ -337,9 +363,6 @@ run_mustfail_stdout()
>   {
>   	local msg
>   	local ret
> -	local tmp_output
> -
> -	tmp_output=$(mktemp --tmpdir btrfs-progs-mustfail-stdout.XXXXXX)
>
>   	msg="$1"
>   	shift
> @@ -349,17 +372,8 @@ run_mustfail_stdout()
>   		exit 1
>   	fi
>
> -	expand_command "$@"
> -	echo "====== RUN MUSTFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
> -	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): ${cmd_array[@]}" \
> -		> /dev/tty; fi
> -	"${cmd_array[@]}" 2>&1 > "$tmp_output"
> +	_run_check "MUSTFAIL" "CMD(mustfail)" "true" "$@"
>   	ret=$?
> -
> -	cat "$tmp_output" >> "$RESULTS"
> -	cat "$tmp_output"
> -	rm "$tmp_output"
> -
>   	if [ "$ret" != 0 ]; then
>   		echo "failed (expected): $@" >> "$RESULTS"
>   		return 0
Josef Bacik Sept. 6, 2023, 1:52 p.m. UTC | #3
On Tue, Sep 05, 2023 at 10:49:56PM +0200, David Sterba wrote:
> On Tue, Sep 05, 2023 at 04:21:53PM -0400, Josef Bacik wrote:
> > I introduced a regression where we were leaking extent buffers, and this
> > resulted in the CI failing because we were spewing these errors.
> > 
> > Instead of waiting for fstests to catch my mistakes, check every command
> > output for leak messages, and fail the test if we detect any of these
> > messages.  I've made this generic enough that we could check for other
> > debug messages in the future.
> > 
> > Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> 
> There already is a script to verify more than just the error leaks, I've
> added it to all the test type running scripts so it would fail.
> > ---
> >  tests/common | 108 +++++++++++++++++++++++++++++----------------------
> >  1 file changed, 61 insertions(+), 47 deletions(-)
> > 
> > diff --git a/tests/common b/tests/common
> > index 602a4122..607ad747 100644
> > --- a/tests/common
> > +++ b/tests/common
> > @@ -160,6 +160,18 @@ _is_target_command()
> >  	return 1
> >  }
> >  
> > +# Check to see if there's any debug messages that may mean we have a problem.
> > +_check_output()
> > +{
> > +	local results="$1"
> > +
> > +	if grep -q "extent buffer leak" "$results"; then
> > +		_fail "extent buffer leak reported"
> 
> There's more than that we'd like to catch, see tests/scan-results.sh.

Yup I prefer your solution, you can ignore this patch.  Thanks,

Josef
David Sterba Sept. 7, 2023, 1:32 p.m. UTC | #4
On Wed, Sep 06, 2023 at 06:57:55AM +0800, Qu Wenruo wrote:
> 
> 
> On 2023/9/6 04:21, Josef Bacik wrote:
> > I introduced a regression where we were leaking extent buffers, and this
> > resulted in the CI failing because we were spewing these errors.
> >
> > Instead of waiting for fstests to catch my mistakes, check every command
> > output for leak messages, and fail the test if we detect any of these
> > messages.  I've made this generic enough that we could check for other
> > debug messages in the future.
> >
> > Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> 
> Another solution is to make debug build of btrfs-progs more noisy when
> eb leak is detected.
> 
> Instead of a graceful exit (which is suitable for release build), a
> noisy BUG_ON()/ASSERT() would definitely catch our attention, and
> requires less work in the test framework.

The eb leak checks are in the kernel-shared directory that does not
share much with the debugging helpers in the other code. Eg. the message
helpers, so we can't even grep for ERROR in the logs when there are no
supposed to be seen.
diff mbox series

Patch

diff --git a/tests/common b/tests/common
index 602a4122..607ad747 100644
--- a/tests/common
+++ b/tests/common
@@ -160,6 +160,18 @@  _is_target_command()
 	return 1
 }
 
+# Check to see if there's any debug messages that may mean we have a problem.
+_check_output()
+{
+	local results="$1"
+
+	if grep -q "extent buffer leak" "$results"; then
+		_fail "extent buffer leak reported"
+		return 1
+	fi
+	return 0
+}
+
 # Expanding extra commands/options for current command string
 # This function is responsible for inserting the following things:
 # - @INSTRUMENT before 'btrfs'  commands
@@ -206,6 +218,48 @@  expand_command()
 	done
 }
 
+# This is the helper for the run_check variants.
+# The first argument is the run_check type
+# The second argument is the run_check type that will get logged to tty
+# The third argument is wether we want the output echo'ed
+# The rest of the arguments are the command
+_run_check()
+{
+	local header_type
+	local test_log_type
+	local do_stdout
+	local tmp_output
+
+	run_type="$1"
+	shift
+
+	test_log_type="$1"
+	shift
+
+	do_stdout="$1"
+	shift
+
+	tmp_output=$(mktemp --tmpdir btrfs-progs-leak-detect.XXXXXX)
+
+	expand_command "$@"
+	echo "====== RUN $run_type ${cmd_array[@]}" >> "$RESULTS" 2>&1
+	if [[ $TEST_LOG =~ tty ]]; then echo "$test_log_type: ${cmd_array[@]}" \
+		> /dev/tty; fi
+	"${cmd_array[@]}" > "$tmp_output" 2>&1
+	ret=$?
+
+	cat "$tmp_output" >> "$RESULTS"
+	[ "$do_stdout" = true ] && cat "$tmp_output"
+
+	if ! _check_output "$tmp_output"; then
+		_fail "bad output"
+		rm "$tmp_output"
+		return 1
+	fi
+	rm "$tmp_output"
+	return "$ret"
+}
+
 # Argument passing magic:
 # the command passed to run_* helpers is inspected, if there's 'btrfs command'
 # found and there are defined additional arguments, they're inserted just after
@@ -216,11 +270,7 @@  expand_command()
 
 run_check()
 {
-	expand_command "$@"
-	echo "====== RUN CHECK ${cmd_array[@]}" >> "$RESULTS" 2>&1
-	if [[ $TEST_LOG =~ tty ]]; then echo "CMD: ${cmd_array[@]}" > /dev/tty; fi
-
-	"${cmd_array[@]}" >> "$RESULTS" 2>&1 || _fail "failed: ${cmd_array[@]}"
+	_run_check "CHECK" "CMD" "false" "$@" || _fail "failed: ${cmd_array[@]}"
 }
 
 # same as run_check but the stderr+stdout output is duplicated on stdout and
@@ -230,12 +280,8 @@  run_check()
 #	filter the output, as INSTRUMENT can easily pollute the output.
 run_check_stdout()
 {
-	expand_command "$@"
-	echo "====== RUN CHECK ${cmd_array[@]}" >> "$RESULTS" 2>&1
-	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(stdout): ${cmd_array[@]}" \
-		> /dev/tty; fi
-	"${cmd_array[@]}" 2>&1 | tee -a "$RESULTS"
-	if [ ${PIPESTATUS[0]} -ne 0 ]; then
+	_run_check "CHECK" "CMD(stdout)" "true" "$@"
+	if [ $? -ne 0 ]; then
 		_fail "failed: $@"
 	fi
 }
@@ -245,11 +291,7 @@  run_check_stdout()
 # output is logged
 run_mayfail()
 {
-	expand_command "$@"
-	echo "====== RUN MAYFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
-	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): ${cmd_array[@]}" \
-		> /dev/tty; fi
-	"${cmd_array[@]}" >> "$RESULTS" 2>&1
+	_run_check "MAYFAIL" "CMD(mayfail)" "false" "$@"
 	ret=$?
 	if [ $ret != 0 ]; then
 		echo "failed (ignored, ret=$ret): $@" >> "$RESULTS"
@@ -271,19 +313,8 @@  run_mayfail()
 # store the output to a variable for further processing.
 run_mayfail_stdout()
 {
-	tmp_output=$(mktemp --tmpdir btrfs-progs-mayfail-stdout.XXXXXX)
-
-	expand_command "$@"
-	echo "====== RUN MAYFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
-	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): ${cmd_array[@]}" \
-		> /dev/tty; fi
-	"${cmd_array[@]}" 2>&1 > "$tmp_output"
+	_run_check "MAYFAIL" "CMD(mayfail)" "true" "$@"
 	ret=$?
-
-	cat "$tmp_output" >> "$RESULTS"
-	cat "$tmp_output"
-	rm -- "$tmp_output"
-
 	if [ "$ret" != 0 ]; then
 		echo "failed (ignored, ret=$ret): $@" >> "$RESULTS"
 		if [ "$ret" == 139 ]; then
@@ -312,12 +343,7 @@  run_mustfail()
 		exit 1
 	fi
 
-
-	expand_command "$@"
-	echo "====== RUN MUSTFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
-	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): ${cmd_array[@]}" \
-		> /dev/tty; fi
-	"${cmd_array[@]}" >> "$RESULTS" 2>&1
+	_run_check "MUSTFAIL" "CMD(mustfail)" "false" "$@"
 	if [ $? != 0 ]; then
 		echo "failed (expected): $@" >> "$RESULTS"
 		return 0
@@ -337,9 +363,6 @@  run_mustfail_stdout()
 {
 	local msg
 	local ret
-	local tmp_output
-
-	tmp_output=$(mktemp --tmpdir btrfs-progs-mustfail-stdout.XXXXXX)
 
 	msg="$1"
 	shift
@@ -349,17 +372,8 @@  run_mustfail_stdout()
 		exit 1
 	fi
 
-	expand_command "$@"
-	echo "====== RUN MUSTFAIL ${cmd_array[@]}" >> "$RESULTS" 2>&1
-	if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): ${cmd_array[@]}" \
-		> /dev/tty; fi
-	"${cmd_array[@]}" 2>&1 > "$tmp_output"
+	_run_check "MUSTFAIL" "CMD(mustfail)" "true" "$@"
 	ret=$?
-
-	cat "$tmp_output" >> "$RESULTS"
-	cat "$tmp_output"
-	rm "$tmp_output"
-
 	if [ "$ret" != 0 ]; then
 		echo "failed (expected): $@" >> "$RESULTS"
 		return 0