topotest: add --memleaks topotest option

Signed-off-by: Lou Berger <lberger@labn.net>
This commit is contained in:
Lou Berger 2022-10-03 00:31:31 +00:00 committed by Christian Hopps
parent c6686550c3
commit d4977708f0
2 changed files with 99 additions and 27 deletions

View File

@ -310,32 +310,6 @@ Test will set exit code which can be used with ``git bisect``.
For the simulated topology, see the description in the python file.
StdErr log from daemos after exit
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To enable the reporting of any messages seen on StdErr after the daemons exit,
the following env variable can be set::
export TOPOTESTS_CHECK_STDERR=Yes
(The value doesn't matter at this time. The check is whether the env
variable exists or not.) There is no pass/fail on this reporting; the
Output will be reported to the console.
Collect Memory Leak Information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FRR processes can report unfreed memory allocations upon exit. To
enable the reporting of memory leaks, define an environment variable
``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
This will enable the check and output to console and the writing of
the information to files with the given prefix (followed by testname),
ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
of a memory leak.
Running Topotests with AddressSanitizer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -561,6 +535,48 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
--gdb-breakpoints=nb_config_diff \
all-protocol-startup
Reporting Memleaks with FRR Memory Statistics
"""""""""""""""""""""""""""""""""""""""""""""
FRR reports all allocated FRR memory objects on exit to standard error.
Topotest can be run to report such output as errors in order to check for
memleaks in FRR memory allocations. Specifying the CLI argument
``--memleaks`` will enable reporting FRR-based memory allocations at exit as errors.
.. code:: shell
sudo -E pytest --memleaks all-protocol-startup
StdErr log from daemos after exit
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When running with ``--memleaks``, to enable the reporting of other,
non-memory related, messages seen on StdErr after the daemons exit,
the following env variable can be set::
export TOPOTESTS_CHECK_STDERR=Yes
(The value doesn't matter at this time. The check is whether the env
variable exists or not.) There is no pass/fail on this reporting; the
Output will be reported to the console.
Collect Memory Leak Information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When running with ``--memleaks``, FRR processes report unfreed memory
allocations upon exit. To enable also reporting of memory leaks to a specific
location, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the
file prefix, i.e.:
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
For tests that support the TOPOTESTS_CHECK_MEMLEAK environment variable, this
will enable output to the information to files with the given prefix (followed
by testname), e.g.,:
file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
of a memory leak.
Detecting Memleaks with Valgrind
""""""""""""""""""""""""""""""""

View File

@ -86,6 +86,12 @@ def pytest_addoption(parser):
),
)
parser.addoption(
"--memleaks",
action="store_true",
help="Report memstat results as errors",
)
parser.addoption(
"--pause",
action="store_true",
@ -189,7 +195,7 @@ def pytest_addoption(parser):
)
def check_for_memleaks():
def check_for_valgrind_memleaks():
assert topotest.g_pytest_config.option.valgrind_memleaks
leaks = []
@ -232,10 +238,46 @@ def check_for_memleaks():
pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons))
def check_for_memleaks():
leaks = []
tgen = get_topogen() # pylint: disable=redefined-outer-name
latest = []
existing = []
if tgen is not None:
logdir = tgen.logdir
if hasattr(tgen, "memstat_existing_files"):
existing = tgen.memstat_existing_files
latest = glob.glob(os.path.join(logdir, "*/*.err"))
daemons = []
for vfile in latest:
if vfile in existing:
continue
with open(vfile, encoding="ascii") as vf:
vfcontent = vf.read()
num = vfcontent.count("memstats:")
if num:
existing.append(vfile) # have summary don't check again
emsg = "{} types in {}".format(num, vfile)
leaks.append(emsg)
daemon = re.match(r".*test[a-z_A-Z0-9\+]*/(.*)\.err", vfile).group(1)
daemons.append("{}({})".format(daemon, num))
if tgen is not None:
tgen.memstat_existing_files = existing
if leaks:
logger.error("memleaks found:\n\t%s", "\n\t".join(leaks))
pytest.fail("memleaks found for daemons: " + " ".join(daemons))
@pytest.fixture(autouse=True, scope="module")
def module_check_memtest(request):
yield
if request.config.option.valgrind_memleaks:
if get_topogen() is not None:
check_for_valgrind_memleaks()
if request.config.option.memleaks:
if get_topogen() is not None:
check_for_memleaks()
@ -264,6 +306,8 @@ def pytest_runtest_call(item: pytest.Item) -> None:
# Check for leaks if requested
if item.config.option.valgrind_memleaks:
check_for_valgrind_memleaks()
if item.config.option.memleaks:
check_for_memleaks()
@ -370,10 +414,22 @@ def pytest_configure(config):
if config.option.topology_only and is_xdist:
pytest.exit("Cannot use --topology-only with distributed test mode")
pytest.exit("Cannot use --topology-only with distributed test mode")
# Check environment now that we have config
if not diagnose_env(rundir):
pytest.exit("environment has errors, please read the logs in %s" % rundir)
# slave TOPOTESTS_CHECK_MEMLEAK to memleaks flag
if config.option.memleaks:
if "TOPOTESTS_CHECK_MEMLEAK" not in os.environ:
os.environ["TOPOTESTS_CHECK_MEMLEAK"] = "/dev/null"
else:
if "TOPOTESTS_CHECK_MEMLEAK" in os.environ:
del os.environ["TOPOTESTS_CHECK_MEMLEAK"]
if "TOPOTESTS_CHECK_STDERR" in os.environ:
del os.environ["TOPOTESTS_CHECK_STDERR"]
@pytest.fixture(autouse=True, scope="session")
def setup_session_auto():