summaryrefslogtreecommitdiff
path: root/gdb/testsuite/Makefile.in
diff options
context:
space:
mode:
authorSergio Durigan Junior <sergiodj@redhat.com>2016-03-05 20:37:11 -0500
committerSergio Durigan Junior <sergiodj@redhat.com>2016-03-05 20:43:40 -0500
commitfb6a751f5f1fe7912e84dd90d06395c357da47c2 (patch)
tree9d421118cde346a6d47dbb4b753df299c4dd1341 /gdb/testsuite/Makefile.in
parentb42585769f4a54a0fab1bdd788d7e1c327459e91 (diff)
Improve analysis of racy testcases
This is an initial attempt to introduce some mechanisms to identify racy testcases present in our testsuite. As can be seen in previous discussions, racy tests are really bothersome and cause our BuildBot to pollute the gdb-testers mailing list with hundreds of false-positives messages every month. Hopefully, identifying these racy tests in advance (and automatically) will contribute to the reduction of noise traffic to gdb-testers, maybe to the point where we will be able to send the failure messages directly to the authors of the commits. I spent some time trying to decide the best way to tackle this problem, and decided that there is no silver bullet. Racy tests are tricky and it is difficult to catch them, so the best solution I could find (for now?) is to run our testsuite a number of times in a row, and then compare the results (i.e., the gdb.sum files generated during each run). The more times you run the tests, the more racy tests you are likely to detect (at the expense of waiting longer and longer). You can also run the tests in parallel, which makes things faster (and contribute to catching more racy tests, because your machine will have less resources for each test and some of them are likely to fail when this happens). I did some tests in my machine (8-core i7, 16GB RAM), and running the whole GDB testsuite 5 times using -j6 took 23 minutes. Not bad. In order to run the racy test machinery, you need to specify the RACY_ITER environment variable. You will assign a number to this variable, which represents the number of times you want to run the tests. So, for example, if you want to run the whole testsuite 3 times in parallel (using 2 cores), you will do: make check RACY_ITER=3 -j2 It is also possible to use the TESTS variable and specify which tests you want to run: make check TEST='gdb.base/default.exp' RACY_ITER=3 -j2 And so on. The output files will be put at the directory gdb/testsuite/racy_outputs/. After make invokes the necessary rules to run the tests, it finally runs a Python script that will analyze the resulting gdb.sum files. This Python script will read each file, and construct a series of sets based on the results of the tests (one set for FAIL's, one for PASS'es, one for KFAIL's, etc.). It will then do some set operations and come up with a list of unique, sorted testcases that are racy. The algorithm behind this is: for state in PASS, FAIL, XFAIL, XPASS...; do if a test's state in every sumfile is $state; then it is not racy else it is racy (The algorithm is actually a bit more complex than that, because it takes into account other things in order to decide whether the test should be ignored or not). IOW, a test must have the same state in every sumfile. After processing everything, the script prints the racy tests it could identify on stdout. I am redirecting this to a file named racy.sum. Something else that I wasn't sure how to deal with was non-unique messages in our testsuite. I decided to do the same thing I do in our BuildBot: include a unique identifier in the end of message, like: gdb.base/xyz.exp: non-unique message gdb.base/xyz.exp: non-unique message <<2>> This means that you will have to be careful about them when you use the racy.sum file. I ran the script several times here, and it did a good job catching some well-known racy tests. Overall, I am satisfied with this approach and I think it will be helpful to have it upstream'ed. I also intend to extend our BuildBot and create new, specialized builders that will be responsible for detecting the racy tests every X number of days. 2016-03-05 Sergio Durigan Junior <sergiodj@redhat.com> * Makefile.in (DEFAULT_RACY_ITER): New variable. (CHECK_TARGET_TMP): Likewise. (check-single-racy): New rule. (check-parallel-racy): Likewise. (TEST_TARGETS): Adjust rule to account for RACY_ITER. (do-check-parallel-racy): New rule. (check-racy/%.exp): Likewise. * README (Racy testcases): New section. * analyze-racy-logs.py: New file.
Diffstat (limited to 'gdb/testsuite/Makefile.in')
-rw-r--r--gdb/testsuite/Makefile.in67
1 files changed, 64 insertions, 3 deletions
diff --git a/gdb/testsuite/Makefile.in b/gdb/testsuite/Makefile.in
index 38c30525ec..7853275617 100644
--- a/gdb/testsuite/Makefile.in
+++ b/gdb/testsuite/Makefile.in
@@ -52,6 +52,12 @@ RUNTESTFLAGS =
FORCE_PARALLEL =
+# Default number of iterations that we will use to run the testsuite
+# if the user does not specify the RACY_ITER environment variable
+# (e.g., when the user calls the make rule directly from the command
+# line).
+DEFAULT_RACY_ITER = 3
+
RUNTEST_FOR_TARGET = `\
if [ -f $${srcdir}/../../dejagnu/runtest ]; then \
echo $${srcdir}/../../dejagnu/runtest; \
@@ -140,7 +146,8 @@ installcheck:
# given. If RUNTESTFLAGS is not empty, then by default the tests will
# be serialized. This can be overridden by setting FORCE_PARALLEL to
# any non-empty value. For a non-GNU make, do not parallelize.
-@GMAKE_TRUE@CHECK_TARGET = $(if $(FORCE_PARALLEL),check-parallel,$(if $(RUNTESTFLAGS),check-single,$(if $(saw_dash_j),check-parallel,check-single)))
+@GMAKE_TRUE@CHECK_TARGET_TMP = $(if $(FORCE_PARALLEL),check-parallel,$(if $(RUNTESTFLAGS),check-single,$(if $(saw_dash_j),check-parallel,check-single)))
+@GMAKE_TRUE@CHECK_TARGET = $(if $(RACY_ITER),$(addsuffix -racy,$(CHECK_TARGET_TMP)),$(CHECK_TARGET_TMP))
@GMAKE_FALSE@CHECK_TARGET = check-single
# Note that we must resort to a recursive make invocation here,
@@ -190,6 +197,26 @@ DO_RUNTEST = \
check-single:
$(DO_RUNTEST) $(RUNTESTFLAGS) $(expanded_tests_or_none)
+check-single-racy:
+ -rm -rf cache racy_outputs temp
+ mkdir -p racy_outputs; \
+ racyiter="$(RACY_ITER)"; \
+ test "x$$racyiter" == "x" && \
+ racyiter=$(DEFAULT_RACY_ITER); \
+ if test $$racyiter -lt 2 ; then \
+ echo "RACY_ITER must be at least 2."; \
+ exit 1; \
+ fi; \
+ trap "exit" INT; \
+ for n in `seq $$racyiter` ; do \
+ mkdir -p racy_outputs/$$n; \
+ $(DO_RUNTEST) --outdir=racy_outputs/$$n $(RUNTESTFLAGS) \
+ $(expanded_tests_or_none); \
+ done; \
+ $(srcdir)/analyze-racy-logs.py \
+ `ls racy_outputs/*/gdb.sum` > racy.sum; \
+ sed -n '/=== gdb Summary ===/,$$ p' racy.sum
+
check-parallel:
-rm -rf cache outputs temp
$(MAKE) -k do-check-parallel; \
@@ -201,6 +228,31 @@ check-parallel:
sed -n '/=== gdb Summary ===/,$$ p' gdb.sum; \
exit $$result
+check-parallel-racy:
+ -rm -rf cache racy_outputs temp
+ racyiter="$(RACY_ITER)"; \
+ test "x$$racyiter" == "x" && \
+ racyiter=$(DEFAULT_RACY_ITER); \
+ if test $$racyiter -lt 2 ; then \
+ echo "RACY_ITER must be at least 2."; \
+ exit 1; \
+ fi; \
+ trap "exit" INT; \
+ for n in `seq $$racyiter` ; do \
+ $(MAKE) -k do-check-parallel-racy \
+ RACY_OUTPUT_N=$$n; \
+ $(SHELL) $(srcdir)/dg-extract-results.sh \
+ `find racy_outputs/$$n -name gdb.sum -print` > \
+ racy_outputs/$$n/gdb.sum; \
+ $(SHELL) $(srcdir)/dg-extract-results.sh -L \
+ `find racy_outputs/$$n -name gdb.log -print` > \
+ racy_outputs/$$n/gdb.log; \
+ sed -n '/=== gdb Summary ===/,$$ p' racy_outputs/$$n/gdb.sum; \
+ done; \
+ $(srcdir)/analyze-racy-logs.py \
+ `ls racy_outputs/*/gdb.sum` > racy.sum; \
+ sed -n '/=== gdb Summary ===/,$$ p' racy.sum
+
# Turn a list of .exp files into "check/" targets. Only examine .exp
# files appearing in a gdb.* directory -- we don't want to pick up
# lib/ by mistake. For example, gdb.linespec/linespec.exp becomes
@@ -214,9 +266,9 @@ slow_tests = gdb.base/break-interp.exp gdb.base/interp.exp \
gdb.base/multi-forks.exp
@GMAKE_TRUE@all_tests := $(shell cd $(srcdir) && find gdb.* -name '*.exp' -print)
@GMAKE_TRUE@reordered_tests := $(slow_tests) $(filter-out $(slow_tests),$(all_tests))
-@GMAKE_TRUE@TEST_TARGETS := $(addprefix check/,$(reordered_tests))
+@GMAKE_TRUE@TEST_TARGETS := $(addprefix $(if $(RACY_ITER),check-racy,check)/,$(reordered_tests))
@GMAKE_TRUE@else
-@GMAKE_TRUE@TEST_TARGETS := $(addprefix check/,$(expanded_tests_or_none))
+@GMAKE_TRUE@TEST_TARGETS := $(addprefix $(if $(RACY_ITER),check-racy,check)/,$(expanded_tests_or_none))
@GMAKE_TRUE@endif
do-check-parallel: $(TEST_TARGETS)
@@ -226,6 +278,15 @@ do-check-parallel: $(TEST_TARGETS)
@GMAKE_TRUE@ -mkdir -p outputs/$*
@GMAKE_TRUE@ @$(DO_RUNTEST) GDB_PARALLEL=yes --outdir=outputs/$* $*.exp $(RUNTESTFLAGS)
+do-check-parallel-racy: $(TEST_TARGETS)
+ @:
+
+@GMAKE_TRUE@check-racy/%.exp:
+@GMAKE_TRUE@ -mkdir -p racy_outputs/$(RACY_OUTPUT_N)/$*
+@GMAKE_TRUE@ $(DO_RUNTEST) GDB_PARALLEL=yes \
+@GMAKE_TRUE@ --outdir=racy_outputs/$(RACY_OUTPUT_N)/$* $*.exp \
+@GMAKE_TRUE@ $(RUNTESTFLAGS)
+
check/no-matching-tests-found:
@echo ""
@echo "No matching tests found."