Skip to content

Commit

Permalink
tests: add support for running things under rr.
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Hopps <[email protected]>
  • Loading branch information
choppsv1 committed Nov 30, 2023
1 parent 3ed1bc8 commit c1ccc6b
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 10 deletions.
22 changes: 22 additions & 0 deletions doc/developer/topotests.rst
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,28 @@ during the config_timing test.
To specify different arguments for ``perf record``, one can use the
``--perf-options`` this will replace the ``-g`` used by default.

Running Daemons under RR Debug (``rr record``)
""""""""""""""""""""""""""""""""""""""""""""""

Topotest can automatically launch any daemon under ``rr(1)`` to collect
execute state. The daemon is run in non-daemon mode with ``rr record``.

The execution state will be saved in the router specific directory
(in a `rr` subdir that rr creates) under the test's run directoy.

Here's an example of collecting ``rr`` execution state from ``mgmtd`` on router
``r1`` during the config_timing test.

.. code:: console
$ sudo -E pytest --rr-routers=r1 --rr-daemons=mgmtd config_timing
...
$ find /tmp/topotests/ -name '*perf.data*'
/tmp/topotests/config_timing.test_config_timing/r1/perf.data
To specify additional arguments for ``rr record``, one can use the
``--rr-options``.

.. _topotests_docker:

Running Tests with Docker
Expand Down
18 changes: 18 additions & 0 deletions tests/topotests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@ def pytest_addoption(parser):
help="Options to pass to `perf record`.",
)

parser.addoption(
"--rr-daemons",
metavar="DAEMON[,DAEMON...]",
help="Comma-separated list of daemons to run `rr` on, or 'all'",
)

parser.addoption(
"--rr-routers",
metavar="ROUTER[,ROUTER...]",
help="Comma-separated list of routers to run `rr` on, or 'all'",
)

parser.addoption(
"--rr-options",
metavar="OPTS",
help="Options to pass to `rr record`.",
)

rundir_help = "directory for running in and log files"
parser.addini("rundir", rundir_help, default="/tmp/topotests")
parser.addoption("--rundir", metavar="DIR", help=rundir_help)
Expand Down
52 changes: 42 additions & 10 deletions tests/topotests/lib/topotest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,7 @@ def __init__(self, name, *posargs, **params):
)

self.perf_daemons = {}
self.rr_daemons = {}
self.valgrind_gdb_daemons = {}

# If this topology is using old API and doesn't have logdir
Expand Down Expand Up @@ -1805,6 +1806,9 @@ def startRouterDaemons(self, daemons=None, tgen=None):
gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons")
gdb_routers = g_pytest_config.get_option_list("--gdb-routers")
gdb_use_emacs = bool(g_pytest_config.option.gdb_use_emacs)
rr_daemons = g_pytest_config.get_option_list("--rr-daemons")
rr_routers = g_pytest_config.get_option_list("--rr-routers")
rr_options = g_pytest_config.get_option("--rr-options", "")
valgrind_extra = bool(g_pytest_config.option.valgrind_extra)
valgrind_leak_kinds = g_pytest_config.option.valgrind_leak_kinds
valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks)
Expand Down Expand Up @@ -1882,16 +1886,18 @@ def start_daemon(daemon, extra_opts=None):
# do not since apparently presence of the pidfile impacts BGP GR
self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase))

def do_gdb():
def do_gdb_or_rr(gdb):
routers = gdb_routers if gdb else rr_routers
daemons = gdb_daemons if gdb else rr_daemons
return (
(gdb_routers or gdb_daemons)
(routers or daemons)
and (
not gdb_routers
or self.name in gdb_routers
or "all" in gdb_routers
not routers
or self.name in routers
or "all" in routers
)
and (
not gdb_daemons or daemon in gdb_daemons or "all" in gdb_daemons
not daemons or daemon in daemons or "all" in daemons
)
)

Expand Down Expand Up @@ -1932,7 +1938,7 @@ def do_gdb():
)

valgrind_logbase = f"{self.logdir}/{self.name}.valgrind.{daemon}"
if do_gdb():
if do_gdb_or_rr(True):
cmdenv += " exec"
cmdenv += (
" /usr/bin/valgrind --num-callers=50"
Expand All @@ -1945,7 +1951,7 @@ def do_gdb():
cmdenv += (
" --gen-suppressions=all --expensive-definedness-checks=yes"
)
if do_gdb():
if do_gdb_or_rr(True):
cmdenv += " --vgdb-error=0"
elif daemon in strace_daemons or "all" in strace_daemons:
cmdenv = "strace -f -D -o {1}/{2}.strace.{0} ".format(
Expand All @@ -1965,9 +1971,12 @@ def do_gdb():
if extra_opts:
cmdopt += " " + extra_opts

if do_gdb_or_rr(True) and do_gdb_or_rr(False):
logger.warning("cant' use gdb and rr at same time")

if (
not gdb_use_emacs or Router.gdb_emacs_router or valgrind_memleaks
) and do_gdb():
) and do_gdb_or_rr(True):
if Router.gdb_emacs_router is not None:
logger.warning(
"--gdb-use-emacs can only run a single router and daemon, using"
Expand Down Expand Up @@ -2040,7 +2049,7 @@ def do_gdb():
self.routertype,
daemon,
)
elif gdb_use_emacs and do_gdb():
elif gdb_use_emacs and do_gdb_or_rr(True):
assert Router.gdb_emacs_router is None
Router.gdb_emacs_router = self

Expand Down Expand Up @@ -2135,6 +2144,29 @@ def emacs_gdb_ready():
logger.debug(
"%s: %s %s started with perf", self, self.routertype, daemon
)
elif do_gdb_or_rr(False):
cmdopt += rediropt
cmd = " ".join(
[
"rr record -o {} {} --".format(self.rundir / "rr", rr_options),
binary,
cmdopt
]
)
p = self.popen(cmd)
self.rr_daemons[daemon] = p
if p.poll() and p.returncode:
self.logger.error(
'%s: Failed to launch "%s" (%s) with rr using: %s',
self,
daemon,
p.returncode,
cmd,
)
else:
logger.debug(
"%s: %s %s started with rr", self, self.routertype, daemon
)
else:
if daemon != "snmpd" and daemon != "snmptrapd":
cmdopt += " -d "
Expand Down

0 comments on commit c1ccc6b

Please sign in to comment.