Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Craig Blackmore <craig.blackmore@embecosm.com>
To: Andrew Burgess <aburgess@redhat.com>, gdb-patches@sourceware.org
Cc: eliz@gnu.org
Subject: Re: [PATCH v4 1/1] gdb: Add python support for demangling register unwind values
Date: Wed, 27 May 2026 11:49:53 +0100	[thread overview]
Message-ID: <54c4f94a-9982-4af9-8473-7ab58184f7dc@embecosm.com> (raw)
In-Reply-To: <87a4ttytz3.fsf@redhat.com>


On 21/05/2026 10:47, Andrew Burgess wrote:
> Craig Blackmore <craig.blackmore@embecosm.com> writes:
>
>> This patch addresses the scenario where a register value is saved in
>> some mangled form on the target and gdb does not have sufficient
>> information to demangle it.
>>
>> After getting a register value from an unwinder, gdb will call out to
>> extension languages to allow the value to be demangled.  This avoids
>> having to write a new unwinder and allows the standard unwinders to be
>> used.
>>
>> This patch adds the python class
>> `gdb.unwinder.UnwindRegisterValueTransformer` which is the base class
>> for transformers which provide methods for transforming unwind register
>> values.  Currently one such method is supported, `demangle`.  There is
>> no similar support for mangling values being written back to the target
>> as this was not something I needed to be able to do.  Such support can
>> be added to the python API in future by supporting a `mangle` method.
>> Currently writing back to a demangled register will produce an "Attempt
>> to assign to an unmodifiable value" error.
> I suspect that most cases a single transformer would only have a single
> task, so having an growing list of different transformer method names
> seems unnecessary.
Agreed.

> Why not make UnwindRegisterValueTransformer's callable (add a __call__
> method), and have the signature be:
>
>    def __call__(self, frame_id, reg_descriptor, value, read_p):
>     # ...
>
> Most of these arguments are as for the current 'demangle', but READ_P
> would be true if the value is being read, of FALSE if it's being
> written.
>
> An possibly better alternative would be for the transformers to have two
> methods 'read' and 'write' like:
>
>    def read(self, frame_id, reg_desc, value):
>      # ...
>
>    def write(self, frame_id, reg_desc, value):
>      # ...
I prefer this alternative because if we later support the write case it
should be obvious if an existing transformer is missing an
implementation for it.

> With the idea being that when a register is being read the read method
> is invoked, and when written the write method is invoked, though right
> now you don't need the write method, so I'm not sure how you'd document
> this.  Maybe you'd document it as TODO in the code, but not actually
> document it in the manual?
I'd suggest a TODO in the code and then noting in the documentation that
transforming a value being written back is not currently supported
(without mentioning the `write` method).

I'll put together a v5.

Thanks,
Craig

