diff options
author | Oto Šťáva <oto.stava@nic.cz> | 2023-08-24 08:05:21 +0200 |
---|---|---|
committer | Aleš Mrázek <ales.mrazek@nic.cz> | 2024-12-03 11:48:59 +0100 |
commit | 4545e040bf81be5837774f0462ed741c89fae024 (patch) | |
tree | 023e104d3fd120e4d62db356404d62999e275274 | |
parent | manager: add option to list PIDs (diff) | |
download | knot-resolver-4545e040bf81be5837774f0462ed741c89fae024.tar.xz knot-resolver-4545e040bf81be5837774f0462ed741c89fae024.zip |
kresctl: add command to run a debugger on subprocesses
-rwxr-xr-x | poe | 2 | ||||
-rw-r--r-- | python/knot_resolver/client/commands/debug.py | 96 | ||||
-rw-r--r-- | python/knot_resolver/client/main.py | 17 |
3 files changed, 113 insertions, 2 deletions
@@ -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() |