From 66c1a568703fd1e14b544da6a1dd9f34e4cc9cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sun, 27 Feb 2022 11:25:10 +0100 Subject: [PATCH 1/4] test-lib: add GIT_SAN_OPTIONS, inherit [AL]SAN_OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change our ASAN_OPTIONS and LSAN_OPTIONS to set defaults for those variables, rather than punting out entirely if we already have them in the environment. We want to take any user-provided settings over our own, but we can do that by prepending our defaults to the variable. The libsanitizer options parsing has "last option wins" semantics. It's now possible to do e.g.: LSAN_OPTIONS=report_objects=1 ./t0006-date.sh And not have the "report_objects=1" setting overwrite our sensible default of "abort_on_error=1", but by prepending to the list we ensure that: LSAN_OPTIONS=report_objects=1:abort_on_error=0 ./t0006-date.sh Will take the desired "abort_on_error=0" over our default. See b0f4c9087e1 (t: support clang/gcc AddressSanitizer, 2014-12-08) for the original pattern being altered here, and 85b81b35ff9 (test-lib: set LSAN_OPTIONS to abort by default, 2017-09-05) for when LSAN_OPTIONS was added in addition to the then-existing ASAN_OPTIONS. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/test-lib.sh | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index e4716b0b86..55f263a02d 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -36,17 +36,33 @@ then fi GIT_BUILD_DIR="$TEST_DIRECTORY"/.. +# Prepend a string to a VAR using an arbitrary ":" delimiter, not +# adding the delimiter if VAR or VALUE is empty. I.e. a generalized: +# +# VAR=$1${VAR:+${1:+$2}$VAR} +# +# Usage (using ":" as the $2 delimiter): +# +# prepend_var VAR : VALUE +prepend_var () { + eval "$1=$3\${$1:+${3:+$2}\$$1}" +} + +# If [AL]SAN is in effect we want to abort so that we notice +# problems. The GIT_SAN_OPTIONS variable can be used to set common +# defaults shared between [AL]SAN_OPTIONS. +prepend_var GIT_SAN_OPTIONS : abort_on_error=1 + # If we were built with ASAN, it may complain about leaks # of program-lifetime variables. Disable it by default to lower # the noise level. This needs to happen at the start of the script, # before we even do our "did we build git yet" check (since we don't # want that one to complain to stderr). -: ${ASAN_OPTIONS=detect_leaks=0:abort_on_error=1} +prepend_var ASAN_OPTIONS : $GIT_SAN_OPTIONS +prepend_var ASAN_OPTIONS : detect_leaks=0 export ASAN_OPTIONS -# If LSAN is in effect we _do_ want leak checking, but we still -# want to abort so that we notice the problems. -: ${LSAN_OPTIONS=abort_on_error=1} +prepend_var LSAN_OPTIONS : $GIT_SAN_OPTIONS export LSAN_OPTIONS if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS From 9dbf20e7f62456400d0011ed8a238ae1f9872665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sun, 27 Feb 2022 11:25:11 +0100 Subject: [PATCH 2/4] test-lib: correct and assert TEST_DIRECTORY overriding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct a misleading comment added by me in 62f539043c7 (test-lib: Allow overriding of TEST_DIRECTORY, 2010-08-19), and add an assertion that TEST_DIRECTORY cannot point to any directory except the "t" directory in the top-level of git.git. This assertion is in effect not new, since we'd already die if that wasn't the case[1], but it and the updated commentary help to make that clearer. The existing comments were also on the wrong arms of the "if". I.e. the "allow tests to override this" was on the "test -z" arm. That came about due to a combination of 62f539043c7 and 85176d72513 (test-lib.sh: convert $TEST_DIRECTORY to an absolute path, 2013-11-17). Those earlier comments could be read as allowing the "$TEST_DIRECTORY" to be some path outside of t/. As explained in the updated comment that's impossible, rather it was meant for *tests* that ran outside of t/, i.e. the "t0000-basic.sh" tests that use "lib-subtest.sh". Those tests have a different working directory, but they set the "TEST_DIRECTORY" to the same path for bootstrapping. The comments now reflect that, and further comment on why we have a hard dependency on this. 1. https://lore.kernel.org/git/220222.86o82z8als.gmgdl@evledraar.gmail.com/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/test-lib.sh | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 55f263a02d..48ee3b16ec 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -19,13 +19,20 @@ # t/ subdirectory and are run in 'trash directory' subdirectory. if test -z "$TEST_DIRECTORY" then - # We allow tests to override this, in case they want to run tests - # outside of t/, e.g. for running tests on the test library - # itself. - TEST_DIRECTORY=$(pwd) -else # ensure that TEST_DIRECTORY is an absolute path so that it # is valid even if the current working directory is changed + TEST_DIRECTORY=$(pwd) +else + # The TEST_DIRECTORY will always be the path to the "t" + # directory in the git.git checkout. This is overridden by + # e.g. t/lib-subtest.sh, but only because its $(pwd) is + # different. Those tests still set "$TEST_DIRECTORY" to the + # same path. + # + # See use of "$GIT_BUILD_DIR" and "$TEST_DIRECTORY" below for + # hard assumptions about "$GIT_BUILD_DIR/t" existing and being + # the "$TEST_DIRECTORY", and e.g. "$TEST_DIRECTORY/helper" + # needing to exist. TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1 fi if test -z "$TEST_OUTPUT_DIRECTORY" @@ -35,6 +42,11 @@ then TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY fi GIT_BUILD_DIR="$TEST_DIRECTORY"/.. +if test "$TEST_DIRECTORY" = "${TEST_DIRECTORY%/t}" +then + echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2 + exit 1 +fi # Prepend a string to a VAR using an arbitrary ":" delimiter, not # adding the delimiter if VAR or VALUE is empty. I.e. a generalized: From b9638d7286fbfef46e325049cdd8cfa3fff3edc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sun, 27 Feb 2022 11:25:12 +0100 Subject: [PATCH 3/4] test-lib: make $GIT_BUILD_DIR an absolute path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the GIT_BUILD_DIR from a path like "/path/to/build/t/.." to "/path/to/build". The "TEST_DIRECTORY" here is already made an absolute path a few lines above this. We could simply do $(cd "$TEST_DIRECTORY"/.." && pwd) here, but as noted in the preceding commit the "$TEST_DIRECTORY" can't be anything except the path containing this test-lib.sh file at this point, so we can more cheaply and equally strip the "/t" off the end. This change will be helpful to LSAN_OPTIONS which will want to strip the build directory path from filenames, which we couldn't do if we had a "/.." in there. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/test-lib.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 48ee3b16ec..ba5186c859 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -41,8 +41,8 @@ then # elsewhere TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY fi -GIT_BUILD_DIR="$TEST_DIRECTORY"/.. -if test "$TEST_DIRECTORY" = "${TEST_DIRECTORY%/t}" +GIT_BUILD_DIR="${TEST_DIRECTORY%/t}" +if test "$TEST_DIRECTORY" = "$GIT_BUILD_DIR" then echo "PANIC: Running in a $TEST_DIRECTORY that doesn't end in '/t'?" >&2 exit 1 @@ -64,6 +64,7 @@ prepend_var () { # problems. The GIT_SAN_OPTIONS variable can be used to set common # defaults shared between [AL]SAN_OPTIONS. prepend_var GIT_SAN_OPTIONS : abort_on_error=1 +prepend_var GIT_SAN_OPTIONS : strip_path_prefix=\"$GIT_BUILD_DIR/\" # If we were built with ASAN, it may complain about leaks # of program-lifetime variables. Disable it by default to lower From 71f26798f24b69b5c7dc29fd169ebcc2a730b5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sun, 27 Feb 2022 11:25:13 +0100 Subject: [PATCH 4/4] test-lib: add "fast_unwind_on_malloc=0" to LSAN_OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add "fast_unwind_on_malloc=0" to LSAN_OPTIONS to get more meaningful stack traces from LSAN. This isn't required under ASAN which will emit traces such as this one for a leak in "t/t0006-date.sh": $ ASAN_OPTIONS=detect_leaks=1 ./t0006-date.sh -vixd [...] Direct leak of 3 byte(s) in 1 object(s) allocated from: #0 0x488b94 in strdup (t/helper/test-tool+0x488b94) #1 0x9444a4 in xstrdup wrapper.c:29:14 #2 0x5995fa in parse_date_format date.c:991:24 #3 0x4d2056 in show_dates t/helper/test-date.c:39:2 #4 0x4d174a in cmd__date t/helper/test-date.c:116:3 #5 0x4cce89 in cmd_main t/helper/test-tool.c:127:11 #6 0x4cd1e3 in main common-main.c:52:11 #7 0x7fef3c695e49 in __libc_start_main csu/../csu/libc-start.c:314:16 #8 0x422b09 in _start (t/helper/test-tool+0x422b09) SUMMARY: AddressSanitizer: 3 byte(s) leaked in 1 allocation(s). Aborted Whereas LSAN would emit this instead: $ ./t0006-date.sh -vixd [...] Direct leak of 3 byte(s) in 1 object(s) allocated from: #0 0x4323b8 in malloc (t/helper/test-tool+0x4323b8) #1 0x7f2be1d614aa in strdup string/strdup.c:42:15 SUMMARY: LeakSanitizer: 3 byte(s) leaked in 1 allocation(s). Aborted Now we'll instead git this sensible stack trace under LSAN. I.e. almost the same one (but starting with "malloc", as is usual for LSAN) as under ASAN: Direct leak of 3 byte(s) in 1 object(s) allocated from: #0 0x4323b8 in malloc (t/helper/test-tool+0x4323b8) #1 0x7f012af5c4aa in strdup string/strdup.c:42:15 #2 0x5cb164 in xstrdup wrapper.c:29:14 #3 0x495ee9 in parse_date_format date.c:991:24 #4 0x453aac in show_dates t/helper/test-date.c:39:2 #5 0x453782 in cmd__date t/helper/test-date.c:116:3 #6 0x451d95 in cmd_main t/helper/test-tool.c:127:11 #7 0x451f1e in main common-main.c:52:11 #8 0x7f012aef5e49 in __libc_start_main csu/../csu/libc-start.c:314:16 #9 0x42e0a9 in _start (t/helper/test-tool+0x42e0a9) SUMMARY: LeakSanitizer: 3 byte(s) leaked in 1 allocation(s). Aborted As the option name suggests this does make things slower, e.g. for t0001-init.sh we're around 10% slower: $ hyperfine -L v 0,1 'LSAN_OPTIONS=fast_unwind_on_malloc={v} make T=t0001-init.sh' -r 3 Benchmark 1: LSAN_OPTIONS=fast_unwind_on_malloc=0 make T=t0001-init.sh Time (mean ± σ): 2.135 s ± 0.015 s [User: 1.951 s, System: 0.554 s] Range (min … max): 2.122 s … 2.152 s 3 runs Benchmark 2: LSAN_OPTIONS=fast_unwind_on_malloc=1 make T=t0001-init.sh Time (mean ± σ): 1.981 s ± 0.055 s [User: 1.769 s, System: 0.488 s] Range (min … max): 1.941 s … 2.044 s 3 runs Summary 'LSAN_OPTIONS=fast_unwind_on_malloc=1 make T=t0001-init.sh' ran 1.08 ± 0.03 times faster than 'LSAN_OPTIONS=fast_unwind_on_malloc=0 make T=t0001-init.sh' I think that's more than worth it to get the more meaningful stack traces, we can always provide LSAN_OPTIONS=fast_unwind_on_malloc=0 for one-off "fast" runs. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/test-lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/test-lib.sh b/t/test-lib.sh index ba5186c859..9af5fb7674 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -76,6 +76,7 @@ prepend_var ASAN_OPTIONS : detect_leaks=0 export ASAN_OPTIONS prepend_var LSAN_OPTIONS : $GIT_SAN_OPTIONS +prepend_var LSAN_OPTIONS : fast_unwind_on_malloc=0 export LSAN_OPTIONS if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS