Skip to content

Commit 3d67527

Browse files
author
MarcoFalke
committed
Merge bitcoin#17633: tests: Add option --valgrind to run the functional tests under Valgrind
5db506b tests: Add option --valgrind to run nodes under valgrind in the functional tests (practicalswift) Pull request description: What is better than fixing bugs? Fixing entire bug classes of course! :) Add option `--valgrind` to run the functional tests under Valgrind. Regular functional testing under Valgrind would have caught many of the uninitialized reads we've seen historically. Let's kill this bug class once and for all: let's never use an uninitialized value ever again. Or at least not one that would be triggered by running the functional tests! :) My hope is that this addition will make it super-easy to run the functional tests under Valgrind and thus increase the probability of people making use of it :) Hopefully `test/functional/test_runner.py --valgrind` will become a natural part of the pre-release QA process. **Usage:** ``` $ test/functional/test_runner.py --help … --valgrind run nodes under the valgrind memory error detector: expect at least a ~10x slowdown, valgrind 3.14 or later required ``` **Live demo:** First, let's re-introduce a memory bug by reverting the recent P2P uninitialized read bug fix from PR bitcoin#17624 ("net: Fix an uninitialized read in ProcessMessage(…, "tx", …) when receiving a transaction we already have"). ``` $ git diff diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 3401eb64c..940adea33 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -114,7 +114,7 @@ inline ValidationState::~ValidationState() {}; class TxValidationState : public ValidationState { private: - TxValidationResult m_result = TxValidationResult::TX_RESULT_UNSET; + TxValidationResult m_result; public: bool Invalid(TxValidationResult result, const std::string &reject_reason="", ``` Second, let's test as normal without Valgrind: ``` $ test/functional/p2p_segwit.py -l INFO 2019-11-28T09:30:42.810000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test__fc8q3qo … 2019-11-28T09:31:57.187000Z TestFramework (INFO): Subtest: test_non_standard_witness_blinding (Segwit active = True) … 2019-11-28T09:32:08.265000Z TestFramework (INFO): Tests successful ``` Third, let's test with `--valgrind` and see if the test fail (as we expect) when the unitialized value is used: ``` $ test/functional/p2p_segwit.py -l INFO --valgrind 2019-11-28T09:32:33.018000Z TestFramework (INFO): Initializing test directory /tmp/bitcoin_func_test_gtjecx2l … 2019-11-28T09:40:36.702000Z TestFramework (INFO): Subtest: test_non_standard_witness_blinding (Segwit active = True) 2019-11-28T09:40:37.813000Z TestFramework (ERROR): Assertion failed ConnectionRefusedError: [Errno 111] Connection refused ``` ACKs for top commit: MarcoFalke: ACK 5db506b jonatack: ACK 5db506b Tree-SHA512: 2eaecacf4da166febad88b2a8ee6d7ac2bcd38d4c1892ca39516b6343e8f8c8814edf5eaf14c90f11a069a0389d24f0713076112ac284de987e72fc5f6cc3795
2 parents d5674c5 + 5db506b commit 3d67527

File tree

2 files changed

+13
-1
lines changed

2 files changed

+13
-1
lines changed

test/functional/test_framework/test_framework.py

+3
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ def parse_args(self):
161161
help="use bitcoin-cli instead of RPC for all commands")
162162
parser.add_argument("--perf", dest="perf", default=False, action="store_true",
163163
help="profile running nodes with perf for the duration of the test")
164+
parser.add_argument("--valgrind", dest="valgrind", default=False, action="store_true",
165+
help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown, valgrind 3.14 or later required")
164166
parser.add_argument("--randomseed", type=int,
165167
help="set a random seed for deterministically reproducing a previous test run")
166168
self.add_options(parser)
@@ -398,6 +400,7 @@ def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None):
398400
extra_args=extra_args[i],
399401
use_cli=self.options.usecli,
400402
start_perf=self.options.perf,
403+
use_valgrind=self.options.valgrind,
401404
))
402405

403406
def start_node(self, i, *args, **kwargs):

test/functional/test_framework/test_node.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class TestNode():
5959
To make things easier for the test writer, any unrecognised messages will
6060
be dispatched to the RPC connection."""
6161

62-
def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
62+
def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False):
6363
"""
6464
Kwargs:
6565
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -96,6 +96,15 @@ def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cl
9696
"-debugexclude=leveldb",
9797
"-uacomment=testnode%d" % i,
9898
]
99+
if use_valgrind:
100+
default_suppressions_file = os.path.join(
101+
os.path.dirname(os.path.realpath(__file__)),
102+
"..", "..", "..", "contrib", "valgrind.supp")
103+
suppressions_file = os.getenv("VALGRIND_SUPPRESSIONS_FILE",
104+
default_suppressions_file)
105+
self.args = ["valgrind", "--suppressions={}".format(suppressions_file),
106+
"--gen-suppressions=all", "--exit-on-first-error=yes",
107+
"--error-exitcode=1", "--quiet"] + self.args
99108

100109
self.cli = TestNodeCLI(bitcoin_cli, self.datadir)
101110
self.use_cli = use_cli

0 commit comments

Comments
 (0)