aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python')
-rw-r--r--gdb/python/lib/gdb/FrameDecorator.py2
-rw-r--r--gdb/python/lib/gdb/FrameIterator.py2
-rw-r--r--gdb/python/lib/gdb/__init__.py155
-rw-r--r--gdb/python/lib/gdb/command/__init__.py2
-rw-r--r--gdb/python/lib/gdb/command/explore.py2
-rw-r--r--gdb/python/lib/gdb/command/frame_filters.py2
-rw-r--r--gdb/python/lib/gdb/command/missing_files.py2
-rw-r--r--gdb/python/lib/gdb/command/pretty_printers.py2
-rw-r--r--gdb/python/lib/gdb/command/prompt.py2
-rw-r--r--gdb/python/lib/gdb/command/type_printers.py2
-rw-r--r--gdb/python/lib/gdb/command/unwinders.py2
-rw-r--r--gdb/python/lib/gdb/command/xmethods.py2
-rw-r--r--gdb/python/lib/gdb/dap/__init__.py4
-rw-r--r--gdb/python/lib/gdb/dap/breakpoint.py12
-rw-r--r--gdb/python/lib/gdb/dap/bt.py2
-rw-r--r--gdb/python/lib/gdb/dap/completions.py63
-rw-r--r--gdb/python/lib/gdb/dap/disassemble.py2
-rw-r--r--gdb/python/lib/gdb/dap/evaluate.py8
-rw-r--r--gdb/python/lib/gdb/dap/events.py9
-rw-r--r--gdb/python/lib/gdb/dap/frames.py8
-rw-r--r--gdb/python/lib/gdb/dap/globalvars.py3
-rw-r--r--gdb/python/lib/gdb/dap/io.py2
-rw-r--r--gdb/python/lib/gdb/dap/launch.py3
-rw-r--r--gdb/python/lib/gdb/dap/locations.py2
-rw-r--r--gdb/python/lib/gdb/dap/memory.py2
-rw-r--r--gdb/python/lib/gdb/dap/modules.py2
-rw-r--r--gdb/python/lib/gdb/dap/next.py17
-rw-r--r--gdb/python/lib/gdb/dap/pause.py2
-rw-r--r--gdb/python/lib/gdb/dap/scopes.py7
-rw-r--r--gdb/python/lib/gdb/dap/server.py124
-rw-r--r--gdb/python/lib/gdb/dap/sources.py11
-rw-r--r--gdb/python/lib/gdb/dap/startup.py2
-rw-r--r--gdb/python/lib/gdb/dap/state.py2
-rw-r--r--gdb/python/lib/gdb/dap/threads.py25
-rw-r--r--gdb/python/lib/gdb/dap/typecheck.py2
-rw-r--r--gdb/python/lib/gdb/dap/varref.py5
-rw-r--r--gdb/python/lib/gdb/disassembler.py2
-rw-r--r--gdb/python/lib/gdb/frames.py2
-rw-r--r--gdb/python/lib/gdb/function/__init__.py2
-rw-r--r--gdb/python/lib/gdb/function/as_string.py2
-rw-r--r--gdb/python/lib/gdb/function/caller_is.py2
-rw-r--r--gdb/python/lib/gdb/function/strfns.py2
-rw-r--r--gdb/python/lib/gdb/missing_debug.py2
-rw-r--r--gdb/python/lib/gdb/missing_files.py2
-rw-r--r--gdb/python/lib/gdb/missing_objfile.py2
-rw-r--r--gdb/python/lib/gdb/printer/__init__.py2
-rw-r--r--gdb/python/lib/gdb/printing.py2
-rw-r--r--gdb/python/lib/gdb/prompt.py2
-rw-r--r--gdb/python/lib/gdb/ptwrite.py2
-rw-r--r--gdb/python/lib/gdb/styling.py13
-rw-r--r--gdb/python/lib/gdb/types.py2
-rw-r--r--gdb/python/lib/gdb/unwinder.py2
-rw-r--r--gdb/python/lib/gdb/xmethod.py2
-rw-r--r--gdb/python/py-all-events.def2
-rw-r--r--gdb/python/py-auto-load.c2
-rw-r--r--gdb/python/py-bpevent.c2
-rw-r--r--gdb/python/py-breakpoint.c8
-rw-r--r--gdb/python/py-cmd.c110
-rw-r--r--gdb/python/py-color.c79
-rw-r--r--gdb/python/py-color.h2
-rw-r--r--gdb/python/py-connection.c6
-rw-r--r--gdb/python/py-continueevent.c2
-rw-r--r--gdb/python/py-dap.c6
-rw-r--r--gdb/python/py-disasm.c13
-rw-r--r--gdb/python/py-event-types.def2
-rw-r--r--gdb/python/py-event.c2
-rw-r--r--gdb/python/py-event.h2
-rw-r--r--gdb/python/py-events.h2
-rw-r--r--gdb/python/py-evtregistry.c2
-rw-r--r--gdb/python/py-evts.c2
-rw-r--r--gdb/python/py-exitedevent.c2
-rw-r--r--gdb/python/py-finishbreakpoint.c2
-rw-r--r--gdb/python/py-frame.c2
-rw-r--r--gdb/python/py-framefilter.c2
-rw-r--r--gdb/python/py-function.c2
-rw-r--r--gdb/python/py-gdb-readline.c6
-rw-r--r--gdb/python/py-inferior.c2
-rw-r--r--gdb/python/py-infevents.c2
-rw-r--r--gdb/python/py-infthread.c2
-rw-r--r--gdb/python/py-instruction.c2
-rw-r--r--gdb/python/py-instruction.h2
-rw-r--r--gdb/python/py-lazy-string.c2
-rw-r--r--gdb/python/py-linetable.c2
-rw-r--r--gdb/python/py-membuf.c2
-rw-r--r--gdb/python/py-mi.c2
-rw-r--r--gdb/python/py-micmd.c6
-rw-r--r--gdb/python/py-newobjfileevent.c2
-rw-r--r--gdb/python/py-objfile.c2
-rw-r--r--gdb/python/py-param.c20
-rw-r--r--gdb/python/py-prettyprint.c2
-rw-r--r--gdb/python/py-progspace.c2
-rw-r--r--gdb/python/py-record-btrace.c2
-rw-r--r--gdb/python/py-record-btrace.h2
-rw-r--r--gdb/python/py-record-full.c2
-rw-r--r--gdb/python/py-record-full.h2
-rw-r--r--gdb/python/py-record.c2
-rw-r--r--gdb/python/py-record.h2
-rw-r--r--gdb/python/py-ref.h2
-rw-r--r--gdb/python/py-registers.c5
-rw-r--r--gdb/python/py-signalevent.c2
-rw-r--r--gdb/python/py-stopevent.c2
-rw-r--r--gdb/python/py-stopevent.h2
-rw-r--r--gdb/python/py-symbol.c73
-rw-r--r--gdb/python/py-symtab.c183
-rw-r--r--gdb/python/py-threadevent.c2
-rw-r--r--gdb/python/py-tui.c2
-rw-r--r--gdb/python/py-type.c96
-rw-r--r--gdb/python/py-uiout.h2
-rw-r--r--gdb/python/py-unwind.c12
-rw-r--r--gdb/python/py-utils.c2
-rw-r--r--gdb/python/py-value.c45
-rw-r--r--gdb/python/py-varobj.c2
-rw-r--r--gdb/python/py-xmethods.c2
-rw-r--r--gdb/python/python-internal.h213
-rw-r--r--gdb/python/python.c110
-rw-r--r--gdb/python/python.h2
116 files changed, 998 insertions, 626 deletions
diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py
index 43efe8a..fa6effa 100644
--- a/gdb/python/lib/gdb/FrameDecorator.py
+++ b/gdb/python/lib/gdb/FrameDecorator.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py
index 75176c3..54534fe 100644
--- a/gdb/python/lib/gdb/FrameIterator.py
+++ b/gdb/python/lib/gdb/FrameIterator.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 146a963..cedd897 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,17 +19,22 @@ import sys
import threading
import traceback
from contextlib import contextmanager
+from importlib import reload
-# Python 3 moved "reload"
-if sys.version_info >= (3, 4):
- from importlib import reload
-else:
- from imp import reload
-
-import _gdb
-
+# The star import imports _gdb names. When the names are used locally, they
+# trigger F405 warnings unless added to the explicit import list.
# Note that two indicators are needed here to silence flake8.
from _gdb import * # noqa: F401,F403
+from _gdb import (
+ STDERR,
+ STDOUT,
+ Command,
+ execute,
+ flush,
+ parameter,
+ selected_inferior,
+ write,
+)
# isort: split
@@ -60,14 +65,14 @@ class _GdbFile(object):
self.write(line)
def flush(self):
- _gdb.flush(stream=self.stream)
+ flush(stream=self.stream)
def write(self, s):
- _gdb.write(s, stream=self.stream)
+ write(s, stream=self.stream)
-sys.stdout = _GdbFile(_gdb.STDOUT)
-sys.stderr = _GdbFile(_gdb.STDERR)
+sys.stdout = _GdbFile(STDOUT)
+sys.stderr = _GdbFile(STDERR)
# Default prompt hook does nothing.
prompt_hook = None
@@ -189,7 +194,7 @@ def GdbSetPythonDirectory(dir):
def current_progspace():
"Return the current Progspace."
- return _gdb.selected_inferior().progspace
+ return selected_inferior().progspace
def objfiles():
@@ -226,14 +231,14 @@ def set_parameter(name, value):
value = "on"
else:
value = "off"
- _gdb.execute("set " + name + " " + str(value), to_string=True)
+ execute("set " + name + " " + str(value), to_string=True)
@contextmanager
def with_parameter(name, value):
"""Temporarily set the GDB parameter NAME to VALUE.
Note that this is a context manager."""
- old_value = _gdb.parameter(name)
+ old_value = parameter(name)
set_parameter(name, value)
try:
# Nothing that useful to return.
@@ -392,3 +397,121 @@ def _handle_missing_objfile(pspace, buildid, filename):
return _handle_missing_files(
pspace, "objfile", lambda h: h(pspace, buildid, filename)
)
+
+
+class ParameterPrefix:
+ # A wrapper around gdb.Command for creating set/show prefixes.
+ #
+ # When creating a gdb.Parameter sub-classes, it is sometimes necessary
+ # to first create a gdb.Command object in order to create the needed
+ # command prefix. However, for parameters, we actually need two
+ # prefixes, a 'set' prefix, and a 'show' prefix. With this helper
+ # class, a single instance of this class will create both prefixes at
+ # once.
+ #
+ # It is important that this class-level documentation not be a __doc__
+ # string. Users are expected to sub-class this ParameterPrefix class
+ # and add their own documentation. If they don't, then GDB will
+ # generate a suitable doc string. But, if this (parent) class has a
+ # __doc__ string of its own, then sub-classes will inherit that __doc__
+ # string, and GDB will not understand that it needs to generate one.
+
+ class _PrefixCommand(Command):
+ """A gdb.Command used to implement both the set and show prefixes.
+
+ This documentation string is not used as the prefix command
+ documentation as it is overridden in the __init__ method below."""
+
+ # This private method is connected to the 'invoke' attribute within
+ # this _PrefixCommand object if the containing ParameterPrefix
+ # object has an invoke_set or invoke_show method.
+ #
+ # This method records within self.__delegate which _PrefixCommand
+ # object is currently active, and then calls the correct invoke
+ # method on the delegat object (the ParameterPrefix sub-class
+ # object).
+ #
+ # Recording the currently active _PrefixCommand object is important;
+ # if from the invoke method the user calls dont_repeat, then this is
+ # forwarded to the currently active _PrefixCommand object.
+ def __invoke(self, args, from_tty):
+
+ # A helper class for use as part of a Python 'with' block.
+ # Records which gdb.Command object is currently running its
+ # invoke method.
+ class MarkActiveCallback:
+ # The CMD is a _PrefixCommand object, and the DELEGATE is
+ # the ParameterPrefix class, or sub-class object. At this
+ # point we simple record both of these within the
+ # MarkActiveCallback object.
+ def __init__(self, cmd, delegate):
+ self.__cmd = cmd
+ self.__delegate = delegate
+
+ # Record the currently active _PrefixCommand object within
+ # the outer ParameterPrefix sub-class object.
+ def __enter__(self):
+ self.__delegate.active_prefix = self.__cmd
+
+ # Once the invoke method has completed, then clear the
+ # _PrefixCommand object that was stored into the outer
+ # ParameterPrefix sub-class object.
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.__delegate.active_prefix = None
+
+ # The self.__cb attribute is set when the _PrefixCommand object
+ # is created, and is either invoke_set or invoke_show within the
+ # ParameterPrefix sub-class object.
+ assert callable(self.__cb)
+
+ # Record the currently active _PrefixCommand object within the
+ # ParameterPrefix sub-class object, then call the relevant
+ # invoke method within the ParameterPrefix sub-class object.
+ with MarkActiveCallback(self, self.__delegate):
+ self.__cb(args, from_tty)
+
+ @staticmethod
+ def __find_callback(delegate, mode):
+ """The MODE is either 'set' or 'show'. Look for an invoke_MODE method
+ on DELEGATE, if a suitable method is found, then return it, otherwise,
+ return None.
+ """
+ cb = getattr(delegate, "invoke_" + mode, None)
+ if callable(cb):
+ return cb
+ return None
+
+ def __init__(self, mode, name, cmd_class, delegate, doc=None):
+ """Setup this gdb.Command. Mode is a string, either 'set' or 'show'.
+ NAME is the name for this prefix command, that is, the
+ words that appear after both 'set' and 'show' in the
+ command name. CMD_CLASS is the usual enum. And DELEGATE
+ is the gdb.ParameterPrefix object this prefix is part of.
+ """
+ assert mode == "set" or mode == "show"
+ if doc is None:
+ self.__doc__ = delegate.__doc__
+ else:
+ self.__doc__ = doc
+ self.__cb = self.__find_callback(delegate, mode)
+ self.__delegate = delegate
+ if self.__cb is not None:
+ self.invoke = self.__invoke
+ super().__init__(mode + " " + name, cmd_class, prefix=True)
+
+ def __init__(self, name, cmd_class, doc=None):
+ """Create a _PrefixCommand for both the set and show prefix commands.
+ NAME is the command name without either the leading 'set ' or
+ 'show ' strings, and CMD_CLASS is the usual enum value.
+ """
+ self.active_prefix = None
+ self._set_prefix_cmd = self._PrefixCommand("set", name, cmd_class, self, doc)
+ self._show_prefix_cmd = self._PrefixCommand("show", name, cmd_class, self, doc)
+
+ # When called from within an invoke method the self.active_prefix
+ # attribute should be set to a gdb.Command sub-class (a _PrefixCommand
+ # object, see above). Forward the dont_repeat call to this object to
+ # register the actual command as none repeating.
+ def dont_repeat(self):
+ if self.active_prefix is not None:
+ self.active_prefix.dont_repeat()
diff --git a/gdb/python/lib/gdb/command/__init__.py b/gdb/python/lib/gdb/command/__init__.py
index f1b13bd..3688152 100644
--- a/gdb/python/lib/gdb/command/__init__.py
+++ b/gdb/python/lib/gdb/command/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/explore.py b/gdb/python/lib/gdb/command/explore.py
index e359fa5..6107338 100644
--- a/gdb/python/lib/gdb/command/explore.py
+++ b/gdb/python/lib/gdb/command/explore.py
@@ -1,5 +1,5 @@
# GDB 'explore' command.
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/frame_filters.py b/gdb/python/lib/gdb/command/frame_filters.py
index 4e1b320..be7be9a 100644
--- a/gdb/python/lib/gdb/command/frame_filters.py
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -1,5 +1,5 @@
# Frame-filter commands.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/missing_files.py b/gdb/python/lib/gdb/command/missing_files.py
index 463853b..09d9684 100644
--- a/gdb/python/lib/gdb/command/missing_files.py
+++ b/gdb/python/lib/gdb/command/missing_files.py
@@ -1,6 +1,6 @@
# Missing debug and objfile related commands.
#
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/pretty_printers.py b/gdb/python/lib/gdb/command/pretty_printers.py
index cb9b9f3..f62d329 100644
--- a/gdb/python/lib/gdb/command/pretty_printers.py
+++ b/gdb/python/lib/gdb/command/pretty_printers.py
@@ -1,5 +1,5 @@
# Pretty-printer commands.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/prompt.py b/gdb/python/lib/gdb/command/prompt.py
index 2cfb25d..6574c83 100644
--- a/gdb/python/lib/gdb/command/prompt.py
+++ b/gdb/python/lib/gdb/command/prompt.py
@@ -1,5 +1,5 @@
# Extended prompt.
-# Copyright (C) 2011-2024 Free Software Foundation, Inc.
+# Copyright (C) 2011-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/type_printers.py b/gdb/python/lib/gdb/command/type_printers.py
index a2be226..9fc654c 100644
--- a/gdb/python/lib/gdb/command/type_printers.py
+++ b/gdb/python/lib/gdb/command/type_printers.py
@@ -1,5 +1,5 @@
# Type printer commands.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/unwinders.py b/gdb/python/lib/gdb/command/unwinders.py
index b863b33..ffedab8 100644
--- a/gdb/python/lib/gdb/command/unwinders.py
+++ b/gdb/python/lib/gdb/command/unwinders.py
@@ -1,5 +1,5 @@
# Unwinder commands.
-# Copyright 2015-2024 Free Software Foundation, Inc.
+# Copyright 2015-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/xmethods.py b/gdb/python/lib/gdb/command/xmethods.py
index b33e570..719c146 100644
--- a/gdb/python/lib/gdb/command/xmethods.py
+++ b/gdb/python/lib/gdb/command/xmethods.py
@@ -1,5 +1,5 @@
# Xmethod commands.
-# Copyright 2013-2024 Free Software Foundation, Inc.
+# Copyright 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/__init__.py b/gdb/python/lib/gdb/dap/__init__.py
index 145aeb6..1c3cf8e 100644
--- a/gdb/python/lib/gdb/dap/__init__.py
+++ b/gdb/python/lib/gdb/dap/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@ from . import startup
# server object. "F401" is the flake8 "imported but unused" code.
from . import breakpoint # noqa: F401
from . import bt # noqa: F401
+from . import completions # noqa: F401
from . import disassemble # noqa: F401
from . import evaluate # noqa: F401
from . import launch # noqa: F401
@@ -95,5 +96,4 @@ def pre_command_loop():
# These are handy for bug reports.
startup.exec_and_log("show version")
startup.exec_and_log("show configuration")
- global server
startup.start_dap(server.main_loop)
diff --git a/gdb/python/lib/gdb/dap/breakpoint.py b/gdb/python/lib/gdb/dap/breakpoint.py
index 5fd0c1f..4d4ca18 100644
--- a/gdb/python/lib/gdb/dap/breakpoint.py
+++ b/gdb/python/lib/gdb/dap/breakpoint.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -51,7 +51,6 @@ def suppress_new_breakpoint_event():
@in_gdb_thread
def _bp_modified(event):
- global _suppress_bp
if not _suppress_bp:
send_event(
"breakpoint",
@@ -64,7 +63,6 @@ def _bp_modified(event):
@in_gdb_thread
def _bp_created(event):
- global _suppress_bp
if not _suppress_bp:
send_event(
"breakpoint",
@@ -77,7 +75,6 @@ def _bp_created(event):
@in_gdb_thread
def _bp_deleted(event):
- global _suppress_bp
if not _suppress_bp:
send_event(
"breakpoint",
@@ -151,7 +148,6 @@ def _remove_entries(table, *names):
# the breakpoint.
@in_gdb_thread
def _set_breakpoints_callback(kind, specs, creator):
- global breakpoint_map
# Try to reuse existing breakpoints if possible.
if kind in breakpoint_map:
saved_map = breakpoint_map[kind]
@@ -330,7 +326,7 @@ def _rewrite_fn_breakpoint(
}
-@request("setFunctionBreakpoints")
+@request("setFunctionBreakpoints", expect_stopped=False)
@capability("supportsFunctionBreakpoints")
def set_fn_breakpoint(*, breakpoints: Sequence, **args):
specs = [_rewrite_fn_breakpoint(**bp) for bp in breakpoints]
@@ -363,7 +359,7 @@ def _rewrite_insn_breakpoint(
}
-@request("setInstructionBreakpoints")
+@request("setInstructionBreakpoints", expect_stopped=False)
@capability("supportsInstructionBreakpoints")
def set_insn_breakpoints(
*, breakpoints: Sequence, offset: Optional[int] = None, **args
@@ -414,7 +410,7 @@ def _rewrite_exception_breakpoint(
}
-@request("setExceptionBreakpoints")
+@request("setExceptionBreakpoints", expect_stopped=False)
@capability("supportsExceptionFilterOptions")
@capability(
"exceptionBreakpointFilters",
diff --git a/gdb/python/lib/gdb/dap/bt.py b/gdb/python/lib/gdb/dap/bt.py
index 0fefa69..41c7d00 100644
--- a/gdb/python/lib/gdb/dap/bt.py
+++ b/gdb/python/lib/gdb/dap/bt.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/completions.py b/gdb/python/lib/gdb/dap/completions.py
new file mode 100644
index 0000000..e5003ff
--- /dev/null
+++ b/gdb/python/lib/gdb/dap/completions.py
@@ -0,0 +1,63 @@
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from typing import Optional
+
+from .frames import select_frame
+from .server import capability, import_column, import_line, request
+from .startup import exec_mi_and_log
+
+
+@request("completions")
+@capability("supportsCompletionsRequest")
+@capability("completionTriggerCharacters", [" ", "."])
+def completions(
+ *,
+ frameId: Optional[int] = None,
+ text: str,
+ column: int,
+ line: Optional[int] = None,
+ **extra,
+):
+ if frameId is not None:
+ select_frame(frameId)
+
+ column = import_column(column)
+ if line is None:
+ line = 1
+ else:
+ line = import_line(line)
+ if text:
+ text = text.splitlines()[line - 1]
+ text = text[: column - 1]
+ else:
+ text = ""
+ mi_result = exec_mi_and_log("-complete", text)
+ result = []
+ completion = None
+ if "completion" in mi_result:
+ completion = mi_result["completion"]
+ result.append({"label": completion, "length": len(completion)})
+ # If `-complete' finds one match then `completion' and `matches'
+ # will contain the same one match.
+ if (
+ completion is not None
+ and len(mi_result["matches"]) == 1
+ and completion == mi_result["matches"][0]
+ ):
+ return {"targets": result}
+ for match in mi_result["matches"]:
+ result.append({"label": match, "length": len(match)})
+ return {"targets": result}
diff --git a/gdb/python/lib/gdb/dap/disassemble.py b/gdb/python/lib/gdb/dap/disassemble.py
index 3c0d517..42cad3e 100644
--- a/gdb/python/lib/gdb/dap/disassemble.py
+++ b/gdb/python/lib/gdb/dap/disassemble.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/evaluate.py b/gdb/python/lib/gdb/dap/evaluate.py
index 34843f4..fcbcc99 100644
--- a/gdb/python/lib/gdb/dap/evaluate.py
+++ b/gdb/python/lib/gdb/dap/evaluate.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -69,7 +69,7 @@ def _repl(command, frame_id):
}
-@request("evaluate")
+@request("evaluate", defer_events=False)
@capability("supportsEvaluateForHovers")
@capability("supportsValueFormattingOptions")
def eval_request(
@@ -110,7 +110,7 @@ def variables(
@capability("supportsSetExpression")
-@request("setExpression")
+@request("setExpression", defer_events=False)
def set_expression(
*, expression: str, value: str, frameId: Optional[int] = None, format=None, **args
):
@@ -126,7 +126,7 @@ def set_expression(
@capability("supportsSetVariable")
-@request("setVariable")
+@request("setVariable", defer_events=False)
def set_variable(
*, variablesReference: int, name: str, value: str, format=None, **args
):
diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py
index 2e6fe98..e8f2655 100644
--- a/gdb/python/lib/gdb/dap/events.py
+++ b/gdb/python/lib/gdb/dap/events.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,12 +17,12 @@ import gdb
from .modules import is_module, make_module
from .scopes import set_finish_value
-from .server import send_event, send_event_maybe_later
+from .server import send_event
from .startup import exec_and_log, in_gdb_thread, log
# True when the inferior is thought to be running, False otherwise.
# This may be accessed from any thread, which can be racy. However,
-# this unimportant because this global is only used for the
+# this is unimportant because this global is only used for the
# 'notStopped' response, which itself is inherently racy.
inferior_running = False
@@ -238,10 +238,9 @@ def _on_stop(event):
):
obj["reason"] = "pause"
else:
- global stop_reason_map
obj["reason"] = stop_reason_map[event.details["reason"]]
_expected_pause = False
- send_event_maybe_later("stopped", obj)
+ send_event("stopped", obj)
# This keeps a bit of state between the start of an inferior call and
diff --git a/gdb/python/lib/gdb/dap/frames.py b/gdb/python/lib/gdb/dap/frames.py
index 770a177..4dacb87 100644
--- a/gdb/python/lib/gdb/dap/frames.py
+++ b/gdb/python/lib/gdb/dap/frames.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -53,12 +53,11 @@ gdb.events.cont.connect(_clear_frame_ids)
@in_gdb_thread
def frame_for_id(id):
"""Given a frame identifier ID, return the corresponding frame."""
- global thread_ids
if id in thread_ids:
thread_id = thread_ids[id]
if thread_id != gdb.selected_thread().global_num:
set_thread(thread_id)
- global _all_frames
+
return _all_frames[id]
@@ -103,10 +102,8 @@ def _frame_id_generator():
# Helper function to assign an ID to a frame.
def get_id(frame):
- global _all_frames
num = len(_all_frames)
_all_frames.append(frame)
- global thread_ids
thread_ids[num] = gdb.selected_thread().global_num
return num
@@ -128,7 +125,6 @@ def _frame_id_generator():
@in_gdb_thread
def _get_frame_iterator():
thread_id = gdb.selected_thread().global_num
- global _iter_map
if thread_id not in _iter_map:
_iter_map[thread_id] = _MemoizingIterator(_frame_id_generator())
return _iter_map[thread_id]
diff --git a/gdb/python/lib/gdb/dap/globalvars.py b/gdb/python/lib/gdb/dap/globalvars.py
index bd9c53a..9d64d28 100644
--- a/gdb/python/lib/gdb/dap/globalvars.py
+++ b/gdb/python/lib/gdb/dap/globalvars.py
@@ -1,4 +1,4 @@
-# Copyright 2024 Free Software Foundation, Inc.
+# Copyright 2024-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -78,7 +78,6 @@ def get_global_scope(frame):
except RuntimeError:
return None
- global _id_to_scope
block = block.static_block
if block in _id_to_scope:
return _id_to_scope[block]
diff --git a/gdb/python/lib/gdb/dap/io.py b/gdb/python/lib/gdb/dap/io.py
index 03031a7..45890da 100644
--- a/gdb/python/lib/gdb/dap/io.py
+++ b/gdb/python/lib/gdb/dap/io.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py
index fc1890c..8ac4c77 100644
--- a/gdb/python/lib/gdb/dap/launch.py
+++ b/gdb/python/lib/gdb/dap/launch.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -164,7 +164,6 @@ def attach(
@request("configurationDone", on_dap_thread=True)
def config_done(**args):
# Handle the launch or attach.
- global _launch_or_attach_promise
if _launch_or_attach_promise is None:
raise DAPException("launch or attach not specified")
# Resolve the launch or attach, but only after the
diff --git a/gdb/python/lib/gdb/dap/locations.py b/gdb/python/lib/gdb/dap/locations.py
index 1ef5a34..fffc038 100644
--- a/gdb/python/lib/gdb/dap/locations.py
+++ b/gdb/python/lib/gdb/dap/locations.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/memory.py b/gdb/python/lib/gdb/dap/memory.py
index 4aa4996..d0f8825 100644
--- a/gdb/python/lib/gdb/dap/memory.py
+++ b/gdb/python/lib/gdb/dap/memory.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/modules.py b/gdb/python/lib/gdb/dap/modules.py
index 69e5a40..b06f771 100644
--- a/gdb/python/lib/gdb/dap/modules.py
+++ b/gdb/python/lib/gdb/dap/modules.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/next.py b/gdb/python/lib/gdb/dap/next.py
index 7e06b1b..898fff1 100644
--- a/gdb/python/lib/gdb/dap/next.py
+++ b/gdb/python/lib/gdb/dap/next.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
import gdb
from .events import exec_and_expect_stop
-from .server import capability, request, send_gdb, send_gdb_with_response
+from .server import capability, request
from .startup import in_gdb_thread
from .state import set_thread
@@ -73,19 +73,14 @@ def step_in(
exec_and_expect_stop(cmd)
-@request("stepOut", defer_stop_events=True)
+@request("stepOut")
def step_out(*, threadId: int, singleThread: bool = False, **args):
_handle_thread_step(threadId, singleThread, True)
exec_and_expect_stop("finish &", propagate_exception=True)
-# This is a server-side request because it is funny: it wants to
-# 'continue' but also return a result, which precludes using
-# response=False. Using 'continue &' would mostly work ok, but this
-# yields races when a stop occurs before the response is sent back to
-# the client.
-@request("continue", on_dap_thread=True)
+@request("continue")
def continue_request(*, threadId: int, singleThread: bool = False, **args):
- locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread))
- send_gdb(lambda: exec_and_expect_stop("continue"))
+ locked = _handle_thread_step(threadId, singleThread)
+ exec_and_expect_stop("continue &")
return {"allThreadsContinued": not locked}
diff --git a/gdb/python/lib/gdb/dap/pause.py b/gdb/python/lib/gdb/dap/pause.py
index d874a60..c254e45 100644
--- a/gdb/python/lib/gdb/dap/pause.py
+++ b/gdb/python/lib/gdb/dap/pause.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/scopes.py b/gdb/python/lib/gdb/dap/scopes.py
index 1b98c4f..7ce3a7f 100644
--- a/gdb/python/lib/gdb/dap/scopes.py
+++ b/gdb/python/lib/gdb/dap/scopes.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ from .sources import make_source
from .startup import in_gdb_thread
from .varref import BaseReference
-# Map DAP frame IDs to scopes. This ensures that scopes are re-used.
+# Map DAP frame IDs to scopes. This ensures that scopes are reused.
frame_to_scope = {}
@@ -120,7 +120,6 @@ class _FinishScopeReference(_ScopeReference):
def fetch_one_child(self, idx):
assert idx == 0
- global _last_return_value
return ("(return)", _last_return_value)
@@ -145,8 +144,6 @@ class _RegisterReference(_ScopeReference):
@request("scopes")
def scopes(*, frameId: int, **extra):
- global _last_return_value
- global frame_to_scope
if frameId in frame_to_scope:
scopes = frame_to_scope[frameId]
else:
diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
index 8fdf029..7dab582 100644
--- a/gdb/python/lib/gdb/dap/server.py
+++ b/gdb/python/lib/gdb/dap/server.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -49,6 +49,7 @@ _server = None
# This is set by the initialize request and is used when rewriting
# line numbers.
_lines_start_at_1 = False
+_columns_start_at_1 = False
class DeferredRequest:
@@ -73,6 +74,13 @@ class DeferredRequest:
self._result = result
@in_dap_thread
+ def defer_events(self):
+ """Return True if events should be deferred during execution.
+
+ This may be overridden by subclasses."""
+ return True
+
+ @in_dap_thread
def invoke(self):
"""Implement the deferred request.
@@ -94,7 +102,10 @@ class DeferredRequest:
"""
with _server.canceller.current_request(self._req):
+ if self.defer_events():
+ _server.set_defer_events()
_server.invoke_request(self._req, self._result, self.invoke)
+ _server.emit_pending_events()
# A subclass of Exception that is used solely for reporting that a
@@ -229,7 +240,7 @@ class Server:
self._out_stream = out_stream
self._child_stream = child_stream
self._delayed_fns_lock = threading.Lock()
- self.defer_stop_events = False
+ self._defer_events = False
self._delayed_fns = []
# This queue accepts JSON objects that are then sent to the
# DAP client. Writing is done in a separate thread to avoid
@@ -306,7 +317,6 @@ class Server:
args = {}
def fn():
- global _commands
return _commands[params["command"]](**args)
self.invoke_request(req, result, fn)
@@ -316,7 +326,7 @@ class Server:
def _read_inferior_output(self):
while True:
line = self._child_stream.readline()
- self.send_event(
+ self.send_event_maybe_later(
"output",
{
"category": "stdout",
@@ -356,6 +366,17 @@ class Server:
self._read_queue.put(None)
@in_dap_thread
+ def emit_pending_events(self):
+ """Emit any pending events."""
+ fns = None
+ with self._delayed_fns_lock:
+ fns = self._delayed_fns
+ self._delayed_fns = []
+ self._defer_events = False
+ for fn in fns:
+ fn()
+
+ @in_dap_thread
def main_loop(self):
"""The main loop of the DAP server."""
# Before looping, start the thread that writes JSON to the
@@ -371,13 +392,7 @@ class Server:
req = cmd["seq"]
with self.canceller.current_request(req):
self._handle_command(cmd)
- fns = None
- with self._delayed_fns_lock:
- fns = self._delayed_fns
- self._delayed_fns = []
- self.defer_stop_events = False
- for fn in fns:
- fn()
+ self.emit_pending_events()
# Got the terminate request. This is handled by the
# JSON-writing thread, so that we can ensure that all
# responses are flushed to the client before exiting.
@@ -386,13 +401,13 @@ class Server:
send_gdb("quit")
@in_dap_thread
- def send_event_later(self, event, body=None):
- """Send a DAP event back to the client, but only after the
- current request has completed."""
+ def set_defer_events(self):
+ """Defer any events until the current request has completed."""
with self._delayed_fns_lock:
- self._delayed_fns.append(lambda: self.send_event(event, body))
+ self._defer_events = True
- @in_gdb_thread
+ # Note that this does not need to be run in any particular thread,
+ # because it uses locks for thread-safety.
def send_event_maybe_later(self, event, body=None):
"""Send a DAP event back to the client, but if a request is in-flight
within the dap thread and that request is configured to delay the event,
@@ -401,10 +416,10 @@ class Server:
with self.canceller.lock:
if self.canceller.in_flight_dap_thread:
with self._delayed_fns_lock:
- if self.defer_stop_events:
- self._delayed_fns.append(lambda: self.send_event(event, body))
+ if self._defer_events:
+ self._delayed_fns.append(lambda: self._send_event(event, body))
return
- self.send_event(event, body)
+ self._send_event(event, body)
@in_dap_thread
def call_function_later(self, fn):
@@ -415,7 +430,7 @@ class Server:
# Note that this does not need to be run in any particular thread,
# because it just creates an object and writes it to a thread-safe
# queue.
- def send_event(self, event, body=None):
+ def _send_event(self, event, body=None):
"""Send an event to the DAP client.
EVENT is the name of the event, a string.
BODY is the body of the event, an arbitrary object."""
@@ -439,22 +454,11 @@ def send_event(event, body=None):
"""Send an event to the DAP client.
EVENT is the name of the event, a string.
BODY is the body of the event, an arbitrary object."""
- global _server
- _server.send_event(event, body)
-
-
-def send_event_maybe_later(event, body=None):
- """Send a DAP event back to the client, but if a request is in-flight
- within the dap thread and that request is configured to delay the event,
- wait until the response has been sent until the event is sent back to
- the client."""
- global _server
_server.send_event_maybe_later(event, body)
def call_function_later(fn):
"""Call FN later -- after the current request's response has been sent."""
- global _server
_server.call_function_later(fn)
@@ -479,7 +483,7 @@ def request(
response: bool = True,
on_dap_thread: bool = False,
expect_stopped: bool = True,
- defer_stop_events: bool = False
+ defer_events: bool = True
):
"""A decorator for DAP requests.
@@ -501,9 +505,9 @@ def request(
inferior is running. When EXPECT_STOPPED is False, the request
will proceed regardless of the inferior's state.
- If DEFER_STOP_EVENTS is True, then make sure any stop events sent
- during the request processing are not sent to the client until the
- response has been sent.
+ If DEFER_EVENTS is True, then make sure any events sent during the
+ request processing are not sent to the client until the response
+ has been sent.
"""
# Validate the parameters.
@@ -526,27 +530,33 @@ def request(
# Verify that the function is run on the correct thread.
if on_dap_thread:
- cmd = in_dap_thread(func)
+ check_cmd = in_dap_thread(func)
else:
func = in_gdb_thread(func)
if response:
- if defer_stop_events:
- global _server
- if _server is not None:
- with _server.delayed_events_lock:
- _server.defer_stop_events = True
def sync_call(**args):
return send_gdb_with_response(lambda: func(**args))
- cmd = sync_call
+ check_cmd = sync_call
else:
def non_sync_call(**args):
return send_gdb(lambda: func(**args))
- cmd = non_sync_call
+ check_cmd = non_sync_call
+
+ if defer_events:
+
+ def deferring(**args):
+ _server.set_defer_events()
+ return check_cmd(**args)
+
+ cmd = deferring
+
+ else:
+ cmd = check_cmd
# If needed, check that the inferior is not running. This
# wrapping is done last, so the check is done first, before
@@ -554,7 +564,6 @@ def request(
if expect_stopped:
cmd = _check_not_running(cmd)
- global _commands
assert name not in _commands
_commands[name] = cmd
return cmd
@@ -567,7 +576,6 @@ def capability(name, value=True):
the DAP capability NAME."""
def wrap(func):
- global _capabilities
assert name not in _capabilities
_capabilities[name] = value
return func
@@ -580,7 +588,6 @@ def client_bool_capability(name, default=False):
If the capability was not specified, or did not have boolean type,
DEFAULT is returned. DEFAULT defaults to False."""
- global _server
if name in _server.config and isinstance(_server.config[name], bool):
return _server.config[name]
return default
@@ -588,11 +595,12 @@ def client_bool_capability(name, default=False):
@request("initialize", on_dap_thread=True)
def initialize(**args):
- global _server, _capabilities
_server.config = args
- _server.send_event_later("initialized")
+ _server.send_event_maybe_later("initialized")
global _lines_start_at_1
_lines_start_at_1 = client_bool_capability("linesStartAt1", True)
+ global _columns_start_at_1
+ _columns_start_at_1 = client_bool_capability("columnsStartAt1", True)
return _capabilities.copy()
@@ -698,11 +706,10 @@ def send_gdb_with_response(fn):
return val
-def export_line(line):
+def export_line(line: int) -> int:
"""Rewrite LINE according to client capability.
This applies the linesStartAt1 capability as needed,
when sending a line number from gdb to the client."""
- global _lines_start_at_1
if not _lines_start_at_1:
# In gdb, lines start at 1, so we only need to change this if
# the client starts at 0.
@@ -710,13 +717,26 @@ def export_line(line):
return line
-def import_line(line):
+def import_line(line: int) -> int:
"""Rewrite LINE according to client capability.
This applies the linesStartAt1 capability as needed,
when the client sends a line number to gdb."""
- global _lines_start_at_1
if not _lines_start_at_1:
# In gdb, lines start at 1, so we only need to change this if
# the client starts at 0.
line = line + 1
return line
+
+
+def export_column(column: int) -> int:
+ """Rewrite COLUMN according to client capability.
+ This applies the columnsStartAt1 capability as needed,
+ when sending a column number from gdb to the client."""
+ return column if _columns_start_at_1 else column - 1
+
+
+def import_column(column: int) -> int:
+ """Rewrite COLUMN according to client capability.
+ This applies the columnsStartAt1 capability as needed,
+ when the client sends a column number to gdb."""
+ return column if _columns_start_at_1 else column + 1
diff --git a/gdb/python/lib/gdb/dap/sources.py b/gdb/python/lib/gdb/dap/sources.py
index a9f4ea6..efcd799 100644
--- a/gdb/python/lib/gdb/dap/sources.py
+++ b/gdb/python/lib/gdb/dap/sources.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,7 +37,6 @@ def make_source(fullname, filename=None):
FILENAME is the base name; if None (the default), then it is
computed from FULLNAME.
"""
- global _source_map
if fullname in _source_map:
result = _source_map[fullname]
else:
@@ -53,7 +52,6 @@ def make_source(fullname, filename=None):
global _next_source
result["sourceReference"] = _next_source
- global _id_map
_id_map[_next_source] = result
_next_source += 1
@@ -66,12 +64,11 @@ def decode_source(source):
"""Decode a Source object.
Finds and returns the filename of a given Source object."""
- if "path" in source:
- return source["path"]
- if "sourceReference" not in source:
+ if "sourceReference" not in source or source["sourceReference"] <= 0:
+ if "path" in source:
+ return source["path"]
raise DAPException("either 'path' or 'sourceReference' must appear in Source")
ref = source["sourceReference"]
- global _id_map
if ref not in _id_map:
raise DAPException("no sourceReference " + str(ref))
return _id_map[ref]["path"]
diff --git a/gdb/python/lib/gdb/dap/startup.py b/gdb/python/lib/gdb/dap/startup.py
index a3f048b..ab3e8fd 100644
--- a/gdb/python/lib/gdb/dap/startup.py
+++ b/gdb/python/lib/gdb/dap/startup.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/state.py b/gdb/python/lib/gdb/dap/state.py
index 57ae355..5fdfbb2 100644
--- a/gdb/python/lib/gdb/dap/state.py
+++ b/gdb/python/lib/gdb/dap/state.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/threads.py b/gdb/python/lib/gdb/dap/threads.py
index e65495b..89046a8 100644
--- a/gdb/python/lib/gdb/dap/threads.py
+++ b/gdb/python/lib/gdb/dap/threads.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,27 +16,32 @@
import gdb
from .server import request
+from .startup import in_gdb_thread
+@in_gdb_thread
def _thread_name(thr):
if thr.name is not None:
return thr.name
if thr.details is not None:
return thr.details
- return None
+ # Always return a name, as the protocol doesn't allow for nameless
+ # threads. Use the local thread number here... it doesn't matter
+ # without multi-inferior but in that case it might make more
+ # sense.
+ return f"Thread #{thr.num}"
-@request("threads")
+@request("threads", expect_stopped=False)
def threads(**args):
result = []
for thr in gdb.selected_inferior().threads():
- one_result = {
- "id": thr.global_num,
- }
- name = _thread_name(thr)
- if name is not None:
- one_result["name"] = name
- result.append(one_result)
+ result.append(
+ {
+ "id": thr.global_num,
+ "name": _thread_name(thr),
+ }
+ )
return {
"threads": result,
}
diff --git a/gdb/python/lib/gdb/dap/typecheck.py b/gdb/python/lib/gdb/dap/typecheck.py
index 55896cc..1496b67 100644
--- a/gdb/python/lib/gdb/dap/typecheck.py
+++ b/gdb/python/lib/gdb/dap/typecheck.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/varref.py b/gdb/python/lib/gdb/dap/varref.py
index b1d8ef7..8a13c51 100644
--- a/gdb/python/lib/gdb/dap/varref.py
+++ b/gdb/python/lib/gdb/dap/varref.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -69,7 +69,6 @@ class BaseReference(ABC):
NAME is a string or None. None means this does not have a
name, e.g., the result of expression evaluation."""
- global all_variables
all_variables.append(self)
self._ref = len(all_variables)
self._name = name
@@ -267,7 +266,7 @@ class VariableReference(BaseReference):
@in_gdb_thread
def find_variable(ref):
"""Given a variable reference, return the corresponding variable object."""
- global all_variables
+
# Variable references are offset by 1.
ref = ref - 1
if ref < 0 or ref > len(all_variables):
diff --git a/gdb/python/lib/gdb/disassembler.py b/gdb/python/lib/gdb/disassembler.py
index 5df0485..8f8e768 100644
--- a/gdb/python/lib/gdb/disassembler.py
+++ b/gdb/python/lib/gdb/disassembler.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2021-2024 Free Software Foundation, Inc.
+# Copyright (C) 2021-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/frames.py b/gdb/python/lib/gdb/frames.py
index a3be80c7..96174e9 100644
--- a/gdb/python/lib/gdb/frames.py
+++ b/gdb/python/lib/gdb/frames.py
@@ -1,5 +1,5 @@
# Frame-filter commands.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/__init__.py b/gdb/python/lib/gdb/function/__init__.py
index 4b64bc3..62b6422 100644
--- a/gdb/python/lib/gdb/function/__init__.py
+++ b/gdb/python/lib/gdb/function/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/as_string.py b/gdb/python/lib/gdb/function/as_string.py
index a255fff..ff1304f 100644
--- a/gdb/python/lib/gdb/function/as_string.py
+++ b/gdb/python/lib/gdb/function/as_string.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2016-2024 Free Software Foundation, Inc.
+# Copyright (C) 2016-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/caller_is.py b/gdb/python/lib/gdb/function/caller_is.py
index bacd8c0..3687e4c 100644
--- a/gdb/python/lib/gdb/function/caller_is.py
+++ b/gdb/python/lib/gdb/function/caller_is.py
@@ -1,5 +1,5 @@
# Caller-is functions.
-# Copyright (C) 2008-2024 Free Software Foundation, Inc.
+# Copyright (C) 2008-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/strfns.py b/gdb/python/lib/gdb/function/strfns.py
index 90c9cea..a620f4f 100644
--- a/gdb/python/lib/gdb/function/strfns.py
+++ b/gdb/python/lib/gdb/function/strfns.py
@@ -1,5 +1,5 @@
# Useful gdb string convenience functions.
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/missing_debug.py b/gdb/python/lib/gdb/missing_debug.py
index 2c2ceba..b03aaad 100644
--- a/gdb/python/lib/gdb/missing_debug.py
+++ b/gdb/python/lib/gdb/missing_debug.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023-2024 Free Software Foundation, Inc.
+# Copyright (C) 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/missing_files.py b/gdb/python/lib/gdb/missing_files.py
index 5f2df88c..9f24db7 100644
--- a/gdb/python/lib/gdb/missing_files.py
+++ b/gdb/python/lib/gdb/missing_files.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023-2024 Free Software Foundation, Inc.
+# Copyright (C) 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/missing_objfile.py b/gdb/python/lib/gdb/missing_objfile.py
index ace0e13..3d06bdd 100644
--- a/gdb/python/lib/gdb/missing_objfile.py
+++ b/gdb/python/lib/gdb/missing_objfile.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2024 Free Software Foundation, Inc.
+# Copyright (C) 2024-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/printer/__init__.py b/gdb/python/lib/gdb/printer/__init__.py
index 6692044..854ff3a 100644
--- a/gdb/python/lib/gdb/printer/__init__.py
+++ b/gdb/python/lib/gdb/printer/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2014-2024 Free Software Foundation, Inc.
+# Copyright (C) 2014-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 0635993..cba27d2 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -1,5 +1,5 @@
# Pretty-printer utilities.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/prompt.py b/gdb/python/lib/gdb/prompt.py
index 497ab83..060474c 100644
--- a/gdb/python/lib/gdb/prompt.py
+++ b/gdb/python/lib/gdb/prompt.py
@@ -1,5 +1,5 @@
# Extended prompt utilities.
-# Copyright (C) 2011-2024 Free Software Foundation, Inc.
+# Copyright (C) 2011-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/ptwrite.py b/gdb/python/lib/gdb/ptwrite.py
index 3be65fe..fcc72de 100644
--- a/gdb/python/lib/gdb/ptwrite.py
+++ b/gdb/python/lib/gdb/ptwrite.py
@@ -1,5 +1,5 @@
# Ptwrite utilities.
-# Copyright (C) 2023 Free Software Foundation, Inc.
+# Copyright (C) 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/styling.py b/gdb/python/lib/gdb/styling.py
index 1c5394e..60c470f 100644
--- a/gdb/python/lib/gdb/styling.py
+++ b/gdb/python/lib/gdb/styling.py
@@ -1,5 +1,5 @@
# Styling related hooks.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@ try:
from pygments import formatters, highlight, lexers
from pygments.filters import TokenMergeFilter
from pygments.token import Comment, Error, Text
+ from pygments.util import ClassNotFound
_formatter = None
@@ -31,10 +32,13 @@ try:
_formatter = formatters.TerminalFormatter()
return _formatter
- def colorize(filename, contents):
+ def colorize(filename, contents, lang):
# Don't want any errors.
try:
- lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
+ try:
+ lexer = lexers.get_lexer_by_name(lang, stripnl=False)
+ except ClassNotFound:
+ lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
formatter = get_formatter()
return highlight(contents, lexer, formatter).encode(
gdb.host_charset(), "backslashreplace"
@@ -76,7 +80,6 @@ try:
# ignore.
pass
- global _asm_lexers
if lexer_type not in _asm_lexers:
_asm_lexers[lexer_type] = lexers.get_lexer_by_name(lexer_type)
_asm_lexers[lexer_type].add_filter(HandleNasmComments())
@@ -94,7 +97,7 @@ try:
except ImportError:
- def colorize(filename, contents):
+ def colorize(filename, contents, lang):
return None
def colorize_disasm(content, gdbarch):
diff --git a/gdb/python/lib/gdb/types.py b/gdb/python/lib/gdb/types.py
index b4af59c..bac1fb9 100644
--- a/gdb/python/lib/gdb/types.py
+++ b/gdb/python/lib/gdb/types.py
@@ -1,5 +1,5 @@
# Type utilities.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/unwinder.py b/gdb/python/lib/gdb/unwinder.py
index bb0db79..3e1f756 100644
--- a/gdb/python/lib/gdb/unwinder.py
+++ b/gdb/python/lib/gdb/unwinder.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2024 Free Software Foundation, Inc.
+# Copyright (C) 2015-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/xmethod.py b/gdb/python/lib/gdb/xmethod.py
index e12d51c..310585a 100644
--- a/gdb/python/lib/gdb/xmethod.py
+++ b/gdb/python/lib/gdb/xmethod.py
@@ -1,5 +1,5 @@
# Python side of the support for xmethods.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/py-all-events.def b/gdb/python/py-all-events.def
index f315e75..c20bb36 100644
--- a/gdb/python/py-all-events.def
+++ b/gdb/python/py-all-events.def
@@ -1,6 +1,6 @@
/* Python event definitions -*- c++ -*-
- Copyright (C) 2017-2024 Free Software Foundation, Inc.
+ Copyright (C) 2017-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-auto-load.c b/gdb/python/py-auto-load.c
index 7dc3a7b..f65b979 100644
--- a/gdb/python/py-auto-load.c
+++ b/gdb/python/py-auto-load.c
@@ -1,6 +1,6 @@
/* GDB routines for supporting auto-loaded scripts.
- Copyright (C) 2010-2024 Free Software Foundation, Inc.
+ Copyright (C) 2010-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-bpevent.c b/gdb/python/py-bpevent.c
index 5982a26..ddf5066 100644
--- a/gdb/python/py-bpevent.c
+++ b/gdb/python/py-bpevent.c
@@ -1,6 +1,6 @@
/* Python interface to inferior breakpoint stop events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 882b825..9ce8671 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -1,6 +1,6 @@
/* Python interface to breakpoints
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -948,7 +948,7 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
else
{
PyErr_SetString (PyExc_RuntimeError,
- _("Line keyword should be an integer or a string. "));
+ _("Line keyword should be an integer or a string."));
return -1;
}
}
@@ -1537,9 +1537,7 @@ PyTypeObject breakpoint_object_type =
0, /* tp_alloc */
};
-void _initialize_py_breakpoint ();
-void
-_initialize_py_breakpoint ()
+INIT_GDB_FILE (py_breakpoint)
{
add_setshow_boolean_cmd
("py-breakpoint", class_maintenance, &pybp_debug,
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 2bb9b82..dc5e270 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -1,6 +1,6 @@
/* gdb commands implemented in Python
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -105,19 +105,17 @@ cmdpy_function (const char *args, int from_tty, cmd_list_element *command)
gdbpy_enter enter_py;
- if (! obj)
+ if (obj == nullptr)
error (_("Invalid invocation of Python command object."));
- if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
- {
- if (obj->command->is_prefix ())
- {
- /* A prefix command does not need an invoke method. */
- return;
- }
- error (_("Python command object missing 'invoke' method."));
- }
- if (! args)
+ /* If we get here for a prefix command then the prefix command had an
+ 'invoke' method when it was created. If the 'invoke' method is now
+ missing, then the user has done something weird (like deleting the
+ invoke method, yuck!). */
+ if (!PyObject_HasAttr ((PyObject *) obj, invoke_cst))
+ error (_("Python command object missing 'invoke' method."));
+
+ if (args == nullptr)
args = "";
gdbpy_ref<> argobj (PyUnicode_Decode (args, strlen (args), host_charset (),
NULL));
@@ -336,24 +334,30 @@ cmdpy_completer (struct cmd_list_element *command,
name of the new command. All earlier words must be existing prefix
commands.
- *BASE_LIST is set to the final prefix command's list of
- *sub-commands.
+ *BASE_LIST is set to the final prefix command's list of sub-commands.
START_LIST is the list in which the search starts.
+ When PREFIX_CMD is not NULL then *PREFIX_CMD is set to the prefix
+ command itself, or NULL, if there is no prefix command.
+
This function returns the name of the new command. On error sets the Python
error and returns NULL. */
gdb::unique_xmalloc_ptr<char>
gdbpy_parse_command_name (const char *name,
struct cmd_list_element ***base_list,
- struct cmd_list_element **start_list)
+ struct cmd_list_element **start_list,
+ struct cmd_list_element **prefix_cmd)
{
struct cmd_list_element *elt;
int len = strlen (name);
int i, lastchar;
const char *prefix_text2;
+ if (prefix_cmd != nullptr)
+ *prefix_cmd = nullptr;
+
/* Skip trailing whitespace. */
for (i = len - 1; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i)
;
@@ -368,9 +372,8 @@ gdbpy_parse_command_name (const char *name,
for (; i > 0 && valid_cmd_char_p (name[i - 1]); --i)
;
- gdb::unique_xmalloc_ptr<char> result ((char *) xmalloc (lastchar - i + 2));
- memcpy (result.get (), &name[i], lastchar - i + 1);
- result.get ()[lastchar - i + 1] = '\0';
+ gdb::unique_xmalloc_ptr<char> result
+ = make_unique_xstrndup (&name[i], lastchar - i + 1);
/* Skip whitespace again. */
for (--i; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i)
@@ -385,7 +388,7 @@ gdbpy_parse_command_name (const char *name,
prefix_text2 = prefix_text.c_str ();
elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1);
- if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
+ if (elt == nullptr || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0')
{
PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
prefix_text.c_str ());
@@ -395,6 +398,8 @@ gdbpy_parse_command_name (const char *name,
if (elt->is_prefix ())
{
*base_list = elt->subcommands;
+ if (prefix_cmd != nullptr)
+ *prefix_cmd = elt;
return result;
}
@@ -469,8 +474,9 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
return -1;
}
+ cmd_list_element *prefix_cmd = nullptr;
gdb::unique_xmalloc_ptr<char> cmd_name
- = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
+ = gdbpy_parse_command_name (name, &cmd_list, &cmdlist, &prefix_cmd);
if (cmd_name == nullptr)
return -1;
@@ -507,26 +513,64 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
if (is_prefix)
{
- int allow_unknown;
-
- /* If we have our own "invoke" method, then allow unknown
- sub-commands. */
- allow_unknown = PyObject_HasAttr (self, invoke_cst);
- cmd = add_prefix_cmd (cmd_name.get (),
- (enum command_class) cmdtype,
- NULL, docstring.release (), &obj->sub_list,
- allow_unknown, cmd_list);
+ bool has_invoke = PyObject_HasAttr (self, invoke_cst) == 1;
+ if (has_invoke)
+ {
+ /* If there's an 'invoke' method, then create the prefix
+ command, but call cmdpy_function to dispatch to the invoke
+ method when the user runs the prefix with no sub-command. */
+ cmd = add_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ nullptr,
+ docstring.release (), &obj->sub_list,
+ 1 /* allow_unknown */, cmd_list);
+ cmd->func = cmdpy_function;
+ }
+ else
+ {
+ /* If there is no 'invoke' method, then create the prefix
+ using the standard prefix callbacks. This means that for
+ 'set prefix' the user will get the help text listing all
+ of the sub-commands, and for 'show prefix', the user will
+ see all of the sub-command values. */
+ if (prefix_cmd != nullptr)
+ {
+ while (prefix_cmd->prefix != nullptr)
+ prefix_cmd = prefix_cmd->prefix;
+ }
+
+ bool is_show = (prefix_cmd != nullptr
+ && prefix_cmd->subcommands == &showlist);
+
+ if (is_show)
+ cmd = add_show_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ docstring.release (),
+ &obj->sub_list,
+ 0 /* allow_unknown */, cmd_list);
+ else
+ cmd = add_basic_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ docstring.release (),
+ &obj->sub_list,
+ 0 /* allow_unknown */, cmd_list);
+ }
}
else
- cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
- docstring.release (), cmd_list);
+ {
+ /* For non-prefix commands, arrange to call cmdpy_function, which
+ invokes the Python 'invoke' method, or raises an exception if
+ the 'invoke' method is missing. */
+ cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
+ docstring.release (), cmd_list);
+ cmd->func = cmdpy_function;
+ }
/* If successful, the above takes ownership of the name, since we set
name_allocated, so release it. */
cmd_name.release ();
- /* There appears to be no API to set this. */
- cmd->func = cmdpy_function;
+ /* There appears to be no API to set these member variables. */
cmd->destroyer = cmdpy_destroyer;
cmd->doc_allocated = 1;
cmd->name_allocated = 1;
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
index 3a90b62..3bbd22d 100644
--- a/gdb/python/py-color.c
+++ b/gdb/python/py-color.c
@@ -1,6 +1,6 @@
/* Python interface to ui_file_style::color objects.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -21,6 +21,7 @@
#include "python-internal.h"
#include "py-color.h"
#include "cli/cli-decode.h"
+#include "cli/cli-style.h"
/* Colorspace constants and their values. */
static struct {
@@ -64,7 +65,8 @@ create_color_object (const ui_file_style::color &color)
bool
gdbpy_is_color (PyObject *obj)
{
- return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type);
+ gdb_assert (obj != nullptr);
+ return PyObject_TypeCheck (obj, &colorpy_object_type) != 0;
}
/* See py-color.h. */
@@ -80,33 +82,33 @@ gdbpy_get_color (PyObject *obj)
static PyObject *
get_attr (PyObject *obj, PyObject *attr_name)
{
- if (! PyUnicode_Check (attr_name))
+ if (!PyUnicode_Check (attr_name))
return PyObject_GenericGetAttr (obj, attr_name);
colorpy_object *self = (colorpy_object *) obj;
const ui_file_style::color &color = self->color;
- if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
{
int value = static_cast<int> (color.colorspace ());
return gdb_py_object_from_longest (value).release ();
}
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
return PyBool_FromLong (color.is_none ());
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
return PyBool_FromLong (color.is_indexed ());
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
return PyBool_FromLong (color.is_direct ());
if (color.is_indexed ()
- && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+ && !PyUnicode_CompareWithASCIIString (attr_name, "index"))
return gdb_py_object_from_longest (color.get_value ()).release ();
if (color.is_direct ()
- && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+ && !PyUnicode_CompareWithASCIIString (attr_name, "components"))
{
uint8_t rgb[3];
color.get_rgb (rgb);
@@ -135,25 +137,29 @@ get_attr (PyObject *obj, PyObject *attr_name)
/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
static PyObject *
-colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+colorpy_escape_sequence (PyObject *self, PyObject *args, PyObject *kwargs)
{
- if (!gdbpy_is_color (self))
- {
- PyErr_SetString (PyExc_RuntimeError,
- _("Object is not gdb.Color."));
- return nullptr;
- }
+ static const char *keywords[] = { "is_foreground", nullptr };
+ PyObject *is_fg_obj;
+
+ /* Parse method arguments. */
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "O!", keywords,
+ &PyBool_Type, &is_fg_obj))
+ return nullptr;
- if (! PyBool_Check (is_fg_obj))
+ /* Python ensures the type of SELF. */
+ gdb_assert (gdbpy_is_color (self));
+
+ /* The argument parsing ensures we have a bool. */
+ gdb_assert (PyBool_Check (is_fg_obj));
+
+ std::string s;
+ if (term_cli_styling ())
{
- PyErr_SetString (PyExc_RuntimeError,
- _("A boolean argument is required."));
- return nullptr;
+ bool is_fg = is_fg_obj == Py_True;
+ s = gdbpy_get_color (self).to_ansi (is_fg);
}
- bool is_fg = is_fg_obj == Py_True;
- std::string s = gdbpy_get_color (self).to_ansi (is_fg);
-
return host_string_to_python_string (s.c_str ()).release ();
}
@@ -175,17 +181,20 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
PyObject *colorspace_obj = nullptr;
color_space colorspace = color_space::MONOCHROME;
- if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+ static const char *keywords[] = { "value", "color_space", nullptr };
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwds, "|OO", keywords,
+ &value_obj, &colorspace_obj))
return -1;
try
{
- if (colorspace_obj)
+ if (colorspace_obj != nullptr)
{
if (PyLong_Check (colorspace_obj))
{
long colorspace_id = -1;
- if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+ if (!gdb_py_int_as_long (colorspace_obj, &colorspace_id))
return -1;
if (!color_space_safe_cast (&colorspace, colorspace_id))
error (_("colorspace %ld is out of range."), colorspace_id);
@@ -201,11 +210,11 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
else if (PyLong_Check (value_obj))
{
long value = -1;
- if (! gdb_py_int_as_long (value_obj, &value))
+ if (!gdb_py_int_as_long (value_obj, &value))
return -1;
if (value < 0 || value > INT_MAX)
error (_("value %ld is out of range."), value);
- if (colorspace_obj)
+ if (colorspace_obj != nullptr)
obj->color = ui_file_style::color (colorspace, value);
else
obj->color = ui_file_style::color (value);
@@ -256,7 +265,6 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
return gdbpy_handle_gdb_exception (-1, except);
}
- Py_INCREF (self);
return 0;
}
@@ -272,10 +280,10 @@ colorpy_str (PyObject *self)
static int
gdbpy_initialize_color (void)
{
- for (auto & pair : colorspace_constants)
- if (PyModule_AddIntConstant (gdb_module, pair.name,
- static_cast<long> (pair.value)) < 0)
- return -1;
+ for (auto &pair : colorspace_constants)
+ if (PyModule_AddIntConstant (gdb_module, pair.name,
+ static_cast<long> (pair.value)) < 0)
+ return -1;
colorpy_object_type.tp_new = PyType_GenericNew;
return gdbpy_type_ready (&colorpy_object_type, gdb_module);
@@ -285,7 +293,8 @@ gdbpy_initialize_color (void)
static PyMethodDef color_methods[] =
{
- { "escape_sequence", colorpy_escape_sequence, METH_O,
+ { "escape_sequence", (PyCFunction) colorpy_escape_sequence,
+ METH_VARARGS | METH_KEYWORDS,
"escape_sequence (is_foreground) -> str.\n\
Return the ANSI escape sequence for this color.\n\
IS_FOREGROUND indicates whether this is a foreground or background color."},
@@ -313,7 +322,7 @@ PyTypeObject colorpy_object_type =
get_attr, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB color object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
diff --git a/gdb/python/py-color.h b/gdb/python/py-color.h
index a3e5e41..d94baa2 100644
--- a/gdb/python/py-color.h
+++ b/gdb/python/py-color.h
@@ -1,6 +1,6 @@
/* Python interface to ui_file_style::color objects.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c
index d2cd10d..a6d9ad0 100644
--- a/gdb/python/py-connection.c
+++ b/gdb/python/py-connection.c
@@ -1,6 +1,6 @@
/* Python interface to inferiors.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -428,9 +428,7 @@ connpy_send_packet (PyObject *self, PyObject *args, PyObject *kw)
/* Global initialization for this file. */
-void _initialize_py_connection ();
-void
-_initialize_py_connection ()
+INIT_GDB_FILE (py_connection)
{
gdb::observers::connection_removed.attach (connpy_connection_removed,
"py-connection");
diff --git a/gdb/python/py-continueevent.c b/gdb/python/py-continueevent.c
index c859b1e..500b5da 100644
--- a/gdb/python/py-continueevent.c
+++ b/gdb/python/py-continueevent.c
@@ -1,6 +1,6 @@
/* Python interface to inferior continue events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-dap.c b/gdb/python/py-dap.c
index d5555c9..9a9875a 100644
--- a/gdb/python/py-dap.c
+++ b/gdb/python/py-dap.c
@@ -1,6 +1,6 @@
/* Python DAP interpreter
- Copyright (C) 2022-2024 Free Software Foundation, Inc.
+ Copyright (C) 2022-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -110,9 +110,7 @@ dap_interp::pre_command_loop ()
call_dap_fn ("pre_command_loop");
}
-void _initialize_py_interp ();
-void
-_initialize_py_interp ()
+INIT_GDB_FILE (py_interp)
{
/* The dap code uses module typing, available starting python 3.5. */
#if PY_VERSION_HEX >= 0x03050000
diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c
index 22d2ac7..17064dc 100644
--- a/gdb/python/py-disasm.c
+++ b/gdb/python/py-disasm.c
@@ -1,6 +1,6 @@
/* Python interface to instruction disassembly.
- Copyright (C) 2021-2024 Free Software Foundation, Inc.
+ Copyright (C) 2021-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -1311,12 +1311,13 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
return {};
}
- /* Check the result is a DisassemblerResult (or a sub-class). */
- if (!PyObject_IsInstance (result.get (),
- (PyObject *) &disasm_result_object_type))
+ /* Check the result is a DisassemblerResult. */
+ if (!PyObject_TypeCheck (result.get (), &disasm_result_object_type))
{
- PyErr_SetString (PyExc_TypeError,
- _("Result is not a DisassemblerResult."));
+ PyErr_Format
+ (PyExc_TypeError,
+ _("Result from Disassembler must be gdb.DisassemblerResult, not %s."),
+ Py_TYPE (result.get ())->tp_name);
gdbpy_print_stack ();
return std::optional<int> (-1);
}
diff --git a/gdb/python/py-event-types.def b/gdb/python/py-event-types.def
index e22f042..15cd9fa 100644
--- a/gdb/python/py-event-types.def
+++ b/gdb/python/py-event-types.def
@@ -1,6 +1,6 @@
/* Python event definitions -*- c++ -*-
- Copyright (C) 2017-2024 Free Software Foundation, Inc.
+ Copyright (C) 2017-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-event.c b/gdb/python/py-event.c
index a918136..da3b52c 100644
--- a/gdb/python/py-event.c
+++ b/gdb/python/py-event.c
@@ -1,6 +1,6 @@
/* Python interface to inferior events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-event.h b/gdb/python/py-event.h
index e4491db..3938368 100644
--- a/gdb/python/py-event.h
+++ b/gdb/python/py-event.h
@@ -1,6 +1,6 @@
/* Python interface to inferior events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-events.h b/gdb/python/py-events.h
index b44c437..d9df3ab 100644
--- a/gdb/python/py-events.h
+++ b/gdb/python/py-events.h
@@ -1,6 +1,6 @@
/* Python interface to inferior events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-evtregistry.c b/gdb/python/py-evtregistry.c
index 7ae3997..2166f75 100644
--- a/gdb/python/py-evtregistry.c
+++ b/gdb/python/py-evtregistry.c
@@ -1,6 +1,6 @@
/* Python interface to inferior thread event registries.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-evts.c b/gdb/python/py-evts.c
index 4e966e1..c905926 100644
--- a/gdb/python/py-evts.c
+++ b/gdb/python/py-evts.c
@@ -1,6 +1,6 @@
/* Python interface to inferior events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-exitedevent.c b/gdb/python/py-exitedevent.c
index 4837468..dfe8a8f 100644
--- a/gdb/python/py-exitedevent.c
+++ b/gdb/python/py-exitedevent.c
@@ -1,6 +1,6 @@
/* Python interface to inferior exit events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index bc53d4e..0ea629f 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -1,6 +1,6 @@
/* Python interface to finish breakpoints
- Copyright (C) 2011-2024 Free Software Foundation, Inc.
+ Copyright (C) 2011-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 88646ee..722a952 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -1,6 +1,6 @@
/* Python interface to stack frames
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
index 88252b0..adf4233 100644
--- a/gdb/python/py-framefilter.c
+++ b/gdb/python/py-framefilter.c
@@ -1,6 +1,6 @@
/* Python frame filters
- Copyright (C) 2013-2024 Free Software Foundation, Inc.
+ Copyright (C) 2013-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-function.c b/gdb/python/py-function.c
index 2b65d8b..86a2f30 100644
--- a/gdb/python/py-function.c
+++ b/gdb/python/py-function.c
@@ -1,6 +1,6 @@
/* Convenience functions implemented in Python.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c
index dd0ee45..70ceebb 100644
--- a/gdb/python/py-gdb-readline.c
+++ b/gdb/python/py-gdb-readline.c
@@ -1,6 +1,6 @@
/* Readline support for Python.
- Copyright (C) 2012-2024 Free Software Foundation, Inc.
+ Copyright (C) 2012-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -29,11 +29,7 @@
static char *
gdbpy_readline_wrapper (FILE *sys_stdin, FILE *sys_stdout,
-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4
const char *prompt)
-#else
- char *prompt)
-#endif
{
int n;
const char *p = NULL;
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 356961c..2aa11d3 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -1,6 +1,6 @@
/* Python interface to inferiors.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-infevents.c b/gdb/python/py-infevents.c
index f53d589..e63ba52 100644
--- a/gdb/python/py-infevents.c
+++ b/gdb/python/py-infevents.c
@@ -1,6 +1,6 @@
/* Python interface to inferior function events.
- Copyright (C) 2013-2024 Free Software Foundation, Inc.
+ Copyright (C) 2013-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 4340666..4f1f8d4 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -1,6 +1,6 @@
/* Python interface to inferior threads.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-instruction.c b/gdb/python/py-instruction.c
index 7d77572..60cd561 100644
--- a/gdb/python/py-instruction.c
+++ b/gdb/python/py-instruction.c
@@ -1,6 +1,6 @@
/* Python interface to instruction objects.
- Copyright 2017-2024 Free Software Foundation, Inc.
+ Copyright 2017-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-instruction.h b/gdb/python/py-instruction.h
index efbdda8..8baf9cb 100644
--- a/gdb/python/py-instruction.h
+++ b/gdb/python/py-instruction.h
@@ -1,6 +1,6 @@
/* Python interface to instruction objects.
- Copyright 2017-2024 Free Software Foundation, Inc.
+ Copyright 2017-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-lazy-string.c b/gdb/python/py-lazy-string.c
index d75f861..265f34a 100644
--- a/gdb/python/py-lazy-string.c
+++ b/gdb/python/py-lazy-string.c
@@ -1,6 +1,6 @@
/* Python interface to lazy strings.
- Copyright (C) 2010-2024 Free Software Foundation, Inc.
+ Copyright (C) 2010-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index fc57f36..90cba09 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -1,6 +1,6 @@
/* Python interface to line tables.
- Copyright (C) 2013-2024 Free Software Foundation, Inc.
+ Copyright (C) 2013-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-membuf.c b/gdb/python/py-membuf.c
index 25ebc99..817dfba 100644
--- a/gdb/python/py-membuf.c
+++ b/gdb/python/py-membuf.c
@@ -1,6 +1,6 @@
/* Python memory buffer interface for reading inferior memory.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index cf75a18..9b871d4 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -1,6 +1,6 @@
/* Python interface to MI commands
- Copyright (C) 2023-2024 Free Software Foundation, Inc.
+ Copyright (C) 2023-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index e051b90..72f427f 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -1,6 +1,6 @@
/* MI Command Set for GDB, the GNU debugger.
- Copyright (C) 2019-2024 Free Software Foundation, Inc.
+ Copyright (C) 2019-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -578,9 +578,7 @@ PyTypeObject micmdpy_object_type = {
0, /* tp_alloc */
};
-void _initialize_py_micmd ();
-void
-_initialize_py_micmd ()
+INIT_GDB_FILE (py_micmd)
{
add_setshow_boolean_cmd
("py-micmd", class_maintenance, &pymicmd_debug,
diff --git a/gdb/python/py-newobjfileevent.c b/gdb/python/py-newobjfileevent.c
index deaee70..b2071f96 100644
--- a/gdb/python/py-newobjfileevent.c
+++ b/gdb/python/py-newobjfileevent.c
@@ -1,6 +1,6 @@
/* Python interface to new object file loading events.
- Copyright (C) 2011-2024 Free Software Foundation, Inc.
+ Copyright (C) 2011-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 6ce58a1..1c6f569 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -1,6 +1,6 @@
/* Python interface to objfiles.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 0e79d21..06237b6 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -1,6 +1,6 @@
/* GDB parameters implemented in Python
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -495,7 +495,11 @@ get_doc_string (PyObject *object, enum doc_string_type doc_type,
}
}
- if (result == nullptr)
+ /* For the set/show docs, if these strings are empty then we set then to
+ a non-empty string. This ensures that the command has some sane
+ documentation for its 'help' text. */
+ if (result == nullptr
+ || (doc_type != doc_string_description && *result == '\0'))
{
if (doc_type == doc_string_description)
result.reset (xstrdup (_("This command is not documented.")));
@@ -904,6 +908,18 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
show_doc = get_doc_string (self, doc_string_show, name);
doc = get_doc_string (self, doc_string_description, cmd_name.get ());
+ /* The set/show docs should always be a non-empty string. */
+ gdb_assert (set_doc != nullptr && *set_doc != '\0');
+ gdb_assert (show_doc != nullptr && *show_doc != '\0');
+
+ /* For the DOC string only, if it is the empty string, then we convert it
+ to NULL. This means GDB will not even display a blank line for this
+ part of the help text, instead the set/show line is all the user will
+ get. */
+ gdb_assert (doc != nullptr);
+ if (*doc == '\0')
+ doc = nullptr;
+
Py_INCREF (self);
try
diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index e061ea1..1e72e90 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -1,6 +1,6 @@
/* Python pretty-printing
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 4dbe18e..61f9b4b 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -1,6 +1,6 @@
/* Python interface to program spaces.
- Copyright (C) 2010-2024 Free Software Foundation, Inc.
+ Copyright (C) 2010-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c
index bdfebf1..620f033 100644
--- a/gdb/python/py-record-btrace.c
+++ b/gdb/python/py-record-btrace.c
@@ -1,6 +1,6 @@
/* Python interface to btrace instruction history.
- Copyright 2016-2024 Free Software Foundation, Inc.
+ Copyright 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-record-btrace.h b/gdb/python/py-record-btrace.h
index 30465bb..e5a1b10 100644
--- a/gdb/python/py-record-btrace.h
+++ b/gdb/python/py-record-btrace.h
@@ -1,6 +1,6 @@
/* Python interface to btrace record targets.
- Copyright 2016-2024 Free Software Foundation, Inc.
+ Copyright 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-record-full.c b/gdb/python/py-record-full.c
index b6623a6..dd23055 100644
--- a/gdb/python/py-record-full.c
+++ b/gdb/python/py-record-full.c
@@ -1,6 +1,6 @@
/* Python interface to btrace instruction history.
- Copyright 2016-2024 Free Software Foundation, Inc.
+ Copyright 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-record-full.h b/gdb/python/py-record-full.h
index df95776..aa07b3a 100644
--- a/gdb/python/py-record-full.h
+++ b/gdb/python/py-record-full.h
@@ -1,6 +1,6 @@
/* Python interface to full record targets.
- Copyright 2016-2024 Free Software Foundation, Inc.
+ Copyright 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c
index 2995dc1..7e7904b 100644
--- a/gdb/python/py-record.c
+++ b/gdb/python/py-record.c
@@ -1,6 +1,6 @@
/* Python interface to record targets.
- Copyright 2016-2024 Free Software Foundation, Inc.
+ Copyright 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h
index 1492de6..98e4863 100644
--- a/gdb/python/py-record.h
+++ b/gdb/python/py-record.h
@@ -1,6 +1,6 @@
/* Python interface to record targets.
- Copyright 2017-2024 Free Software Foundation, Inc.
+ Copyright 2017-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h
index 938068e..04e8b82 100644
--- a/gdb/python/py-ref.h
+++ b/gdb/python/py-ref.h
@@ -1,6 +1,6 @@
/* Python reference-holding class
- Copyright (C) 2016-2024 Free Software Foundation, Inc.
+ Copyright (C) 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c
index 75f99e5..9be2e3c 100644
--- a/gdb/python/py-registers.c
+++ b/gdb/python/py-registers.c
@@ -1,6 +1,6 @@
/* Python interface to register, and register group information.
- Copyright (C) 2020-2024 Free Software Foundation, Inc.
+ Copyright (C) 2020-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -403,8 +403,7 @@ gdbpy_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
PyErr_SetString (PyExc_ValueError, "Bad register");
}
/* The register could be a gdb.RegisterDescriptor object. */
- else if (PyObject_IsInstance (pyo_reg_id,
- (PyObject *) &register_descriptor_object_type))
+ else if (PyObject_TypeCheck (pyo_reg_id, &register_descriptor_object_type))
{
register_descriptor_object *reg
= (register_descriptor_object *) pyo_reg_id;
diff --git a/gdb/python/py-signalevent.c b/gdb/python/py-signalevent.c
index bf51106..5d58aa1 100644
--- a/gdb/python/py-signalevent.c
+++ b/gdb/python/py-signalevent.c
@@ -1,6 +1,6 @@
/* Python interface to inferior signal stop events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-stopevent.c b/gdb/python/py-stopevent.c
index 485bbb1..e49f240 100644
--- a/gdb/python/py-stopevent.c
+++ b/gdb/python/py-stopevent.c
@@ -1,6 +1,6 @@
/* Python interface to inferior stop events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-stopevent.h b/gdb/python/py-stopevent.h
index 450d8c2..fbfa87e 100644
--- a/gdb/python/py-stopevent.h
+++ b/gdb/python/py-stopevent.h
@@ -1,6 +1,6 @@
/* Python interface to inferior events.
- Copyright (C) 2009-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index 3ce1049..3028a30 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -29,12 +29,6 @@ struct symbol_object {
PyObject_HEAD
/* The GDB symbol structure this object is wrapping. */
struct symbol *symbol;
- /* A symbol object is associated with an objfile, so keep track with
- doubly-linked list, rooted in the objfile. This lets us
- invalidate the underlying struct symbol when the objfile is
- deleted. */
- symbol_object *prev;
- symbol_object *next;
};
/* Require a valid symbol. All access to symbol_object->symbol should be
@@ -50,26 +44,8 @@ struct symbol_object {
} \
} while (0)
-/* A deleter that is used when an objfile is about to be freed. */
-struct symbol_object_deleter
-{
- void operator() (symbol_object *obj)
- {
- while (obj)
- {
- symbol_object *next = obj->next;
-
- obj->symbol = NULL;
- obj->next = NULL;
- obj->prev = NULL;
-
- obj = next;
- }
- }
-};
-
-static const registry<objfile>::key<symbol_object, symbol_object_deleter>
- sympy_objfile_data_key;
+static const gdbpy_registry<gdbpy_memoizing_registry_storage<symbol_object,
+ symbol, &symbol_object::symbol>> sympy_registry;
static PyObject *
sympy_str (PyObject *self)
@@ -347,19 +323,18 @@ static void
set_symbol (symbol_object *obj, struct symbol *symbol)
{
obj->symbol = symbol;
- obj->prev = NULL;
- if (symbol->is_objfile_owned ()
- && symbol->symtab () != NULL)
+ if (symbol->is_objfile_owned ())
{
- struct objfile *objfile = symbol->objfile ();
-
- obj->next = sympy_objfile_data_key.get (objfile);
- if (obj->next)
- obj->next->prev = obj;
- sympy_objfile_data_key.set (objfile, obj);
+ /* Can it really happen that symbol->symtab () is NULL? */
+ if (symbol->symtab () != nullptr)
+ {
+ sympy_registry.add (symbol->objfile (), obj);
+ }
}
else
- obj->next = NULL;
+ {
+ sympy_registry.add (symbol->arch (), obj);
+ }
}
/* Create a new symbol object (gdb.Symbol) that encapsulates the struct
@@ -369,6 +344,15 @@ symbol_to_symbol_object (struct symbol *sym)
{
symbol_object *sym_obj;
+ /* Look if there's already a gdb.Symbol object for given SYMBOL
+ and if so, return it. */
+ if (sym->is_objfile_owned ())
+ sym_obj = sympy_registry.lookup (sym->objfile (), sym);
+ else
+ sym_obj = sympy_registry.lookup (sym->arch (), sym);
+ if (sym_obj != nullptr)
+ return (PyObject*)sym_obj;
+
sym_obj = PyObject_New (symbol_object, &symbol_object_type);
if (sym_obj)
set_symbol (sym_obj, sym);
@@ -390,15 +374,14 @@ sympy_dealloc (PyObject *obj)
{
symbol_object *sym_obj = (symbol_object *) obj;
- if (sym_obj->prev)
- sym_obj->prev->next = sym_obj->next;
- else if (sym_obj->symbol != NULL
- && sym_obj->symbol->is_objfile_owned ()
- && sym_obj->symbol->symtab () != NULL)
- sympy_objfile_data_key.set (sym_obj->symbol->objfile (), sym_obj->next);
- if (sym_obj->next)
- sym_obj->next->prev = sym_obj->prev;
- sym_obj->symbol = NULL;
+ if (sym_obj->symbol != nullptr)
+ {
+ if (sym_obj->symbol->is_objfile_owned ())
+ sympy_registry.remove (sym_obj->symbol->objfile (), sym_obj);
+ else
+ sympy_registry.remove (sym_obj->symbol->arch (), sym_obj);
+ }
+
Py_TYPE (obj)->tp_free (obj);
}
diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index 99a5094..9f7bdb0 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -1,6 +1,6 @@
/* Python interface to symbol tables.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -28,39 +28,12 @@ struct symtab_object {
PyObject_HEAD
/* The GDB Symbol table structure. */
struct symtab *symtab;
- /* A symtab object is associated with an objfile, so keep track with
- a doubly-linked list, rooted in the objfile. This allows
- invalidation of the underlying struct symtab when the objfile is
- deleted. */
- symtab_object *prev;
- symtab_object *next;
-};
-
-/* This function is called when an objfile is about to be freed.
- Invalidate the symbol table as further actions on the symbol table
- would result in bad data. All access to obj->symtab should be
- gated by STPY_REQUIRE_VALID which will raise an exception on
- invalid symbol tables. */
-struct stpy_deleter
-{
- void operator() (symtab_object *obj)
- {
- while (obj)
- {
- symtab_object *next = obj->next;
-
- obj->symtab = NULL;
- obj->next = NULL;
- obj->prev = NULL;
- obj = next;
- }
- }
};
extern PyTypeObject symtab_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("symtab_object");
-static const registry<objfile>::key<symtab_object, stpy_deleter>
- stpy_objfile_data_key;
+static const gdbpy_registry<gdbpy_memoizing_registry_storage<symtab_object,
+ symtab, &symtab_object::symtab>> stpy_registry;
/* Require a valid symbol table. All access to symtab_object->symtab
should be gated by this call. */
@@ -77,8 +50,6 @@ static const registry<objfile>::key<symtab_object, stpy_deleter>
struct sal_object {
PyObject_HEAD
- /* The GDB Symbol table structure. */
- PyObject *symtab;
/* The GDB Symbol table and line structure. */
struct symtab_and_line *sal;
/* A Symtab and line object is associated with an objfile, so keep
@@ -94,34 +65,19 @@ struct sal_object {
data. All access to obj->sal should be gated by
SALPY_REQUIRE_VALID which will raise an exception on invalid symbol
table and line objects. */
-struct salpy_deleter
+struct salpy_invalidator
{
void operator() (sal_object *obj)
{
- gdbpy_enter enter_py;
-
- while (obj)
- {
- sal_object *next = obj->next;
-
- gdbpy_ref<> tmp (obj->symtab);
- obj->symtab = Py_None;
- Py_INCREF (Py_None);
-
- obj->next = NULL;
- obj->prev = NULL;
- xfree (obj->sal);
- obj->sal = NULL;
-
- obj = next;
- }
+ xfree (obj->sal);
+ obj->sal = nullptr;
}
};
extern PyTypeObject sal_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("sal_object");
-static const registry<objfile>::key<sal_object, salpy_deleter>
- salpy_objfile_data_key;
+static const gdbpy_registry<gdbpy_tracking_registry_storage<sal_object,
+ symtab_and_line, &sal_object::sal, salpy_invalidator>> salpy_registry;
/* Require a valid symbol table and line object. All access to
sal_object->sal should be gated by this call. */
@@ -272,18 +228,15 @@ salpy_str (PyObject *self)
{
const char *filename;
sal_object *sal_obj;
- struct symtab_and_line *sal = NULL;
+ struct symtab_and_line *sal = nullptr;
SALPY_REQUIRE_VALID (self, sal);
sal_obj = (sal_object *) self;
- if (sal_obj->symtab == Py_None)
+ if (sal_obj->sal->symtab == nullptr)
filename = "<unknown>";
else
- {
- symtab *symtab = symtab_object_to_symtab (sal_obj->symtab);
- filename = symtab_to_filename_for_display (symtab);
- }
+ filename = symtab_to_filename_for_display (sal_obj->sal->symtab);
return PyUnicode_FromFormat ("symbol and line for %s, line %d", filename,
sal->line);
@@ -292,16 +245,12 @@ salpy_str (PyObject *self)
static void
stpy_dealloc (PyObject *obj)
{
- symtab_object *symtab = (symtab_object *) obj;
-
- if (symtab->prev)
- symtab->prev->next = symtab->next;
- else if (symtab->symtab)
- stpy_objfile_data_key.set (symtab->symtab->compunit ()->objfile (),
- symtab->next);
- if (symtab->next)
- symtab->next->prev = symtab->prev;
- symtab->symtab = NULL;
+ symtab_object *symtab_obj = (symtab_object *) obj;
+
+ if (symtab_obj->symtab != nullptr)
+ stpy_registry.remove (symtab_obj->symtab->compunit ()->objfile(),
+ symtab_obj);
+
Py_TYPE (obj)->tp_free (obj);
}
@@ -346,13 +295,13 @@ static PyObject *
salpy_get_symtab (PyObject *self, void *closure)
{
struct symtab_and_line *sal;
- sal_object *self_sal = (sal_object *) self;
SALPY_REQUIRE_VALID (self, sal);
- Py_INCREF (self_sal->symtab);
-
- return (PyObject *) self_sal->symtab;
+ if (sal->symtab == nullptr)
+ Py_RETURN_NONE;
+ else
+ return symtab_to_symtab_object (sal->symtab);
}
/* Implementation of gdb.Symtab_and_line.is_valid (self) -> Boolean.
@@ -375,17 +324,10 @@ salpy_dealloc (PyObject *self)
{
sal_object *self_sal = (sal_object *) self;
- if (self_sal->prev)
- self_sal->prev->next = self_sal->next;
- else if (self_sal->symtab != Py_None)
- salpy_objfile_data_key.set
- (symtab_object_to_symtab (self_sal->symtab)->compunit ()->objfile (),
- self_sal->next);
-
- if (self_sal->next)
- self_sal->next->prev = self_sal->prev;
+ if (self_sal->sal != nullptr && self_sal->sal->symtab != nullptr)
+ salpy_registry.remove (self_sal->sal->symtab->compunit ()->objfile (),
+ self_sal);
- Py_DECREF (self_sal->symtab);
xfree (self_sal->sal);
Py_TYPE (self)->tp_free (self);
}
@@ -395,48 +337,20 @@ salpy_dealloc (PyObject *self)
Also, register the sal_object life-cycle with the life-cycle of the
object file associated with this sal, if needed. If a failure
occurs during the sal population, this function will return -1. */
-static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
+static void
set_sal (sal_object *sal_obj, struct symtab_and_line sal)
{
- PyObject *symtab_obj;
-
- if (sal.symtab)
- {
- symtab_obj = symtab_to_symtab_object (sal.symtab);
- /* If a symtab existed in the sal, but it cannot be duplicated,
- we exit. */
- if (symtab_obj == NULL)
- return -1;
- }
- else
- {
- symtab_obj = Py_None;
- Py_INCREF (Py_None);
- }
-
sal_obj->sal = ((struct symtab_and_line *)
xmemdup (&sal, sizeof (struct symtab_and_line),
sizeof (struct symtab_and_line)));
- sal_obj->symtab = symtab_obj;
- sal_obj->prev = NULL;
+ sal_obj->prev = nullptr;
+ sal_obj->next = nullptr;
/* If the SAL does not have a symtab, we do not add it to the
objfile cleanup observer linked list. */
- if (sal_obj->symtab != Py_None)
- {
- symtab *symtab = symtab_object_to_symtab (sal_obj->symtab);
-
- sal_obj->next
- = salpy_objfile_data_key.get (symtab->compunit ()->objfile ());
- if (sal_obj->next)
- sal_obj->next->prev = sal_obj;
-
- salpy_objfile_data_key.set (symtab->compunit ()->objfile (), sal_obj);
- }
- else
- sal_obj->next = NULL;
-
- return 0;
+ symtab *symtab = sal_obj->sal->symtab;
+ if (symtab != nullptr)
+ salpy_registry.add (symtab->compunit ()->objfile (), sal_obj);
}
/* Given a symtab, and a symtab_object that has previously been
@@ -448,16 +362,8 @@ static void
set_symtab (symtab_object *obj, struct symtab *symtab)
{
obj->symtab = symtab;
- obj->prev = NULL;
- if (symtab)
- {
- obj->next = stpy_objfile_data_key.get (symtab->compunit ()->objfile ());
- if (obj->next)
- obj->next->prev = obj;
- stpy_objfile_data_key.set (symtab->compunit ()->objfile (), obj);
- }
- else
- obj->next = NULL;
+ if (symtab != nullptr)
+ stpy_registry.add (symtab->compunit ()->objfile (), obj);
}
/* Create a new symbol table (gdb.Symtab) object that encapsulates the
@@ -467,6 +373,16 @@ symtab_to_symtab_object (struct symtab *symtab)
{
symtab_object *symtab_obj;
+ /* Look if there's already a gdb.Symtab object for given SYMTAB
+ and if so, return it. */
+ if (symtab != nullptr)
+ {
+ symtab_obj = stpy_registry.lookup (symtab->compunit ()->objfile (),
+ symtab);
+ if (symtab_obj != nullptr)
+ return (PyObject*)symtab_obj;
+ }
+
symtab_obj = PyObject_New (symtab_object, &symtab_object_type);
if (symtab_obj)
set_symtab (symtab_obj, symtab);
@@ -479,14 +395,13 @@ symtab_to_symtab_object (struct symtab *symtab)
PyObject *
symtab_and_line_to_sal_object (struct symtab_and_line sal)
{
- gdbpy_ref<sal_object> sal_obj (PyObject_New (sal_object, &sal_object_type));
- if (sal_obj != NULL)
- {
- if (set_sal (sal_obj.get (), sal) < 0)
- return NULL;
- }
+ sal_object *sal_obj;
+
+ sal_obj = PyObject_New (sal_object, &sal_object_type);
+ if (sal_obj != nullptr)
+ set_sal (sal_obj, sal);
- return (PyObject *) sal_obj.release ();
+ return (PyObject *) sal_obj;
}
/* Return struct symtab_and_line reference that is wrapped by this
@@ -560,7 +475,7 @@ PyTypeObject symtab_object_type = {
"gdb.Symtab", /*tp_name*/
sizeof (symtab_object), /*tp_basicsize*/
0, /*tp_itemsize*/
- stpy_dealloc, /*tp_dealloc*/
+ stpy_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
diff --git a/gdb/python/py-threadevent.c b/gdb/python/py-threadevent.c
index 557f2b7..f0a16a1 100644
--- a/gdb/python/py-threadevent.c
+++ b/gdb/python/py-threadevent.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009-2024 Free Software Foundation, Inc.
+/* Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c
index 09f0a60..23a713e 100644
--- a/gdb/python/py-tui.c
+++ b/gdb/python/py-tui.c
@@ -1,6 +1,6 @@
/* TUI windows implemented in Python
- Copyright (C) 2020-2024 Free Software Foundation, Inc.
+ Copyright (C) 2020-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index 11a96d5..c546aa7 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -1,6 +1,6 @@
/* Python interface to types.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -32,12 +32,6 @@ struct type_object
{
PyObject_HEAD
struct type *type;
-
- /* If a Type object is associated with an objfile, it is kept on a
- doubly-linked list, rooted in the objfile. This lets us copy the
- underlying struct type when the objfile is deleted. */
- struct type_object *prev;
- struct type_object *next;
};
extern PyTypeObject type_object_type
@@ -164,7 +158,7 @@ convert_field (struct type *type, int field)
}
else
{
- if (type->field (field).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK)
+ if (type->field (field).loc_is_dwarf_block ())
arg = gdbpy_ref<>::new_reference (Py_None);
else
arg = gdb_py_object_from_longest (type->field (field).loc_bitpos ());
@@ -1162,75 +1156,61 @@ typy_richcompare (PyObject *self, PyObject *other, int op)
-/* Deleter that saves types when an objfile is being destroyed. */
-struct typy_deleter
+/* Forward declaration, see below. */
+static void set_type (type_object *obj, struct type *type);
+
+/* Invalidator that saves types when an objfile is being destroyed. */
+struct typy_invalidator
{
void operator() (type_object *obj)
{
- if (!gdb_python_initialized)
- return;
-
- /* This prevents another thread from freeing the objects we're
- operating on. */
- gdbpy_enter enter_py;
-
- copied_types_hash_t copied_types;
-
- while (obj)
+ if (obj->type->is_objfile_owned ())
{
- type_object *next = obj->next;
+ copied_types_hash_t copied_types;
- copied_types.clear ();
- obj->type = copy_type_recursive (obj->type, copied_types);
-
- obj->next = NULL;
- obj->prev = NULL;
-
- obj = next;
+ /* Set a copied (now arch-owned) type. As a side-effect this
+ adds OBJ to per-arch list. We do not need to remove it from
+ per-objfile list since the objfile is going to go completely
+ anyway. */
+ set_type (obj, copy_type_recursive (obj->type, copied_types));
+ }
+ else
+ {
+ obj->type = nullptr;
}
}
};
-static const registry<objfile>::key<type_object, typy_deleter>
- typy_objfile_data_key;
+static const gdbpy_registry<gdbpy_memoizing_registry_storage<type_object,
+ type, &type_object::type, typy_invalidator>> typy_registry;
static void
set_type (type_object *obj, struct type *type)
{
+ gdb_assert (type != nullptr);
+
obj->type = type;
- obj->prev = NULL;
- if (type != nullptr && type->objfile_owner () != nullptr)
- {
- struct objfile *objfile = type->objfile_owner ();
- obj->next = typy_objfile_data_key.get (objfile);
- if (obj->next)
- obj->next->prev = obj;
- typy_objfile_data_key.set (objfile, obj);
- }
+ if (type->objfile_owner () != nullptr)
+ typy_registry.add (type->objfile_owner (), obj);
else
- obj->next = NULL;
+ typy_registry.add (type->arch_owner (), obj);
}
static void
typy_dealloc (PyObject *obj)
{
- type_object *type = (type_object *) obj;
+ type_object *type_obj = (type_object *) obj;
- if (type->prev)
- type->prev->next = type->next;
- else if (type->type != nullptr && type->type->objfile_owner () != nullptr)
+ if (type_obj->type != nullptr)
{
- /* Must reset head of list. */
- struct objfile *objfile = type->type->objfile_owner ();
-
- if (objfile)
- typy_objfile_data_key.set (objfile, type->next);
+ if (type_obj->type->is_objfile_owned ())
+ typy_registry.remove (type_obj->type->objfile_owner (), type_obj);
+ else
+ typy_registry.remove (type_obj->type->arch_owner (), type_obj);
}
- if (type->next)
- type->next->prev = type->prev;
- Py_TYPE (type)->tp_free (type);
+ Py_TYPE (obj)->tp_free (obj);
}
/* Return number of fields ("length" of the field dictionary). */
@@ -1473,6 +1453,16 @@ type_to_type_object (struct type *type)
return gdbpy_handle_gdb_exception (nullptr, except);
}
+ /* Look if there's already a gdb.Type object for given TYPE
+ and if so, return it. */
+ if (type->is_objfile_owned ())
+ type_obj = typy_registry.lookup (type->objfile_owner (), type);
+ else
+ type_obj = typy_registry.lookup (type->arch_owner (), type);
+
+ if (type_obj != nullptr)
+ return (PyObject*)type_obj;
+
type_obj = PyObject_New (type_object, &type_object_type);
if (type_obj)
set_type (type_obj, type);
@@ -1684,7 +1674,7 @@ PyTypeObject type_object_type =
"gdb.Type", /*tp_name*/
sizeof (type_object), /*tp_basicsize*/
0, /*tp_itemsize*/
- typy_dealloc, /*tp_dealloc*/
+ typy_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
diff --git a/gdb/python/py-uiout.h b/gdb/python/py-uiout.h
index 5f8c530..f6fbd83 100644
--- a/gdb/python/py-uiout.h
+++ b/gdb/python/py-uiout.h
@@ -1,6 +1,6 @@
/* Python implementation of ui_out
- Copyright (C) 2023-2024 Free Software Foundation, Inc.
+ Copyright (C) 2023-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index af8ee98..dc078ec 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -1,6 +1,6 @@
/* Python frame unwinder interface.
- Copyright (C) 2015-2024 Free Software Foundation, Inc.
+ Copyright (C) 2015-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -929,9 +929,9 @@ frame_unwind_python::sniff (const frame_info_ptr &this_frame,
/* Received UnwindInfo, cache data. */
PyObject *pyo_unwind_info = PyTuple_GET_ITEM (pyo_execute_ret.get (), 0);
- if (PyObject_IsInstance (pyo_unwind_info,
- (PyObject *) &unwind_info_object_type) <= 0)
- error (_("A Unwinder should return gdb.UnwindInfo instance."));
+ if (!PyObject_TypeCheck (pyo_unwind_info, &unwind_info_object_type))
+ error (_("an Unwinder should return gdb.UnwindInfo, not %s."),
+ Py_TYPE (pyo_unwind_info)->tp_name);
{
unwind_info_object *unwind_info =
@@ -1029,9 +1029,7 @@ gdbpy_initialize_unwind (void)
return 0;
}
-void _initialize_py_unwind ();
-void
-_initialize_py_unwind ()
+INIT_GDB_FILE (py_unwind)
{
add_setshow_boolean_cmd
("py-unwind", class_maintenance, &pyuw_debug,
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 0bc18a6..df8b888 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -1,6 +1,6 @@
/* General utility routines for GDB/Python.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index 02c50b4..8a2e263 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -1,6 +1,6 @@
/* Python interface to values.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -60,7 +60,6 @@ struct value_object {
struct value_object *prev;
struct value *value;
PyObject *address;
- PyObject *type;
PyObject *dynamic_type;
PyObject *content_bytes;
};
@@ -84,8 +83,6 @@ valpy_clear_value (value_object *self)
self->value = nullptr;
Py_CLEAR (self->address);
- Py_CLEAR (self->type);
- Py_CLEAR (self->dynamic_type);
Py_CLEAR (self->content_bytes);
}
@@ -438,14 +435,7 @@ valpy_get_type (PyObject *self, void *closure)
{
value_object *obj = (value_object *) self;
- if (!obj->type)
- {
- obj->type = type_to_type_object (obj->value->type ());
- if (!obj->type)
- return NULL;
- }
- Py_INCREF (obj->type);
- return obj->type;
+ return type_to_type_object (obj->value->type ());
}
/* Return dynamic type of the value. */
@@ -454,13 +444,7 @@ static PyObject *
valpy_get_dynamic_type (PyObject *self, void *closure)
{
value_object *obj = (value_object *) self;
- struct type *type = NULL;
-
- if (obj->dynamic_type != NULL)
- {
- Py_INCREF (obj->dynamic_type);
- return obj->dynamic_type;
- }
+ struct type *type = nullptr;
try
{
@@ -493,23 +477,14 @@ valpy_get_dynamic_type (PyObject *self, void *closure)
else if (type->code () == TYPE_CODE_STRUCT)
type = value_rtti_type (val, NULL, NULL, NULL);
else
- {
- /* Re-use object's static type. */
- type = NULL;
- }
+ type = val->type ();
}
catch (const gdb_exception &except)
{
return gdbpy_handle_gdb_exception (nullptr, except);
}
- if (type == NULL)
- obj->dynamic_type = valpy_get_type (self, NULL);
- else
- obj->dynamic_type = type_to_type_object (type);
-
- Py_XINCREF (obj->dynamic_type);
- return obj->dynamic_type;
+ return type_to_type_object (type);
}
/* Implementation of gdb.Value.lazy_string ([encoding] [, length]) ->
@@ -1121,8 +1096,7 @@ valpy_getitem (PyObject *self, PyObject *key)
res_val = value_struct_elt (&tmp, {}, field.get (), NULL,
"struct/class/union");
else if (bitpos >= 0)
- res_val = value_struct_elt_bitpos (&tmp, bitpos, field_type,
- "struct/class/union");
+ res_val = value_struct_elt_bitpos (tmp, bitpos, field_type);
else if (base_class_type != NULL)
{
struct type *val_type;
@@ -1937,15 +1911,14 @@ value_to_value_object (struct value *val)
value_object *val_obj;
val_obj = PyObject_New (value_object, &value_object_type);
- if (val_obj != NULL)
+ if (val_obj != nullptr)
{
val->incref ();
val_obj->value = val;
val_obj->next = nullptr;
val_obj->prev = nullptr;
- val_obj->address = NULL;
- val_obj->type = NULL;
- val_obj->dynamic_type = NULL;
+ val_obj->address = nullptr;
+ val_obj->dynamic_type = nullptr;
val_obj->content_bytes = nullptr;
note_value (val_obj);
}
diff --git a/gdb/python/py-varobj.c b/gdb/python/py-varobj.c
index cbfea26..2d36091 100644
--- a/gdb/python/py-varobj.c
+++ b/gdb/python/py-varobj.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013-2024 Free Software Foundation, Inc.
+/* Copyright (C) 2013-2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/py-xmethods.c b/gdb/python/py-xmethods.c
index 2fae043..deac98f 100644
--- a/gdb/python/py-xmethods.c
+++ b/gdb/python/py-xmethods.c
@@ -1,6 +1,6 @@
/* Support for debug methods in Python.
- Copyright (C) 2013-2024 Free Software Foundation, Inc.
+ Copyright (C) 2013-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index c48f260..7f4237e 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1,6 +1,6 @@
/* Gdb/Python header for private use by Python module.
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -22,6 +22,7 @@
#include "extension.h"
#include "extension-priv.h"
+#include "registry.h"
/* These WITH_* macros are defined by the CPython API checker that
comes with the Python plugin for GCC. See:
@@ -87,6 +88,8 @@
#include <frameobject.h>
#include "py-ref.h"
+static_assert (PY_VERSION_HEX >= 0x03040000);
+
#define Py_TPFLAGS_CHECKTYPES 0
/* If Python.h does not define WITH_THREAD, then the various
@@ -134,17 +137,6 @@ typedef unsigned long gdb_py_ulongest;
#endif /* HAVE_LONG_LONG */
-#if PY_VERSION_HEX < 0x03020000
-typedef long Py_hash_t;
-#endif
-
-/* PyMem_RawMalloc appeared in Python 3.4. For earlier versions, we can just
- fall back to PyMem_Malloc. */
-
-#if PY_VERSION_HEX < 0x03040000
-#define PyMem_RawMalloc PyMem_Malloc
-#endif
-
/* A template variable holding the format character (as for
Py_BuildValue) for a given type. */
template<typename T>
@@ -518,7 +510,8 @@ PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
PyObject *gdbpy_parameter_value (const setting &var);
gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name
(const char *name, struct cmd_list_element ***base_list,
- struct cmd_list_element **start_list);
+ struct cmd_list_element **start_list,
+ struct cmd_list_element **prefix_cmd = nullptr);
PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args,
PyObject *kw);
@@ -1145,4 +1138,198 @@ gdbpy_type_ready (PyTypeObject *type, PyObject *mod = nullptr)
# define PyType_Ready POISONED_PyType_Ready
#endif
+/* A class to manage lifecycle of Python objects for objects that are "owned"
+ by an objfile or a gdbarch. It keeps track of Python objects and when
+ the "owning" object (objfile or gdbarch) is about to be freed, ensures that
+ all Python objects "owned" by that object are properly invalidated.
+
+ The actual tracking of "owned" Python objects is handled externally
+ by storage class. Storage object is created for each owning object
+ on demand and it is deleted when owning object is about to be freed.
+
+ The storage class must provide two member types:
+
+ * obj_type - the type of Python object whose lifecycle is managed.
+ * val_type - the type of GDB structure the Python objects are
+ representing.
+
+ It must also provide following methods:
+
+ void add (obj_type *obj);
+ void remove (obj_type *obj);
+
+ Memoizing storage must in addition to method above provide:
+
+ obj_type *lookup (val_type *val);
+
+ Finally it must invalidate all registered Python objects upon deletion. */
+template <typename Storage>
+class gdbpy_registry
+{
+public:
+ using obj_type = typename Storage::obj_type;
+ using val_type = typename Storage::val_type;
+
+ /* Register Python object OBJ as being "owned" by OWNER. When OWNER is
+ about to be freed, OBJ will be invalidated. */
+ template <typename O>
+ void add (O *owner, obj_type *obj) const
+ {
+ get_storage (owner)->add (obj);
+ }
+
+ /* Unregister Python object OBJ. OBJ will no longer be invalidated when
+ OWNER is about to be be freed. */
+ template <typename O>
+ void remove (O *owner, obj_type *obj) const
+ {
+ get_storage (owner)->remove (obj);
+ }
+
+ /* Lookup pre-existing Python object for given VAL. Return such object
+ if found, otherwise return NULL. This method always returns new
+ reference. */
+ template <typename O>
+ obj_type *lookup (O *owner, val_type *val) const
+ {
+ obj_type *obj = get_storage (owner)->lookup (val);
+ Py_XINCREF (obj);
+ return obj;
+ }
+
+private:
+
+ template<typename O>
+ using StorageKey = typename registry<O>::template key<Storage>;
+
+ template<typename O>
+ Storage *get_storage (O *owner, const StorageKey<O> &key) const
+ {
+ Storage *r = key.get (owner);
+ if (r == nullptr)
+ {
+ r = new Storage();
+ key.set (owner, r);
+ }
+ return r;
+ }
+
+ Storage *get_storage (struct objfile* objf) const
+ {
+ return get_storage (objf, m_key_for_objf);
+ }
+
+ Storage *get_storage (struct gdbarch* arch) const
+ {
+ return get_storage (arch, m_key_for_arch);
+ }
+
+ const registry<objfile>::key<Storage> m_key_for_objf;
+ const registry<gdbarch>::key<Storage> m_key_for_arch;
+};
+
+/* Default invalidator for Python objects. */
+template <typename P, typename V, V* P::*val_slot>
+struct gdbpy_default_invalidator
+{
+ void operator() (P *obj)
+ {
+ obj->*val_slot = nullptr;
+ }
+};
+
+/* A "storage" implementation suitable for temporary (on-demand) objects. */
+template <typename P,
+ typename V,
+ V* P::*val_slot,
+ typename Invalidator = gdbpy_default_invalidator<P, V, val_slot>>
+class gdbpy_tracking_registry_storage
+{
+public:
+ using obj_type = P;
+ using val_type = V;
+
+ void add (obj_type *obj)
+ {
+ gdb_assert (obj != nullptr && obj->*val_slot != nullptr);
+
+ m_objects.insert (obj);
+ }
+
+ void remove (obj_type *obj)
+ {
+ gdb_assert (obj != nullptr && obj->*val_slot != nullptr);
+ gdb_assert (m_objects.contains (obj));
+
+ m_objects.erase (obj);
+ }
+
+ ~gdbpy_tracking_registry_storage ()
+ {
+ Invalidator invalidate;
+ gdbpy_enter enter_py;
+
+ for (auto each : m_objects)
+ invalidate (each);
+ m_objects.clear ();
+ }
+
+protected:
+ gdb::unordered_set<obj_type *> m_objects;
+};
+
+/* A "storage" implementation suitable for memoized (interned) Python objects.
+
+ Python objects are memoized (interned) temporarily, meaning that when user
+ drops all their references the Python object is deallocated and removed
+ from storage.
+ */
+template <typename P,
+ typename V,
+ V* P::*val_slot,
+ typename Invalidator = gdbpy_default_invalidator<P, V, val_slot>>
+class gdbpy_memoizing_registry_storage
+{
+public:
+ using obj_type = P;
+ using val_type = V;
+
+ void add (obj_type *obj)
+ {
+ gdb_assert (obj != nullptr && obj->*val_slot != nullptr);
+
+ m_objects[obj->*val_slot] = obj;
+ }
+
+ void remove (obj_type *obj)
+ {
+ gdb_assert (obj != nullptr && obj->*val_slot != nullptr);
+ gdb_assert (m_objects.contains (obj->*val_slot));
+
+ m_objects.erase (obj->*val_slot);
+ }
+
+ obj_type *lookup (val_type *val) const
+ {
+ auto result = m_objects.find (val);
+ if (result != m_objects.end ())
+ return result->second;
+ else
+ return nullptr;
+ }
+
+ ~gdbpy_memoizing_registry_storage ()
+ {
+ Invalidator invalidate;
+ gdbpy_enter enter_py;
+
+ for (auto each : m_objects)
+ invalidate (each.second);
+ m_objects.clear ();
+ }
+
+protected:
+ gdb::unordered_map<val_type *, obj_type *> m_objects;
+};
+
#endif /* GDB_PYTHON_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 8f8030c..cb0d642 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1,6 +1,6 @@
/* General python/gdb code
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -36,6 +36,7 @@
#include "run-on-main-thread.h"
#include "observable.h"
#include "build-id.h"
+#include "cli/cli-style.h"
#if GDB_SELF_TEST
#include "gdbsupport/selftest.h"
@@ -127,7 +128,8 @@ static bool gdbpy_check_quit_flag (const struct extension_language_defn *);
static enum ext_lang_rc gdbpy_before_prompt_hook
(const struct extension_language_defn *, const char *current_gdb_prompt);
static std::optional<std::string> gdbpy_colorize
- (const std::string &filename, const std::string &contents);
+ (const std::string &filename, const std::string &contents,
+ enum language lang);
static std::optional<std::string> gdbpy_colorize_disasm
(const std::string &content, gdbarch *gdbarch);
static ext_lang_missing_file_result gdbpy_handle_missing_debuginfo
@@ -660,12 +662,14 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
const char *arg;
PyObject *from_tty_obj = nullptr;
PyObject *to_string_obj = nullptr;
- static const char *keywords[] = { "command", "from_tty", "to_string",
- nullptr };
+ PyObject *styling = nullptr;
+ static const char *keywords[]
+ = { "command", "from_tty", "to_string", "styling", nullptr };
- if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg,
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg,
&PyBool_Type, &from_tty_obj,
- &PyBool_Type, &to_string_obj))
+ &PyBool_Type, &to_string_obj,
+ &PyBool_Type, &styling))
return nullptr;
bool from_tty = false;
@@ -686,6 +690,15 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
to_string = (cmp != 0);
}
+ bool styling_p = !to_string;
+ if (styling != nullptr)
+ {
+ int cmp = PyObject_IsTrue (styling);
+ if (cmp < 0)
+ return nullptr;
+ styling_p = (cmp != 0);
+ }
+
std::string to_string_res;
scoped_restore preventer = prevent_dont_repeat ();
@@ -745,14 +758,29 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
scoped_restore save_uiout = make_scoped_restore (&current_uiout);
+ /* If the Python 'styling' argument was False then temporarily
+ disable styling. Otherwise, don't do anything, styling could
+ already be disabled for some other reason, we shouldn't override
+ that and force styling on. */
+ std::optional<scoped_disable_styling> disable_styling;
+ if (!styling_p)
+ disable_styling.emplace ();
+
/* Use the console interpreter uiout to have the same print format
for console or MI. */
interp = interp_lookup (current_ui, "console");
current_uiout = interp->interp_ui_out ();
if (to_string)
- to_string_res = execute_control_commands_to_string (lines.get (),
- from_tty);
+ {
+ /* Pass 'true' here to always request styling, however, if
+ the scoped_disable_styling disabled styling, or the user
+ has globally disabled styling, then the output will not be
+ styled. */
+ to_string_res
+ = execute_control_commands_to_string (lines.get (), from_tty,
+ true);
+ }
else
execute_control_commands (lines.get (), from_tty);
}
@@ -1268,7 +1296,8 @@ gdbpy_before_prompt_hook (const struct extension_language_defn *extlang,
/* This is the extension_language_ops.colorize "method". */
static std::optional<std::string>
-gdbpy_colorize (const std::string &filename, const std::string &contents)
+gdbpy_colorize (const std::string &filename, const std::string &contents,
+ enum language lang)
{
if (!gdb_python_initialized)
return {};
@@ -1302,6 +1331,13 @@ gdbpy_colorize (const std::string &filename, const std::string &contents)
return {};
}
+ gdbpy_ref<> lang_arg (PyUnicode_FromString (language_str (lang)));
+ if (lang_arg == nullptr)
+ {
+ gdbpy_print_stack ();
+ return {};
+ }
+
/* The pygments library, which is what we currently use for applying
styling, is happy to take input as a bytes object, and to figure out
the encoding for itself. This removes the need for us to figure out
@@ -1322,6 +1358,7 @@ gdbpy_colorize (const std::string &filename, const std::string &contents)
gdbpy_ref<> result (PyObject_CallFunctionObjArgs (hook.get (),
fname_arg.get (),
contents_arg.get (),
+ lang_arg.get (),
nullptr));
if (result == nullptr)
{
@@ -1575,16 +1612,53 @@ gdbpy_flush (PyObject *self, PyObject *args, PyObject *kw)
{
case 1:
{
- gdb_flush (gdb_stderr);
+ if (gdb_stderr != nullptr)
+ gdb_flush (gdb_stderr);
break;
}
case 2:
{
- gdb_flush (gdb_stdlog);
+ if (gdb_stdlog != nullptr)
+ gdb_flush (gdb_stdlog);
break;
}
default:
- gdb_flush (gdb_stdout);
+ if (gdb_stdout != nullptr)
+ gdb_flush (gdb_stdout);
+ }
+
+ Py_RETURN_NONE;
+}
+
+/* Implement gdb.warning(). Takes a single text string argument and emit a
+ warning using GDB's 'warning' function. The input text string must not
+ be empty. */
+
+static PyObject *
+gdbpy_warning (PyObject *self, PyObject *args, PyObject *kw)
+{
+ const char *text;
+ static const char *keywords[] = { "text", nullptr };
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s", keywords, &text))
+ return nullptr;
+
+ if (strlen (text) == 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("Empty text string passed to gdb.warning"));
+ return nullptr;
+ }
+
+ try
+ {
+ warning ("%s", text);
+ }
+ catch (const gdb_exception &ex)
+ {
+ /* The warning() call probably cannot throw an exception. But just
+ in case it ever does. */
+ return gdbpy_handle_gdb_exception (nullptr, ex);
}
Py_RETURN_NONE;
@@ -2406,7 +2480,7 @@ py_initialize ()
/foo/lib/pythonX.Y/...
This must be done before calling Py_Initialize. */
gdb::unique_xmalloc_ptr<char> progname
- (concat (ldirname (python_libdir.c_str ()).c_str (), SLASH_STRING, "bin",
+ (concat (gdb_ldirname (python_libdir.c_str ()).c_str (), SLASH_STRING, "bin",
SLASH_STRING, "python", (char *) NULL));
{
@@ -2643,9 +2717,7 @@ test_python ()
/* See python.h. */
cmd_list_element *python_cmd_element = nullptr;
-void _initialize_python ();
-void
-_initialize_python ()
+INIT_GDB_FILE (python)
{
cmd_list_element *python_interactive_cmd
= add_com ("python-interactive", class_obscure,
@@ -3084,6 +3156,12 @@ Return the current print options." },
METH_VARARGS | METH_KEYWORDS,
"notify_mi (name, data) -> None\n\
Output async record to MI channels if any." },
+
+ { "warning", (PyCFunction) gdbpy_warning,
+ METH_VARARGS | METH_KEYWORDS,
+ "warning (text) -> None\n\
+Print a warning." },
+
{NULL, NULL, 0, NULL}
};
diff --git a/gdb/python/python.h b/gdb/python/python.h
index e803285..f564af7 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -1,6 +1,6 @@
/* Python/gdb header for generic use in gdb
- Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ Copyright (C) 2008-2025 Free Software Foundation, Inc.
This file is part of GDB.