From e6079f4fa4f355fefcc7093a482aaf9a37729964 Mon Sep 17 00:00:00 2001 From: Christian Hopps Date: Sun, 23 Apr 2023 21:53:44 -0400 Subject: [PATCH] tests: add --perf and --perf-options for profiling daemons Signed-off-by: Christian Hopps --- tests/topotests/conftest.py | 21 +++++++++++++++-- tests/topotests/lib/topotest.py | 40 +++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 8e4d13df7d..2b33aa24b0 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -66,8 +66,8 @@ def pytest_addoption(parser): action="append", metavar="DAEMON[,ROUTER[,...]", help=( - "Tail-F of DAEMON log file. Specify routers in comma-separated list after " - "daemon to limit to a subset of routers" + "Tail-F the DAEMON log file on all or a subset of ROUTERs." + " Option can be given multiple times." ), ) @@ -103,6 +103,23 @@ def pytest_addoption(parser): help="Comma-separated list of networks to capture packets on, or 'all'", ) + parser.addoption( + "--perf", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Collect performance data from given DAEMON on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( + "--perf-options", + metavar="OPTS", + default="-g", + help="Options to pass to `perf 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) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 93daf0bd8c..967f09ecbd 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1184,10 +1184,9 @@ def rlimit_atleast(rname, min_value, raises=False): def fix_netns_limits(ns): - # Maximum read and write socket buffer sizes - sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20]) - sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20]) + sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2**20]) + sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2**20]) sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0) sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0) @@ -1246,8 +1245,8 @@ def fix_host_limits(): sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024) # Maximum read and write socket buffer sizes - sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20) - sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20) + sysctl_atleast(None, "net.core.rmem_max", 16 * 2**20) + sysctl_atleast(None, "net.core.wmem_max", 16 * 2**20) # Garbage Collection Settings for ARP and Neighbors sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024) @@ -1289,7 +1288,6 @@ class Router(Node): "A Node with IPv4/IPv6 forwarding enabled" def __init__(self, name, *posargs, **params): - # Backward compatibility: # Load configuration defaults like topogen. self.config_defaults = configparser.ConfigParser( @@ -1305,6 +1303,8 @@ class Router(Node): os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini") ) + self.perf_daemons = {} + # If this topology is using old API and doesn't have logdir # specified, then attempt to generate an unique logdir. self.logdir = params.get("logdir") @@ -1724,6 +1724,16 @@ class Router(Node): ).split()[2] logger.info("{}: running version: {}".format(self.name, self.version)) + perfds = {} + perf_options = g_pytest_config.get_option("--perf-options", "-g") + for perf in g_pytest_config.get_option("--perf", []): + if "," in perf: + daemon, routers = perf.split(",", 1) + perfds[daemon] = routers.split(",") + else: + daemon = perf + perfds[daemon] = ["all"] + logd_options = {} for logd in g_pytest_config.get_option("--logd", []): if "," in logd: @@ -1805,7 +1815,6 @@ class Router(Node): tail_log_files.append( "{}/{}/{}.log".format(self.logdir, self.name, daemon) ) - if extra_opts: cmdopt += " " + extra_opts @@ -1832,6 +1841,23 @@ class Router(Node): logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon ) + elif daemon in perfds and (self.name in perfds[daemon] or "all" in perfds[daemon]): + cmdopt += rediropt + cmd = " ".join(["perf record {} --".format(perf_options), binary, cmdopt]) + p = self.popen(cmd) + self.perf_daemons[daemon] = p + if p.poll() and p.returncode: + self.logger.error( + '%s: Failed to launch "%s" (%s) with perf using: %s', + self, + daemon, + p.returncode, + cmd, + ) + else: + logger.debug( + "%s: %s %s started with perf", self, self.routertype, daemon + ) else: if daemon != "snmpd": cmdopt += " -d " -- 2.39.5