]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
kresctl: add command to run a debugger on subprocesses
authorOto Šťáva <oto.stava@nic.cz>
Thu, 24 Aug 2023 06:05:21 +0000 (08:05 +0200)
committerAleš Mrázek <ales.mrazek@nic.cz>
Tue, 3 Dec 2024 10:48:59 +0000 (11:48 +0100)
poe
python/knot_resolver/client/commands/debug.py [new file with mode: 0644]
python/knot_resolver/client/main.py

diff --git a/poe b/poe
index d1f58894cd8620dffb1e7231fe708572b24e4366..815428a37f55057096543851caa9d3f71c2dd5e8 100755 (executable)
--- 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 (file)
index 0000000..0d11292
--- /dev/null
@@ -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)
index 75cd6a77fe252b86c1f8a2a86afc95acb9e5aaf8..461b7fc42989d721e5095155ced8730edc6ec3b8 100644 (file)
@@ -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()