Skip to content

Commit ad0e623

Browse files
Thomas Rastgitster
authored andcommitted
test-lib: support running tests under valgrind in parallel
With the new --valgrind-parallel=<n> option, we support running the tests in a single test script under valgrind in parallel using 'n' processes. This really follows the dumbest approach possible, as follows: * We spawn the test script 'n' times, using a throw-away TEST_OUTPUT_DIRECTORY. Each of the instances is given options that ensures that it only runs every n-th test under valgrind, but together they cover the entire range. * We add up the numbers from the individual tests, and provide the usual output. This is really a gross hack at this point, and should be improved. In particular we should keep the actual outputs somewhere more easily discoverable, and summarize them to the user. Nevertheless, this is already workable and gives a speedup of more than 2 on a dual-core (hyperthreaded) machine, using n=4. This is expected since the overhead of valgrind is so big (on the order of 20x under good conditions, and a large startup overhead at every git invocation) that redundantly running the non-valgrind tests in between is not that expensive. Helped-by: Jeff King <[email protected]> Signed-off-by: Thomas Rast <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e939e15 commit ad0e623

File tree

1 file changed

+84
-22
lines changed

1 file changed

+84
-22
lines changed

t/test-lib.sh

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ do
204204
--valgrind-only=*)
205205
valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
206206
shift ;;
207+
--valgrind-parallel=*)
208+
valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)')
209+
shift ;;
210+
--valgrind-only-stride=*)
211+
valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)')
212+
shift ;;
213+
--valgrind-only-offset=*)
214+
valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)')
215+
shift ;;
207216
--tee)
208217
shift ;; # was handled already
209218
--root=*)
@@ -217,7 +226,7 @@ do
217226
esac
218227
done
219228

220-
if test -n "$valgrind_only"
229+
if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
221230
then
222231
test -z "$valgrind" && valgrind=memcheck
223232
test -z "$verbose" && verbose_only="$valgrind_only"
@@ -367,7 +376,9 @@ maybe_teardown_verbose () {
367376
last_verbose=t
368377
maybe_setup_verbose () {
369378
test -z "$verbose_only" && return
370-
if match_pattern_list $test_count $verbose_only
379+
if match_pattern_list $test_count $verbose_only ||
380+
{ test -n "$valgrind_only_stride" &&
381+
expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; }
371382
then
372383
exec 4>&2 3>&1
373384
# Emit a delimiting blank line when going from
@@ -391,13 +402,17 @@ maybe_teardown_valgrind () {
391402

392403
maybe_setup_valgrind () {
393404
test -z "$GIT_VALGRIND" && return
394-
if test -z "$valgrind_only"
405+
if test -z "$valgrind_only" && test -z "$valgrind_only_stride"
395406
then
396407
GIT_VALGRIND_ENABLED=t
397408
return
398409
fi
399410
GIT_VALGRIND_ENABLED=
400411
if match_pattern_list $test_count $valgrind_only
412+
then
413+
GIT_VALGRIND_ENABLED=t
414+
elif test -n "$valgrind_only_stride" &&
415+
expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null
401416
then
402417
GIT_VALGRIND_ENABLED=t
403418
fi
@@ -552,6 +567,9 @@ test_done () {
552567
esac
553568
}
554569

570+
571+
# Set up a directory that we can put in PATH which redirects all git
572+
# calls to 'valgrind git ...'.
555573
if test -n "$valgrind"
556574
then
557575
make_symlink () {
@@ -599,33 +617,42 @@ then
599617
make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
600618
}
601619

602-
# override all git executables in TEST_DIRECTORY/..
603-
GIT_VALGRIND=$TEST_DIRECTORY/valgrind
604-
mkdir -p "$GIT_VALGRIND"/bin
605-
for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
606-
do
607-
make_valgrind_symlink $file
608-
done
609-
# special-case the mergetools loadables
610-
make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
611-
OLDIFS=$IFS
612-
IFS=:
613-
for path in $PATH
614-
do
615-
ls "$path"/git-* 2> /dev/null |
616-
while read file
620+
# In the case of --valgrind-parallel, we only need to do the
621+
# wrapping once, in the main script. The worker children all
622+
# have $valgrind_only_stride set, so we can skip based on that.
623+
if test -z "$valgrind_only_stride"
624+
then
625+
# override all git executables in TEST_DIRECTORY/..
626+
GIT_VALGRIND=$TEST_DIRECTORY/valgrind
627+
mkdir -p "$GIT_VALGRIND"/bin
628+
for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
617629
do
618-
make_valgrind_symlink "$file"
630+
make_valgrind_symlink $file
619631
done
620-
done
621-
IFS=$OLDIFS
632+
# special-case the mergetools loadables
633+
make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
634+
OLDIFS=$IFS
635+
IFS=:
636+
for path in $PATH
637+
do
638+
ls "$path"/git-* 2> /dev/null |
639+
while read file
640+
do
641+
make_valgrind_symlink "$file"
642+
done
643+
done
644+
IFS=$OLDIFS
645+
fi
622646
PATH=$GIT_VALGRIND/bin:$PATH
623647
GIT_EXEC_PATH=$GIT_VALGRIND/bin
624648
export GIT_VALGRIND
625649
GIT_VALGRIND_MODE="$valgrind"
626650
export GIT_VALGRIND_MODE
627651
GIT_VALGRIND_ENABLED=t
628-
test -n "$valgrind_only" && GIT_VALGRIND_ENABLED=
652+
if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
653+
then
654+
GIT_VALGRIND_ENABLED=
655+
fi
629656
export GIT_VALGRIND_ENABLED
630657
elif test -n "$GIT_TEST_INSTALLED"
631658
then
@@ -711,6 +738,41 @@ then
711738
else
712739
mkdir -p "$TRASH_DIRECTORY"
713740
fi
741+
742+
# Gross hack to spawn N sub-instances of the tests in parallel, and
743+
# summarize the results. Note that if this is enabled, the script
744+
# terminates at the end of this 'if' block.
745+
if test -n "$valgrind_parallel"
746+
then
747+
for i in $(test_seq 1 $valgrind_parallel)
748+
do
749+
root="$TRASH_DIRECTORY/vgparallel-$i"
750+
mkdir "$root"
751+
TEST_OUTPUT_DIRECTORY="$root" \
752+
${SHELL_PATH} "$0" \
753+
--root="$root" --statusprefix="[$i] " \
754+
--valgrind="$valgrind" \
755+
--valgrind-only-stride="$valgrind_parallel" \
756+
--valgrind-only-offset="$i" &
757+
pids="$pids $!"
758+
done
759+
trap "kill $pids" INT TERM HUP
760+
wait $pids
761+
trap - INT TERM HUP
762+
for i in $(test_seq 1 $valgrind_parallel)
763+
do
764+
root="$TRASH_DIRECTORY/vgparallel-$i"
765+
eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts |
766+
sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')"
767+
test_count=$(expr $test_count + $inner_total)
768+
test_success=$(expr $test_success + $inner_success)
769+
test_fixed=$(expr $test_fixed + $inner_fixed)
770+
test_broken=$(expr $test_broken + $inner_broken)
771+
test_failure=$(expr $test_failure + $inner_failed)
772+
done
773+
test_done
774+
fi
775+
714776
# Use -P to resolve symlinks in our working directory so that the cwd
715777
# in subprocesses like git equals our $PWD (for pathname comparisons).
716778
cd -P "$TRASH_DIRECTORY" || exit 1

0 commit comments

Comments
 (0)