Skip to content

Commit

Permalink
tests: add --gdb-use-emacs option
Browse files Browse the repository at this point in the history
When specified `--gdb-use-emacs` will launch the daemon with gdb inside a
running emacs server using `emacsclient --eval` commands.

Signed-off-by: Christian Hopps <[email protected]>
  • Loading branch information
choppsv1 committed Oct 22, 2023
1 parent 571b403 commit ef02570
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
6 changes: 6 additions & 0 deletions doc/developer/topotests.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""""""""""""""""""""""""""""""""""""""""""""

Expand Down
6 changes: 6 additions & 0 deletions tests/topotests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
69 changes: 65 additions & 4 deletions tests/topotests/lib/topotest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
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 lib import micronet

Expand Down Expand Up @@ -1363,6 +1363,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.
Expand Down Expand Up @@ -1799,6 +1801,7 @@ def startRouterDaemons(self, daemons=None, tgen=None):
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")
Expand Down Expand Up @@ -1925,13 +1928,19 @@ def start_daemon(daemon, extra_opts=None):
if extra_opts:
cmdopt += " " + extra_opts

if (
(gdb_routers or gdb_daemons)
if ((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 "

Expand All @@ -1942,9 +1951,61 @@ def start_daemon(daemon, extra_opts=None):
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)
gdbcmd = f"{sudo_path} {pre_cmd} gdb -i=mi {binary}"

commander.cmd_raises(ecbin + ["--eval", f'(gdb "{gdbcmd}"))',])

# TODO: figure out a better way to determine when gdb is ready in emacs
time.sleep(10)

# 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
)
Expand Down

0 comments on commit ef02570

Please sign in to comment.