mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-30 22:06:20 +00:00
topotest: add --memleaks topotest option
Signed-off-by: Lou Berger <lberger@labn.net>
This commit is contained in:
parent
c6686550c3
commit
d4977708f0
@ -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.
|
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
|
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 \
|
--gdb-breakpoints=nb_config_diff \
|
||||||
all-protocol-startup
|
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
|
Detecting Memleaks with Valgrind
|
||||||
""""""""""""""""""""""""""""""""
|
""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
@ -86,6 +86,12 @@ def pytest_addoption(parser):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.addoption(
|
||||||
|
"--memleaks",
|
||||||
|
action="store_true",
|
||||||
|
help="Report memstat results as errors",
|
||||||
|
)
|
||||||
|
|
||||||
parser.addoption(
|
parser.addoption(
|
||||||
"--pause",
|
"--pause",
|
||||||
action="store_true",
|
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
|
assert topotest.g_pytest_config.option.valgrind_memleaks
|
||||||
|
|
||||||
leaks = []
|
leaks = []
|
||||||
@ -232,10 +238,46 @@ def check_for_memleaks():
|
|||||||
pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons))
|
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")
|
@pytest.fixture(autouse=True, scope="module")
|
||||||
def module_check_memtest(request):
|
def module_check_memtest(request):
|
||||||
yield
|
yield
|
||||||
if request.config.option.valgrind_memleaks:
|
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:
|
if get_topogen() is not None:
|
||||||
check_for_memleaks()
|
check_for_memleaks()
|
||||||
|
|
||||||
@ -264,6 +306,8 @@ def pytest_runtest_call(item: pytest.Item) -> None:
|
|||||||
|
|
||||||
# Check for leaks if requested
|
# Check for leaks if requested
|
||||||
if item.config.option.valgrind_memleaks:
|
if item.config.option.valgrind_memleaks:
|
||||||
|
check_for_valgrind_memleaks()
|
||||||
|
if item.config.option.memleaks:
|
||||||
check_for_memleaks()
|
check_for_memleaks()
|
||||||
|
|
||||||
|
|
||||||
@ -370,10 +414,22 @@ def pytest_configure(config):
|
|||||||
if config.option.topology_only and is_xdist:
|
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")
|
||||||
|
|
||||||
|
pytest.exit("Cannot use --topology-only with distributed test mode")
|
||||||
|
|
||||||
# Check environment now that we have config
|
# Check environment now that we have config
|
||||||
if not diagnose_env(rundir):
|
if not diagnose_env(rundir):
|
||||||
pytest.exit("environment has errors, please read the logs in %s" % 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")
|
@pytest.fixture(autouse=True, scope="session")
|
||||||
def setup_session_auto():
|
def setup_session_auto():
|
||||||
|
Loading…
Reference in New Issue
Block a user