summaryrefslogtreecommitdiff
path: root/share/gdb/python
diff options
context:
space:
mode:
Diffstat (limited to 'share/gdb/python')
-rw-r--r--share/gdb/python/gdb/FrameDecorator.py300
-rw-r--r--share/gdb/python/gdb/FrameIterator.py49
-rw-r--r--share/gdb/python/gdb/__init__.py261
-rw-r--r--share/gdb/python/gdb/command/__init__.py14
-rw-r--r--share/gdb/python/gdb/command/explore.py785
-rw-r--r--share/gdb/python/gdb/command/frame_filters.py479
-rw-r--r--share/gdb/python/gdb/command/pretty_printers.py395
-rw-r--r--share/gdb/python/gdb/command/prompt.py66
-rw-r--r--share/gdb/python/gdb/command/type_printers.py125
-rw-r--r--share/gdb/python/gdb/command/unwinders.py199
-rw-r--r--share/gdb/python/gdb/command/xmethods.py270
-rw-r--r--share/gdb/python/gdb/disassembler.py178
-rw-r--r--share/gdb/python/gdb/frames.py233
-rw-r--r--share/gdb/python/gdb/function/__init__.py14
-rw-r--r--share/gdb/python/gdb/function/as_string.py38
-rw-r--r--share/gdb/python/gdb/function/caller_is.py157
-rw-r--r--share/gdb/python/gdb/function/strfns.py104
-rw-r--r--share/gdb/python/gdb/printer/__init__.py14
-rw-r--r--share/gdb/python/gdb/printer/bound_registers.py40
-rw-r--r--share/gdb/python/gdb/printing.py285
-rw-r--r--share/gdb/python/gdb/prompt.py163
-rw-r--r--share/gdb/python/gdb/styling.py101
-rw-r--r--share/gdb/python/gdb/types.py183
-rw-r--r--share/gdb/python/gdb/unwinder.py95
-rw-r--r--share/gdb/python/gdb/xmethod.py274
25 files changed, 4822 insertions, 0 deletions
diff --git a/share/gdb/python/gdb/FrameDecorator.py b/share/gdb/python/gdb/FrameDecorator.py
new file mode 100644
index 0000000..82be4fc
--- /dev/null
+++ b/share/gdb/python/gdb/FrameDecorator.py
@@ -0,0 +1,300 @@
+# Copyright (C) 2013-2023 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/>.
+
+import gdb
+
+
+class FrameDecorator(object):
+ """Basic implementation of a Frame Decorator"""
+
+ """ This base frame decorator decorates a frame or another frame
+ decorator, and provides convenience methods. If this object is
+ wrapping a frame decorator, defer to that wrapped object's method
+ if it has one. This allows for frame decorators that have
+ sub-classed FrameDecorator object, but also wrap other frame
+ decorators on the same frame to correctly execute.
+
+ E.g
+
+ If the result of frame filters running means we have one gdb.Frame
+ wrapped by multiple frame decorators, all sub-classed from
+ FrameDecorator, the resulting hierarchy will be:
+
+ Decorator1
+ -- (wraps) Decorator2
+ -- (wraps) FrameDecorator
+ -- (wraps) gdb.Frame
+
+ In this case we have two frame decorators, both of which are
+ sub-classed from FrameDecorator. If Decorator1 just overrides the
+ 'function' method, then all of the other methods are carried out
+ by the super-class FrameDecorator. But Decorator2 may have
+ overriden other methods, so FrameDecorator will look at the
+ 'base' parameter and defer to that class's methods. And so on,
+ down the chain."""
+
+ # 'base' can refer to a gdb.Frame or another frame decorator. In
+ # the latter case, the child class will have called the super
+ # method and _base will be an object conforming to the Frame Filter
+ # class.
+ def __init__(self, base):
+ self._base = base
+
+ @staticmethod
+ def _is_limited_frame(frame):
+ """Internal utility to determine if the frame is special or
+ limited."""
+ sal = frame.find_sal()
+
+ if (
+ not sal.symtab
+ or not sal.symtab.filename
+ or frame.type() == gdb.DUMMY_FRAME
+ or frame.type() == gdb.SIGTRAMP_FRAME
+ ):
+
+ return True
+
+ return False
+
+ def elided(self):
+ """Return any elided frames that this class might be
+ wrapping, or None."""
+ if hasattr(self._base, "elided"):
+ return self._base.elided()
+
+ return None
+
+ def function(self):
+ """Return the name of the frame's function or an address of
+ the function of the frame. First determine if this is a
+ special frame. If not, try to determine filename from GDB's
+ frame internal function API. Finally, if a name cannot be
+ determined return the address. If this function returns an
+ address, GDB will attempt to determine the function name from
+ its internal minimal symbols store (for example, for inferiors
+ without debug-info)."""
+
+ # Both gdb.Frame, and FrameDecorator have a method called
+ # "function", so determine which object this is.
+ if not isinstance(self._base, gdb.Frame):
+ if hasattr(self._base, "function"):
+ # If it is not a gdb.Frame, and there is already a
+ # "function" method, use that.
+ return self._base.function()
+
+ frame = self.inferior_frame()
+
+ if frame.type() == gdb.DUMMY_FRAME:
+ return "<function called from gdb>"
+ elif frame.type() == gdb.SIGTRAMP_FRAME:
+ return "<signal handler called>"
+
+ func = frame.function()
+
+ # If we cannot determine the function name, return the
+ # address. If GDB detects an integer value from this function
+ # it will attempt to find the function name from minimal
+ # symbols via its own internal functions.
+ if func is None:
+ pc = frame.pc()
+ return pc
+
+ return str(func)
+
+ def address(self):
+ """Return the address of the frame's pc"""
+
+ if hasattr(self._base, "address"):
+ return self._base.address()
+
+ frame = self.inferior_frame()
+ return frame.pc()
+
+ def filename(self):
+ """Return the filename associated with this frame, detecting
+ and returning the appropriate library name is this is a shared
+ library."""
+
+ if hasattr(self._base, "filename"):
+ return self._base.filename()
+
+ frame = self.inferior_frame()
+ sal = frame.find_sal()
+ if not sal.symtab or not sal.symtab.filename:
+ pc = frame.pc()
+ return gdb.solib_name(pc)
+ else:
+ return sal.symtab.filename
+
+ def frame_args(self):
+ """Return an iterable of frame arguments for this frame, if
+ any. The iterable object contains objects conforming with the
+ Symbol/Value interface. If there are no frame arguments, or
+ if this frame is deemed to be a special case, return None."""
+
+ if hasattr(self._base, "frame_args"):
+ return self._base.frame_args()
+
+ frame = self.inferior_frame()
+ if self._is_limited_frame(frame):
+ return None
+
+ args = FrameVars(frame)
+ return args.fetch_frame_args()
+
+ def frame_locals(self):
+ """Return an iterable of local variables for this frame, if
+ any. The iterable object contains objects conforming with the
+ Symbol/Value interface. If there are no frame locals, or if
+ this frame is deemed to be a special case, return None."""
+
+ if hasattr(self._base, "frame_locals"):
+ return self._base.frame_locals()
+
+ frame = self.inferior_frame()
+ if self._is_limited_frame(frame):
+ return None
+
+ args = FrameVars(frame)
+ return args.fetch_frame_locals()
+
+ def line(self):
+ """Return line number information associated with the frame's
+ pc. If symbol table/line information does not exist, or if
+ this frame is deemed to be a special case, return None"""
+
+ if hasattr(self._base, "line"):
+ return self._base.line()
+
+ frame = self.inferior_frame()
+ if self._is_limited_frame(frame):
+ return None
+
+ sal = frame.find_sal()
+ if sal:
+ return sal.line
+ else:
+ return None
+
+ def inferior_frame(self):
+ """Return the gdb.Frame underpinning this frame decorator."""
+
+ # If 'base' is a frame decorator, we want to call its inferior
+ # frame method. If '_base' is a gdb.Frame, just return that.
+ if hasattr(self._base, "inferior_frame"):
+ return self._base.inferior_frame()
+ return self._base
+
+
+class SymValueWrapper(object):
+ """A container class conforming to the Symbol/Value interface
+ which holds frame locals or frame arguments."""
+
+ def __init__(self, symbol, value):
+ self.sym = symbol
+ self.val = value
+
+ def value(self):
+ """Return the value associated with this symbol, or None"""
+ return self.val
+
+ def symbol(self):
+ """Return the symbol, or Python text, associated with this
+ symbol, or None"""
+ return self.sym
+
+
+class FrameVars(object):
+
+ """Utility class to fetch and store frame local variables, or
+ frame arguments."""
+
+ def __init__(self, frame):
+ self.frame = frame
+ self.symbol_class = {
+ gdb.SYMBOL_LOC_STATIC: True,
+ gdb.SYMBOL_LOC_REGISTER: True,
+ gdb.SYMBOL_LOC_ARG: True,
+ gdb.SYMBOL_LOC_REF_ARG: True,
+ gdb.SYMBOL_LOC_LOCAL: True,
+ gdb.SYMBOL_LOC_REGPARM_ADDR: True,
+ gdb.SYMBOL_LOC_COMPUTED: True,
+ }
+
+ def fetch_b(self, sym):
+ """Local utility method to determine if according to Symbol
+ type whether it should be included in the iterator. Not all
+ symbols are fetched, and only symbols that return
+ True from this method should be fetched."""
+
+ # SYM may be a string instead of a symbol in the case of
+ # synthetic local arguments or locals. If that is the case,
+ # always fetch.
+ if isinstance(sym, str):
+ return True
+
+ sym_type = sym.addr_class
+
+ return self.symbol_class.get(sym_type, False)
+
+ def fetch_frame_locals(self):
+ """Public utility method to fetch frame local variables for
+ the stored frame. Frame arguments are not fetched. If there
+ are no frame local variables, return an empty list."""
+ lvars = []
+
+ try:
+ block = self.frame.block()
+ except RuntimeError:
+ block = None
+
+ while block is not None:
+ if block.is_global or block.is_static:
+ break
+ for sym in block:
+ if sym.is_argument:
+ continue
+ if self.fetch_b(sym):
+ lvars.append(SymValueWrapper(sym, None))
+
+ block = block.superblock
+
+ return lvars
+
+ def fetch_frame_args(self):
+ """Public utility method to fetch frame arguments for the
+ stored frame. Frame arguments are the only type fetched. If
+ there are no frame argument variables, return an empty list."""
+
+ args = []
+
+ try:
+ block = self.frame.block()
+ except RuntimeError:
+ block = None
+
+ while block is not None:
+ if block.function is not None:
+ break
+ block = block.superblock
+
+ if block is not None:
+ for sym in block:
+ if not sym.is_argument:
+ continue
+ args.append(SymValueWrapper(sym, None))
+
+ return args
diff --git a/share/gdb/python/gdb/FrameIterator.py b/share/gdb/python/gdb/FrameIterator.py
new file mode 100644
index 0000000..190ec94
--- /dev/null
+++ b/share/gdb/python/gdb/FrameIterator.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2013-2023 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/>.
+
+
+class FrameIterator(object):
+ """A gdb.Frame iterator. Iterates over gdb.Frames or objects that
+ conform to that interface."""
+
+ def __init__(self, frame_obj):
+ """Initialize a FrameIterator.
+
+ Arguments:
+ frame_obj the starting frame."""
+
+ super(FrameIterator, self).__init__()
+ self.frame = frame_obj
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ """next implementation.
+
+ Returns:
+ The next oldest frame."""
+
+ result = self.frame
+ if result is None:
+ raise StopIteration
+ self.frame = result.older()
+ return result
+
+ # Python 3.x requires __next__(self) while Python 2.x requires
+ # next(self). Define next(self), and for Python 3.x create this
+ # wrapper.
+ def __next__(self):
+ return self.next()
diff --git a/share/gdb/python/gdb/__init__.py b/share/gdb/python/gdb/__init__.py
new file mode 100644
index 0000000..6f3f194
--- /dev/null
+++ b/share/gdb/python/gdb/__init__.py
@@ -0,0 +1,261 @@
+# Copyright (C) 2010-2023 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/>.
+
+import traceback
+import os
+import sys
+import _gdb
+from contextlib import contextmanager
+
+# Python 3 moved "reload"
+if sys.version_info >= (3, 4):
+ from importlib import reload
+else:
+ from imp import reload
+
+from _gdb import *
+
+# Historically, gdb.events was always available, so ensure it's
+# still available without an explicit import.
+import _gdbevents as events
+
+sys.modules["gdb.events"] = events
+
+
+class _GdbFile(object):
+ # These two are needed in Python 3
+ encoding = "UTF-8"
+ errors = "strict"
+
+ def __init__(self, stream):
+ self.stream = stream
+
+ def close(self):
+ # Do nothing.
+ return None
+
+ def isatty(self):
+ return False
+
+ def writelines(self, iterable):
+ for line in iterable:
+ self.write(line)
+
+ def flush(self):
+ flush(stream=self.stream)
+
+ def write(self, s):
+ write(s, stream=self.stream)
+
+
+sys.stdout = _GdbFile(STDOUT)
+
+sys.stderr = _GdbFile(STDERR)
+
+# Default prompt hook does nothing.
+prompt_hook = None
+
+# Ensure that sys.argv is set to something.
+# We do not use PySys_SetArgvEx because it did not appear until 2.6.6.
+sys.argv = [""]
+
+# Initial pretty printers.
+pretty_printers = []
+
+# Initial type printers.
+type_printers = []
+# Initial xmethod matchers.
+xmethods = []
+# Initial frame filters.
+frame_filters = {}
+# Initial frame unwinders.
+frame_unwinders = []
+
+
+def _execute_unwinders(pending_frame):
+ """Internal function called from GDB to execute all unwinders.
+
+ Runs each currently enabled unwinder until it finds the one that
+ can unwind given frame.
+
+ Arguments:
+ pending_frame: gdb.PendingFrame instance.
+
+ Returns:
+ Tuple with:
+
+ [0] gdb.UnwindInfo instance
+ [1] Name of unwinder that claimed the frame (type `str`)
+
+ or None, if no unwinder has claimed the frame.
+ """
+ for objfile in objfiles():
+ for unwinder in objfile.frame_unwinders:
+ if unwinder.enabled:
+ unwind_info = unwinder(pending_frame)
+ if unwind_info is not None:
+ return (unwind_info, unwinder.name)
+
+ for unwinder in current_progspace().frame_unwinders:
+ if unwinder.enabled:
+ unwind_info = unwinder(pending_frame)
+ if unwind_info is not None:
+ return (unwind_info, unwinder.name)
+
+ for unwinder in frame_unwinders:
+ if unwinder.enabled:
+ unwind_info = unwinder(pending_frame)
+ if unwind_info is not None:
+ return (unwind_info, unwinder.name)
+
+ return None
+
+
+def _execute_file(filepath):
+ """This function is used to replace Python 2's PyRun_SimpleFile.
+
+ Loads and executes the given file.
+
+ We could use the runpy module, but its documentation says:
+ "Furthermore, any functions and classes defined by the executed code are
+ not guaranteed to work correctly after a runpy function has returned."
+ """
+ globals = sys.modules["__main__"].__dict__
+ set_file = False
+ # Set file (if not set) so that the imported file can use it (e.g. to
+ # access file-relative paths). This matches what PyRun_SimpleFile does.
+ if not hasattr(globals, "__file__"):
+ globals["__file__"] = filepath
+ set_file = True
+ try:
+ with open(filepath, "rb") as file:
+ # We pass globals also as locals to match what Python does
+ # in PyRun_SimpleFile.
+ compiled = compile(file.read(), filepath, "exec")
+ exec(compiled, globals, globals)
+ finally:
+ if set_file:
+ del globals["__file__"]
+
+
+# Convenience variable to GDB's python directory
+PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
+
+# Auto-load all functions/commands.
+
+# Packages to auto-load.
+
+packages = ["function", "command", "printer"]
+
+# pkgutil.iter_modules is not available prior to Python 2.6. Instead,
+# manually iterate the list, collating the Python files in each module
+# path. Construct the module name, and import.
+
+
+def _auto_load_packages():
+ for package in packages:
+ location = os.path.join(os.path.dirname(__file__), package)
+ if os.path.exists(location):
+ py_files = filter(
+ lambda x: x.endswith(".py") and x != "__init__.py", os.listdir(location)
+ )
+
+ for py_file in py_files:
+ # Construct from foo.py, gdb.module.foo
+ modname = "%s.%s.%s" % (__name__, package, py_file[:-3])
+ try:
+ if modname in sys.modules:
+ # reload modules with duplicate names
+ reload(__import__(modname))
+ else:
+ __import__(modname)
+ except:
+ sys.stderr.write(traceback.format_exc() + "\n")
+
+
+_auto_load_packages()
+
+
+def GdbSetPythonDirectory(dir):
+ """Update sys.path, reload gdb and auto-load packages."""
+ global PYTHONDIR
+
+ try:
+ sys.path.remove(PYTHONDIR)
+ except ValueError:
+ pass
+ sys.path.insert(0, dir)
+
+ PYTHONDIR = dir
+
+ # note that reload overwrites the gdb module without deleting existing
+ # attributes
+ reload(__import__(__name__))
+ _auto_load_packages()
+
+
+def current_progspace():
+ "Return the current Progspace."
+ return selected_inferior().progspace
+
+
+def objfiles():
+ "Return a sequence of the current program space's objfiles."
+ return current_progspace().objfiles()
+
+
+def solib_name(addr):
+ """solib_name (Long) -> String.\n\
+Return the name of the shared library holding a given address, or None."""
+ return current_progspace().solib_name(addr)
+
+
+def block_for_pc(pc):
+ "Return the block containing the given pc value, or None."
+ return current_progspace().block_for_pc(pc)
+
+
+def find_pc_line(pc):
+ """find_pc_line (pc) -> Symtab_and_line.
+ Return the gdb.Symtab_and_line object corresponding to the pc value."""
+ return current_progspace().find_pc_line(pc)
+
+
+def set_parameter(name, value):
+ """Set the GDB parameter NAME to VALUE."""
+ # Handle the specific cases of None and booleans here, because
+ # gdb.parameter can return them, but they can't be passed to 'set'
+ # this way.
+ if value is None:
+ value = "unlimited"
+ elif isinstance(value, bool):
+ if value:
+ value = "on"
+ else:
+ value = "off"
+ 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 = parameter(name)
+ set_parameter(name, value)
+ try:
+ # Nothing that useful to return.
+ yield None
+ finally:
+ set_parameter(name, old_value)
diff --git a/share/gdb/python/gdb/command/__init__.py b/share/gdb/python/gdb/command/__init__.py
new file mode 100644
index 0000000..5f369bf
--- /dev/null
+++ b/share/gdb/python/gdb/command/__init__.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2010-2023 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/>.
diff --git a/share/gdb/python/gdb/command/explore.py b/share/gdb/python/gdb/command/explore.py
new file mode 100644
index 0000000..db72f33
--- /dev/null
+++ b/share/gdb/python/gdb/command/explore.py
@@ -0,0 +1,785 @@
+# GDB 'explore' command.
+# Copyright (C) 2012-2023 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/>.
+
+"""Implementation of the GDB 'explore' command using the GDB Python API."""
+
+import gdb
+import sys
+
+
+class Explorer(object):
+ """Internal class which invokes other explorers."""
+
+ # This map is filled by the Explorer.init_env() function
+ type_code_to_explorer_map = {}
+
+ _SCALAR_TYPE_LIST = (
+ gdb.TYPE_CODE_CHAR,
+ gdb.TYPE_CODE_INT,
+ gdb.TYPE_CODE_BOOL,
+ gdb.TYPE_CODE_FLT,
+ gdb.TYPE_CODE_VOID,
+ gdb.TYPE_CODE_ENUM,
+ )
+
+ @staticmethod
+ def guard_expr(expr):
+ length = len(expr)
+ guard = False
+
+ if expr[0] == "(" and expr[length - 1] == ")":
+ pass
+ else:
+ i = 0
+ while i < length:
+ c = expr[i]
+ if (
+ c == "_"
+ or ("a" <= c and c <= "z")
+ or ("A" <= c and c <= "Z")
+ or ("0" <= c and c <= "9")
+ ):
+ pass
+ else:
+ guard = True
+ break
+ i += 1
+
+ if guard:
+ return "(" + expr + ")"
+ else:
+ return expr
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Main function to explore an expression value.
+
+ Arguments:
+ expr: The expression string that is being explored.
+ value: The gdb.Value value of the expression.
+ is_child: Boolean value to indicate if the expression is a child.
+ An expression is a child if it is derived from the main
+ expression entered by the user. For example, if the user
+ entered an expression which evaluates to a struct, then
+ when exploring the fields of the struct, is_child is set
+ to True internally.
+
+ Returns:
+ No return value.
+ """
+ type_code = value.type.code
+ if type_code in Explorer.type_code_to_explorer_map:
+ explorer_class = Explorer.type_code_to_explorer_map[type_code]
+ while explorer_class.explore_expr(expr, value, is_child):
+ pass
+ else:
+ print("Explorer for type '%s' not yet available.\n" % str(value.type))
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Main function to explore a data type.
+
+ Arguments:
+ name: The string representing the path to the data type being
+ explored.
+ datatype: The gdb.Type value of the data type being explored.
+ is_child: Boolean value to indicate if the name is a child.
+ A name is a child if it is derived from the main name
+ entered by the user. For example, if the user entered
+ the name of struct type, then when exploring the fields
+ of the struct, is_child is set to True internally.
+
+ Returns:
+ No return value.
+ """
+ type_code = datatype.code
+ if type_code in Explorer.type_code_to_explorer_map:
+ explorer_class = Explorer.type_code_to_explorer_map[type_code]
+ while explorer_class.explore_type(name, datatype, is_child):
+ pass
+ else:
+ print("Explorer for type '%s' not yet available.\n" % str(datatype))
+
+ @staticmethod
+ def init_env():
+ """Initializes the Explorer environment.
+ This function should be invoked before starting any exploration. If
+ invoked before an exploration, it need not be invoked for subsequent
+ explorations.
+ """
+ Explorer.type_code_to_explorer_map = {
+ gdb.TYPE_CODE_CHAR: ScalarExplorer,
+ gdb.TYPE_CODE_INT: ScalarExplorer,
+ gdb.TYPE_CODE_BOOL: ScalarExplorer,
+ gdb.TYPE_CODE_FLT: ScalarExplorer,
+ gdb.TYPE_CODE_VOID: ScalarExplorer,
+ gdb.TYPE_CODE_ENUM: ScalarExplorer,
+ gdb.TYPE_CODE_STRUCT: CompoundExplorer,
+ gdb.TYPE_CODE_UNION: CompoundExplorer,
+ gdb.TYPE_CODE_PTR: PointerExplorer,
+ gdb.TYPE_CODE_REF: ReferenceExplorer,
+ gdb.TYPE_CODE_RVALUE_REF: ReferenceExplorer,
+ gdb.TYPE_CODE_TYPEDEF: TypedefExplorer,
+ gdb.TYPE_CODE_ARRAY: ArrayExplorer,
+ }
+
+ @staticmethod
+ def is_scalar_type(type):
+ """Checks whether a type is a scalar type.
+ A type is a scalar type of its type is
+ gdb.TYPE_CODE_CHAR or
+ gdb.TYPE_CODE_INT or
+ gdb.TYPE_CODE_BOOL or
+ gdb.TYPE_CODE_FLT or
+ gdb.TYPE_CODE_VOID or
+ gdb.TYPE_CODE_ENUM.
+
+ Arguments:
+ type: The type to be checked.
+
+ Returns:
+ 'True' if 'type' is a scalar type. 'False' otherwise.
+ """
+ return type.code in Explorer._SCALAR_TYPE_LIST
+
+ @staticmethod
+ def return_to_parent_value():
+ """A utility function which prints that the current exploration session
+ is returning to the parent value. Useful when exploring values.
+ """
+ print("\nReturning to parent value...\n")
+
+ @staticmethod
+ def return_to_parent_value_prompt():
+ """A utility function which prompts the user to press the 'enter' key
+ so that the exploration session can shift back to the parent value.
+ Useful when exploring values.
+ """
+ input("\nPress enter to return to parent value: ")
+
+ @staticmethod
+ def return_to_enclosing_type():
+ """A utility function which prints that the current exploration session
+ is returning to the enclosing type. Useful when exploring types.
+ """
+ print("\nReturning to enclosing type...\n")
+
+ @staticmethod
+ def return_to_enclosing_type_prompt():
+ """A utility function which prompts the user to press the 'enter' key
+ so that the exploration session can shift back to the enclosing type.
+ Useful when exploring types.
+ """
+ input("\nPress enter to return to enclosing type: ")
+
+
+class ScalarExplorer(object):
+ """Internal class used to explore scalar values."""
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Function to explore scalar values.
+ See Explorer.explore_expr and Explorer.is_scalar_type for more
+ information.
+ """
+ print("'%s' is a scalar value of type '%s'." % (expr, value.type))
+ print("%s = %s" % (expr, str(value)))
+
+ if is_child:
+ Explorer.return_to_parent_value_prompt()
+ Explorer.return_to_parent_value()
+
+ return False
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Function to explore scalar types.
+ See Explorer.explore_type and Explorer.is_scalar_type for more
+ information.
+ """
+ if datatype.code == gdb.TYPE_CODE_ENUM:
+ if is_child:
+ print("%s is of an enumerated type '%s'." % (name, str(datatype)))
+ else:
+ print("'%s' is an enumerated type." % name)
+ else:
+ if is_child:
+ print("%s is of a scalar type '%s'." % (name, str(datatype)))
+ else:
+ print("'%s' is a scalar type." % name)
+
+ if is_child:
+ Explorer.return_to_enclosing_type_prompt()
+ Explorer.return_to_enclosing_type()
+
+ return False
+
+
+class PointerExplorer(object):
+ """Internal class used to explore pointer values."""
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Function to explore pointer values.
+ See Explorer.explore_expr for more information.
+ """
+ print(
+ "'%s' is a pointer to a value of type '%s'"
+ % (expr, str(value.type.target()))
+ )
+ option = input(
+ "Continue exploring it as a pointer to a single " "value [y/n]: "
+ )
+ if option == "y":
+ deref_value = None
+ try:
+ deref_value = value.dereference()
+ str(deref_value)
+ except gdb.MemoryError:
+ print(
+ "'%s' a pointer pointing to an invalid memory " "location." % expr
+ )
+ if is_child:
+ Explorer.return_to_parent_value_prompt()
+ return False
+ Explorer.explore_expr(
+ "*%s" % Explorer.guard_expr(expr), deref_value, is_child
+ )
+ return False
+
+ option = input("Continue exploring it as a pointer to an " "array [y/n]: ")
+ if option == "y":
+ while True:
+ index = 0
+ try:
+ index = int(
+ input(
+ "Enter the index of the element you "
+ "want to explore in '%s': " % expr
+ )
+ )
+ except ValueError:
+ break
+ element_expr = "%s[%d]" % (Explorer.guard_expr(expr), index)
+ element = value[index]
+ try:
+ str(element)
+ except gdb.MemoryError:
+ print("Cannot read value at index %d." % index)
+ continue
+ Explorer.explore_expr(element_expr, element, True)
+ return False
+
+ if is_child:
+ Explorer.return_to_parent_value()
+ return False
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Function to explore pointer types.
+ See Explorer.explore_type for more information.
+ """
+ target_type = datatype.target()
+ print("\n%s is a pointer to a value of type '%s'." % (name, str(target_type)))
+
+ Explorer.explore_type("the pointee type of %s" % name, target_type, is_child)
+ return False
+
+
+class ReferenceExplorer(object):
+ """Internal class used to explore reference (TYPE_CODE_REF) values."""
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Function to explore array values.
+ See Explorer.explore_expr for more information.
+ """
+ referenced_value = value.referenced_value()
+ Explorer.explore_expr(expr, referenced_value, is_child)
+ return False
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Function to explore pointer types.
+ See Explorer.explore_type for more information.
+ """
+ target_type = datatype.target()
+ Explorer.explore_type(name, target_type, is_child)
+ return False
+
+
+class ArrayExplorer(object):
+ """Internal class used to explore arrays."""
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Function to explore array values.
+ See Explorer.explore_expr for more information.
+ """
+ target_type = value.type.target()
+ print("'%s' is an array of '%s'." % (expr, str(target_type)))
+ index = 0
+ try:
+ index = int(
+ input(
+ "Enter the index of the element you want to "
+ "explore in '%s': " % expr
+ )
+ )
+ except ValueError:
+ if is_child:
+ Explorer.return_to_parent_value()
+ return False
+
+ element = None
+ try:
+ element = value[index]
+ str(element)
+ except gdb.MemoryError:
+ print("Cannot read value at index %d." % index)
+ input("Press enter to continue... ")
+ return True
+
+ Explorer.explore_expr(
+ "%s[%d]" % (Explorer.guard_expr(expr), index), element, True
+ )
+ return True
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Function to explore array types.
+ See Explorer.explore_type for more information.
+ """
+ target_type = datatype.target()
+ print("%s is an array of '%s'." % (name, str(target_type)))
+
+ Explorer.explore_type("the array element of %s" % name, target_type, is_child)
+ return False
+
+
+class CompoundExplorer(object):
+ """Internal class used to explore struct, classes and unions."""
+
+ @staticmethod
+ def _print_fields(print_list):
+ """Internal function which prints the fields of a struct/class/union."""
+ max_field_name_length = 0
+ for pair in print_list:
+ if max_field_name_length < len(pair[0]):
+ max_field_name_length = len(pair[0])
+
+ for pair in print_list:
+ print(" %*s = %s" % (max_field_name_length, pair[0], pair[1]))
+
+ @staticmethod
+ def _get_real_field_count(fields):
+ real_field_count = 0
+ for field in fields:
+ if not field.artificial:
+ real_field_count = real_field_count + 1
+
+ return real_field_count
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Function to explore structs/classes and union values.
+ See Explorer.explore_expr for more information.
+ """
+ datatype = value.type
+ type_code = datatype.code
+ fields = datatype.fields()
+
+ if type_code == gdb.TYPE_CODE_STRUCT:
+ type_desc = "struct/class"
+ else:
+ type_desc = "union"
+
+ if CompoundExplorer._get_real_field_count(fields) == 0:
+ print(
+ "The value of '%s' is a %s of type '%s' with no fields."
+ % (expr, type_desc, str(value.type))
+ )
+ if is_child:
+ Explorer.return_to_parent_value_prompt()
+ return False
+
+ print(
+ "The value of '%s' is a %s of type '%s' with the following "
+ "fields:\n" % (expr, type_desc, str(value.type))
+ )
+
+ has_explorable_fields = False
+ choice_to_compound_field_map = {}
+ current_choice = 0
+ print_list = []
+ for field in fields:
+ if field.artificial:
+ continue
+ field_full_name = Explorer.guard_expr(expr) + "." + field.name
+ if field.is_base_class:
+ field_value = value.cast(field.type)
+ else:
+ field_value = value[field.name]
+ literal_value = ""
+ if type_code == gdb.TYPE_CODE_UNION:
+ literal_value = "<Enter %d to explore this field of type " "'%s'>" % (
+ current_choice,
+ str(field.type),
+ )
+ has_explorable_fields = True
+ else:
+ if Explorer.is_scalar_type(field.type):
+ literal_value = "%s .. (Value of type '%s')" % (
+ str(field_value),
+ str(field.type),
+ )
+ else:
+ if field.is_base_class:
+ field_desc = "base class"
+ else:
+ field_desc = "field"
+ literal_value = "<Enter %d to explore this %s of type " "'%s'>" % (
+ current_choice,
+ field_desc,
+ str(field.type),
+ )
+ has_explorable_fields = True
+
+ choice_to_compound_field_map[str(current_choice)] = (
+ field_full_name,
+ field_value,
+ )
+ current_choice = current_choice + 1
+
+ print_list.append((field.name, literal_value))
+
+ CompoundExplorer._print_fields(print_list)
+ print("")
+
+ if has_explorable_fields:
+ choice = input("Enter the field number of choice: ")
+ if choice in choice_to_compound_field_map:
+ Explorer.explore_expr(
+ choice_to_compound_field_map[choice][0],
+ choice_to_compound_field_map[choice][1],
+ True,
+ )
+ return True
+ else:
+ if is_child:
+ Explorer.return_to_parent_value()
+ else:
+ if is_child:
+ Explorer.return_to_parent_value_prompt()
+
+ return False
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Function to explore struct/class and union types.
+ See Explorer.explore_type for more information.
+ """
+ type_code = datatype.code
+ type_desc = ""
+ if type_code == gdb.TYPE_CODE_STRUCT:
+ type_desc = "struct/class"
+ else:
+ type_desc = "union"
+
+ fields = datatype.fields()
+ if CompoundExplorer._get_real_field_count(fields) == 0:
+ if is_child:
+ print(
+ "%s is a %s of type '%s' with no fields."
+ % (name, type_desc, str(datatype))
+ )
+ Explorer.return_to_enclosing_type_prompt()
+ else:
+ print("'%s' is a %s with no fields." % (name, type_desc))
+ return False
+
+ if is_child:
+ print(
+ "%s is a %s of type '%s' "
+ "with the following fields:\n" % (name, type_desc, str(datatype))
+ )
+ else:
+ print("'%s' is a %s with the following " "fields:\n" % (name, type_desc))
+
+ current_choice = 0
+ choice_to_compound_field_map = {}
+ print_list = []
+ for field in fields:
+ if field.artificial:
+ continue
+ if field.is_base_class:
+ field_desc = "base class"
+ else:
+ field_desc = "field"
+ rhs = "<Enter %d to explore this %s of type '%s'>" % (
+ current_choice,
+ field_desc,
+ str(field.type),
+ )
+ print_list.append((field.name, rhs))
+ choice_to_compound_field_map[str(current_choice)] = (
+ field.name,
+ field.type,
+ field_desc,
+ )
+ current_choice = current_choice + 1
+
+ CompoundExplorer._print_fields(print_list)
+ print("")
+
+ if len(choice_to_compound_field_map) > 0:
+ choice = input("Enter the field number of choice: ")
+ if choice in choice_to_compound_field_map:
+ if is_child:
+ new_name = "%s '%s' of %s" % (
+ choice_to_compound_field_map[choice][2],
+ choice_to_compound_field_map[choice][0],
+ name,
+ )
+ else:
+ new_name = "%s '%s' of '%s'" % (
+ choice_to_compound_field_map[choice][2],
+ choice_to_compound_field_map[choice][0],
+ name,
+ )
+ Explorer.explore_type(
+ new_name, choice_to_compound_field_map[choice][1], True
+ )
+ return True
+ else:
+ if is_child:
+ Explorer.return_to_enclosing_type()
+ else:
+ if is_child:
+ Explorer.return_to_enclosing_type_prompt()
+
+ return False
+
+
+class TypedefExplorer(object):
+ """Internal class used to explore values whose type is a typedef."""
+
+ @staticmethod
+ def explore_expr(expr, value, is_child):
+ """Function to explore typedef values.
+ See Explorer.explore_expr for more information.
+ """
+ actual_type = value.type.strip_typedefs()
+ print(
+ "The value of '%s' is of type '%s' "
+ "which is a typedef of type '%s'"
+ % (expr, str(value.type), str(actual_type))
+ )
+
+ Explorer.explore_expr(expr, value.cast(actual_type), is_child)
+ return False
+
+ @staticmethod
+ def explore_type(name, datatype, is_child):
+ """Function to explore typedef types.
+ See Explorer.explore_type for more information.
+ """
+ actual_type = datatype.strip_typedefs()
+ if is_child:
+ print(
+ "The type of %s is a typedef of type '%s'." % (name, str(actual_type))
+ )
+ else:
+ print("The type '%s' is a typedef of type '%s'." % (name, str(actual_type)))
+
+ Explorer.explore_type(name, actual_type, is_child)
+ return False
+
+
+class ExploreUtils(object):
+ """Internal class which provides utilities for the main command classes."""
+
+ @staticmethod
+ def check_args(name, arg_str):
+ """Utility to check if adequate number of arguments are passed to an
+ explore command.
+
+ Arguments:
+ name: The name of the explore command.
+ arg_str: The argument string passed to the explore command.
+
+ Returns:
+ True if adequate arguments are passed, false otherwise.
+
+ Raises:
+ gdb.GdbError if adequate arguments are not passed.
+ """
+ if len(arg_str) < 1:
+ raise gdb.GdbError("ERROR: '%s' requires an argument." % name)
+ return False
+ else:
+ return True
+
+ @staticmethod
+ def get_type_from_str(type_str):
+ """A utility function to deduce the gdb.Type value from a string
+ representing the type.
+
+ Arguments:
+ type_str: The type string from which the gdb.Type value should be
+ deduced.
+
+ Returns:
+ The deduced gdb.Type value if possible, None otherwise.
+ """
+ try:
+ # Assume the current language to be C/C++ and make a try.
+ return gdb.parse_and_eval("(%s *)0" % type_str).type.target()
+ except RuntimeError:
+ # If assumption of current language to be C/C++ was wrong, then
+ # lookup the type using the API.
+ try:
+ return gdb.lookup_type(type_str)
+ except RuntimeError:
+ return None
+
+ @staticmethod
+ def get_value_from_str(value_str):
+ """A utility function to deduce the gdb.Value value from a string
+ representing the value.
+
+ Arguments:
+ value_str: The value string from which the gdb.Value value should
+ be deduced.
+
+ Returns:
+ The deduced gdb.Value value if possible, None otherwise.
+ """
+ try:
+ return gdb.parse_and_eval(value_str)
+ except RuntimeError:
+ return None
+
+
+class ExploreCommand(gdb.Command):
+ """Explore a value or a type valid in the current context.
+
+ Usage: explore ARG
+
+ - ARG is either a valid expression or a type name.
+ - At any stage of exploration, hit the return key (instead of a
+ choice, if any) to return to the enclosing type or value."""
+
+ def __init__(self):
+ super(ExploreCommand, self).__init__(
+ name="explore", command_class=gdb.COMMAND_DATA, prefix=True
+ )
+
+ def invoke(self, arg_str, from_tty):
+ if ExploreUtils.check_args("explore", arg_str) is False:
+ return
+
+ # Check if it is a value
+ value = ExploreUtils.get_value_from_str(arg_str)
+ if value is not None:
+ Explorer.explore_expr(arg_str, value, False)
+ return
+
+ # If it is not a value, check if it is a type
+ datatype = ExploreUtils.get_type_from_str(arg_str)
+ if datatype is not None:
+ Explorer.explore_type(arg_str, datatype, False)
+ return
+
+ # If it is neither a value nor a type, raise an error.
+ raise gdb.GdbError(
+ (
+ "'%s' neither evaluates to a value nor is a type "
+ "in the current context." % arg_str
+ )
+ )
+
+
+class ExploreValueCommand(gdb.Command):
+ """Explore value of an expression valid in the current context.
+
+ Usage: explore value ARG
+
+ - ARG is a valid expression.
+ - At any stage of exploration, hit the return key (instead of a
+ choice, if any) to return to the enclosing value."""
+
+ def __init__(self):
+ super(ExploreValueCommand, self).__init__(
+ name="explore value", command_class=gdb.COMMAND_DATA
+ )
+
+ def invoke(self, arg_str, from_tty):
+ if ExploreUtils.check_args("explore value", arg_str) is False:
+ return
+
+ value = ExploreUtils.get_value_from_str(arg_str)
+ if value is None:
+ raise gdb.GdbError(
+ (
+ " '%s' does not evaluate to a value in the current "
+ "context." % arg_str
+ )
+ )
+ return
+
+ Explorer.explore_expr(arg_str, value, False)
+
+
+class ExploreTypeCommand(gdb.Command):
+ """Explore a type or the type of an expression.
+
+ Usage: explore type ARG
+
+ - ARG is a valid expression or a type name.
+ - At any stage of exploration, hit the return key (instead of a
+ choice, if any) to return to the enclosing type."""
+
+ def __init__(self):
+ super(ExploreTypeCommand, self).__init__(
+ name="explore type", command_class=gdb.COMMAND_DATA
+ )
+
+ def invoke(self, arg_str, from_tty):
+ if ExploreUtils.check_args("explore type", arg_str) is False:
+ return
+
+ datatype = ExploreUtils.get_type_from_str(arg_str)
+ if datatype is not None:
+ Explorer.explore_type(arg_str, datatype, False)
+ return
+
+ value = ExploreUtils.get_value_from_str(arg_str)
+ if value is not None:
+ print("'%s' is of type '%s'." % (arg_str, str(value.type)))
+ Explorer.explore_type(str(value.type), value.type, False)
+ return
+
+ raise gdb.GdbError(
+ ("'%s' is not a type or value in the current " "context." % arg_str)
+ )
+
+
+Explorer.init_env()
+
+ExploreCommand()
+ExploreValueCommand()
+ExploreTypeCommand()
diff --git a/share/gdb/python/gdb/command/frame_filters.py b/share/gdb/python/gdb/command/frame_filters.py
new file mode 100644
index 0000000..0fca0e7
--- /dev/null
+++ b/share/gdb/python/gdb/command/frame_filters.py
@@ -0,0 +1,479 @@
+# Frame-filter commands.
+# Copyright (C) 2013-2023 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/>.
+
+"""GDB commands for working with frame-filters."""
+
+import sys
+import gdb
+import gdb.frames
+
+# GDB Commands.
+class SetFilterPrefixCmd(gdb.Command):
+ """Prefix command for 'set' frame-filter related operations."""
+
+ def __init__(self):
+ super(SetFilterPrefixCmd, self).__init__(
+ "set frame-filter", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True
+ )
+
+
+class ShowFilterPrefixCmd(gdb.Command):
+ """Prefix command for 'show' frame-filter related operations."""
+
+ def __init__(self):
+ super(ShowFilterPrefixCmd, self).__init__(
+ "show frame-filter", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True
+ )
+
+
+class InfoFrameFilter(gdb.Command):
+ """List all registered Python frame-filters.
+
+ Usage: info frame-filters"""
+
+ def __init__(self):
+ super(InfoFrameFilter, self).__init__("info frame-filter", gdb.COMMAND_DATA)
+
+ @staticmethod
+ def enabled_string(state):
+ """Return "Yes" if filter is enabled, otherwise "No"."""
+ if state:
+ return "Yes"
+ else:
+ return "No"
+
+ def print_list(self, title, frame_filters, blank_line):
+ sorted_frame_filters = sorted(
+ frame_filters.items(),
+ key=lambda i: gdb.frames.get_priority(i[1]),
+ reverse=True,
+ )
+
+ if len(sorted_frame_filters) == 0:
+ return 0
+
+ print(title)
+ print(" Priority Enabled Name")
+ for frame_filter in sorted_frame_filters:
+ name = frame_filter[0]
+ try:
+ priority = "{:<8}".format(str(gdb.frames.get_priority(frame_filter[1])))
+ enabled = "{:<7}".format(
+ self.enabled_string(gdb.frames.get_enabled(frame_filter[1]))
+ )
+ print(" %s %s %s" % (priority, enabled, name))
+ except Exception:
+ e = sys.exc_info()[1]
+ print(" Error printing filter '" + name + "': " + str(e))
+ if blank_line:
+ print("")
+ return 1
+
+ def invoke(self, arg, from_tty):
+ any_printed = self.print_list("global frame-filters:", gdb.frame_filters, True)
+
+ cp = gdb.current_progspace()
+ any_printed += self.print_list(
+ "progspace %s frame-filters:" % cp.filename, cp.frame_filters, True
+ )
+
+ for objfile in gdb.objfiles():
+ any_printed += self.print_list(
+ "objfile %s frame-filters:" % objfile.filename,
+ objfile.frame_filters,
+ False,
+ )
+
+ if any_printed == 0:
+ print("No frame filters.")
+
+
+# Internal enable/disable functions.
+
+
+def _enable_parse_arg(cmd_name, arg):
+ """Internal worker function to take an argument from
+ enable/disable and return a tuple of arguments.
+
+ Arguments:
+ cmd_name: Name of the command invoking this function.
+ args: The argument as a string.
+
+ Returns:
+ A tuple containing the dictionary, and the argument, or just
+ the dictionary in the case of "all".
+ """
+
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc == 0:
+ raise gdb.GdbError(cmd_name + " requires an argument")
+ if argv[0] == "all":
+ if argc > 1:
+ raise gdb.GdbError(
+ cmd_name + ": with 'all' " "you may not specify a filter."
+ )
+ elif argc != 2:
+ raise gdb.GdbError(cmd_name + " takes exactly two arguments.")
+
+ return argv
+
+
+def _do_enable_frame_filter(command_tuple, flag):
+ """Worker for enabling/disabling frame_filters.
+
+ Arguments:
+ command_type: A tuple with the first element being the
+ frame filter dictionary, and the second being
+ the frame filter name.
+ flag: True for Enable, False for Disable.
+ """
+
+ list_op = command_tuple[0]
+ op_list = gdb.frames.return_list(list_op)
+
+ if list_op == "all":
+ for item in op_list:
+ gdb.frames.set_enabled(item, flag)
+ else:
+ frame_filter = command_tuple[1]
+ try:
+ ff = op_list[frame_filter]
+ except KeyError:
+ msg = "frame-filter '" + str(frame_filter) + "' not found."
+ raise gdb.GdbError(msg)
+
+ gdb.frames.set_enabled(ff, flag)
+
+
+def _complete_frame_filter_list(text, word, all_flag):
+ """Worker for frame filter dictionary name completion.
+
+ Arguments:
+ text: The full text of the command line.
+ word: The most recent word of the command line.
+ all_flag: Whether to include the word "all" in completion.
+
+ Returns:
+ A list of suggested frame filter dictionary name completions
+ from text/word analysis. This list can be empty when there
+ are no suggestions for completion.
+ """
+ if all_flag:
+ filter_locations = ["all", "global", "progspace"]
+ else:
+ filter_locations = ["global", "progspace"]
+ for objfile in gdb.objfiles():
+ filter_locations.append(objfile.filename)
+
+ # If the user just asked for completions with no completion
+ # hints, just return all the frame filter dictionaries we know
+ # about.
+ if text == "":
+ return filter_locations
+
+ # Otherwise filter on what we know.
+ flist = filter(lambda x, y=text: x.startswith(y), filter_locations)
+
+ # If we only have one completion, complete it and return it.
+ if len(flist) == 1:
+ flist[0] = flist[0][len(text) - len(word) :]
+
+ # Otherwise, return an empty list, or a list of frame filter
+ # dictionaries that the previous filter operation returned.
+ return flist
+
+
+def _complete_frame_filter_name(word, printer_dict):
+ """Worker for frame filter name completion.
+
+ Arguments:
+
+ word: The most recent word of the command line.
+
+ printer_dict: The frame filter dictionary to search for frame
+ filter name completions.
+
+ Returns: A list of suggested frame filter name completions
+ from word analysis of the frame filter dictionary. This list
+ can be empty when there are no suggestions for completion.
+ """
+
+ printer_keys = printer_dict.keys()
+ if word == "":
+ return printer_keys
+
+ flist = filter(lambda x, y=word: x.startswith(y), printer_keys)
+ return flist
+
+
+class EnableFrameFilter(gdb.Command):
+ """GDB command to enable the specified frame-filter.
+
+ Usage: enable frame-filter DICTIONARY [NAME]
+
+ DICTIONARY is the name of the frame filter dictionary on which to
+ operate. If dictionary is set to "all", perform operations on all
+ dictionaries. Named dictionaries are: "global" for the global
+ frame filter dictionary, "progspace" for the program space's frame
+ filter dictionary. If either all, or the two named dictionaries
+ are not specified, the dictionary name is assumed to be the name
+ of an "objfile" -- a shared library or an executable.
+
+ NAME matches the name of the frame-filter to operate on."""
+
+ def __init__(self):
+ super(EnableFrameFilter, self).__init__("enable frame-filter", gdb.COMMAND_DATA)
+
+ def complete(self, text, word):
+ """Completion function for both frame filter dictionary, and
+ frame filter name."""
+ if text.count(" ") == 0:
+ return _complete_frame_filter_list(text, word, True)
+ else:
+ printer_list = gdb.frames.return_list(text.split()[0].rstrip())
+ return _complete_frame_filter_name(word, printer_list)
+
+ def invoke(self, arg, from_tty):
+ command_tuple = _enable_parse_arg("enable frame-filter", arg)
+ _do_enable_frame_filter(command_tuple, True)
+
+
+class DisableFrameFilter(gdb.Command):
+ """GDB command to disable the specified frame-filter.
+
+ Usage: disable frame-filter DICTIONARY [NAME]
+
+ DICTIONARY is the name of the frame filter dictionary on which to
+ operate. If dictionary is set to "all", perform operations on all
+ dictionaries. Named dictionaries are: "global" for the global
+ frame filter dictionary, "progspace" for the program space's frame
+ filter dictionary. If either all, or the two named dictionaries
+ are not specified, the dictionary name is assumed to be the name
+ of an "objfile" -- a shared library or an executable.
+
+ NAME matches the name of the frame-filter to operate on."""
+
+ def __init__(self):
+ super(DisableFrameFilter, self).__init__(
+ "disable frame-filter", gdb.COMMAND_DATA
+ )
+
+ def complete(self, text, word):
+ """Completion function for both frame filter dictionary, and
+ frame filter name."""
+ if text.count(" ") == 0:
+ return _complete_frame_filter_list(text, word, True)
+ else:
+ printer_list = gdb.frames.return_list(text.split()[0].rstrip())
+ return _complete_frame_filter_name(word, printer_list)
+
+ def invoke(self, arg, from_tty):
+ command_tuple = _enable_parse_arg("disable frame-filter", arg)
+ _do_enable_frame_filter(command_tuple, False)
+
+
+class SetFrameFilterPriority(gdb.Command):
+ """GDB command to set the priority of the specified frame-filter.
+
+ Usage: set frame-filter priority DICTIONARY NAME PRIORITY
+
+ DICTIONARY is the name of the frame filter dictionary on which to
+ operate. Named dictionaries are: "global" for the global frame
+ filter dictionary, "progspace" for the program space's framefilter
+ dictionary. If either of these two are not specified, the
+ dictionary name is assumed to be the name of an "objfile" -- a
+ shared library or an executable.
+
+ NAME matches the name of the frame filter to operate on.
+
+ PRIORITY is the an integer to assign the new priority to the frame
+ filter."""
+
+ def __init__(self):
+ super(SetFrameFilterPriority, self).__init__(
+ "set frame-filter " "priority", gdb.COMMAND_DATA
+ )
+
+ def _parse_pri_arg(self, arg):
+ """Internal worker to parse a priority from a tuple.
+
+ Arguments:
+ arg: Tuple which contains the arguments from the command.
+
+ Returns:
+ A tuple containing the dictionary, name and priority from
+ the arguments.
+
+ Raises:
+ gdb.GdbError: An error parsing the arguments.
+ """
+
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc != 3:
+ print("set frame-filter priority " "takes exactly three arguments.")
+ return None
+
+ return argv
+
+ def _set_filter_priority(self, command_tuple):
+ """Internal worker for setting priority of frame-filters, by
+ parsing a tuple and calling _set_priority with the parsed
+ tuple.
+
+ Arguments:
+ command_tuple: Tuple which contains the arguments from the
+ command.
+ """
+
+ list_op = command_tuple[0]
+ frame_filter = command_tuple[1]
+
+ # GDB returns arguments as a string, so convert priority to
+ # a number.
+ priority = int(command_tuple[2])
+
+ op_list = gdb.frames.return_list(list_op)
+
+ try:
+ ff = op_list[frame_filter]
+ except KeyError:
+ msg = "frame-filter '" + str(frame_filter) + "' not found."
+ raise gdb.GdbError(msg)
+
+ gdb.frames.set_priority(ff, priority)
+
+ def complete(self, text, word):
+ """Completion function for both frame filter dictionary, and
+ frame filter name."""
+ if text.count(" ") == 0:
+ return _complete_frame_filter_list(text, word, False)
+ else:
+ printer_list = gdb.frames.return_list(text.split()[0].rstrip())
+ return _complete_frame_filter_name(word, printer_list)
+
+ def invoke(self, arg, from_tty):
+ command_tuple = self._parse_pri_arg(arg)
+ if command_tuple is not None:
+ self._set_filter_priority(command_tuple)
+
+
+class ShowFrameFilterPriority(gdb.Command):
+ """GDB command to show the priority of the specified frame-filter.
+
+ Usage: show frame-filter priority DICTIONARY NAME
+
+ DICTIONARY is the name of the frame filter dictionary on which to
+ operate. Named dictionaries are: "global" for the global frame
+ filter dictionary, "progspace" for the program space's framefilter
+ dictionary. If either of these two are not specified, the
+ dictionary name is assumed to be the name of an "objfile" -- a
+ shared library or an executable.
+
+ NAME matches the name of the frame-filter to operate on."""
+
+ def __init__(self):
+ super(ShowFrameFilterPriority, self).__init__(
+ "show frame-filter " "priority", gdb.COMMAND_DATA
+ )
+
+ def _parse_pri_arg(self, arg):
+ """Internal worker to parse a dictionary and name from a
+ tuple.
+
+ Arguments:
+ arg: Tuple which contains the arguments from the command.
+
+ Returns:
+ A tuple containing the dictionary, and frame filter name.
+
+ Raises:
+ gdb.GdbError: An error parsing the arguments.
+ """
+
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc != 2:
+ print("show frame-filter priority " "takes exactly two arguments.")
+ return None
+
+ return argv
+
+ def get_filter_priority(self, frame_filters, name):
+ """Worker for retrieving the priority of frame_filters.
+
+ Arguments:
+ frame_filters: Name of frame filter dictionary.
+ name: object to select printers.
+
+ Returns:
+ The priority of the frame filter.
+
+ Raises:
+ gdb.GdbError: A frame filter cannot be found.
+ """
+
+ op_list = gdb.frames.return_list(frame_filters)
+
+ try:
+ ff = op_list[name]
+ except KeyError:
+ msg = "frame-filter '" + str(name) + "' not found."
+ raise gdb.GdbError(msg)
+
+ return gdb.frames.get_priority(ff)
+
+ def complete(self, text, word):
+ """Completion function for both frame filter dictionary, and
+ frame filter name."""
+
+ if text.count(" ") == 0:
+ return _complete_frame_filter_list(text, word, False)
+ else:
+ printer_list = frame._return_list(text.split()[0].rstrip())
+ return _complete_frame_filter_name(word, printer_list)
+
+ def invoke(self, arg, from_tty):
+ command_tuple = self._parse_pri_arg(arg)
+ if command_tuple is None:
+ return
+ filter_name = command_tuple[1]
+ list_name = command_tuple[0]
+ try:
+ priority = self.get_filter_priority(list_name, filter_name)
+ except Exception:
+ e = sys.exc_info()[1]
+ print("Error printing filter priority for '" + name + "':" + str(e))
+ else:
+ print(
+ "Priority of filter '"
+ + filter_name
+ + "' in list '"
+ + list_name
+ + "' is: "
+ + str(priority)
+ )
+
+
+# Register commands
+SetFilterPrefixCmd()
+ShowFilterPrefixCmd()
+InfoFrameFilter()
+EnableFrameFilter()
+DisableFrameFilter()
+SetFrameFilterPriority()
+ShowFrameFilterPriority()
diff --git a/share/gdb/python/gdb/command/pretty_printers.py b/share/gdb/python/gdb/command/pretty_printers.py
new file mode 100644
index 0000000..a3b582e
--- /dev/null
+++ b/share/gdb/python/gdb/command/pretty_printers.py
@@ -0,0 +1,395 @@
+# Pretty-printer commands.
+# Copyright (C) 2010-2023 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/>.
+
+"""GDB commands for working with pretty-printers."""
+
+import copy
+import gdb
+import re
+
+
+def parse_printer_regexps(arg):
+ """Internal utility to parse a pretty-printer command argv.
+
+ Arguments:
+ arg: The arguments to the command. The format is:
+ [object-regexp [name-regexp]].
+ Individual printers in a collection are named as
+ printer-name;subprinter-name.
+
+ Returns:
+ The result is a 3-tuple of compiled regular expressions, except that
+ the resulting compiled subprinter regexp is None if not provided.
+
+ Raises:
+ SyntaxError: an error processing ARG
+ """
+
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ object_regexp = "" # match everything
+ name_regexp = "" # match everything
+ subname_regexp = None
+ if argc > 3:
+ raise SyntaxError("too many arguments")
+ if argc >= 1:
+ object_regexp = argv[0]
+ if argc >= 2:
+ name_subname = argv[1].split(";", 1)
+ name_regexp = name_subname[0]
+ if len(name_subname) == 2:
+ subname_regexp = name_subname[1]
+ # That re.compile raises SyntaxError was determined empirically.
+ # We catch it and reraise it to provide a slightly more useful
+ # error message for the user.
+ try:
+ object_re = re.compile(object_regexp)
+ except SyntaxError:
+ raise SyntaxError("invalid object regexp: %s" % object_regexp)
+ try:
+ name_re = re.compile(name_regexp)
+ except SyntaxError:
+ raise SyntaxError("invalid name regexp: %s" % name_regexp)
+ if subname_regexp is not None:
+ try:
+ subname_re = re.compile(subname_regexp)
+ except SyntaxError:
+ raise SyntaxError("invalid subname regexp: %s" % subname_regexp)
+ else:
+ subname_re = None
+ return (object_re, name_re, subname_re)
+
+
+def printer_enabled_p(printer):
+ """Internal utility to see if printer (or subprinter) is enabled."""
+ if hasattr(printer, "enabled"):
+ return printer.enabled
+ else:
+ return True
+
+
+class InfoPrettyPrinter(gdb.Command):
+ """GDB command to list all registered pretty-printers.
+
+ Usage: info pretty-printer [OBJECT-REGEXP [NAME-REGEXP]]
+
+ OBJECT-REGEXP is a regular expression matching the objects to list.
+ Objects are "global", the program space's file, and the objfiles within
+ that program space.
+
+ NAME-REGEXP matches the name of the pretty-printer.
+ Individual printers in a collection are named as
+ printer-name;subprinter-name."""
+
+ def __init__(self):
+ super(InfoPrettyPrinter, self).__init__("info pretty-printer", gdb.COMMAND_DATA)
+
+ @staticmethod
+ def enabled_string(printer):
+ """Return "" if PRINTER is enabled, otherwise " [disabled]"."""
+ if printer_enabled_p(printer):
+ return ""
+ else:
+ return " [disabled]"
+
+ @staticmethod
+ def printer_name(printer):
+ """Return the printer's name."""
+ if hasattr(printer, "name"):
+ return printer.name
+ if hasattr(printer, "__name__"):
+ return printer.__name__
+ # This "shouldn't happen", but the public API allows for
+ # direct additions to the pretty-printer list, and we shouldn't
+ # crash because someone added a bogus printer.
+ # Plus we want to give the user a way to list unknown printers.
+ return "unknown"
+
+ def list_pretty_printers(self, pretty_printers, name_re, subname_re):
+ """Print a list of pretty-printers."""
+ # A potential enhancement is to provide an option to list printers in
+ # "lookup order" (i.e. unsorted).
+ sorted_pretty_printers = sorted(
+ copy.copy(pretty_printers), key=self.printer_name
+ )
+ for printer in sorted_pretty_printers:
+ name = self.printer_name(printer)
+ enabled = self.enabled_string(printer)
+ if name_re.match(name):
+ print(" %s%s" % (name, enabled))
+ if hasattr(printer, "subprinters") and printer.subprinters is not None:
+ sorted_subprinters = sorted(
+ copy.copy(printer.subprinters), key=self.printer_name
+ )
+ for subprinter in sorted_subprinters:
+ if not subname_re or subname_re.match(subprinter.name):
+ print(
+ " %s%s"
+ % (subprinter.name, self.enabled_string(subprinter))
+ )
+
+ def invoke1(
+ self, title, printer_list, obj_name_to_match, object_re, name_re, subname_re
+ ):
+ """Subroutine of invoke to simplify it."""
+ if printer_list and object_re.match(obj_name_to_match):
+ print(title)
+ self.list_pretty_printers(printer_list, name_re, subname_re)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ (object_re, name_re, subname_re) = parse_printer_regexps(arg)
+ self.invoke1(
+ "global pretty-printers:",
+ gdb.pretty_printers,
+ "global",
+ object_re,
+ name_re,
+ subname_re,
+ )
+ cp = gdb.current_progspace()
+ self.invoke1(
+ "progspace %s pretty-printers:" % cp.filename,
+ cp.pretty_printers,
+ "progspace",
+ object_re,
+ name_re,
+ subname_re,
+ )
+ for objfile in gdb.objfiles():
+ self.invoke1(
+ "objfile %s pretty-printers:" % objfile.filename,
+ objfile.pretty_printers,
+ objfile.filename,
+ object_re,
+ name_re,
+ subname_re,
+ )
+
+
+def count_enabled_printers(pretty_printers):
+ """Return a 2-tuple of number of enabled and total printers."""
+ enabled = 0
+ total = 0
+ for printer in pretty_printers:
+ if hasattr(printer, "subprinters") and printer.subprinters is not None:
+ if printer_enabled_p(printer):
+ for subprinter in printer.subprinters:
+ if printer_enabled_p(subprinter):
+ enabled += 1
+ total += len(printer.subprinters)
+ else:
+ if printer_enabled_p(printer):
+ enabled += 1
+ total += 1
+ return (enabled, total)
+
+
+def count_all_enabled_printers():
+ """Return a 2-tuble of the enabled state and total number of all printers.
+ This includes subprinters.
+ """
+ enabled_count = 0
+ total_count = 0
+ (t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers)
+ enabled_count += t_enabled
+ total_count += t_total
+ (t_enabled, t_total) = count_enabled_printers(
+ gdb.current_progspace().pretty_printers
+ )
+ enabled_count += t_enabled
+ total_count += t_total
+ for objfile in gdb.objfiles():
+ (t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers)
+ enabled_count += t_enabled
+ total_count += t_total
+ return (enabled_count, total_count)
+
+
+def pluralize(text, n, suffix="s"):
+ """Return TEXT pluralized if N != 1."""
+ if n != 1:
+ return "%s%s" % (text, suffix)
+ else:
+ return text
+
+
+def show_pretty_printer_enabled_summary():
+ """Print the number of printers enabled/disabled.
+ We count subprinters individually.
+ """
+ (enabled_count, total_count) = count_all_enabled_printers()
+ print("%d of %d printers enabled" % (enabled_count, total_count))
+
+
+def do_enable_pretty_printer_1(pretty_printers, name_re, subname_re, flag):
+ """Worker for enabling/disabling pretty-printers.
+
+ Arguments:
+ pretty_printers: list of pretty-printers
+ name_re: regular-expression object to select printers
+ subname_re: regular expression object to select subprinters or None
+ if all are affected
+ flag: True for Enable, False for Disable
+
+ Returns:
+ The number of printers affected.
+ This is just for informational purposes for the user.
+ """
+ total = 0
+ for printer in pretty_printers:
+ if (
+ hasattr(printer, "name")
+ and name_re.match(printer.name)
+ or hasattr(printer, "__name__")
+ and name_re.match(printer.__name__)
+ ):
+ if hasattr(printer, "subprinters") and printer.subprinters is not None:
+ if not subname_re:
+ # Only record printers that change state.
+ if printer_enabled_p(printer) != flag:
+ for subprinter in printer.subprinters:
+ if printer_enabled_p(subprinter):
+ total += 1
+ # NOTE: We preserve individual subprinter settings.
+ printer.enabled = flag
+ else:
+ # NOTE: Whether this actually disables the subprinter
+ # depends on whether the printer's lookup function supports
+ # the "enable" API. We can only assume it does.
+ for subprinter in printer.subprinters:
+ if subname_re.match(subprinter.name):
+ # Only record printers that change state.
+ if (
+ printer_enabled_p(printer)
+ and printer_enabled_p(subprinter) != flag
+ ):
+ total += 1
+ subprinter.enabled = flag
+ else:
+ # This printer has no subprinters.
+ # If the user does "disable pretty-printer .* .* foo"
+ # should we disable printers that don't have subprinters?
+ # How do we apply "foo" in this context? Since there is no
+ # "foo" subprinter it feels like we should skip this printer.
+ # There's still the issue of how to handle
+ # "disable pretty-printer .* .* .*", and every other variation
+ # that can match everything. For now punt and only support
+ # "disable pretty-printer .* .*" (i.e. subname is elided)
+ # to disable everything.
+ if not subname_re:
+ # Only record printers that change state.
+ if printer_enabled_p(printer) != flag:
+ total += 1
+ printer.enabled = flag
+ return total
+
+
+def do_enable_pretty_printer(arg, flag):
+ """Internal worker for enabling/disabling pretty-printers."""
+ (object_re, name_re, subname_re) = parse_printer_regexps(arg)
+
+ total = 0
+ if object_re.match("global"):
+ total += do_enable_pretty_printer_1(
+ gdb.pretty_printers, name_re, subname_re, flag
+ )
+ cp = gdb.current_progspace()
+ if object_re.match("progspace"):
+ total += do_enable_pretty_printer_1(
+ cp.pretty_printers, name_re, subname_re, flag
+ )
+ for objfile in gdb.objfiles():
+ if object_re.match(objfile.filename):
+ total += do_enable_pretty_printer_1(
+ objfile.pretty_printers, name_re, subname_re, flag
+ )
+
+ if flag:
+ state = "enabled"
+ else:
+ state = "disabled"
+ print("%d %s %s" % (total, pluralize("printer", total), state))
+
+ # Print the total list of printers currently enabled/disabled.
+ # This is to further assist the user in determining whether the result
+ # is expected. Since we use regexps to select it's useful.
+ show_pretty_printer_enabled_summary()
+
+
+# Enable/Disable one or more pretty-printers.
+#
+# This is intended for use when a broken pretty-printer is shipped/installed
+# and the user wants to disable that printer without disabling all the other
+# printers.
+#
+# A useful addition would be -v (verbose) to show each printer affected.
+
+
+class EnablePrettyPrinter(gdb.Command):
+ """GDB command to enable the specified pretty-printer.
+
+ Usage: enable pretty-printer [OBJECT-REGEXP [NAME-REGEXP]]
+
+ OBJECT-REGEXP is a regular expression matching the objects to examine.
+ Objects are "global", the program space's file, and the objfiles within
+ that program space.
+
+ NAME-REGEXP matches the name of the pretty-printer.
+ Individual printers in a collection are named as
+ printer-name;subprinter-name."""
+
+ def __init__(self):
+ super(EnablePrettyPrinter, self).__init__(
+ "enable pretty-printer", gdb.COMMAND_DATA
+ )
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_pretty_printer(arg, True)
+
+
+class DisablePrettyPrinter(gdb.Command):
+ """GDB command to disable the specified pretty-printer.
+
+ Usage: disable pretty-printer [OBJECT-REGEXP [NAME-REGEXP]]
+
+ OBJECT-REGEXP is a regular expression matching the objects to examine.
+ Objects are "global", the program space's file, and the objfiles within
+ that program space.
+
+ NAME-REGEXP matches the name of the pretty-printer.
+ Individual printers in a collection are named as
+ printer-name;subprinter-name."""
+
+ def __init__(self):
+ super(DisablePrettyPrinter, self).__init__(
+ "disable pretty-printer", gdb.COMMAND_DATA
+ )
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_pretty_printer(arg, False)
+
+
+def register_pretty_printer_commands():
+ """Call from a top level script to install the pretty-printer commands."""
+ InfoPrettyPrinter()
+ EnablePrettyPrinter()
+ DisablePrettyPrinter()
+
+
+register_pretty_printer_commands()
diff --git a/share/gdb/python/gdb/command/prompt.py b/share/gdb/python/gdb/command/prompt.py
new file mode 100644
index 0000000..775fa1a
--- /dev/null
+++ b/share/gdb/python/gdb/command/prompt.py
@@ -0,0 +1,66 @@
+# Extended prompt.
+# Copyright (C) 2011-2023 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/>.
+
+"""GDB command for working with extended prompts."""
+
+import gdb
+import gdb.prompt
+
+
+class _ExtendedPrompt(gdb.Parameter):
+
+ """Set the extended prompt.
+
+ Usage: set extended-prompt VALUE
+
+ Substitutions are applied to VALUE to compute the real prompt.
+
+ The currently defined substitutions are:"""
+
+ # Add the prompt library's dynamically generated help to the
+ # __doc__ string.
+ __doc__ = __doc__ + "\n" + gdb.prompt.prompt_help()
+
+ set_doc = "Set the extended prompt."
+ show_doc = "Show the extended prompt."
+
+ def __init__(self):
+ super(_ExtendedPrompt, self).__init__(
+ "extended-prompt", gdb.COMMAND_SUPPORT, gdb.PARAM_STRING_NOESCAPE
+ )
+ self.value = ""
+ self.hook_set = False
+
+ def get_show_string(self, pvalue):
+ if self.value:
+ return "The extended prompt is: " + self.value
+ else:
+ return "The extended prompt is not set."
+
+ def get_set_string(self):
+ if self.hook_set is False:
+ gdb.prompt_hook = self.before_prompt_hook
+ self.hook_set = True
+ return ""
+
+ def before_prompt_hook(self, current):
+ if self.value:
+ return gdb.prompt.substitute_prompt(self.value)
+ else:
+ return None
+
+
+_ExtendedPrompt()
diff --git a/share/gdb/python/gdb/command/type_printers.py b/share/gdb/python/gdb/command/type_printers.py
new file mode 100644
index 0000000..35e6201
--- /dev/null
+++ b/share/gdb/python/gdb/command/type_printers.py
@@ -0,0 +1,125 @@
+# Type printer commands.
+# Copyright (C) 2010-2023 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/>.
+
+import copy
+import gdb
+
+"""GDB commands for working with type-printers."""
+
+
+class InfoTypePrinter(gdb.Command):
+ """GDB command to list all registered type-printers.
+
+ Usage: info type-printers"""
+
+ def __init__(self):
+ super(InfoTypePrinter, self).__init__("info type-printers", gdb.COMMAND_DATA)
+
+ def list_type_printers(self, type_printers):
+ """Print a list of type printers."""
+ # A potential enhancement is to provide an option to list printers in
+ # "lookup order" (i.e. unsorted).
+ sorted_type_printers = sorted(copy.copy(type_printers), key=lambda x: x.name)
+ for printer in sorted_type_printers:
+ if printer.enabled:
+ enabled = ""
+ else:
+ enabled = " [disabled]"
+ print(" %s%s" % (printer.name, enabled))
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ sep = ""
+ for objfile in gdb.objfiles():
+ if objfile.type_printers:
+ print("%sType printers for %s:" % (sep, objfile.filename))
+ self.list_type_printers(objfile.type_printers)
+ sep = "\n"
+ if gdb.current_progspace().type_printers:
+ print("%sType printers for program space:" % sep)
+ self.list_type_printers(gdb.current_progspace().type_printers)
+ sep = "\n"
+ if gdb.type_printers:
+ print("%sGlobal type printers:" % sep)
+ self.list_type_printers(gdb.type_printers)
+
+
+class _EnableOrDisableCommand(gdb.Command):
+ def __init__(self, setting, name):
+ super(_EnableOrDisableCommand, self).__init__(name, gdb.COMMAND_DATA)
+ self.setting = setting
+
+ def set_some(self, name, printers):
+ result = False
+ for p in printers:
+ if name == p.name:
+ p.enabled = self.setting
+ result = True
+ return result
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ for name in arg.split():
+ ok = False
+ for objfile in gdb.objfiles():
+ if self.set_some(name, objfile.type_printers):
+ ok = True
+ if self.set_some(name, gdb.current_progspace().type_printers):
+ ok = True
+ if self.set_some(name, gdb.type_printers):
+ ok = True
+ if not ok:
+ print("No type printer named '%s'" % name)
+
+ def add_some(self, result, word, printers):
+ for p in printers:
+ if p.name.startswith(word):
+ result.append(p.name)
+
+ def complete(self, text, word):
+ result = []
+ for objfile in gdb.objfiles():
+ self.add_some(result, word, objfile.type_printers)
+ self.add_some(result, word, gdb.current_progspace().type_printers)
+ self.add_some(result, word, gdb.type_printers)
+ return result
+
+
+class EnableTypePrinter(_EnableOrDisableCommand):
+ """GDB command to enable the specified type printer.
+
+ Usage: enable type-printer NAME
+
+ NAME is the name of the type-printer."""
+
+ def __init__(self):
+ super(EnableTypePrinter, self).__init__(True, "enable type-printer")
+
+
+class DisableTypePrinter(_EnableOrDisableCommand):
+ """GDB command to disable the specified type-printer.
+
+ Usage: disable type-printer NAME
+
+ NAME is the name of the type-printer."""
+
+ def __init__(self):
+ super(DisableTypePrinter, self).__init__(False, "disable type-printer")
+
+
+InfoTypePrinter()
+EnableTypePrinter()
+DisableTypePrinter()
diff --git a/share/gdb/python/gdb/command/unwinders.py b/share/gdb/python/gdb/command/unwinders.py
new file mode 100644
index 0000000..68087aa
--- /dev/null
+++ b/share/gdb/python/gdb/command/unwinders.py
@@ -0,0 +1,199 @@
+# Unwinder commands.
+# Copyright 2015-2023 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/>.
+
+import gdb
+import re
+
+
+def validate_regexp(exp, idstring):
+ try:
+ return re.compile(exp)
+ except SyntaxError:
+ raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp))
+
+
+def parse_unwinder_command_args(arg):
+ """Internal utility to parse unwinder command argv.
+
+ Arguments:
+ arg: The arguments to the command. The format is:
+ [locus-regexp [name-regexp]]
+
+ Returns:
+ A 2-tuple of compiled regular expressions.
+
+ Raises:
+ SyntaxError: an error processing ARG
+ """
+
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+ raise SyntaxError("Too many arguments.")
+ locus_regexp = ""
+ name_regexp = ""
+ if argc >= 1:
+ locus_regexp = argv[0]
+ if argc >= 2:
+ name_regexp = argv[1]
+ return (
+ validate_regexp(locus_regexp, "locus"),
+ validate_regexp(name_regexp, "unwinder"),
+ )
+
+
+class InfoUnwinder(gdb.Command):
+ """GDB command to list unwinders.
+
+ Usage: info unwinder [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ unwinder. If it is omitted, all registered unwinders from all
+ loci are listed. A locus can be 'global', 'progspace' to list
+ the unwinders from the current progspace, or a regular expression
+ matching filenames of objfiles.
+
+ NAME-REGEXP is a regular expression to filter unwinder names. If
+ this omitted for a specified locus, then all registered unwinders
+ in the locus are listed."""
+
+ def __init__(self):
+ super(InfoUnwinder, self).__init__("info unwinder", gdb.COMMAND_STACK)
+
+ def list_unwinders(self, title, unwinders, name_re):
+ """Lists the unwinders whose name matches regexp.
+
+ Arguments:
+ title: The line to print before the list.
+ unwinders: The list of the unwinders.
+ name_re: unwinder name filter.
+ """
+ if not unwinders:
+ return
+ print(title)
+ for unwinder in unwinders:
+ if name_re.match(unwinder.name):
+ print(
+ " %s%s"
+ % (unwinder.name, "" if unwinder.enabled else " [disabled]")
+ )
+
+ def invoke(self, arg, from_tty):
+ locus_re, name_re = parse_unwinder_command_args(arg)
+ if locus_re.match("global"):
+ self.list_unwinders("Global:", gdb.frame_unwinders, name_re)
+ if locus_re.match("progspace"):
+ cp = gdb.current_progspace()
+ self.list_unwinders(
+ "Progspace %s:" % cp.filename, cp.frame_unwinders, name_re
+ )
+ for objfile in gdb.objfiles():
+ if locus_re.match(objfile.filename):
+ self.list_unwinders(
+ "Objfile %s:" % objfile.filename, objfile.frame_unwinders, name_re
+ )
+
+
+def do_enable_unwinder1(unwinders, name_re, flag):
+ """Enable/disable unwinders whose names match given regex.
+
+ Arguments:
+ unwinders: The list of unwinders.
+ name_re: Unwinder name filter.
+ flag: Enable/disable.
+
+ Returns:
+ The number of unwinders affected.
+ """
+ total = 0
+ for unwinder in unwinders:
+ if name_re.match(unwinder.name):
+ unwinder.enabled = flag
+ total += 1
+ return total
+
+
+def do_enable_unwinder(arg, flag):
+ """Enable/disable unwinder(s)."""
+ (locus_re, name_re) = parse_unwinder_command_args(arg)
+ total = 0
+ if locus_re.match("global"):
+ total += do_enable_unwinder1(gdb.frame_unwinders, name_re, flag)
+ if locus_re.match("progspace"):
+ total += do_enable_unwinder1(
+ gdb.current_progspace().frame_unwinders, name_re, flag
+ )
+ for objfile in gdb.objfiles():
+ if locus_re.match(objfile.filename):
+ total += do_enable_unwinder1(objfile.frame_unwinders, name_re, flag)
+ if total > 0:
+ gdb.invalidate_cached_frames()
+ print(
+ "%d unwinder%s %s"
+ % (total, "" if total == 1 else "s", "enabled" if flag else "disabled")
+ )
+
+
+class EnableUnwinder(gdb.Command):
+ """GDB command to enable unwinders.
+
+ Usage: enable unwinder [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression specifying the unwinders to
+ enable. It can 'global', 'progspace', or the name of an objfile
+ within that progspace.
+
+ NAME_REGEXP is a regular expression to filter unwinder names. If
+ this omitted for a specified locus, then all registered unwinders
+ in the locus are affected."""
+
+ def __init__(self):
+ super(EnableUnwinder, self).__init__("enable unwinder", gdb.COMMAND_STACK)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_unwinder(arg, True)
+
+
+class DisableUnwinder(gdb.Command):
+ """GDB command to disable the specified unwinder.
+
+ Usage: disable unwinder [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression specifying the unwinders to
+ disable. It can 'global', 'progspace', or the name of an objfile
+ within that progspace.
+
+ NAME_REGEXP is a regular expression to filter unwinder names. If
+ this omitted for a specified locus, then all registered unwinders
+ in the locus are affected."""
+
+ def __init__(self):
+ super(DisableUnwinder, self).__init__("disable unwinder", gdb.COMMAND_STACK)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_unwinder(arg, False)
+
+
+def register_unwinder_commands():
+ """Installs the unwinder commands."""
+ InfoUnwinder()
+ EnableUnwinder()
+ DisableUnwinder()
+
+
+register_unwinder_commands()
diff --git a/share/gdb/python/gdb/command/xmethods.py b/share/gdb/python/gdb/command/xmethods.py
new file mode 100644
index 0000000..4bf8969
--- /dev/null
+++ b/share/gdb/python/gdb/command/xmethods.py
@@ -0,0 +1,270 @@
+# Xmethod commands.
+# Copyright 2013-2023 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/>.
+
+import gdb
+import re
+
+"""GDB commands for working with xmethods."""
+
+
+def validate_xm_regexp(part_name, regexp):
+ try:
+ return re.compile(regexp)
+ except SyntaxError:
+ raise SyntaxError("Invalid %s regexp: %s", part_name, regexp)
+
+
+def parse_xm_command_args(arg):
+ """Parses the arguments passed to a xmethod command.
+
+ Arguments:
+ arg: The argument string passed to a xmethod command.
+
+ Returns:
+ A 3-tuple: (<locus matching regular expression>,
+ <matcher matching regular expression>,
+ <name matching regular experession>)
+ """
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+ raise SyntaxError("Too many arguments to command.")
+ locus_regexp = ""
+ matcher_name_regexp = ""
+ xm_name_regexp = None
+ if argc >= 1:
+ locus_regexp = argv[0]
+ if argc == 2:
+ parts = argv[1].split(";", 1)
+ matcher_name_regexp = parts[0]
+ if len(parts) > 1:
+ xm_name_regexp = parts[1]
+ if xm_name_regexp:
+ name_re = validate_xm_regexp("xmethod name", xm_name_regexp)
+ else:
+ name_re = None
+ return (
+ validate_xm_regexp("locus", locus_regexp),
+ validate_xm_regexp("matcher name", matcher_name_regexp),
+ name_re,
+ )
+
+
+def get_global_method_matchers(locus_re, matcher_re):
+ """Returns a dict of matching globally registered xmethods.
+
+ Arguments:
+ locus_re: Even though only globally registered xmethods are
+ looked up, they will be looked up only if 'global' matches
+ LOCUS_RE.
+ matcher_re: The regular expression matching the names of xmethods.
+
+ Returns:
+ A dict of matching globally registered xmethod matchers. The only
+ key in the dict will be 'global'.
+ """
+ locus_str = "global"
+ xm_dict = {locus_str: []}
+ if locus_re.match("global"):
+ xm_dict[locus_str].extend([m for m in gdb.xmethods if matcher_re.match(m.name)])
+ return xm_dict
+
+
+def get_method_matchers_in_loci(loci, locus_re, matcher_re):
+ """Returns a dict of matching registered xmethods in the LOCI.
+
+ Arguments:
+ loci: The list of loci to lookup matching xmethods in.
+ locus_re: If a locus is an objfile, then xmethod matchers will be
+ looked up in it only if its filename matches the regular
+ expression LOCUS_RE. If a locus is the current progspace,
+ then xmethod matchers will be looked up in it only if the
+ string "progspace" matches LOCUS_RE.
+ matcher_re: The regular expression to match the xmethod matcher
+ names.
+
+ Returns:
+ A dict of matching xmethod matchers. The keys of the dict are the
+ filenames of the loci the xmethod matchers belong to.
+ """
+ xm_dict = {}
+ for locus in loci:
+ if isinstance(locus, gdb.Progspace):
+ if not locus_re.match("progspace"):
+ continue
+ locus_type = "progspace"
+ else:
+ if not locus_re.match(locus.filename):
+ continue
+ locus_type = "objfile"
+ locus_str = "%s %s" % (locus_type, locus.filename)
+ xm_dict[locus_str] = [m for m in locus.xmethods if matcher_re.match(m.name)]
+ return xm_dict
+
+
+def print_xm_info(xm_dict, name_re):
+ """Print a dictionary of xmethods."""
+
+ def get_status_string(m):
+ if not m.enabled:
+ return " [disabled]"
+ else:
+ return ""
+
+ if not xm_dict:
+ return
+ for locus_str in xm_dict:
+ if not xm_dict[locus_str]:
+ continue
+ print("Xmethods in %s:" % locus_str)
+ for matcher in xm_dict[locus_str]:
+ print(" %s%s" % (matcher.name, get_status_string(matcher)))
+ if not matcher.methods:
+ continue
+ for m in matcher.methods:
+ if name_re is None or name_re.match(m.name):
+ print(" %s%s" % (m.name, get_status_string(m)))
+
+
+def set_xm_status1(xm_dict, name_re, status):
+ """Set the status (enabled/disabled) of a dictionary of xmethods."""
+ for locus_str, matchers in xm_dict.items():
+ for matcher in matchers:
+ if not name_re:
+ # If the name regex is missing, then set the status of the
+ # matcher and move on.
+ matcher.enabled = status
+ continue
+ if not matcher.methods:
+ # The methods attribute could be None. Move on.
+ continue
+ for m in matcher.methods:
+ if name_re.match(m.name):
+ m.enabled = status
+
+
+def set_xm_status(arg, status):
+ """Set the status (enabled/disabled) of xmethods matching ARG.
+ This is a helper function for enable/disable commands. ARG is the
+ argument string passed to the commands.
+ """
+ locus_re, matcher_re, name_re = parse_xm_command_args(arg)
+ set_xm_status1(get_global_method_matchers(locus_re, matcher_re), name_re, status)
+ set_xm_status1(
+ get_method_matchers_in_loci([gdb.current_progspace()], locus_re, matcher_re),
+ name_re,
+ status,
+ )
+ set_xm_status1(
+ get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re),
+ name_re,
+ status,
+ )
+
+
+class InfoXMethod(gdb.Command):
+ """GDB command to list registered xmethod matchers.
+
+ Usage: info xmethod [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ xmethod matchers. If it is omitted, all registered xmethod matchers
+ from all loci are listed. A locus could be 'global', a regular expression
+ matching the current program space's filename, or a regular expression
+ matching filenames of objfiles. Locus could be 'progspace' to specify that
+ only xmethods from the current progspace should be listed.
+
+ NAME-REGEXP is a regular expression matching the names of xmethod
+ matchers. If this omitted for a specified locus, then all registered
+ xmethods in the locus are listed. To list only a certain xmethods
+ managed by a single matcher, the name regexp can be specified as
+ matcher-name-regexp;xmethod-name-regexp."""
+
+ def __init__(self):
+ super(InfoXMethod, self).__init__("info xmethod", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ locus_re, matcher_re, name_re = parse_xm_command_args(arg)
+ print_xm_info(get_global_method_matchers(locus_re, matcher_re), name_re)
+ print_xm_info(
+ get_method_matchers_in_loci(
+ [gdb.current_progspace()], locus_re, matcher_re
+ ),
+ name_re,
+ )
+ print_xm_info(
+ get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re), name_re
+ )
+
+
+class EnableXMethod(gdb.Command):
+ """GDB command to enable a specified (group of) xmethod(s).
+
+ Usage: enable xmethod [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ xmethod matchers. If it is omitted, all registered xmethods matchers
+ from all loci are enabled. A locus could be 'global', a regular expression
+ matching the current program space's filename, or a regular expression
+ matching filenames of objfiles. Locus could be 'progspace' to specify that
+ only xmethods from the current progspace should be enabled.
+
+ NAME-REGEXP is a regular expression matching the names of xmethods
+ within a given locus. If this omitted for a specified locus, then all
+ registered xmethod matchers in the locus are enabled. To enable only
+ a certain xmethods managed by a single matcher, the name regexp can be
+ specified as matcher-name-regexp;xmethod-name-regexp."""
+
+ def __init__(self):
+ super(EnableXMethod, self).__init__("enable xmethod", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_xm_status(arg, True)
+
+
+class DisableXMethod(gdb.Command):
+ """GDB command to disable a specified (group of) xmethod(s).
+
+ Usage: disable xmethod [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ xmethod matchers. If it is omitted, all registered xmethod matchers
+ from all loci are disabled. A locus could be 'global', a regular
+ expression matching the current program space's filename, or a regular
+ expression filenames of objfiles. Locus could be 'progspace' to specify
+ that only xmethods from the current progspace should be disabled.
+
+ NAME-REGEXP is a regular expression matching the names of xmethods
+ within a given locus. If this omitted for a specified locus, then all
+ registered xmethod matchers in the locus are disabled. To disable
+ only a certain xmethods managed by a single matcher, the name regexp
+ can be specified as matcher-name-regexp;xmethod-name-regexp."""
+
+ def __init__(self):
+ super(DisableXMethod, self).__init__("disable xmethod", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_xm_status(arg, False)
+
+
+def register_xmethod_commands():
+ """Installs the xmethod commands."""
+ InfoXMethod()
+ EnableXMethod()
+ DisableXMethod()
+
+
+register_xmethod_commands()
diff --git a/share/gdb/python/gdb/disassembler.py b/share/gdb/python/gdb/disassembler.py
new file mode 100644
index 0000000..88f65f5
--- /dev/null
+++ b/share/gdb/python/gdb/disassembler.py
@@ -0,0 +1,178 @@
+# Copyright (C) 2021-2023 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/>.
+
+"""Disassembler related module."""
+
+import gdb
+import _gdb.disassembler
+
+# Re-export everything from the _gdb.disassembler module, which is
+# defined within GDB's C++ code.
+from _gdb.disassembler import *
+
+# Module global dictionary of gdb.disassembler.Disassembler objects.
+# The keys of this dictionary are bfd architecture names, or the
+# special value None.
+#
+# When a request to disassemble comes in we first lookup the bfd
+# architecture name from the gdbarch, if that name exists in this
+# dictionary then we use that Disassembler object.
+#
+# If there's no architecture specific disassembler then we look for
+# the key None in this dictionary, and if that key exists, we use that
+# disassembler.
+#
+# If none of the above checks found a suitable disassembler, then no
+# disassembly is performed in Python.
+_disassemblers_dict = {}
+
+
+class Disassembler(object):
+ """A base class from which all user implemented disassemblers must
+ inherit."""
+
+ def __init__(self, name):
+ """Constructor. Takes a name, which should be a string, which can be
+ used to identify this disassembler in diagnostic messages."""
+ self.name = name
+
+ def __call__(self, info):
+ """A default implementation of __call__. All sub-classes must
+ override this method. Calling this default implementation will throw
+ a NotImplementedError exception."""
+ raise NotImplementedError("Disassembler.__call__")
+
+
+def register_disassembler(disassembler, architecture=None):
+ """Register a disassembler. DISASSEMBLER is a sub-class of
+ gdb.disassembler.Disassembler. ARCHITECTURE is either None or a
+ string, the name of an architecture known to GDB.
+
+ DISASSEMBLER is registered as a disassembler for ARCHITECTURE, or
+ all architectures when ARCHITECTURE is None.
+
+ Returns the previous disassembler registered with this
+ ARCHITECTURE value.
+ """
+
+ if not isinstance(disassembler, Disassembler) and disassembler is not None:
+ raise TypeError("disassembler should sub-class gdb.disassembler.Disassembler")
+
+ old = None
+ if architecture in _disassemblers_dict:
+ old = _disassemblers_dict[architecture]
+ del _disassemblers_dict[architecture]
+ if disassembler is not None:
+ _disassemblers_dict[architecture] = disassembler
+
+ # Call the private _set_enabled function within the
+ # _gdb.disassembler module. This function sets a global flag
+ # within GDB's C++ code that enables or dissables the Python
+ # disassembler functionality, this improves performance of the
+ # disassembler by avoiding unneeded calls into Python when we know
+ # that no disassemblers are registered.
+ _gdb.disassembler._set_enabled(len(_disassemblers_dict) > 0)
+ return old
+
+
+def _print_insn(info):
+ """This function is called by GDB when it wants to disassemble an
+ instruction. INFO describes the instruction to be
+ disassembled."""
+
+ def lookup_disassembler(arch):
+ try:
+ name = arch.name()
+ if name is None:
+ return None
+ if name in _disassemblers_dict:
+ return _disassemblers_dict[name]
+ if None in _disassemblers_dict:
+ return _disassemblers_dict[None]
+ return None
+ except:
+ # It's pretty unlikely this exception case will ever
+ # trigger, one situation would be if the user somehow
+ # corrupted the _disassemblers_dict variable such that it
+ # was no longer a dictionary.
+ return None
+
+ disassembler = lookup_disassembler(info.architecture)
+ if disassembler is None:
+ return None
+ return disassembler(info)
+
+
+class maint_info_py_disassemblers_cmd(gdb.Command):
+ """
+ List all registered Python disassemblers.
+
+ List the name of all registered Python disassemblers, next to the
+ name of the architecture for which the disassembler is registered.
+
+ The global Python disassembler is listed next to the string
+ 'GLOBAL'.
+
+ The disassembler that matches the architecture of the currently
+ selected inferior will be marked, this is an indication of which
+ disassembler will be invoked if any disassembly is performed in
+ the current inferior.
+ """
+
+ def __init__(self):
+ super().__init__("maintenance info python-disassemblers", gdb.COMMAND_USER)
+
+ def invoke(self, args, from_tty):
+ # If no disassemblers are registered, tell the user.
+ if len(_disassemblers_dict) == 0:
+ print("No Python disassemblers registered.")
+ return
+
+ # Figure out the longest architecture name, so we can
+ # correctly format the table of results.
+ longest_arch_name = 0
+ for architecture in _disassemblers_dict:
+ if architecture is not None:
+ name = _disassemblers_dict[architecture].name
+ if len(name) > longest_arch_name:
+ longest_arch_name = len(name)
+
+ # Figure out the name of the current architecture. There
+ # should always be a current inferior, but if, somehow, there
+ # isn't, then leave curr_arch as the empty string, which will
+ # not then match agaisnt any architecture in the dictionary.
+ curr_arch = ""
+ if gdb.selected_inferior() is not None:
+ curr_arch = gdb.selected_inferior().architecture().name()
+
+ # Now print the dictionary of registered disassemblers out to
+ # the user.
+ match_tag = "\t(Matches current architecture)"
+ fmt_len = max(longest_arch_name, len("Architecture"))
+ format_string = "{:" + str(fmt_len) + "s} {:s}"
+ print(format_string.format("Architecture", "Disassember Name"))
+ for architecture in _disassemblers_dict:
+ if architecture is not None:
+ name = _disassemblers_dict[architecture].name
+ if architecture == curr_arch:
+ name += match_tag
+ match_tag = ""
+ print(format_string.format(architecture, name))
+ if None in _disassemblers_dict:
+ name = _disassemblers_dict[None].name + match_tag
+ print(format_string.format("GLOBAL", name))
+
+
+maint_info_py_disassemblers_cmd()
diff --git a/share/gdb/python/gdb/frames.py b/share/gdb/python/gdb/frames.py
new file mode 100644
index 0000000..5f8119c
--- /dev/null
+++ b/share/gdb/python/gdb/frames.py
@@ -0,0 +1,233 @@
+# Frame-filter commands.
+# Copyright (C) 2013-2023 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/>.
+
+"""Internal functions for working with frame-filters."""
+
+import gdb
+from gdb.FrameIterator import FrameIterator
+from gdb.FrameDecorator import FrameDecorator
+import itertools
+import collections
+
+
+def get_priority(filter_item):
+ """Internal worker function to return the frame-filter's priority
+ from a frame filter object. This is a fail free function as it is
+ used in sorting and filtering. If a badly implemented frame
+ filter does not implement the priority attribute, return zero
+ (otherwise sorting/filtering will fail and prevent other frame
+ filters from executing).
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+
+ Returns:
+ The priority of the frame filter from the "priority"
+ attribute, or zero.
+ """
+ # Do not fail here, as the sort will fail. If a filter has not
+ # (incorrectly) set a priority, set it to zero.
+ return getattr(filter_item, "priority", 0)
+
+
+def set_priority(filter_item, priority):
+ """Internal worker function to set the frame-filter's priority.
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+ priority: The priority to assign as an integer.
+ """
+
+ filter_item.priority = priority
+
+
+def get_enabled(filter_item):
+ """Internal worker function to return a filter's enabled state
+ from a frame filter object. This is a fail free function as it is
+ used in sorting and filtering. If a badly implemented frame
+ filter does not implement the enabled attribute, return False
+ (otherwise sorting/filtering will fail and prevent other frame
+ filters from executing).
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+
+ Returns:
+ The enabled state of the frame filter from the "enabled"
+ attribute, or False.
+ """
+
+ # If the filter class is badly implemented when called from the
+ # Python filter command, do not cease filter operations, just set
+ # enabled to False.
+ return getattr(filter_item, "enabled", False)
+
+
+def set_enabled(filter_item, state):
+ """Internal Worker function to set the frame-filter's enabled
+ state.
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+ state: True or False, depending on desired state.
+ """
+
+ filter_item.enabled = state
+
+
+def return_list(name):
+ """Internal Worker function to return the frame filter
+ dictionary, depending on the name supplied as an argument. If the
+ name is not "all", "global" or "progspace", it is assumed to name
+ an object-file.
+
+ Arguments:
+ name: The name of the list, as specified by GDB user commands.
+
+ Returns:
+ A dictionary object for a single specified dictionary, or a
+ list containing all the items for "all"
+
+ Raises:
+ gdb.GdbError: A dictionary of that name cannot be found.
+ """
+
+ # If all dictionaries are wanted in the case of "all" we
+ # cannot return a combined dictionary as keys() may clash in
+ # between different dictionaries. As we just want all the frame
+ # filters to enable/disable them all, just return the combined
+ # items() as a chained iterator of dictionary values.
+ if name == "all":
+ glob = gdb.frame_filters.values()
+ prog = gdb.current_progspace().frame_filters.values()
+ return_iter = itertools.chain(glob, prog)
+ for objfile in gdb.objfiles():
+ return_iter = itertools.chain(return_iter, objfile.frame_filters.values())
+
+ return return_iter
+
+ if name == "global":
+ return gdb.frame_filters
+ else:
+ if name == "progspace":
+ cp = gdb.current_progspace()
+ return cp.frame_filters
+ else:
+ for objfile in gdb.objfiles():
+ if name == objfile.filename:
+ return objfile.frame_filters
+
+ msg = "Cannot find frame-filter dictionary for '" + name + "'"
+ raise gdb.GdbError(msg)
+
+
+def _sort_list():
+ """Internal Worker function to merge all known frame-filter
+ lists, prune any filters with the state set to "disabled", and
+ sort the list on the frame-filter's "priority" attribute.
+
+ Returns:
+ sorted_list: A sorted, pruned list of frame filters to
+ execute.
+ """
+
+ all_filters = return_list("all")
+ sorted_frame_filters = sorted(all_filters, key=get_priority, reverse=True)
+
+ sorted_frame_filters = filter(get_enabled, sorted_frame_filters)
+
+ return sorted_frame_filters
+
+
+def execute_frame_filters(frame, frame_low, frame_high):
+ """Internal function called from GDB that will execute the chain
+ of frame filters. Each filter is executed in priority order.
+ After the execution completes, slice the iterator to frame_low -
+ frame_high range.
+
+ Arguments:
+ frame: The initial frame.
+
+ frame_low: The low range of the slice. If this is a negative
+ integer then it indicates a backward slice (ie bt -4) which
+ counts backward from the last frame in the backtrace.
+
+ frame_high: The high range of the slice. If this is -1 then
+ it indicates all frames until the end of the stack from
+ frame_low.
+
+ Returns:
+ frame_iterator: The sliced iterator after all frame
+ filters have had a change to execute, or None if no frame
+ filters are registered.
+ """
+
+ # Get a sorted list of frame filters.
+ sorted_list = list(_sort_list())
+
+ # Check to see if there are any frame-filters. If not, just
+ # return None and let default backtrace printing occur.
+ if len(sorted_list) == 0:
+ return None
+
+ frame_iterator = FrameIterator(frame)
+
+ # Apply a basic frame decorator to all gdb.Frames. This unifies
+ # the interface. Python 3.x moved the itertools.imap
+ # functionality to map(), so check if it is available.
+ if hasattr(itertools, "imap"):
+ frame_iterator = itertools.imap(FrameDecorator, frame_iterator)
+ else:
+ frame_iterator = map(FrameDecorator, frame_iterator)
+
+ for ff in sorted_list:
+ frame_iterator = ff.filter(frame_iterator)
+
+ # Slicing
+
+ # Is this a slice from the end of the backtrace, ie bt -2?
+ if frame_low < 0:
+ count = 0
+ slice_length = abs(frame_low)
+ # We cannot use MAXLEN argument for deque as it is 2.6 onwards
+ # and some GDB versions might be < 2.6.
+ sliced = collections.deque()
+
+ for frame_item in frame_iterator:
+ if count >= slice_length:
+ sliced.popleft()
+ count = count + 1
+ sliced.append(frame_item)
+
+ return iter(sliced)
+
+ # -1 for frame_high means until the end of the backtrace. Set to
+ # None if that is the case, to indicate to itertools.islice to
+ # slice to the end of the iterator.
+ if frame_high == -1:
+ frame_high = None
+ else:
+ # As frames start from 0, add one to frame_high so islice
+ # correctly finds the end
+ frame_high = frame_high + 1
+
+ sliced = itertools.islice(frame_iterator, frame_low, frame_high)
+
+ return sliced
diff --git a/share/gdb/python/gdb/function/__init__.py b/share/gdb/python/gdb/function/__init__.py
new file mode 100644
index 0000000..34496c1
--- /dev/null
+++ b/share/gdb/python/gdb/function/__init__.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2012-2023 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/>.
diff --git a/share/gdb/python/gdb/function/as_string.py b/share/gdb/python/gdb/function/as_string.py
new file mode 100644
index 0000000..4bd6579
--- /dev/null
+++ b/share/gdb/python/gdb/function/as_string.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2016-2023 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/>.
+
+import gdb
+
+
+class _AsString(gdb.Function):
+ """Return the string representation of a value.
+
+ Usage: $_as_string (VALUE)
+
+ Arguments:
+
+ VALUE: any value
+
+ Returns:
+ The string representation of the value."""
+
+ def __init__(self):
+ super(_AsString, self).__init__("_as_string")
+
+ def invoke(self, val):
+ return str(val)
+
+
+_AsString()
diff --git a/share/gdb/python/gdb/function/caller_is.py b/share/gdb/python/gdb/function/caller_is.py
new file mode 100644
index 0000000..220b222
--- /dev/null
+++ b/share/gdb/python/gdb/function/caller_is.py
@@ -0,0 +1,157 @@
+# Caller-is functions.
+# Copyright (C) 2008-2023 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/>.
+
+import gdb
+import re
+
+
+class CallerIs(gdb.Function):
+ """Check the calling function's name.
+
+ Usage: $_caller_is (NAME [, NUMBER-OF-FRAMES])
+
+ Arguments:
+
+ NAME: The name of the function to search for.
+
+ NUMBER-OF-FRAMES: How many stack frames to traverse back from the currently
+ selected frame to compare with. If the value is greater than the depth of
+ the stack from that point then the result is False.
+ The default is 1.
+
+ Returns:
+ True if the function's name at the specified frame is equal to NAME."""
+
+ def __init__(self):
+ super(CallerIs, self).__init__("_caller_is")
+
+ def invoke(self, name, nframes=1):
+ if nframes < 0:
+ raise ValueError("nframes must be >= 0")
+ frame = gdb.selected_frame()
+ while nframes > 0:
+ frame = frame.older()
+ if frame is None:
+ return False
+ nframes = nframes - 1
+ return frame.name() == name.string()
+
+
+class CallerMatches(gdb.Function):
+ """Compare the calling function's name with a regexp.
+
+ Usage: $_caller_matches (REGEX [, NUMBER-OF-FRAMES])
+
+ Arguments:
+
+ REGEX: The regular expression to compare the function's name with.
+
+ NUMBER-OF-FRAMES: How many stack frames to traverse back from the currently
+ selected frame to compare with. If the value is greater than the depth of
+ the stack from that point then the result is False.
+ The default is 1.
+
+ Returns:
+ True if the function's name at the specified frame matches REGEX."""
+
+ def __init__(self):
+ super(CallerMatches, self).__init__("_caller_matches")
+
+ def invoke(self, name, nframes=1):
+ if nframes < 0:
+ raise ValueError("nframes must be >= 0")
+ frame = gdb.selected_frame()
+ while nframes > 0:
+ frame = frame.older()
+ if frame is None:
+ return False
+ nframes = nframes - 1
+ return re.match(name.string(), frame.name()) is not None
+
+
+class AnyCallerIs(gdb.Function):
+ """Check all calling function's names.
+
+ Usage: $_any_caller_is (NAME [, NUMBER-OF-FRAMES])
+
+ Arguments:
+
+ NAME: The name of the function to search for.
+
+ NUMBER-OF-FRAMES: How many stack frames to traverse back from the currently
+ selected frame to compare with. If the value is greater than the depth of
+ the stack from that point then the result is False.
+ The default is 1.
+
+ Returns:
+ True if any function's name is equal to NAME."""
+
+ def __init__(self):
+ super(AnyCallerIs, self).__init__("_any_caller_is")
+
+ def invoke(self, name, nframes=1):
+ if nframes < 0:
+ raise ValueError("nframes must be >= 0")
+ frame = gdb.selected_frame()
+ while nframes >= 0:
+ if frame.name() == name.string():
+ return True
+ frame = frame.older()
+ if frame is None:
+ return False
+ nframes = nframes - 1
+ return False
+
+
+class AnyCallerMatches(gdb.Function):
+ """Compare all calling function's names with a regexp.
+
+ Usage: $_any_caller_matches (REGEX [, NUMBER-OF-FRAMES])
+
+ Arguments:
+
+ REGEX: The regular expression to compare the function's name with.
+
+ NUMBER-OF-FRAMES: How many stack frames to traverse back from the currently
+ selected frame to compare with. If the value is greater than the depth of
+ the stack from that point then the result is False.
+ The default is 1.
+
+ Returns:
+ True if any function's name matches REGEX."""
+
+ def __init__(self):
+ super(AnyCallerMatches, self).__init__("_any_caller_matches")
+
+ def invoke(self, name, nframes=1):
+ if nframes < 0:
+ raise ValueError("nframes must be >= 0")
+ frame = gdb.selected_frame()
+ name_re = re.compile(name.string())
+ while nframes >= 0:
+ if name_re.match(frame.name()) is not None:
+ return True
+ frame = frame.older()
+ if frame is None:
+ return False
+ nframes = nframes - 1
+ return False
+
+
+CallerIs()
+CallerMatches()
+AnyCallerIs()
+AnyCallerMatches()
diff --git a/share/gdb/python/gdb/function/strfns.py b/share/gdb/python/gdb/function/strfns.py
new file mode 100644
index 0000000..7dc464b
--- /dev/null
+++ b/share/gdb/python/gdb/function/strfns.py
@@ -0,0 +1,104 @@
+# Useful gdb string convenience functions.
+# Copyright (C) 2012-2023 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/>.
+
+"""$_memeq, $_strlen, $_streq, $_regex"""
+
+import gdb
+import re
+
+
+class _MemEq(gdb.Function):
+ """$_memeq - compare bytes of memory.
+
+ Usage: $_memeq (A, B, LEN)
+
+ Returns:
+ True if LEN bytes at A and B compare equally."""
+
+ def __init__(self):
+ super(_MemEq, self).__init__("_memeq")
+
+ def invoke(self, a, b, length):
+ if length < 0:
+ raise ValueError("length must be non-negative")
+ if length == 0:
+ return True
+ # The argument(s) to vector are [low_bound,]high_bound.
+ byte_vector = gdb.lookup_type("char").vector(length - 1)
+ ptr_byte_vector = byte_vector.pointer()
+ a_ptr = a.reinterpret_cast(ptr_byte_vector)
+ b_ptr = b.reinterpret_cast(ptr_byte_vector)
+ return a_ptr.dereference() == b_ptr.dereference()
+
+
+class _StrLen(gdb.Function):
+ """$_strlen - compute string length.
+
+ Usage: $_strlen (A)
+
+ Returns:
+ Length of string A, assumed to be a string in the current language."""
+
+ def __init__(self):
+ super(_StrLen, self).__init__("_strlen")
+
+ def invoke(self, a):
+ s = a.string()
+ return len(s)
+
+
+class _StrEq(gdb.Function):
+ """$_streq - check string equality.
+
+ Usage: $_streq (A, B)
+
+ Returns:
+ True if A and B are identical strings in the current language.
+
+ Example (amd64-linux):
+ catch syscall open
+ cond $bpnum $_streq((char*) $rdi, "foo")"""
+
+ def __init__(self):
+ super(_StrEq, self).__init__("_streq")
+
+ def invoke(self, a, b):
+ return a.string() == b.string()
+
+
+class _RegEx(gdb.Function):
+ """$_regex - check if a string matches a regular expression.
+
+ Usage: $_regex (STRING, REGEX)
+
+ Returns:
+ True if string STRING (in the current language) matches the
+ regular expression REGEX."""
+
+ def __init__(self):
+ super(_RegEx, self).__init__("_regex")
+
+ def invoke(self, string, regex):
+ s = string.string()
+ r = re.compile(regex.string())
+ return bool(r.match(s))
+
+
+# GDB will import us automagically via gdb/__init__.py.
+_MemEq()
+_StrLen()
+_StrEq()
+_RegEx()
diff --git a/share/gdb/python/gdb/printer/__init__.py b/share/gdb/python/gdb/printer/__init__.py
new file mode 100644
index 0000000..b25b7ea
--- /dev/null
+++ b/share/gdb/python/gdb/printer/__init__.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2014-2023 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/>.
diff --git a/share/gdb/python/gdb/printer/bound_registers.py b/share/gdb/python/gdb/printer/bound_registers.py
new file mode 100644
index 0000000..e156675
--- /dev/null
+++ b/share/gdb/python/gdb/printer/bound_registers.py
@@ -0,0 +1,40 @@
+# Pretty-printers for bounds registers.
+# Copyright (C) 2013-2023 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/>.
+
+import sys
+
+import gdb.printing
+
+
+class MpxBound128Printer:
+ """Adds size field to a mpx __gdb_builtin_type_bound128 type."""
+
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ upper = self.val["ubound"]
+ lower = self.val["lbound"]
+ size = upper - lower
+ if size > -1:
+ size = size + 1
+ result = "{lbound = %s, ubound = %s} : size %s" % (lower, upper, size)
+ return result
+
+
+gdb.printing.add_builtin_pretty_printer(
+ "mpx_bound128", "^builtin_type_bound128", MpxBound128Printer
+)
diff --git a/share/gdb/python/gdb/printing.py b/share/gdb/python/gdb/printing.py
new file mode 100644
index 0000000..d6b7b85
--- /dev/null
+++ b/share/gdb/python/gdb/printing.py
@@ -0,0 +1,285 @@
+# Pretty-printer utilities.
+# Copyright (C) 2010-2023 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/>.
+
+"""Utilities for working with pretty-printers."""
+
+import gdb
+import gdb.types
+import re
+import sys
+
+
+class PrettyPrinter(object):
+ """A basic pretty-printer.
+
+ Attributes:
+ name: A unique string among all printers for the context in which
+ it is defined (objfile, progspace, or global(gdb)), and should
+ meaningfully describe what can be pretty-printed.
+ E.g., "StringPiece" or "protobufs".
+ subprinters: An iterable object with each element having a `name'
+ attribute, and, potentially, "enabled" attribute.
+ Or this is None if there are no subprinters.
+ enabled: A boolean indicating if the printer is enabled.
+
+ Subprinters are for situations where "one" pretty-printer is actually a
+ collection of several printers. E.g., The libstdc++ pretty-printer has
+ a pretty-printer for each of several different types, based on regexps.
+ """
+
+ # While one might want to push subprinters into the subclass, it's
+ # present here to formalize such support to simplify
+ # commands/pretty_printers.py.
+
+ def __init__(self, name, subprinters=None):
+ self.name = name
+ self.subprinters = subprinters
+ self.enabled = True
+
+ def __call__(self, val):
+ # The subclass must define this.
+ raise NotImplementedError("PrettyPrinter __call__")
+
+
+class SubPrettyPrinter(object):
+ """Baseclass for sub-pretty-printers.
+
+ Sub-pretty-printers needn't use this, but it formalizes what's needed.
+
+ Attributes:
+ name: The name of the subprinter.
+ enabled: A boolean indicating if the subprinter is enabled.
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.enabled = True
+
+
+def register_pretty_printer(obj, printer, replace=False):
+ """Register pretty-printer PRINTER with OBJ.
+
+ The printer is added to the front of the search list, thus one can override
+ an existing printer if one needs to. Use a different name when overriding
+ an existing printer, otherwise an exception will be raised; multiple
+ printers with the same name are disallowed.
+
+ Arguments:
+ obj: Either an objfile, progspace, or None (in which case the printer
+ is registered globally).
+ printer: Either a function of one argument (old way) or any object
+ which has attributes: name, enabled, __call__.
+ replace: If True replace any existing copy of the printer.
+ Otherwise if the printer already exists raise an exception.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ TypeError: A problem with the type of the printer.
+ ValueError: The printer's name contains a semicolon ";".
+ RuntimeError: A printer with the same name is already registered.
+
+ If the caller wants the printer to be listable and disableable, it must
+ follow the PrettyPrinter API. This applies to the old way (functions) too.
+ If printer is an object, __call__ is a method of two arguments:
+ self, and the value to be pretty-printed. See PrettyPrinter.
+ """
+
+ # Watch for both __name__ and name.
+ # Functions get the former for free, but we don't want to use an
+ # attribute named __foo__ for pretty-printers-as-objects.
+ # If printer has both, we use `name'.
+ if not hasattr(printer, "__name__") and not hasattr(printer, "name"):
+ raise TypeError("printer missing attribute: name")
+ if hasattr(printer, "name") and not hasattr(printer, "enabled"):
+ raise TypeError("printer missing attribute: enabled")
+ if not hasattr(printer, "__call__"):
+ raise TypeError("printer missing attribute: __call__")
+
+ if hasattr(printer, "name"):
+ name = printer.name
+ else:
+ name = printer.__name__
+ if obj is None or obj is gdb:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering global %s pretty-printer ...\n" % name)
+ obj = gdb
+ else:
+ if gdb.parameter("verbose"):
+ gdb.write(
+ "Registering %s pretty-printer for %s ...\n" % (name, obj.filename)
+ )
+
+ # Printers implemented as functions are old-style. In order to not risk
+ # breaking anything we do not check __name__ here.
+ if hasattr(printer, "name"):
+ if not isinstance(printer.name, str):
+ raise TypeError("printer name is not a string")
+ # If printer provides a name, make sure it doesn't contain ";".
+ # Semicolon is used by the info/enable/disable pretty-printer commands
+ # to delimit subprinters.
+ if printer.name.find(";") >= 0:
+ raise ValueError("semicolon ';' in printer name")
+ # Also make sure the name is unique.
+ # Alas, we can't do the same for functions and __name__, they could
+ # all have a canonical name like "lookup_function".
+ # PERF: gdb records printers in a list, making this inefficient.
+ i = 0
+ for p in obj.pretty_printers:
+ if hasattr(p, "name") and p.name == printer.name:
+ if replace:
+ del obj.pretty_printers[i]
+ break
+ else:
+ raise RuntimeError(
+ "pretty-printer already registered: %s" % printer.name
+ )
+ i = i + 1
+
+ obj.pretty_printers.insert(0, printer)
+
+
+class RegexpCollectionPrettyPrinter(PrettyPrinter):
+ """Class for implementing a collection of regular-expression based pretty-printers.
+
+ Intended usage:
+
+ pretty_printer = RegexpCollectionPrettyPrinter("my_library")
+ pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer)
+ ...
+ pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter)
+ register_pretty_printer(obj, pretty_printer)
+ """
+
+ class RegexpSubprinter(SubPrettyPrinter):
+ def __init__(self, name, regexp, gen_printer):
+ super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name)
+ self.regexp = regexp
+ self.gen_printer = gen_printer
+ self.compiled_re = re.compile(regexp)
+
+ def __init__(self, name):
+ super(RegexpCollectionPrettyPrinter, self).__init__(name, [])
+
+ def add_printer(self, name, regexp, gen_printer):
+ """Add a printer to the list.
+
+ The printer is added to the end of the list.
+
+ Arguments:
+ name: The name of the subprinter.
+ regexp: The regular expression, as a string.
+ gen_printer: A function/method that given a value returns an
+ object to pretty-print it.
+
+ Returns:
+ Nothing.
+ """
+
+ # NOTE: A previous version made the name of each printer the regexp.
+ # That makes it awkward to pass to the enable/disable commands (it's
+ # cumbersome to make a regexp of a regexp). So now the name is a
+ # separate parameter.
+
+ self.subprinters.append(self.RegexpSubprinter(name, regexp, gen_printer))
+
+ def __call__(self, val):
+ """Lookup the pretty-printer for the provided value."""
+
+ # Get the type name.
+ typename = gdb.types.get_basic_type(val.type).tag
+ if not typename:
+ typename = val.type.name
+ if not typename:
+ return None
+
+ # Iterate over table of type regexps to determine
+ # if a printer is registered for that type.
+ # Return an instantiation of the printer if found.
+ for printer in self.subprinters:
+ if printer.enabled and printer.compiled_re.search(typename):
+ return printer.gen_printer(val)
+
+ # Cannot find a pretty printer. Return None.
+ return None
+
+
+# A helper class for printing enum types. This class is instantiated
+# with a list of enumerators to print a particular Value.
+class _EnumInstance:
+ def __init__(self, enumerators, val):
+ self.enumerators = enumerators
+ self.val = val
+
+ def to_string(self):
+ flag_list = []
+ v = int(self.val)
+ any_found = False
+ for (e_name, e_value) in self.enumerators:
+ if v & e_value != 0:
+ flag_list.append(e_name)
+ v = v & ~e_value
+ any_found = True
+ if not any_found or v != 0:
+ # Leftover value.
+ flag_list.append("<unknown: 0x%x>" % v)
+ return "0x%x [%s]" % (int(self.val), " | ".join(flag_list))
+
+
+class FlagEnumerationPrinter(PrettyPrinter):
+ """A pretty-printer which can be used to print a flag-style enumeration.
+ A flag-style enumeration is one where the enumerators are or'd
+ together to create values. The new printer will print these
+ symbolically using '|' notation. The printer must be registered
+ manually. This printer is most useful when an enum is flag-like,
+ but has some overlap. GDB's built-in printing will not handle
+ this case, but this printer will attempt to."""
+
+ def __init__(self, enum_type):
+ super(FlagEnumerationPrinter, self).__init__(enum_type)
+ self.initialized = False
+
+ def __call__(self, val):
+ if not self.initialized:
+ self.initialized = True
+ flags = gdb.lookup_type(self.name)
+ self.enumerators = []
+ for field in flags.fields():
+ self.enumerators.append((field.name, field.enumval))
+ # Sorting the enumerators by value usually does the right
+ # thing.
+ self.enumerators.sort(key=lambda x: x[1])
+
+ if self.enabled:
+ return _EnumInstance(self.enumerators, val)
+ else:
+ return None
+
+
+# Builtin pretty-printers.
+# The set is defined as empty, and files in printing/*.py add their printers
+# to this with add_builtin_pretty_printer.
+
+_builtin_pretty_printers = RegexpCollectionPrettyPrinter("builtin")
+
+register_pretty_printer(None, _builtin_pretty_printers)
+
+# Add a builtin pretty-printer.
+
+
+def add_builtin_pretty_printer(name, regexp, printer):
+ _builtin_pretty_printers.add_printer(name, regexp, printer)
diff --git a/share/gdb/python/gdb/prompt.py b/share/gdb/python/gdb/prompt.py
new file mode 100644
index 0000000..9bbfcb9
--- /dev/null
+++ b/share/gdb/python/gdb/prompt.py
@@ -0,0 +1,163 @@
+# Extended prompt utilities.
+# Copyright (C) 2011-2023 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/>.
+
+""" Extended prompt library functions."""
+
+import gdb
+import os
+
+
+def _prompt_pwd(ignore):
+ "The current working directory."
+ return os.getcwd()
+
+
+def _prompt_object_attr(func, what, attr, nattr):
+ """Internal worker for fetching GDB attributes."""
+ if attr is None:
+ attr = nattr
+ try:
+ obj = func()
+ except gdb.error:
+ return "<no %s>" % what
+ if hasattr(obj, attr):
+ result = getattr(obj, attr)
+ if callable(result):
+ result = result()
+ return result
+ else:
+ return "<no attribute %s on current %s>" % (attr, what)
+
+
+def _prompt_frame(attr):
+ "The selected frame; an argument names a frame parameter."
+ return _prompt_object_attr(gdb.selected_frame, "frame", attr, "name")
+
+
+def _prompt_thread(attr):
+ "The selected thread; an argument names a thread parameter."
+ return _prompt_object_attr(gdb.selected_thread, "thread", attr, "num")
+
+
+def _prompt_version(attr):
+ "The version of GDB."
+ return gdb.VERSION
+
+
+def _prompt_esc(attr):
+ "The ESC character."
+ return "\033"
+
+
+def _prompt_bs(attr):
+ "A backslash."
+ return "\\"
+
+
+def _prompt_n(attr):
+ "A newline."
+ return "\n"
+
+
+def _prompt_r(attr):
+ "A carriage return."
+ return "\r"
+
+
+def _prompt_param(attr):
+ "A parameter's value; the argument names the parameter."
+ return gdb.parameter(attr)
+
+
+def _prompt_noprint_begin(attr):
+ "Begins a sequence of non-printing characters."
+ return "\001"
+
+
+def _prompt_noprint_end(attr):
+ "Ends a sequence of non-printing characters."
+ return "\002"
+
+
+prompt_substitutions = {
+ "e": _prompt_esc,
+ "\\": _prompt_bs,
+ "n": _prompt_n,
+ "r": _prompt_r,
+ "v": _prompt_version,
+ "w": _prompt_pwd,
+ "f": _prompt_frame,
+ "t": _prompt_thread,
+ "p": _prompt_param,
+ "[": _prompt_noprint_begin,
+ "]": _prompt_noprint_end,
+}
+
+
+def prompt_help():
+ """Generate help dynamically from the __doc__ strings of attribute
+ functions."""
+
+ result = ""
+ keys = sorted(prompt_substitutions.keys())
+ for key in keys:
+ result += " \\%s\t%s\n" % (key, prompt_substitutions[key].__doc__)
+ result += """
+A substitution can be used in a simple form, like "\\f".
+An argument can also be passed to it, like "\\f{name}".
+The meaning of the argument depends on the particular substitution."""
+ return result
+
+
+def substitute_prompt(prompt):
+ "Perform substitutions on PROMPT."
+
+ result = ""
+ plen = len(prompt)
+ i = 0
+ while i < plen:
+ if prompt[i] == "\\":
+ i = i + 1
+ if i >= plen:
+ break
+ cmdch = prompt[i]
+
+ if cmdch in prompt_substitutions:
+ cmd = prompt_substitutions[cmdch]
+
+ if i + 1 < plen and prompt[i + 1] == "{":
+ j = i + 1
+ while j < plen and prompt[j] != "}":
+ j = j + 1
+ # Just ignore formatting errors.
+ if j >= plen or prompt[j] != "}":
+ arg = None
+ else:
+ arg = prompt[i + 2 : j]
+ i = j
+ else:
+ arg = None
+ result += str(cmd(arg))
+ else:
+ # Unrecognized escapes are turned into the escaped
+ # character itself.
+ result += prompt[i]
+ else:
+ result += prompt[i]
+
+ i = i + 1
+
+ return result
diff --git a/share/gdb/python/gdb/styling.py b/share/gdb/python/gdb/styling.py
new file mode 100644
index 0000000..8540ab2
--- /dev/null
+++ b/share/gdb/python/gdb/styling.py
@@ -0,0 +1,101 @@
+# Styling related hooks.
+# Copyright (C) 2010-2023 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/>.
+
+"""Utilities for styling."""
+
+import gdb
+
+try:
+ from pygments import formatters, lexers, highlight
+ from pygments.token import Error, Comment, Text
+ from pygments.filters import TokenMergeFilter
+
+ _formatter = None
+
+ def get_formatter():
+ global _formatter
+ if _formatter is None:
+ _formatter = formatters.TerminalFormatter()
+ return _formatter
+
+ def colorize(filename, contents):
+ # Don't want any errors.
+ try:
+ lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
+ formatter = get_formatter()
+ return highlight(contents, lexer, formatter).encode(
+ gdb.host_charset(), "backslashreplace"
+ )
+ except:
+ return None
+
+ class HandleNasmComments(TokenMergeFilter):
+ @staticmethod
+ def fix_comments(lexer, stream):
+ in_comment = False
+ for ttype, value in stream:
+ if ttype is Error and value == "#":
+ in_comment = True
+ if in_comment:
+ if ttype is Text and value == "\n":
+ in_comment = False
+ else:
+ ttype = Comment.Single
+ yield ttype, value
+
+ def filter(self, lexer, stream):
+ f = HandleNasmComments.fix_comments
+ return super().filter(lexer, f(lexer, stream))
+
+ _asm_lexers = {}
+
+ def __get_asm_lexer(gdbarch):
+ lexer_type = "asm"
+ try:
+ # For an i386 based architecture, in 'intel' mode, use the nasm
+ # lexer.
+ flavor = gdb.parameter("disassembly-flavor")
+ if flavor == "intel" and gdbarch.name()[:4] == "i386":
+ lexer_type = "nasm"
+ except:
+ # If GDB is built without i386 support then attempting to fetch
+ # the 'disassembly-flavor' parameter will throw an error, which we
+ # 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())
+ _asm_lexers[lexer_type].add_filter("raiseonerror")
+ return _asm_lexers[lexer_type]
+
+ def colorize_disasm(content, gdbarch):
+ # Don't want any errors.
+ try:
+ lexer = __get_asm_lexer(gdbarch)
+ formatter = get_formatter()
+ return highlight(content, lexer, formatter).rstrip().encode()
+ except:
+ return content
+
+except:
+
+ def colorize(filename, contents):
+ return None
+
+ def colorize_disasm(content, gdbarch):
+ return None
diff --git a/share/gdb/python/gdb/types.py b/share/gdb/python/gdb/types.py
new file mode 100644
index 0000000..dcec301
--- /dev/null
+++ b/share/gdb/python/gdb/types.py
@@ -0,0 +1,183 @@
+# Type utilities.
+# Copyright (C) 2010-2023 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/>.
+
+"""Utilities for working with gdb.Types."""
+
+import gdb
+
+
+def get_basic_type(type_):
+ """Return the "basic" type of a type.
+
+ Arguments:
+ type_: The type to reduce to its basic type.
+
+ Returns:
+ type_ with const/volatile is stripped away,
+ and typedefs/references converted to the underlying type.
+ """
+
+ while (
+ type_.code == gdb.TYPE_CODE_REF
+ or type_.code == gdb.TYPE_CODE_RVALUE_REF
+ or type_.code == gdb.TYPE_CODE_TYPEDEF
+ ):
+ if type_.code == gdb.TYPE_CODE_REF or type_.code == gdb.TYPE_CODE_RVALUE_REF:
+ type_ = type_.target()
+ else:
+ type_ = type_.strip_typedefs()
+ return type_.unqualified()
+
+
+def has_field(type_, field):
+ """Return True if a type has the specified field.
+
+ Arguments:
+ type_: The type to examine.
+ It must be one of gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION.
+ field: The name of the field to look up.
+
+ Returns:
+ True if the field is present either in type_ or any baseclass.
+
+ Raises:
+ TypeError: The type is not a struct or union.
+ """
+
+ type_ = get_basic_type(type_)
+ if type_.code != gdb.TYPE_CODE_STRUCT and type_.code != gdb.TYPE_CODE_UNION:
+ raise TypeError("not a struct or union")
+ for f in type_.fields():
+ if f.is_base_class:
+ if has_field(f.type, field):
+ return True
+ else:
+ # NOTE: f.name could be None
+ if f.name == field:
+ return True
+ return False
+
+
+def make_enum_dict(enum_type):
+ """Return a dictionary from a program's enum type.
+
+ Arguments:
+ enum_type: The enum to compute the dictionary for.
+
+ Returns:
+ The dictionary of the enum.
+
+ Raises:
+ TypeError: The type is not an enum.
+ """
+
+ if enum_type.code != gdb.TYPE_CODE_ENUM:
+ raise TypeError("not an enum type")
+ enum_dict = {}
+ for field in enum_type.fields():
+ # The enum's value is stored in "enumval".
+ enum_dict[field.name] = field.enumval
+ return enum_dict
+
+
+def deep_items(type_):
+ """Return an iterator that recursively traverses anonymous fields.
+
+ Arguments:
+ type_: The type to traverse. It should be one of
+ gdb.TYPE_CODE_STRUCT or gdb.TYPE_CODE_UNION.
+
+ Returns:
+ an iterator similar to gdb.Type.iteritems(), i.e., it returns
+ pairs of key, value, but for any anonymous struct or union
+ field that field is traversed recursively, depth-first.
+ """
+ for k, v in type_.iteritems():
+ if k:
+ yield k, v
+ else:
+ for i in deep_items(v.type):
+ yield i
+
+
+class TypePrinter(object):
+ """The base class for type printers.
+
+ Instances of this type can be used to substitute type names during
+ 'ptype'.
+
+ A type printer must have at least 'name' and 'enabled' attributes,
+ and supply an 'instantiate' method.
+
+ The 'instantiate' method must either return None, or return an
+ object which has a 'recognize' method. This method must accept a
+ gdb.Type argument and either return None, meaning that the type
+ was not recognized, or a string naming the type.
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.enabled = True
+
+ def instantiate(self):
+ return None
+
+
+# Helper function for computing the list of type recognizers.
+def _get_some_type_recognizers(result, plist):
+ for printer in plist:
+ if printer.enabled:
+ inst = printer.instantiate()
+ if inst is not None:
+ result.append(inst)
+ return None
+
+
+def get_type_recognizers():
+ "Return a list of the enabled type recognizers for the current context."
+ result = []
+
+ # First try the objfiles.
+ for objfile in gdb.objfiles():
+ _get_some_type_recognizers(result, objfile.type_printers)
+ # Now try the program space.
+ _get_some_type_recognizers(result, gdb.current_progspace().type_printers)
+ # Finally, globals.
+ _get_some_type_recognizers(result, gdb.type_printers)
+
+ return result
+
+
+def apply_type_recognizers(recognizers, type_obj):
+ """Apply the given list of type recognizers to the type TYPE_OBJ.
+ If any recognizer in the list recognizes TYPE_OBJ, returns the name
+ given by the recognizer. Otherwise, this returns None."""
+ for r in recognizers:
+ result = r.recognize(type_obj)
+ if result is not None:
+ return result
+ return None
+
+
+def register_type_printer(locus, printer):
+ """Register a type printer.
+ PRINTER is the type printer instance.
+ LOCUS is either an objfile, a program space, or None, indicating
+ global registration."""
+
+ if locus is None:
+ locus = gdb
+ locus.type_printers.insert(0, printer)
diff --git a/share/gdb/python/gdb/unwinder.py b/share/gdb/python/gdb/unwinder.py
new file mode 100644
index 0000000..a854d8d
--- /dev/null
+++ b/share/gdb/python/gdb/unwinder.py
@@ -0,0 +1,95 @@
+# Copyright (C) 2015-2023 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/>.
+
+"""Unwinder class and register_unwinder function."""
+
+import gdb
+
+
+class Unwinder(object):
+ """Base class (or a template) for frame unwinders written in Python.
+
+ An unwinder has a single method __call__ and the attributes
+ described below.
+
+ Attributes:
+ name: The name of the unwinder.
+ enabled: A boolean indicating whether the unwinder is enabled.
+ """
+
+ def __init__(self, name):
+ """Constructor.
+
+ Args:
+ name: An identifying name for the unwinder.
+ """
+ self.name = name
+ self.enabled = True
+
+ def __call__(self, pending_frame):
+ """GDB calls this method to unwind a frame.
+
+ Arguments:
+ pending_frame: gdb.PendingFrame instance.
+
+ Returns:
+ gdb.UnwindInfo instance.
+ """
+ raise NotImplementedError("Unwinder __call__.")
+
+
+def register_unwinder(locus, unwinder, replace=False):
+ """Register unwinder in given locus.
+
+ The unwinder is prepended to the locus's unwinders list. Unwinder
+ name should be unique.
+
+ Arguments:
+ locus: Either an objfile, progspace, or None (in which case
+ the unwinder is registered globally).
+ unwinder: An object of a gdb.Unwinder subclass
+ replace: If True, replaces existing unwinder with the same name.
+ Otherwise, raises exception if unwinder with the same
+ name already exists.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ RuntimeError: Unwinder name is not unique
+ TypeError: Bad locus type
+ """
+ if locus is None:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering global %s unwinder ...\n" % unwinder.name)
+ locus = gdb
+ elif isinstance(locus, gdb.Objfile) or isinstance(locus, gdb.Progspace):
+ if gdb.parameter("verbose"):
+ gdb.write(
+ "Registering %s unwinder for %s ...\n" % (unwinder.name, locus.filename)
+ )
+ else:
+ raise TypeError("locus should be gdb.Objfile or gdb.Progspace or None")
+
+ i = 0
+ for needle in locus.frame_unwinders:
+ if needle.name == unwinder.name:
+ if replace:
+ del locus.frame_unwinders[i]
+ else:
+ raise RuntimeError("Unwinder %s already exists." % unwinder.name)
+ i += 1
+ locus.frame_unwinders.insert(0, unwinder)
+ gdb.invalidate_cached_frames()
diff --git a/share/gdb/python/gdb/xmethod.py b/share/gdb/python/gdb/xmethod.py
new file mode 100644
index 0000000..ab6cd28
--- /dev/null
+++ b/share/gdb/python/gdb/xmethod.py
@@ -0,0 +1,274 @@
+# Python side of the support for xmethods.
+# Copyright (C) 2013-2023 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/>.
+
+"""Utilities for defining xmethods"""
+
+import gdb
+import re
+import sys
+
+
+class XMethod(object):
+ """Base class (or a template) for an xmethod description.
+
+ Currently, the description requires only the 'name' and 'enabled'
+ attributes. Description objects are managed by 'XMethodMatcher'
+ objects (see below). Note that this is only a template for the
+ interface of the XMethodMatcher.methods objects. One could use
+ this class or choose to use an object which supports this exact same
+ interface. Also, an XMethodMatcher can choose not use it 'methods'
+ attribute. In such cases this class (or an equivalent) is not used.
+
+ Attributes:
+ name: The name of the xmethod.
+ enabled: A boolean indicating if the xmethod is enabled.
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.enabled = True
+
+
+class XMethodMatcher(object):
+ """Abstract base class for matching an xmethod.
+
+ When looking for xmethods, GDB invokes the `match' method of a
+ registered xmethod matcher to match the object type and method name.
+ The `match' method in concrete classes derived from this class should
+ return an `XMethodWorker' object, or a list of `XMethodWorker'
+ objects if there is a match (see below for 'XMethodWorker' class).
+
+ Attributes:
+ name: The name of the matcher.
+ enabled: A boolean indicating if the matcher is enabled.
+ methods: A sequence of objects of type 'XMethod', or objects
+ which have at least the attributes of an 'XMethod' object.
+ This list is used by the 'enable'/'disable'/'info' commands to
+ enable/disable/list the xmethods registered with GDB. See
+ the 'match' method below to know how this sequence is used.
+ This attribute is None if the matcher chooses not have any
+ xmethods managed by it.
+ """
+
+ def __init__(self, name):
+ """
+ Args:
+ name: An identifying name for the xmethod or the group of
+ xmethods returned by the `match' method.
+ """
+ self.name = name
+ self.enabled = True
+ self.methods = None
+
+ def match(self, class_type, method_name):
+ """Match class type and method name.
+
+ In derived classes, it should return an XMethodWorker object, or a
+ sequence of 'XMethodWorker' objects. Only those xmethod workers
+ whose corresponding 'XMethod' descriptor object is enabled should be
+ returned.
+
+ Args:
+ class_type: The class type (gdb.Type object) to match.
+ method_name: The name (string) of the method to match.
+ """
+ raise NotImplementedError("XMethodMatcher match")
+
+
+class XMethodWorker(object):
+ """Base class for all xmethod workers defined in Python.
+
+ An xmethod worker is an object which matches the method arguments, and
+ invokes the method when GDB wants it to. Internally, GDB first invokes the
+ 'get_arg_types' method to perform overload resolution. If GDB selects to
+ invoke this Python xmethod, then it invokes it via the overridden
+ '__call__' method. The 'get_result_type' method is used to implement
+ 'ptype' on the xmethod.
+
+ Derived classes should override the 'get_arg_types', 'get_result_type'
+ and '__call__' methods.
+ """
+
+ def get_arg_types(self):
+ """Return arguments types of an xmethod.
+
+ A sequence of gdb.Type objects corresponding to the arguments of the
+ xmethod are returned. If the xmethod takes no arguments, then 'None'
+ or an empty sequence is returned. If the xmethod takes only a single
+ argument, then a gdb.Type object or a sequence with a single gdb.Type
+ element is returned.
+ """
+ raise NotImplementedError("XMethodWorker get_arg_types")
+
+ def get_result_type(self, *args):
+ """Return the type of the result of the xmethod.
+
+ Args:
+ args: Arguments to the method. Each element of the tuple is a
+ gdb.Value object. The first element is the 'this' pointer
+ value. These are the same arguments passed to '__call__'.
+
+ Returns:
+ A gdb.Type object representing the type of the result of the
+ xmethod.
+ """
+ raise NotImplementedError("XMethodWorker get_result_type")
+
+ def __call__(self, *args):
+ """Invoke the xmethod.
+
+ Args:
+ args: Arguments to the method. Each element of the tuple is a
+ gdb.Value object. The first element is the 'this' pointer
+ value.
+
+ Returns:
+ A gdb.Value corresponding to the value returned by the xmethod.
+ Returns 'None' if the method does not return anything.
+ """
+ raise NotImplementedError("XMethodWorker __call__")
+
+
+class SimpleXMethodMatcher(XMethodMatcher):
+ """A utility class to implement simple xmethod mathers and workers.
+
+ See the __init__ method below for information on how instances of this
+ class can be used.
+
+ For simple classes and methods, one can choose to use this class. For
+ complex xmethods, which need to replace/implement template methods on
+ possibly template classes, one should implement their own xmethod
+ matchers and workers. See py-xmethods.py in testsuite/gdb.python
+ directory of the GDB source tree for examples.
+ """
+
+ class SimpleXMethodWorker(XMethodWorker):
+ def __init__(self, method_function, arg_types):
+ self._arg_types = arg_types
+ self._method_function = method_function
+
+ def get_arg_types(self):
+ return self._arg_types
+
+ def __call__(self, *args):
+ return self._method_function(*args)
+
+ def __init__(
+ self, name, class_matcher, method_matcher, method_function, *arg_types
+ ):
+ """
+ Args:
+ name: Name of the xmethod matcher.
+ class_matcher: A regular expression used to match the name of the
+ class whose method this xmethod is implementing/replacing.
+ method_matcher: A regular expression used to match the name of the
+ method this xmethod is implementing/replacing.
+ method_function: A Python callable which would be called via the
+ 'invoke' method of the worker returned by the objects of this
+ class. This callable should accept the object (*this) as the
+ first argument followed by the rest of the arguments to the
+ method. All arguments to this function should be gdb.Value
+ objects.
+ arg_types: The gdb.Type objects corresponding to the arguments that
+ this xmethod takes. It can be None, or an empty sequence,
+ or a single gdb.Type object, or a sequence of gdb.Type objects.
+ """
+ XMethodMatcher.__init__(self, name)
+ assert callable(method_function), (
+ "The 'method_function' argument to 'SimpleXMethodMatcher' "
+ "__init__ method should be a callable."
+ )
+ self._method_function = method_function
+ self._class_matcher = class_matcher
+ self._method_matcher = method_matcher
+ self._arg_types = arg_types
+
+ def match(self, class_type, method_name):
+ cm = re.match(self._class_matcher, str(class_type.unqualified().tag))
+ mm = re.match(self._method_matcher, method_name)
+ if cm and mm:
+ return SimpleXMethodMatcher.SimpleXMethodWorker(
+ self._method_function, self._arg_types
+ )
+
+
+# A helper function for register_xmethod_matcher which returns an error
+# object if MATCHER is not having the requisite attributes in the proper
+# format.
+
+
+def _validate_xmethod_matcher(matcher):
+ if not hasattr(matcher, "match"):
+ return TypeError("Xmethod matcher is missing method: match")
+ if not hasattr(matcher, "name"):
+ return TypeError("Xmethod matcher is missing attribute: name")
+ if not hasattr(matcher, "enabled"):
+ return TypeError("Xmethod matcher is missing attribute: enabled")
+ if not isinstance(matcher.name, str):
+ return TypeError("Attribute 'name' of xmethod matcher is not a " "string")
+ if matcher.name.find(";") >= 0:
+ return ValueError("Xmethod matcher name cannot contain ';' in it")
+
+
+# A helper function for register_xmethod_matcher which looks up an
+# xmethod matcher with NAME in LOCUS. Returns the index of the xmethod
+# matcher in 'xmethods' sequence attribute of the LOCUS. If NAME is not
+# found in LOCUS, then -1 is returned.
+
+
+def _lookup_xmethod_matcher(locus, name):
+ for i in range(0, len(locus.xmethods)):
+ if locus.xmethods[i].name == name:
+ return i
+ return -1
+
+
+def register_xmethod_matcher(locus, matcher, replace=False):
+ """Registers a xmethod matcher MATCHER with a LOCUS.
+
+ Arguments:
+ locus: The locus in which the xmethods should be registered.
+ It can be 'None' to indicate that the xmethods should be
+ registered globally. Or, it could be a gdb.Objfile or a
+ gdb.Progspace object in which the xmethods should be
+ registered.
+ matcher: The xmethod matcher to register with the LOCUS. It
+ should be an instance of 'XMethodMatcher' class.
+ replace: If True, replace any existing xmethod matcher with the
+ same name in the locus. Otherwise, if a matcher with the same name
+ exists in the locus, raise an exception.
+ """
+ err = _validate_xmethod_matcher(matcher)
+ if err:
+ raise err
+ if not locus:
+ locus = gdb
+ if locus == gdb:
+ locus_name = "global"
+ else:
+ locus_name = locus.filename
+ index = _lookup_xmethod_matcher(locus, matcher.name)
+ if index >= 0:
+ if replace:
+ del locus.xmethods[index]
+ else:
+ raise RuntimeError(
+ "Xmethod matcher already registered with "
+ "%s: %s" % (locus_name, matcher.name)
+ )
+ if gdb.parameter("verbose"):
+ gdb.write("Registering xmethod matcher '%s' with %s' ...\n")
+ locus.xmethods.insert(0, matcher)