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
+execution state. The daemon is run in the foreground 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
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)
)
self.perf_daemons = {}
+ self.rr_daemons = {}
self.valgrind_gdb_daemons = {}
# If this topology is using old API and doesn't have logdir
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)
# 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)
- and (
- not gdb_routers
- or self.name in gdb_routers
- or "all" in gdb_routers
- )
- and (
- not gdb_daemons or daemon in gdb_daemons or "all" in gdb_daemons
- )
+ (routers or daemons)
+ and (not routers or self.name in routers or "all" in routers)
+ and (not daemons or daemon in daemons or "all" in daemons)
)
rediropt = " > {0}.out 2> {0}.err".format(daemon)
)
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"
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(
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"
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
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 "