> This would fully move away from the "demangle" concept and focus more on
> the transformer concept.
>
> Thoughts?
>
> Thanks,
> Andrew
>
>
>
>> Only one transformer can be registered globally at any one time.
>> Attempting to register more than one transformer or to any other locus
>> produces an error.  The function for registering transformers takes a
>> locus to allow other loci to be supported in future.  Registered
>> transformers are stored in a list so that registering multiple
>> transformers may be supported in future.
>>
>> `gdbpy_get_register_descriptor` is now externally visible as it is used
>> to create a gdb.RegisterDescriptor object to pass to the `demangle`
>> method.
>>
>> This patch adds read-only attribute `gdb.Value.lval_type` which
>> transformer methods can use to decide to skip modifying values that did
>> not come directly from the target and may not need transforming, for
>> example, values taken directly from DWARF.
>> ---
>>   gdb/NEWS                                      |  20 +++
>>   gdb/doc/python.texi                           |  93 ++++++++++
>>   gdb/extension-priv.h                          |   8 +
>>   gdb/extension.c                               |  35 ++++
>>   gdb/extension.h                               |   6 +
>>   gdb/frame.c                                   |   9 +
>>   gdb/guile/guile.c                             |   2 +
>>   gdb/python/lib/gdb/__init__.py                |  21 +++
>>   gdb/python/lib/gdb/unwinder.py                |  89 ++++++++-
>>   gdb/python/py-registers.c                     |   2 +-
>>   gdb/python/py-unwind.c                        | 169 ++++++++++++++++++
>>   gdb/python/py-value.c                         |  21 +++
>>   gdb/python/python-internal.h                  |   3 +
>>   gdb/python/python.c                           |   4 +-
>>   .../gdb.python/py-unwind-transformer-error.py |  33 ++++
>>   .../gdb.python/py-unwind-transformer.c        |  80 +++++++++
>>   .../gdb.python/py-unwind-transformer.exp      | 115 ++++++++++++
>>   .../gdb.python/py-unwind-transformer.py       |  48 +++++
>>   gdb/testsuite/gdb.python/py-value.exp         |   8 +
>>   19 files changed, 763 insertions(+), 3 deletions(-)
>>   create mode 100644 gdb/testsuite/gdb.python/py-unwind-transformer-error.py
>>   create mode 100644 gdb/testsuite/gdb.python/py-unwind-transformer.c
>>   create mode 100644 gdb/testsuite/gdb.python/py-unwind-transformer.exp
>>   create mode 100644 gdb/testsuite/gdb.python/py-unwind-transformer.py
>>
>> diff --git a/gdb/NEWS b/gdb/NEWS
>> index e233906153a..085a32c9709 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -457,6 +457,26 @@ info threads [-gid] [-stopped] [-running] [ID]...
>>        unavailability like gdb.Value.is_optimized_out checks for
>>        optimized out values.
>>   
>> +  ** New constants gdb.NOT_LVAL, gdb.LVAL_REGISTER and gdb.LVAL_MEMORY
>> +     to represent the lvalue type of a value.
>> +
>> +  ** New read-only attribute gdb.Value.lval_type which indicates the
>> +     lvalue type of the value.  Currently supports not_lval,
>> +     lval_register and lval_memory.  Any other type is mapped to
>> +     not_lval.  Additional types may be supported in future.
>> +
>> +  ** Added gdb.unwinder.UnwindRegisterValueTransformer.  This is the
>> +     base class for transformers which provide methods for transforming
>> +     unwind register values.  Currently one such method, demangle, is
>> +     supported, which is called after obtaining a register value from an
>> +     unwinder and allows that value to be demangled before being used by
>> +     GDB.
>> +
>> +  ** New function gdb.unwinder.register_unwind_register_value_transformer
>> +     that can register an instance of a sub-class of
>> +     gdb.unwinder.UnwindRegisterValueTransformer as a transformer for
>> +     unwind register values.
>> +
>>   * Guile API
>>   
>>     ** New type <gdb:color> for dealing with colors.
>> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
>> index 96b46a98091..f4db846a36c 100644
>> --- a/gdb/doc/python.texi
>> +++ b/gdb/doc/python.texi
>> @@ -977,6 +977,22 @@ fetched when the value is needed, or when the @code{fetch_lazy}
>>   method is invoked.
>>   @end defvar
>>   
>> +@defvar Value.lval_type
>> +The lvalue type of this @code{gdb.Value}.  The value of this attribute
>> +can be one of:
>> +@table @code
>> +@item gdb.LVAL_REGISTER
>> +The @code{gdb.Value} is in a register.
>> +
>> +@item gdb.LVAL_MEMORY
>> +The @code{gdb.Value} is in memory.
>> +
>> +@item gdb.NOT_LVAL
>> +The @code{gdb.Value} is not an lval or not covered by any of the other codes.
>> +@end table
>> +Additional lvalue types may be added in future.
>> +@end defvar
>> +
>>   @defvar Value.bytes
>>   The value of this attribute is a @code{bytes} object containing the
>>   bytes that make up this @code{Value}'s complete value in little endian
>> @@ -3111,6 +3127,22 @@ no such value was passed.
>>   @end defvar
>>   @end deftp
>>   
>> +@deftp {class} gdb.unwinder.UnwindRegisterValueTransformer
>> +
>> +The @code{UnwindRegisterValueTransformer} class is a base class from
>> +which unwind register value transformers created by users can derive,
>> +though it is not required that they derive from this class, so long as
>> +they have the required @code{name} attribute.
>> +
>> +@defun UnwindRegisterValueTransformer.__init__ (name)
>> +The @var{name} is the name of this transformer.
>> +@end defun
>> +
>> +@defvar UnwindRegisterValueTransformer.name
>> +A read-only attribute which is a string, the name of this transformer.
>> +@end defvar
>> +@end deftp
>> +
>>   @subheading Registering an Unwinder
>>   
>>   Object files and program spaces can have unwinders registered with
>> @@ -3199,6 +3231,67 @@ the matching unwinders are enabled.  The @code{enabled} field of each
>>   matching unwinder is set to @code{True}.
>>   @end table
>>   
>> +@subheading Transforming Unwind Register Values
>> +
>> +An unwind register value transformer provides methods for transforming
>> +unwind register values.  Currently only one type of transform,
>> +demangling, is supported, by implementing the @code{demangle} method.
>> +Additional transforms may be added in future.
>> +
>> +After getting a register value from an unwinder, @value{GDBN} will call
>> +the @code{demangle} method of registered transformers to allow them to
>> +demangle the value.  This is useful in case a register value needs
>> +demangling before @value{GDBN} uses it and avoids the need to write a
>> +new unwinder.
>> +
>> +@subheading Registering an Unwind Register Value Transformer
>> +
>> +Currently, only one transformer can be registered at any one time and it
>> +is registered globally.  Attempting to register more than one
>> +transformer or to a different locus will raise an exception.
>> +
>> +The @code{gdb.unwinder} module provides the function to register a
>> +transformer:
>> +
>> +@defun gdb.unwinder.register_unwind_register_value_transformer (locus, transformer, replace=False)
>> +@var{locus} specifies to which transformer list to prepend the
>> +@var{transformer}.  Only @code{None} is supported, which registers the
>> +transformer globally.  Attempting to register to any other locus will
>> +raise an exception.  Only one transformer can be registered at any one
>> +time.  An attempt to add a second transformer raises an exception unless
>> +@var{replace} is @code{True} and the new and old transformer have the
>> +same name, in which case the old transformer is deleted and the new
>> +transformer is registered in its place.
>> +@end defun
>> +
>> +@subheading Skeleton Code for an Unwind Register Value Transformer
>> +
>> +Here is an example of how to structure a user created transformer:
>> +
>> +@smallexample
>> +from gdb.unwinder import UnwindRegisterValueTransformer
>> +
>> +class MyUnwindRegisterValueTransformer(UnwindRegisterValueTransformer):
>> +    def __init__(self):
>> +        super().__init__("MyTransformer_Name")
>> +
>> +    def demangle(self, frame, reg, value):
>> +        """
>> +        Return new value if demangled, otherwise None.
>> +        """
>> +        if not <frame we need to handle>:
>> +            return None
>> +
>> +        if <reg needs demangling>:
>> +            new_value = ... compute demangled value ...
>> +            return new_value
>> +
>> +        return None
>> +
>> +my_transformer = MyUnwindRegisterValueTransformer()
>> +gdb.unwinder.register_unwind_register_value_transformer(None, my_transformer, False)
>> +@end smallexample
>> +
>>   @node Xmethods In Python
>>   @subsubsection Xmethods In Python
>>   @cindex xmethods in Python
>> diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
>> index 4ef87415413..deca276d4d6 100644
>> --- a/gdb/extension-priv.h
>> +++ b/gdb/extension-priv.h
>> @@ -310,6 +310,14 @@ struct extension_language_ops
>>   				  program_space *pspace,
>>   				  const struct bfd_build_id *build_id,
>>   				  const char *filename);
>> +
>> +  /* Update the unwind VALUE of a register.  Return EXT_LANG_RC_OK if VALUE was
>> +     updated, EXT_LANG_RC_NOP if it was left unchanged and EXT_LANG_RC_ERROR if
>> +     there was an error.  */
>> +
>> +  enum ext_lang_rc (*demangle_unwind_register_value) (frame_info_ptr frame,
>> +						      int regnum,
>> +						      struct value **value);
>>   };
>>   
>>   /* State necessary to restore a signal handler to its previous value.  */
>> diff --git a/gdb/extension.c b/gdb/extension.c
>> index d8ef8123ab5..82314af1d88 100644
>> --- a/gdb/extension.c
>> +++ b/gdb/extension.c
>> @@ -1139,6 +1139,41 @@ ext_lang_before_prompt (const char *current_gdb_prompt)
>>       }
>>   }
>>   
>> +/* See extension.h.  */
>> +
>> +struct value *
>> +ext_lang_demangle_unwind_register_value (frame_info_ptr frame, int regnum,
>> +					 struct value *val)
>> +{
>> +  gdb_assert (val != nullptr);
>> +  for (const struct extension_language_defn *extlang : extension_languages)
>> +    {
>> +      enum ext_lang_rc rc;
>> +
>> +      if (extlang->ops == nullptr
>> +	  || extlang->ops->demangle_unwind_register_value == nullptr)
>> +	continue;
>> +
>> +      rc = extlang->ops->demangle_unwind_register_value (frame, regnum, &val);
>> +      /* If the demangler changed the value or errored then do not call any
>> +	 others.  */
>> +      switch (rc)
>> +	{
>> +	case EXT_LANG_RC_OK:
>> +	case EXT_LANG_RC_ERROR:
>> +	  return val;
>> +	/* If the demangler did not change the value then continue calling other
>> +	   demanglers for other extension languages.  */
>> +	case EXT_LANG_RC_NOP:
>> +	  break;
>> +	default:
>> +	  gdb_assert_not_reached (
>> +	    "bad return from demangle_unwind_register_value");
>> +	}
>> +    }
>> +  return val;
>> +}
>> +
>>   INIT_GDB_FILE (extension)
>>   {
>>     gdb::observers::before_prompt.attach (ext_lang_before_prompt, "extension");
>> diff --git a/gdb/extension.h b/gdb/extension.h
>> index e351b5b672e..63cbea22c3f 100644
>> --- a/gdb/extension.h
>> +++ b/gdb/extension.h
>> @@ -433,6 +433,12 @@ extern ext_lang_missing_file_result ext_lang_find_objfile_from_buildid
>>     (program_space *pspace, const struct bfd_build_id *build_id,
>>      const char *filename);
>>   
>> +/* Take the unwound VALUE of the register identified by REGNUM being unwound
>> +   from FRAME and return the demangled value.  */
>> +
>> +extern struct value * ext_lang_demangle_unwind_register_value
>> +  (frame_info_ptr frame, int regnum, struct value *value);
>> +
>>   #if GDB_SELF_TEST
>>   namespace selftests {
>>   extern void (*hook_set_active_ext_lang) ();
>> diff --git a/gdb/frame.c b/gdb/frame.c
>> index 61d37316c6a..f20b09376e2 100644
>> --- a/gdb/frame.c
>> +++ b/gdb/frame.c
>> @@ -1340,6 +1340,15 @@ frame_unwind_register_value (const frame_info_ptr &next_frame, int regnum)
>>   	       user_reg_map_regnum_to_name (gdbarch, regnum));
>>       }
>>   
>> +  /* Allow extension languages to demangle the unwound value.
>> +
>> +     Do not demangle before frame id has been computed as this would cause
>> +     recursion as gdbpy_demangle_unwind_register_value calls
>> +     frame_info_to_frame_object which calls get_frame_id.
>> +  */
>> +  if (next_frame->this_id.p == frame_id_status::COMPUTED)
>> +    value = ext_lang_demangle_unwind_register_value (next_frame, regnum, value);
>> +
>>     if (frame_debug)
>>       {
>>         string_file debug_file;
>> diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
>> index ec5f6d08951..4397faeb435 100644
>> --- a/gdb/guile/guile.c
>> +++ b/gdb/guile/guile.c
>> @@ -138,6 +138,8 @@ static const struct extension_language_ops guile_extension_ops =
>>     NULL, /* gdbscm_get_matching_xmethod_workers */
>>     NULL, /* gdbscm_colorize */
>>     NULL, /* gdbscm_print_insn */
>> +
>> +  nullptr, /* gdbscm_demangle_unwind_register_value */
>>   };
>>   #endif
>>   
>> diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
>> index 1a4aefcd802..83aba4552fb 100644
>> --- a/gdb/python/lib/gdb/__init__.py
>> +++ b/gdb/python/lib/gdb/__init__.py
>> @@ -104,6 +104,8 @@ frame_unwinders = []
>>   # The missing file handlers.  Each item is a tuple with the form
>>   # (TYPE, HANDLER) where TYPE is a string either 'debug' or 'objfile'.
>>   missing_file_handlers = []
>> +# Initial unwind register value transformers.
>> +unwind_register_value_transformers = []
>>   
>>   
>>   def _execute_unwinders(pending_frame):
>> @@ -144,6 +146,25 @@ def _execute_unwinders(pending_frame):
>>   
>>       return None
>>   
>> +def _execute_unwind_register_value_demanglers(frame, reg, value):
>> +    """Internal function called from GDB to execute unwind register value
>> +       demanglers.
>> +
>> +    Arguments:
>> +        frame: gdb.Frame instance.
>> +        reg: gdb.RegisterDescriptor instance.
>> +        value: gdb.Value instance.
>> +
>> +    Returns:
>> +        gdb.Value instance if value changed, otherwise None.
>> +    """
>> +    transformer_count = len(unwind_register_value_transformers)
>> +    assert transformer_count < 2
>> +
>> +    if transformer_count == 1:
>> +        return unwind_register_value_transformers[0].demangle(frame, reg, value)
>> +
>> +    return None
>>   
>>   # Convenience variable to GDB's python directory
>>   PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
>> diff --git a/gdb/python/lib/gdb/unwinder.py b/gdb/python/lib/gdb/unwinder.py
>> index dc456d1987f..37a3db21945 100644
>> --- a/gdb/python/lib/gdb/unwinder.py
>> +++ b/gdb/python/lib/gdb/unwinder.py
>> @@ -16,7 +16,7 @@
>>   """Unwinder class and register_unwinder function."""
>>   
>>   import gdb
>> -
>> +import _gdb.unwinder
>>   
>>   class Unwinder(object):
>>       """Base class (or a template) for frame unwinders written in Python.
>> @@ -138,3 +138,90 @@ def register_unwinder(locus, unwinder, replace=False):
>>           i += 1
>>       locus.frame_unwinders.insert(0, unwinder)
>>       gdb.invalidate_cached_frames()
>> +
>> +class UnwindRegisterValueTransformer(object):
>> +    """Base class (or a template) for unwind register value transformers written
>> +    in Python.
>> +    """
>> +    def __init__(self, name):
>> +        """Constructor.
>> +
>> +        Arguments:
>> +            name: An identifying name for the transformer.
>> +        """
>> +
>> +        if not isinstance(name, str):
>> +            raise TypeError("incorrect type for name: %s" % type(name))
>> +
>> +        self._name = name
>> +
>> +    @property
>> +    def name(self):
>> +        return self._name
>> +
>> +    def demangle(self, frame, reg, value):
>> +        """GDB calls this method to demangle a register unwind value.
>> +
>> +        Arguments:
>> +            frame: gdb.Frame instance.
>> +            reg: gdb.RegisterDescriptor instance.
>> +            value: gdb.Value instance.
>> +
>> +        Returns:
>> +            gdb.Value instance if value changed, otherwise None.
>> +        """
>> +        raise NotImplementedError("UnwindRegisterValueTransformer demangle.")
>> +
>> +def register_unwind_register_value_transformer(locus, transformer, replace=False):
>> +    """Register unwind register value transformer in given locus.
>> +
>> +    Currently only one transformer can be registered at any one time and
>> +    it must be registered globally.
>> +
>> +    Arguments:
>> +        locus: Must be None (which registers the transformer globally).
>> +               Any other locus will result in an exception.
>> +        transformer: An object of a gdb.unwinder.UnwindRegisterValueTransformer
>> +                     subclass.
>> +        replace: If True, replaces existing transformer if it has the
>> +                 same name.  Otherwise, raises exception if there is
>> +                 already a registered transformer.
>> +
>> +    Returns:
>> +        Nothing.
>> +
>> +    Raises:
>> +        TypeError: Bad locus type
>> +        RuntimeError: Only one transformer can be registered
>> +    """
>> +    if locus is None:
>> +        if gdb.parameter("verbose"):
>> +            gdb.write(
>> +                "Registering global unwind register value transformer '%s'\n"
>> +                % transformer.name)
>> +        locus = gdb
>> +    else:
>> +        raise TypeError("locus should be None")
>> +
>> +    transformer_count = len(locus.unwind_register_value_transformers)
>> +
>> +    assert transformer_count < 2
>> +
>> +    if transformer_count == 1:
>> +        if replace and transformer.name == locus.unwind_register_value_transformers[0].name:
>> +            del locus.unwind_register_value_transformers[0]
>> +        else:
>> +            raise RuntimeError("Only one unwind register value transformer can "
>> +                               "be registered")
>> +
>> +    locus.unwind_register_value_transformers.insert(0, transformer)
>> +
>> +    # Call the private _set_transformer_enabled function within the
>> +    # _gdb.unwind module.  This function sets a global flag within GDB's
>> +    # C++ code that enables or disables the Python unwind value
>> +    # transformer functionality, this improves performance of unwinding
>> +    # by avoiding unneeded calls into Python when we know that no
>> +    # transformers are registered.
>> +    _gdb.unwinder._set_transformer_enabled(True)
>> +
>> +    gdb.invalidate_cached_frames()
>> diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c
>> index c6e0748e935..15a72a162c2 100644
>> --- a/gdb/python/py-registers.c
>> +++ b/gdb/python/py-registers.c
>> @@ -137,7 +137,7 @@ gdbpy_reggroup_name (PyObject *self, void *closure)
>>      each REGNUM (in GDBARCH) only one descriptor is ever created, which is
>>      then cached on the GDBARCH.  */
>>   
>> -static gdbpy_ref<>
>> +gdbpy_ref<>
>>   gdbpy_get_register_descriptor (struct gdbarch *gdbarch,
>>   			       int regnum)
>>   {
>> diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
>> index dcf86f7db3d..198a2e4aa74 100644
>> --- a/gdb/python/py-unwind.c
>> +++ b/gdb/python/py-unwind.c
>> @@ -30,6 +30,7 @@
>>   #include "stack.h"
>>   #include "charset.h"
>>   #include "block.h"
>> +#include "value.h"
>>   
>>   
>>   /* Debugging of Python unwinders.  */
>> @@ -1004,6 +1005,62 @@ pyuw_on_new_gdbarch (gdbarch *newarch)
>>       }
>>   }
>>   
>> +static bool python_transformer_enabled = false;
>> +
>> +/* Implement gdb._set_transformer_enabled function.  Takes a boolean parameter
>> +   and sets whether GDB should enter the Python unwind register value
>> +   transformer code or not.
>> +
>> +   This is called from within the Python code when a new transformer is
>> +   registered.  When no transformers are registered the global C++ flag is set
>> +   to false, and GDB never even enters the Python environment to check for a
>> +   transformer.
>> +
>> +   When the user registers a new Python transformer, the global C++ flag
>> +   is set to true, and now GDB will enter the Python environment to use the
>> +   transformer.  */
>> +
>> +static PyObject *
>> +pyuw_set_transformer_enabled (PyObject *self, PyObject *args, PyObject *kw)
>> +{
>> +  PyObject *newstate;
>> +  static const char *keywords[] = { "state", nullptr };
>> +  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O!", keywords,
>> +	  &PyBool_Type, &newstate))
>> +    return nullptr;
>> +
>> +  python_transformer_enabled = newstate == Py_True;
>> +  Py_RETURN_NONE;
>> +}
>> +
>> +/* These are the methods we add into the _gdb.unwinder module, which
>> +   are then imported into the gdb.unwinder module.  These are global
>> +   functions that support unwinding.  */
>> +
>> +PyMethodDef python_unwinder_methods[] =
>> +{
>> +  { "_set_transformer_enabled", (PyCFunction) pyuw_set_transformer_enabled,
>> +    METH_VARARGS | METH_KEYWORDS,
>> +    "_set_transformer_enabled (STATE) -> None\n\
>> +Set whether GDB should call into the Python transformer code or not." },
>> +  {nullptr, nullptr, 0, nullptr}
>> +};
>> +
>> +/* Structure to define the _gdb.unwinder module.  */
>> +
>> +static struct PyModuleDef python_unwinder_module_def =
>> +{
>> +  PyModuleDef_HEAD_INIT,
>> +  "_gdb.unwinder",
>> +  nullptr,
>> +  -1,
>> +  python_unwinder_methods,
>> +  nullptr,
>> +  nullptr,
>> +  nullptr,
>> +  nullptr
>> +};
>> +
>>   /* Initialize unwind machinery.  */
>>   
>>   static int
>> @@ -1017,6 +1074,19 @@ gdbpy_initialize_unwind ()
>>     if (gdbpy_type_ready (&unwind_info_object_type) < 0)
>>       return -1;
>>   
>> +  PyObject *gdb_unwinder_module;
>> +  gdb_unwinder_module = PyModule_Create (&python_unwinder_module_def);
>> +
>> +  if (gdb_pymodule_addobject (gdb_module, "unwinder",
>> +			      gdb_unwinder_module) < 0)
>> +    return -1;
>> +
>> +  /* This is needed so that 'import _gdb.unwinder' will work.  */
>> +  PyObject *dict = PyImport_GetModuleDict ();
>> +  if (PyDict_SetItemString (dict, "_gdb.unwinder",
>> +			    gdb_unwinder_module) < 0)
>> +    return -1;
>> +
>>     return 0;
>>   }
>>   
>> @@ -1170,3 +1240,102 @@ PyTypeObject unwind_info_object_type =
>>     0,                              /* tp_init */
>>     0,                              /* tp_alloc */
>>   };
>> +
>> +/* Execute python demanglers and handle the result.  */
>> +
>> +enum ext_lang_rc
>> +gdbpy_demangle_unwind_register_value (frame_info_ptr frame, int regnum,
>> +				      struct value **val)
>> +{
>> +  if (!gdb_python_initialized || !python_transformer_enabled)
>> +    return EXT_LANG_RC_ERROR;
>> +
>> +  gdbarch *gdbarch = get_frame_arch (frame);
>> +  gdbpy_enter enter_py (gdbarch);
>> +
>> +  /* Disable transformers whilst creating a python frame object if the frame is
>> +     the sentinel frame and regnum is pc.  This is to avoid recursion as the
>> +     check for a corrupt stack in `frame_info_to_frame_object` results in
>> +     another call to this function with the same arguments due to the following
>> +     chain of calls:
>> +
>> +       get_prev_frame
>> +	 -> get_frame_pc_if_available
>> +	   -> frame_unwind_pc (frame->next)
>> +	     -> ...
>> +	       -> frame_unwind_register_value (frame, pc)
>> +		 -> ...
>> +		   -> gdbpy_demangle_unwind_register_value (frame, pc, ...)
>> +
>> +     The recursive call is passed the sentinel frame again because for the
>> +     sentinel frame:  frame->next == frame.  */
>> +  int pc_regnum = gdbarch_pc_regnum (gdbarch);
>> +  if (regnum == pc_regnum && is_sentinel_frame_id (get_frame_id (frame)))
>> +    python_transformer_enabled = false;
>> +  gdbpy_ref<> py_frame = frame_info_to_frame_object (frame);
>> +  if (regnum == pc_regnum && is_sentinel_frame_id (get_frame_id (frame)))
>> +    python_transformer_enabled = true;
>> +
>> +  gdbpy_ref<> py_reg_obj (gdbpy_get_register_descriptor (gdbarch, regnum));
>> +  if (py_reg_obj == nullptr)
>> +    {
>> +      gdbpy_print_stack ();
>> +      return EXT_LANG_RC_ERROR;
>> +    }
>> +
>> +  gdbpy_ref<> py_value_obj (value_to_value_object (*val));
>> +  if (py_value_obj == nullptr)
>> +    {
>> +      gdbpy_print_stack ();
>> +      return EXT_LANG_RC_ERROR;
>> +    }
>> +
>> +  /* Call demangler.  */
>> +  const char *func_name = "_execute_unwind_register_value_demanglers";
>> +  if (gdb_python_module == nullptr
>> +      || ! PyObject_HasAttrString (gdb_python_module, func_name))
>> +    {
>> +      PyErr_SetString (PyExc_NameError,
>> +	"Installation error: gdb._execute_unwind_register_value_demanglers "
>> +	"function is missing");
>> +      gdbpy_print_stack ();
>> +      return EXT_LANG_RC_ERROR;
>> +    }
>> +  gdbpy_ref<> pyo_execute (
>> +    PyObject_GetAttrString (gdb_python_module, func_name));
>> +  if (pyo_execute == nullptr)
>> +    {
>> +      gdbpy_print_stack ();
>> +      return EXT_LANG_RC_ERROR;
>> +    }
>> +
>> +  /* Demangler should return a gdb.Value if value has been changed, otherwise
>> +     None.  */
>> +  gdbpy_ref<> pyo_execute_ret
>> +    (PyObject_CallFunctionObjArgs (pyo_execute.get (), py_frame.get (),
>> +				   py_reg_obj.get (), py_value_obj.get (),
>> +				   nullptr));
>> +  if (pyo_execute_ret == nullptr)
>> +    {
>> +      gdbpy_print_stack ();
>> +      return EXT_LANG_RC_ERROR;
>> +    }
>> +  /* Use original value.  */
>> +  if (pyo_execute_ret == Py_None)
>> +    return EXT_LANG_RC_NOP;
>> +
>> +  /* Verify the return value is a gdb.Value.  */
>> +  struct value *new_val = convert_value_from_python (pyo_execute_ret.get ());
>> +
>> +  if (new_val == nullptr)
>> +    {
>> +      gdbpy_print_stack ();
>> +      error (_("The `demangle` method of an unwind register value transformer "
>> +	       "should return a gdb.Value instance if the value is changed, "
>> +	       "otherwise None"));
>> +    }
>> +  /* Finally update val to the demangled value.  */
>> +  *val = new_val;
>> +
>> +  return EXT_LANG_RC_OK;
>> +}
>> diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
>> index a5b2303728c..79b72716d0e 100644
>> --- a/gdb/python/py-value.c
>> +++ b/gdb/python/py-value.c
>> @@ -1406,6 +1406,19 @@ valpy_fetch_lazy (PyObject *self, PyObject *args)
>>     Py_RETURN_NONE;
>>   }
>>   
>> +/* Return the lval_type of this value.  */
>> +static PyObject *
>> +valpy_get_lval_type (PyObject *self, void *closure)
>> +{
>> +  struct value *value = ((value_object *) self)->value;
>> +
>> +  enum lval_type type = value->lval ();
>> +  if (type != lval_register && type != lval_memory)
>> +    type = not_lval;
>> +
>> +  return gdb_py_object_from_longest (type).release ();
>> +}
>> +
>>   /* Calculate and return the address of the PyObject as the value of
>>      the builtin __hash__ call.  */
>>   static Py_hash_t
>> @@ -2245,6 +2258,12 @@ gdbpy_is_value_object (PyObject *obj)
>>   static int
>>   gdbpy_initialize_values ()
>>   {
>> +  if (PyModule_AddIntConstant (gdb_module, "NOT_LVAL", not_lval) < 0
>> +      || PyModule_AddIntConstant (gdb_module, "LVAL_REGISTER",
>> +				  lval_register) < 0
>> +      || PyModule_AddIntConstant (gdb_module, "LVAL_MEMORY", lval_memory) < 0)
>> +    return -1;
>> +
>>     return gdbpy_type_ready (&value_object_type);
>>   }
>>   
>> @@ -2269,6 +2288,8 @@ static gdb_PyGetSetDef value_object_getset[] = {
>>       "Boolean telling whether the value is lazy (not fetched yet\n\
>>   from the inferior).  A lazy value is fetched when needed, or when\n\
>>   the \"fetch_lazy()\" method is called.", NULL },
>> +  { "lval_type", valpy_get_lval_type, nullptr,
>> +    "Return the type of the lval", nullptr },
>>     { "bytes", valpy_get_bytes, valpy_set_bytes,
>>       "Return a bytearray containing the bytes of this value.", nullptr },
>>     {NULL}  /* Sentinel */
>> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
>> index 37bc37691fe..b8d0e2429b8 100644
>> --- a/gdb/python/python-internal.h
>> +++ b/gdb/python/python-internal.h
>> @@ -447,6 +447,8 @@ extern enum ext_lang_bp_stop gdbpy_breakpoint_cond_says_stop
>>     (const struct extension_language_defn *, struct breakpoint *);
>>   extern int gdbpy_breakpoint_has_cond (const struct extension_language_defn *,
>>   				      struct breakpoint *b);
>> +extern enum ext_lang_rc gdbpy_demangle_unwind_register_value
>> +  (frame_info_ptr frame, int regnum, struct value **val);
>>   
>>   extern enum ext_lang_rc gdbpy_get_matching_xmethod_workers
>>     (const struct extension_language_defn *extlang,
>> @@ -521,6 +523,7 @@ PyObject *gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw);
>>   gdbpy_ref<> gdbarch_to_arch_object (struct gdbarch *gdbarch);
>>   PyObject *gdbpy_all_architecture_names (PyObject *self, PyObject *args);
>>   
>> +gdbpy_ref<> gdbpy_get_register_descriptor (struct gdbarch *gdbarch, int regnum);
>>   PyObject *gdbpy_new_register_descriptor_iterator (struct gdbarch *gdbarch,
>>   						  const char *group_name);
>>   PyObject *gdbpy_new_reggroup_iterator (struct gdbarch *gdbarch);
>> diff --git a/gdb/python/python.c b/gdb/python/python.c
>> index 502fe682b2c..5837c5afcb8 100644
>> --- a/gdb/python/python.c
>> +++ b/gdb/python/python.c
>> @@ -185,7 +185,9 @@ static const struct extension_language_ops python_extension_ops =
>>     gdbpy_print_insn,
>>   
>>     gdbpy_handle_missing_debuginfo,
>> -  gdbpy_find_objfile_from_buildid
>> +  gdbpy_find_objfile_from_buildid,
>> +
>> +  gdbpy_demangle_unwind_register_value
>>   };
>>   
>>   #endif /* HAVE_PYTHON */
>> diff --git a/gdb/testsuite/gdb.python/py-unwind-transformer-error.py b/gdb/testsuite/gdb.python/py-unwind-transformer-error.py
>> new file mode 100644
>> index 00000000000..c926f374f1f
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-unwind-transformer-error.py
>> @@ -0,0 +1,33 @@
>> +# Copyright (C) 2025 Free Software Foundation, Inc.
>> +
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation; either version 3 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> +
>> +from gdb.unwinder import UnwindRegisterValueTransformer
>> +
>> +class TestUnwindRegisterValueTransformer(UnwindRegisterValueTransformer):
>> +
>> +    def __init__(self):
>> +        super().__init__("test transformer")
>> +
>> +    def demangle(self, frame, reg, value):
>> +        """
>> +        Return a type that cannot be converted to a gdb.Value.
>> +        """
>> +        return []
>> +
>> +test_transformer = TestUnwindRegisterValueTransformer()
>> +gdb.unwinder.register_unwind_register_value_transformer(None, test_transformer,
>> +                                                        True)
>> +
>> +print("Python script imported")
>> diff --git a/gdb/testsuite/gdb.python/py-unwind-transformer.c b/gdb/testsuite/gdb.python/py-unwind-transformer.c
>> new file mode 100644
>> index 00000000000..9ac9672d325
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-unwind-transformer.c
>> @@ -0,0 +1,80 @@
>> +/* This test program is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2025 Free Software Foundation, Inc.
>> +
>> +   This program is free software; you can redistribute it and/or modify
>> +   it under the terms of the GNU General Public License as published by
>> +   the Free Software Foundation; either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   This program is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU General Public License for more details.
>> +
>> +   You should have received a copy of the GNU General Public License
>> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
>> +
>> +/* This is the test program loaded into GDB by the py-unwind-transformer test.
>> +
>> +   The start/end of each non-inlined function mangles/demangles the saved RBP
>> +   and RIP to simulate a target that stores RBP and RIP in a mangled form on the
>> +   stack.  */
>> +
>> +#define MY_FRAME ((volatile __UINTPTR_TYPE__ *)__builtin_frame_address (0))
>> +
>> +/* Mangle previous RBP and RIP using bitwise negation.
>> +
>> +   This is ABI-specifc.  On AMD64, the word at RBP contains contains previous
>> +   RBP and the word at RBP+8 contains previous RIP (unless -fomit-frame-pointer
>> +   was enabled *and* the compiler was able to omit RBP).  */
>> +
>> +#define MANGLE(FRAME)                                \
>> +  do                                                 \
>> +    {                                                \
>> +      *FRAME = ~*FRAME;                              \
>> +      *(FRAME + 1) = ~*(FRAME + 1);                  \
>> +    } while (0)
>> +
>> +#define DEMANGLE MANGLE
>> +
>> +/* This function does not have its own frame when inlined so does not do any
>> +   mangling.  */
>> +__attribute__ ((always_inline))
>> +static inline void
>> +f3_inline (void)
>> +{
>> +  __attribute__ ((unused))
>> +  volatile int x = 5;
>> +}
>> +
>> +static void
>> +f2 (void)
>> +{
>> +  MANGLE (MY_FRAME);
>> +
>> +  f3_inline ();
>> +
>> +  DEMANGLE (MY_FRAME);
>> +}
>> +
>> +static void
>> +f1 (void)
>> +{
>> +  MANGLE (MY_FRAME);
>> +
>> +  f2 ();
>> +
>> +  DEMANGLE (MY_FRAME);
>> +}
>> +
>> +int
>> +main ()
>> +{
>> +  MANGLE (MY_FRAME);
>> +
>> +  f1 ();
>> +
>> +  DEMANGLE (MY_FRAME);
>> +  return 0;
>> +}
>> diff --git a/gdb/testsuite/gdb.python/py-unwind-transformer.exp b/gdb/testsuite/gdb.python/py-unwind-transformer.exp
>> new file mode 100644
>> index 00000000000..91922e4a286
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-unwind-transformer.exp
>> @@ -0,0 +1,115 @@
>> +# Copyright (C) 2025 Free Software Foundation, Inc.
>> +
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation; either version 3 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> +
>> +# This file is part of the GDB testsuite.  It tests python support for unwind
>> +# register value transformers.
>> +
>> +# The transformer for this test is written for a hypothetical x86_64 target that
>> +# mangles RBP and RIP when saving them to the stack.  The C file for this test
>> +# simulates this behaviour by mangling the saved RBP and RIP at the start of
>> +# each function and demangling them at the end.
>> +
>> +require allow_python_tests
>> +
>> +standard_testfile
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {debug}] } {
>> +    return -1
>> +}
>> +
>> +# This test runs on a specific platform.
>> +require is_x86_64_m64_target
>> +
>> +# The following tests require execution.
>> +if {![runto_main]} {
>> +    return 0
>> +}
>> +
>> +# Continue to the innermost frame of the execution.
>> +gdb_breakpoint "f3_inline"
>> +gdb_continue_to_breakpoint "break in f3_inline"
>> +
>> +# Without the transformer sourced the backtrace should be incorrect.
>> +#
>> +# * Inline frame #0 is ok because it shares the same frame as #1.
>> +# * Frame #1 is ok because the frame address and program counter are in
>> +# registers RBP and RIP whose contents is not mangled.
>> +# * Frame #2 is not ok because its frame address and program counter are mangled
>> +# in memory.
>> +gdb_test_lines "bt 3" "broken backtrace when transformer not sourced" \
>> +[multi_line \
>> +    "#0 \[^\r\n\]* f3_inline \\(\\) at \[^\r\n\]+" \
>> +    "#1 \[^\r\n\]* f2 \\(\\) at \[^\r\n\]+" \
>> +    "#2 \[^\r\n\]* \\?\\? \\(\\)" \
>> +    "\\(More stack frames follow...\\)"]
>> +
>> +# Source the python unwind value transformer
>> +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
>> +
>> +gdb_test "source ${pyfile}" \
>> +    "Python script imported" \
>> +    "import python scripts"
>> +
>> +# The saved RBP and RIP should now be demangled before GDB uses them and the
>> +# backtrace should be correct.
>> +gdb_test_lines "bt" "correct backtrace when transformer sourced" \
>> +[multi_line \
>> +    "#0 \[^\r\n\]* f3_inline \\(\\) at \[^\r\n\]+" \
>> +    "#1 \[^\r\n\]* f2 \\(\\) at \[^\r\n\]+" \
>> +    "#2 \[^\r\n\]* f1 \\(\\) at \[^\r\n\]+" \
>> +    "#3 \[^\r\n\]* main \\(\\) at \[^\r\n\]+"]
>> +
>> +gdb_test_lines "frame 2" "select frame 2" \
>> +[multi_line \
>> +    "#2 \[^\r\n\]* f1 \\(\\) at \[^\r\n\]+" \
>> +    "\[0-9\]+.*f2 \\(\\);"]
>> +
>> +# Writing back to a demangled register is not currently supported.  Check that
>> +# it fails gracefully.
>> +gdb_test "p \$pc = 0x12345678" "Attempt to assign to an unmodifiable value." \
>> +    "error writing back to demangled register"
>> +
>> +# Check for error message when trying to add too many transformers.
>> +# Same name, replace = False.
>> +gdb_test_no_output "python obj = UnwindRegisterValueTransformer('test_transformer')" \
>> +  "Instantiate transformer 1"
>> +gdb_test \
>> +  "python gdb.unwinder.register_unwind_register_value_transformer(None, obj)" \
>> +  "Only one unwind register value transformer can be registered" \
>> +  "exceed transformer limit: replace=False"
>> +# Different name, replace = True.
>> +gdb_test_no_output "python obj = UnwindRegisterValueTransformer('test_transformer2')" \
>> +  "Instantiate transformer 2"
>> +gdb_test \
>> +  "python gdb.unwinder.register_unwind_register_value_transformer(None, obj, True)" \
>> +  "Only one unwind register value transformer can be registered" \
>> +  "exceed transformer limit: replace=True"
>> +
>> +# Check for error message when trying to register a transformer to an
>> +# unsupported locus.
>> +gdb_test_no_output "python pspace = gdb.selected_inferior().progspace"
>> +gdb_test_no_output "python obj = UnwindRegisterValueTransformer('test_transformer')" \
>> +  "Instantiate transformer 3"
>> +gdb_test "python gdb.unwinder.register_unwind_register_value_transformer(pspace, obj)" "locus should be None" "unsupported locus"
>> +
>> +# Source a python unwind value transformer that returns an invalid type.
>> +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}-error.py]
>> +# Check for error message on invalid return type.
>> +gdb_test "source ${pyfile}" \
>> +[multi_line \
>> +    "Python script imported" \
>> +    "Python Exception <class 'TypeError'>: Could not convert Python object: [].*" \
>> +    "The `demangle` method of an unwind register value transformer should return a gdb.Value instance if the value is changed, otherwise None.*"] \
>> +"error on invalid return type"
>> diff --git a/gdb/testsuite/gdb.python/py-unwind-transformer.py b/gdb/testsuite/gdb.python/py-unwind-transformer.py
>> new file mode 100644
>> index 00000000000..a113aaea9b2
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-unwind-transformer.py
>> @@ -0,0 +1,48 @@
>> +# Copyright (C) 2025 Free Software Foundation, Inc.
>> +
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation; either version 3 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> +
>> +from gdb.unwinder import UnwindRegisterValueTransformer
>> +
>> +class TestUnwindRegisterValueTransformer(UnwindRegisterValueTransformer):
>> +
>> +    def __init__(self):
>> +        super().__init__("test transformer")
>> +
>> +    def demangle1(self, value):
>> +        """
>> +        For this test case mangled values are the bitwise negation of the
>> +        original value.
>> +        """
>> +        mask = 0xffffffffffffffff
>> +        return value.cast(gdb.Value (mask).type) ^ mask
>> +
>> +    def demangle(self, frame, reg, value):
>> +        """
>> +        Demangle RBP or RIP if their value is from memory.
>> +        """
>> +        if frame.type() == gdb.SENTINEL_FRAME or frame.type() == gdb.INLINE_FRAME:
>> +            return None
>> +
>> +        if ((reg.name == 'rbp' or reg.name == 'rip')
>> +            and value.lval_type == gdb.LVAL_MEMORY):
>> +                return self.demangle1(value)
>> +
>> +        return None
>> +
>> +
>> +test_transformer = TestUnwindRegisterValueTransformer()
>> +gdb.unwinder.register_unwind_register_value_transformer(None, test_transformer, False)
>> +
>> +print("Python script imported")
>> diff --git a/gdb/testsuite/gdb.python/py-value.exp b/gdb/testsuite/gdb.python/py-value.exp
>> index a384f4139dd..8b534c786ee 100644
>> --- a/gdb/testsuite/gdb.python/py-value.exp
>> +++ b/gdb/testsuite/gdb.python/py-value.exp
>> @@ -257,6 +257,14 @@ proc test_value_in_inferior {} {
>>     # Smoke-test is_optimized_out attribute
>>     gdb_test "python print ('result = %s' % arg0.is_optimized_out)" "= False" "test is_optimized_out attribute"
>>   
>> +  # Test lval_type attribute
>> +  gdb_test "python print (gdb.parse_and_eval ('\$pc').lval_type == gdb.LVAL_REGISTER)" "True" "test lval_type attribute: lval_register"
>> +  gdb_test "python print (gdb.parse_and_eval ('*(int *)\$sp').lval_type == gdb.LVAL_MEMORY)" "True" "test lval_type attribute: lval_memory"
>> +  gdb_test "python print (gdb.Value(5).type.optimized_out().lval_type == gdb.NOT_LVAL)" "True" "test lval_type attribute: not_lval"
>> +  # Unsupported types are mapped to not_lval
>> +  gdb_test "p \$myvar = 3"
>> +  gdb_test "python print (gdb.parse_and_eval ('\$myvar').lval_type == gdb.NOT_LVAL)" "True" "test lval_type attribute: lval_internalvar"
>> +
>>     # Test address attribute
>>     gdb_test "python print ('result = %s' % arg0.address)" "= 0x\[\[:xdigit:\]\]+" "test address attribute"
>>   
>> -- 
>> 2.43.0

  reply	other threads:[~2026-05-27 10:50 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-10 11:04 [RFC PATCH] " Craig Blackmore
2025-04-10 12:31 ` Eli Zaretskii
2025-05-08 10:17 ` [RFC PATCH v2] " Craig Blackmore
2025-05-08 11:40   ` Eli Zaretskii
2025-05-12 13:15   ` Andrew Burgess
2026-04-30 15:19     ` Craig Blackmore
2026-04-30 15:31     ` [PATCH v3] " Craig Blackmore
2026-04-30 16:05       ` Eli Zaretskii
2026-05-20 13:16       ` [PATCH v4 0/1] " Craig Blackmore
2026-05-20 13:16         ` [PATCH v4 1/1] " Craig Blackmore
2026-05-20 15:44           ` Eli Zaretskii
2026-05-21  9:47           ` Andrew Burgess
2026-05-27 10:49             ` Craig Blackmore [this message]
2026-06-08 12:04         ` [PATCH v5 0/1] " Craig Blackmore
2026-06-08 12:04           ` [PATCH v5 1/1] gdb: Add python support for transforming " Craig Blackmore
2026-06-08 12:13             ` Eli Zaretskii
2026-07-15  9:56           ` [PING][PATCH v5 0/1] gdb: Add python support for demangling " Craig Blackmore
2026-07-29 15:01           ` [PATCH " Craig Blackmore

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=54c4f94a-9982-4af9-8473-7ab58184f7dc@embecosm.com \
    --to=craig.blackmore@embecosm.com \
    --cc=aburgess@redhat.com \
    --cc=eliz@gnu.org \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox