summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOto Šťáva <oto.stava@nic.cz>2023-08-24 08:05:21 +0200
committerAleš Mrázek <ales.mrazek@nic.cz>2024-12-03 11:48:59 +0100
commit4545e040bf81be5837774f0462ed741c89fae024 (patch)
tree023e104d3fd120e4d62db356404d62999e275274
parentmanager: add option to list PIDs (diff)
downloadknot-resolver-4545e040bf81be5837774f0462ed741c89fae024.tar.xz
knot-resolver-4545e040bf81be5837774f0462ed741c89fae024.zip
kresctl: add command to run a debugger on subprocesses
-rwxr-xr-xpoe2
-rw-r--r--python/knot_resolver/client/commands/debug.py96
-rw-r--r--python/knot_resolver/client/main.py17
3 files changed, 113 insertions, 2 deletions
diff --git a/poe b/poe
index d1f58894..815428a3 100755
--- a/poe
+++ b/poe
@@ -1,4 +1,4 @@
#!/bin/sh
script_dir="$(dirname "$(readlink -f "$0")")"
-exec poetry --directory "$script_dir" run poe --root "$script_dir" "$@"
+exec poetry --directory "$script_dir" run -- poe --root "$script_dir" "$@"
diff --git a/python/knot_resolver/client/commands/debug.py b/python/knot_resolver/client/commands/debug.py
new file mode 100644
index 00000000..0d112927
--- /dev/null
+++ b/python/knot_resolver/client/commands/debug.py
@@ -0,0 +1,96 @@
+import argparse
+import json
+import os
+import sys
+from typing import List, Optional, Tuple, Type
+
+from knot_resolver.client.command import Command, CommandArgs, CompWords, register_command
+from knot_resolver.utils import which
+from knot_resolver.utils.requests import request
+
+PIDS_TYPE = List
+
+
+@register_command
+class DebugCommand(Command):
+ def __init__(self, namespace: argparse.Namespace) -> None:
+ self.proc_type: Optional[str] = namespace.proc_type
+ self.sudo: bool = namespace.sudo
+ self.gdb: str = namespace.gdb
+ self.gdb_args: List[str] = namespace.extra
+ super().__init__(namespace)
+
+ @staticmethod
+ def register_args_subparser(
+ subparser: "argparse._SubParsersAction[argparse.ArgumentParser]",
+ ) -> Tuple[argparse.ArgumentParser, "Type[Command]"]:
+ debug = subparser.add_parser(
+ "debug",
+ help="Run GDB on the manager's subprocesses - runs with sudo by default to avoid ptrace_scope issues",
+ )
+ debug.add_argument(
+ "proc_type",
+ help="Optional, the type of process to debug. May be 'kresd' (default), 'gc', or 'all'.",
+ type=str,
+ nargs="?",
+ default="kresd",
+ )
+ debug.add_argument(
+ "--no-sudo",
+ dest="sudo",
+ help="Do not run GDB with sudo (may not work if your ptrace_scope is 1 or higher)",
+ action="store_false",
+ )
+ debug.add_argument(
+ "--gdb",
+ help="GDB command (may be a command on PATH, or an absolute path)",
+ type=str,
+ default="gdb",
+ )
+ return debug, DebugCommand
+
+ @staticmethod
+ def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords:
+ return {}
+
+ def run(self, args: CommandArgs) -> None: # noqa: PLR0912, PLR0915
+ gdb_cmd = str(which.which(self.gdb))
+ sudo_cmd = str(which.which("sudo"))
+
+ response = request(args.socket, "GET", f"pids/{self.proc_type}")
+ if response.status != 200:
+ print(response, file=sys.stderr)
+ sys.exit(1)
+
+ pids = json.loads(response.body)
+ if not isinstance(pids, PIDS_TYPE):
+ print(
+ f"Unexpected response type '{type(pids).__name__}' from manager. Expected '{PIDS_TYPE.__name__}'",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+ if len(pids) == 0:
+ print(
+ f"There are no processes of type '{self.proc_type}' available to debug",
+ file=sys.stderr,
+ )
+
+ exec_args = []
+
+ if self.sudo:
+ exec_args.extend([sudo_cmd, "--"])
+
+ # attach to PIDs
+ exec_args.extend([gdb_cmd, "--pid", str(pids[0])])
+ inferior = 2
+ for pid in pids[1:]:
+ exec_args.extend(["-init-eval-command", "add-inferior"])
+ exec_args.extend(["-init-eval-command", f"inferior {inferior}"])
+ exec_args.extend(["-init-eval-command", f"attach {pid}"])
+ inferior += 1
+
+ exec_args.extend(["-init-eval-command", "inferior 1"])
+ exec_args.extend(self.gdb_args)
+
+ print(f"exec_args = {exec_args}")
+ os.execl(*exec_args)
diff --git a/python/knot_resolver/client/main.py b/python/knot_resolver/client/main.py
index 75cd6a77..461b7fc4 100644
--- a/python/knot_resolver/client/main.py
+++ b/python/knot_resolver/client/main.py
@@ -1,6 +1,7 @@
import argparse
import importlib
import os
+import sys
from knot_resolver.constants import VERSION
@@ -68,7 +69,21 @@ def main() -> None:
parser = create_main_argument_parser()
install_commands_parsers(parser)
- namespace = parser.parse_args()
+ # TODO: This is broken with unpatched versions of poethepoet, because they drop the `--` pseudo-argument.
+ # Patch submitted at <https://github.com/nat-n/poethepoet/pull/163>.
+ try:
+ pa_index = sys.argv.index("--", 1)
+ argv_to_parse = sys.argv[1:pa_index]
+ argv_extra = sys.argv[(pa_index + 1) :]
+ except ValueError:
+ argv_to_parse = sys.argv[1:]
+ argv_extra = []
+
+ namespace = parser.parse_args(argv_to_parse)
+ if hasattr(namespace, "extra"):
+ raise TypeError("'extra' is already an attribute - this is disallowed for commands")
+ namespace.extra = argv_extra
+
client = KresClient(namespace, parser)
client.execute()