Merge pull request #14634 from LabNConsulting/chopps/gdb-use-emacs

tests: add --gdb-use-emacs option
This commit is contained in:
Donatas Abraitis 2023-10-24 08:58:40 +03:00 committed by GitHub
commit 614d7873d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 7 deletions

View File

@ -583,6 +583,12 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
--gdb-breakpoints=nb_config_diff \
all-protocol-startup
Finally, for Emacs users, you can specify ``--gdb-use-emacs``. When specified
the first router and daemon to be launched in gdb will be launched and run with
Emacs gdb functionality by using `emacsclient --eval` commands. This provides an
IDE debugging experience for Emacs users. This functionality works best when
using password-less sudo.
Reporting Memleaks with FRR Memory Statistics
"""""""""""""""""""""""""""""""""""""""""""""

View File

@ -102,6 +102,12 @@ def pytest_addoption(parser):
help="Comma-separated list of routers to spawn gdb on, or 'all'",
)
parser.addoption(
"--gdb-use-emacs",
action="store_true",
help="Use emacsclient to run gdb instead of a shell",
)
parser.addoption(
"--logd",
action="append",

View File

@ -31,7 +31,8 @@ from copy import deepcopy
import lib.topolog as topolog
from lib.micronet_compat import Node
from lib.topolog import logger
from munet.base import Timeout
from munet.base import commander, get_exec_path_host, Timeout
from munet.testing.util import retry
from lib import micronet
@ -1261,8 +1262,8 @@ 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)
@ -1321,8 +1322,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)
@ -1363,6 +1364,8 @@ def setup_node_tmpdir(logdir, name):
class Router(Node):
"A Node with IPv4/IPv6 forwarding enabled"
gdb_emacs_router = None
def __init__(self, name, *posargs, **params):
# Backward compatibility:
# Load configuration defaults like topogen.
@ -1799,6 +1802,7 @@ class Router(Node):
gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints")
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)
valgrind_extra = bool(g_pytest_config.option.valgrind_extra)
valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks)
strace_daemons = g_pytest_config.get_option_list("--strace-daemons")
@ -1926,12 +1930,19 @@ class Router(Node):
cmdopt += " " + extra_opts
if (
(gdb_routers or gdb_daemons)
(not gdb_use_emacs or Router.gdb_emacs_router)
and (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)
):
if Router.gdb_emacs_router is not None:
logger.warning(
"--gdb-use-emacs can only run a single router and daemon, using"
" new window"
)
if daemon == "snmpd":
cmdopt += " -f "
@ -1942,9 +1953,84 @@ class Router(Node):
for bp in gdb_breakpoints:
gdbcmd += " -ex 'b {}'".format(bp)
gdbcmd += " -ex 'run {}'".format(cmdopt)
self.run_in_window(gdbcmd, daemon)
logger.info(
"%s: %s %s launched in gdb window", self, self.routertype, daemon
)
elif (
gdb_use_emacs
and (daemon in gdb_daemons)
and (not gdb_routers or self.name in gdb_routers)
):
assert Router.gdb_emacs_router is None
Router.gdb_emacs_router = self
if daemon == "snmpd":
cmdopt += " -f "
cmdopt += rediropt
sudo_path = get_exec_path_host("sudo")
ecbin = [
sudo_path,
"-Eu",
os.environ["SUDO_USER"],
get_exec_path_host("emacsclient"),
]
pre_cmd = self._get_pre_cmd(True, False, ns_only=True, root_level=True)
# why fail:? gdb -i=mi -iex='set debuginfod enabled off' {binary} "
gdbcmd = f"{sudo_path} {pre_cmd} gdb -i=mi {binary} "
commander.cmd_raises(
ecbin
+ [
"--eval",
f'(gdb "{gdbcmd}"))',
]
)
elcheck = (
'(ignore-errors (with-current-buffer "*gud-nsenter*"'
" (and (string-match-p"
' "(gdb) "'
" (buffer-substring-no-properties "
' (- (point-max) 10) (point-max))) "ready")))'
)
@retry(10)
def emacs_gdb_ready():
check = commander.cmd_nostatus(ecbin + ["--eval", elcheck])
return None if "ready" in check else False
emacs_gdb_ready()
# target gdb commands
cmd = "set breakpoint pending on"
self.cmd_raises(
ecbin
+ [
"--eval",
f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
]
)
# gdb breakpoints
for bp in gdb_breakpoints:
self.cmd_raises(
ecbin
+ [
"--eval",
f'(gud-gdb-run-command-fetch-lines "br {bp}" "*gud-gdb*")',
]
)
# gdb run cmd
self.cmd_raises(
ecbin
+ [
"--eval",
f'(gud-gdb-run-command-fetch-lines "run {cmdopt}" "*gud-gdb*")',
]
)
logger.info(
"%s: %s %s launched in gdb window", self, self.routertype, daemon
)