Fixes for gdb stubs (#13169)

* gdb: Clarify a comment
* gdb: Fix gdb.unwinder.Unwinder.__call__ argument. It takes a gdb.PendingFrame, not a gdb.Frame.
* gdb: Unwinders may implement a proto without subclassing gdb.unwinder.Unwinder
* gdb: Fix Breakpoint.__init__

 1. `line` should be `int|str`, not just `int` (IDK what a string means,
    but that it can be a string is clear if you read
    py-breakpoint.c:bppy_init().
 2. `type` argument should be able to be passed to the "location" form,
    not just the "spec" form, even if
    https://sourceware.org/gdb/current/onlinedocs/gdb.html/Breakpoints-In-Python.html
    neglects to mention it (don't worry, I'll be submitting a patch to fix
    the doc soon).
 3. Fix the positional argument order (based on GDB's sources, it isn't
    really documented)
 4. Use more `@overloads` to enforce that at least 1 of `function`,
    `label`, or `line` are given in the location form.
This commit is contained in:
Luke T. Shumaker
2024-12-03 08:35:06 -07:00
committed by GitHub
parent fcf30cfd40
commit 18d27d734a
3 changed files with 133 additions and 17 deletions

View File

@@ -1,5 +1,5 @@
version = "15.0.*"
# This is the official web portal for GDB,
# This is the official web portal for the GDB Git repo,
# see https://sourceware.org/gdb/current/ for other ways of obtaining the source code.
upstream_repository = "https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=tree"
extra_description = """\

View File

@@ -7,12 +7,11 @@ import threading
from _typeshed import Incomplete
from collections.abc import Callable, Iterator, Mapping, Sequence
from contextlib import AbstractContextManager
from typing import Any, Final, Generic, Literal, Protocol, TypeVar, final, overload
from typing import Any, Final, Generic, Literal, Protocol, TypeVar, final, overload, type_check_only
from typing_extensions import TypeAlias, deprecated
import gdb.FrameDecorator
import gdb.types
import gdb.unwinder
import gdb.xmethod
# The following submodules are automatically imported
@@ -289,7 +288,15 @@ class PendingFrame:
class UnwindInfo:
def add_saved_register(self, reg: str | RegisterDescriptor | int, value: Value, /) -> None: ...
frame_unwinders: list[gdb.unwinder.Unwinder]
@type_check_only
class _Unwinder(Protocol):
@property
def name(self) -> str: ...
enabled: bool
def __call__(self, pending_frame: PendingFrame) -> UnwindInfo | None: ...
frame_unwinders: list[_Unwinder]
# Inferiors
@@ -468,7 +475,7 @@ class Progspace:
pretty_printers: list[_PrettyPrinterLookupFunction]
type_printers: list[gdb.types._TypePrinter]
frame_filters: dict[str, _FrameFilter]
frame_unwinders: list[gdb.unwinder.Unwinder]
frame_unwinders: list[_Unwinder]
missing_debug_handlers: Incomplete
def block_for_pc(self, pc: int, /) -> Block | None: ...
@@ -493,7 +500,7 @@ class Objfile:
pretty_printers: list[_PrettyPrinterLookupFunction]
type_printers: list[gdb.types._TypePrinter]
frame_filters: dict[str, _FrameFilter]
frame_unwinders: list[gdb.unwinder.Unwinder]
frame_unwinders: list[_Unwinder]
is_file: bool
def is_valid(self) -> bool: ...
@@ -664,21 +671,131 @@ class LineTable:
# Breakpoints
class Breakpoint:
@overload
def __init__(
self, spec: str, type: int = ..., wp_class: int = ..., internal: bool = ..., temporary: bool = ..., qualified: bool = ...
) -> None: ...
# The where="spec" form of __init__(). See py-breakpoints.c:bppy_init():keywords for the positional order.
@overload
def __init__(
self,
source: str = ...,
function: str = ...,
label: str = ...,
line: int = ...,
# where
spec: str,
# options
type: int = ...,
wp_class: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
# The where="location" form of __init__(). A watchpoint (`type=BP_WATCHPOINT`) cannot be created with this form.
#
# We exclude the `wp_class` (watchpoint class) option here, even though py-breakpoints.c accepts it. It doesn't make sense
# unless type==BP_WATCHPOINT, and is silently ignored in those cases; allowing it in those cases is likely an oversight, not
# an intentional allowance.
#
# We repeat this 7 times because the type system doesn't have simple a way for us to say "at least one of `function`, `label`,
# or `line`", so we must repeat it for each combination of the 3.
#
# The len=3 combination.
@overload
def __init__(
self,
*,
# where
source: str = ...,
function: str,
label: str,
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
# The 3 len=2 combinations.
@overload
def __init__(
self,
*,
source: str = ...,
# where
label: str,
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
function: str,
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
function: str,
label: str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
# The 3 len=1 combinations.
@overload
def __init__(
self,
*,
source: str = ...,
# where
function: str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
label: str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
# Methods.
def stop(self) -> bool: ...
def is_valid(self) -> bool: ...
def delete(self) -> None: ...

View File

@@ -1,5 +1,4 @@
import gdb
from gdb import Frame, UnwindInfo
class FrameId:
def __init__(self, sp: gdb.Value | int, pc: gdb.Value | int, special: gdb.Value | int | None = None) -> None: ...
@@ -16,6 +15,6 @@ class Unwinder:
enabled: bool
def __init__(self, name: str) -> None: ...
def __call__(self, pending_frame: Frame) -> UnwindInfo | None: ...
def __call__(self, pending_frame: gdb.PendingFrame) -> gdb.UnwindInfo | None: ...
def register_unwinder(locus: gdb.Objfile | gdb.Progspace | None, unwinder: Unwinder, replace: bool = ...) -> None: ...
def register_unwinder(locus: gdb.Objfile | gdb.Progspace | None, unwinder: gdb._Unwinder, replace: bool = ...) -> None: ...