Fix missing help strings for Pwndbg commands in GDB (#2351)

* Fix missing help strings in GDB with the `help` command

* Add a test that checks whether the `help` command in GDB works for Pwndbg commands
This commit is contained in:
Matt. 2024-08-12 11:56:25 -03:00 committed by GitHub
parent 12d8c3960f
commit 8b24e27152
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 42 additions and 6 deletions

View File

@ -90,11 +90,13 @@ class Command:
is_alias: bool = False,
aliases: List[str] = [],
category: CommandCategory = CommandCategory.MISC,
doc: str | None = None,
) -> None:
self.is_alias = is_alias
self.aliases = aliases
self.category = category
self.shell = shell
self.doc = doc
if command_name is None:
command_name = function.__name__
@ -102,7 +104,7 @@ class Command:
def _handler(_debugger, arguments, is_interactive):
self.invoke(arguments, is_interactive)
self.handle = pwndbg.dbg.add_command(command_name, _handler)
self.handle = pwndbg.dbg.add_command(command_name, _handler, doc)
self.function = function
if command_name in command_names:
@ -547,7 +549,7 @@ class _ArgparsedCommand(Command):
file = io.StringIO()
self.parser.print_help(file)
file.seek(0)
self.__doc__ = file.read()
doc = file.read()
# Note: function.__doc__ is used in the `pwndbg [filter]` command display
function.__doc__ = self.parser.description.strip()
@ -555,6 +557,7 @@ class _ArgparsedCommand(Command):
super().__init__( # type: ignore[misc]
function,
command_name=command_name,
doc=doc,
*a,
**kw,
)

View File

@ -352,7 +352,7 @@ class Debugger:
raise NotImplementedError()
def add_command(
self, name: str, handler: Callable[[Debugger, str, bool], None]
self, name: str, handler: Callable[[Debugger, str, bool], None], doc: str | None
) -> CommandHandle:
"""
Adds a command with the given name to the debugger, that invokes the

View File

@ -129,9 +129,11 @@ class GDBCommand(gdb.Command):
debugger: GDB,
name: str,
handler: Callable[[pwndbg.dbg_mod.Debugger, str, bool], None],
doc: str | None,
):
self.debugger = debugger
self.handler = handler
self.__doc__ = doc
super().__init__(name, gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)
def invoke(self, args: str, from_tty: bool) -> None:
@ -326,9 +328,12 @@ class GDB(pwndbg.dbg_mod.Debugger):
@override
def add_command(
self, name: str, handler: Callable[[pwndbg.dbg_mod.Debugger, str, bool], None]
self,
name: str,
handler: Callable[[pwndbg.dbg_mod.Debugger, str, bool], None],
doc: str | None,
) -> pwndbg.dbg_mod.CommandHandle:
command = GDBCommand(self, name, handler)
command = GDBCommand(self, name, handler, doc)
return GDBCommandHandle(command)
@override

View File

@ -260,7 +260,10 @@ class LLDB(pwndbg.dbg_mod.Debugger):
@override
def add_command(
self, command_name: str, handler: Callable[[pwndbg.dbg_mod.Debugger, str, bool], None]
self,
command_name: str,
handler: Callable[[pwndbg.dbg_mod.Debugger, str, bool], None],
doc: str | None,
) -> pwndbg.dbg_mod.CommandHandle:
debugger = self

View File

@ -0,0 +1,25 @@
from __future__ import annotations
import gdb
from pwndbg import commands
def test_command_help_strings(start_binary):
"""
Tests whether the `help` command works for Pwndbg commands. We go through
every command and check whether the value of `help <command>` matches the
help string we pass to the Debugger-agnostic API when it's being registered.
"""
for command in commands.commands:
help_str = gdb.execute(f"help {command.__name__}", from_tty=False, to_string=True)
if command.doc is None:
assert help_str.strip() == "This command is not documented."
else:
truth = [line.strip() for line in command.doc.splitlines() if len(line.strip()) > 0]
gdb_out = [line.strip() for line in help_str.splitlines() if len(line.strip()) > 0]
# We check both of these cases since for some commands GDB will
# output the list of aliases as the first line.
assert truth == gdb_out or truth == gdb_out[1:]