+2020-07-28 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * python/py-frame.c: Remove 'user-regs.h' include.
+ (frapy_read_register): Rewrite to make use of
+ gdbpy_parse_register_id.
+ * python/py-registers.c (gdbpy_parse_register_id): New function,
+ moved here from python/py-unwind.c. Updated the return type, and
+ also accepts register descriptor objects.
+ * python/py-unwind.c: Remove 'user-regs.h' include.
+ (pyuw_parse_register_id): Moved to python/py-registers.c.
+ (unwind_infopy_add_saved_register): Update to use
+ gdbpy_parse_register_id.
+ (pending_framepy_read_register): Likewise.
+ * python/python-internal.h (gdbpy_parse_register_id): Declare.
+
2020-07-28 Andrew Burgess <andrew.burgess@embecosm.com>
* python/py-registers.c: Add 'user-regs.h' include.
+2020-07-28 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * python.texi (Unwinding Frames in Python): Update descriptions
+ for PendingFrame.read_register and
+ gdb.UnwindInfo.add_saved_register.
+ (Frames In Python): Update description of Frame.read_register.
+
2020-07-28 Andrew Burgess <andrew.burgess@embecosm.com>
* python.texi (Registers In Python): Document new find function.
@defun PendingFrame.read_register (reg)
This method returns the contents of the register @var{reg} in the
-frame as a @code{gdb.Value} object. @var{reg} can be either a
-register number or a register name; the values are platform-specific.
-They are usually found in the corresponding
-@file{@var{platform}-tdep.h} file in the @value{GDBN} source tree. If
-@var{reg} does not name a register for the current architecture, this
-method will throw an exception.
+frame as a @code{gdb.Value} object. For a description of the
+acceptable values of @var{reg} see
+@ref{gdbpy_frame_read_register,,Frame.read_register}. If @var{reg}
+does not name a register for the current architecture, this method
+will throw an exception.
Note that this method will always return a @code{gdb.Value} for a
valid register name. This does not mean that the value will be valid.
specify caller registers that have been saved in this frame:
@defun gdb.UnwindInfo.add_saved_register (reg, value)
-@var{reg} identifies the register. It can be a number or a name, just
-as for the @code{PendingFrame.read_register} method above.
+@var{reg} identifies the register, for a description of the acceptable
+values see @ref{gdbpy_frame_read_register,,Frame.read_register}.
@var{value} is a register value (a @code{gdb.Value} object).
@end defun
@anchor{gdbpy_frame_read_register}
@defun Frame.read_register (register)
-Return the value of @var{register} in this frame. The @var{register}
-argument must be a string (e.g., @code{'sp'} or @code{'rax'}).
-Returns a @code{Gdb.Value} object. Throws an exception if @var{register}
-does not exist.
+Return the value of @var{register} in this frame. Returns a
+@code{Gdb.Value} object. Throws an exception if @var{register} does
+not exist. The @var{register} argument must be one of the following:
+@enumerate
+@item
+A string that is the name of a valid register (e.g., @code{'sp'} or
+@code{'rax'}).
+@item
+A @code{gdb.RegisterDescriptor} object (@pxref{Registers In Python}).
+@item
+A @value{GDBN} internal, platform specific number. Using these
+numbers is supported for historic reasons, but is not recommended as
+future changes to @value{GDBN} could change the mapping between
+numbers and the registers they represent, breaking any Python code
+that uses the platform-specific numbers. The numbers are usually
+found in the corresponding @file{@var{platform}-tdep.h} file in the
+@value{GDBN} source tree.
+@end enumerate
+Using a string to access registers will be slightly slower than the
+other two methods as @value{GDBN} must look up the mapping between
+name and internal register number. If performance is critical
+consider looking up and caching a @code{gdb.RegisterDescriptor}
+object.
@end defun
@defun Frame.read_var (variable @r{[}, block@r{]})
#include "python-internal.h"
#include "symfile.h"
#include "objfiles.h"
-#include "user-regs.h"
typedef struct {
PyObject_HEAD
static PyObject *
frapy_read_register (PyObject *self, PyObject *args)
{
- const char *regnum_str;
+ PyObject *pyo_reg_id;
struct value *val = NULL;
- if (!PyArg_ParseTuple (args, "s", ®num_str))
+ if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id))
return NULL;
-
try
{
struct frame_info *frame;
FRAPY_REQUIRE_VALID (self, frame);
- regnum = user_reg_map_name_to_regnum (get_frame_arch (frame),
- regnum_str,
- strlen (regnum_str));
- if (regnum >= 0)
- val = value_of_register (regnum, frame);
+ if (!gdbpy_parse_register_id (get_frame_arch (frame), pyo_reg_id,
+ ®num))
+ {
+ PyErr_SetString (PyExc_ValueError, "Bad register");
+ return NULL;
+ }
+
+ gdb_assert (regnum >= 0);
+ val = value_of_register (regnum, frame);
if (val == NULL)
- PyErr_SetString (PyExc_ValueError, _("Unknown register."));
+ PyErr_SetString (PyExc_ValueError, _("Can't read register."));
}
catch (const gdb_exception &except)
{
Py_RETURN_NONE;
}
+/* See python-internal.h. */
+
+bool
+gdbpy_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
+ int *reg_num)
+{
+ gdb_assert (pyo_reg_id != NULL);
+
+ /* The register could be a string, its name. */
+ if (gdbpy_is_string (pyo_reg_id))
+ {
+ gdb::unique_xmalloc_ptr<char> reg_name (gdbpy_obj_to_string (pyo_reg_id));
+
+ if (reg_name != NULL)
+ {
+ *reg_num = user_reg_map_name_to_regnum (gdbarch, reg_name.get (),
+ strlen (reg_name.get ()));
+ return *reg_num >= 0;
+ }
+ }
+ /* The register could be its internal GDB register number. */
+ else if (PyInt_Check (pyo_reg_id))
+ {
+ long value;
+ if (gdb_py_int_as_long (pyo_reg_id, &value) && (int) value == value)
+ {
+ if (user_reg_map_regnum_to_name (gdbarch, value) != NULL)
+ {
+ *reg_num = (int) value;
+ return true;
+ }
+ }
+ }
+ /* The register could be a gdb.RegisterDescriptor object. */
+ else if (PyObject_IsInstance (pyo_reg_id,
+ (PyObject *) ®ister_descriptor_object_type))
+ {
+ register_descriptor_object *reg
+ = (register_descriptor_object *) pyo_reg_id;
+ if (reg->gdbarch == gdbarch)
+ {
+ *reg_num = reg->regnum;
+ return true;
+ }
+ else
+ PyErr_SetString (PyExc_ValueError,
+ _("Invalid Architecture in RegisterDescriptor"));
+ }
+
+ gdb_assert (PyErr_Occurred ());
+ return false;
+}
+
/* Initializes the new Python classes from this file in the gdb module. */
int
#include "python-internal.h"
#include "regcache.h"
#include "valprint.h"
-#include "user-regs.h"
#define TRACE_PY_UNWIND(level, args...) if (pyuw_debug >= level) \
{ fprintf_unfiltered (gdb_stdlog, args); }
static struct gdbarch_data *pyuw_gdbarch_data;
-/* Parses register id, which can be either a number or a name.
- Returns 1 on success, 0 otherwise. */
-
-static int
-pyuw_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
- int *reg_num)
-{
- if (pyo_reg_id == NULL)
- return 0;
- if (gdbpy_is_string (pyo_reg_id))
- {
- gdb::unique_xmalloc_ptr<char> reg_name (gdbpy_obj_to_string (pyo_reg_id));
-
- if (reg_name == NULL)
- return 0;
- *reg_num = user_reg_map_name_to_regnum (gdbarch, reg_name.get (),
- strlen (reg_name.get ()));
- return *reg_num >= 0;
- }
- else if (PyInt_Check (pyo_reg_id))
- {
- long value;
- if (gdb_py_int_as_long (pyo_reg_id, &value) && (int) value == value)
- {
- *reg_num = (int) value;
- return user_reg_map_regnum_to_name (gdbarch, *reg_num) != NULL;
- }
- }
- return 0;
-}
-
/* Convert gdb.Value instance to inferior's pointer. Return 1 on success,
0 on failure. */
if (!PyArg_UnpackTuple (args, "previous_frame_register", 2, 2,
&pyo_reg_id, &pyo_reg_value))
return NULL;
- if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
+ if (!gdbpy_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
{
PyErr_SetString (PyExc_ValueError, "Bad register");
return NULL;
}
if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id))
return NULL;
- if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
+ if (!gdbpy_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
{
PyErr_SetString (PyExc_ValueError, "Bad register");
return NULL;
/* A unique_ptr specialization for Py_buffer. */
typedef std::unique_ptr<Py_buffer, Py_buffer_deleter> Py_buffer_up;
+/* Parse a register number from PYO_REG_ID and place the register number
+ into *REG_NUM. The register is a register for GDBARCH.
+
+ If a register is parsed successfully then *REG_NUM will have been
+ updated, and true is returned. Otherwise the contents of *REG_NUM are
+ undefined, and false is returned.
+
+ The PYO_REG_ID object can be a string, the name of the register. This
+ is the slowest approach as GDB has to map the name to a number for each
+ call. Alternatively PYO_REG_ID can be an internal GDB register
+ number. This is quick but should not be encouraged as this means
+ Python scripts are now dependent on GDB's internal register numbering.
+ Final PYO_REG_ID can be a gdb.RegisterDescriptor object, these objects
+ can be looked up by name once, and then cache the register number so
+ should be as quick as using a register number. */
+
+extern bool gdbpy_parse_register_id (struct gdbarch *gdbarch,
+ PyObject *pyo_reg_id, int *reg_num);
+
#endif /* PYTHON_PYTHON_INTERNAL_H */
+2020-07-28 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.python/py-unwind.py: Update to make use of a register
+ descriptor.
+
2020-07-28 Andrew Burgess <andrew.burgess@embecosm.com>
* gdb.python/py-arch-reg-names.exp: Add additional tests.
class TestUnwinder(Unwinder):
AMD64_RBP = 6
AMD64_RSP = 7
- AMD64_RIP = 16
+ AMD64_RIP = None
def __init__(self):
Unwinder.__init__(self, "test unwinder")
self.char_ptr_t = gdb.lookup_type("unsigned char").pointer()
self.char_ptr_ptr_t = self.char_ptr_t.pointer()
+ self._last_arch = None
+
+ # Update the register descriptor AMD64_RIP based on ARCH.
+ def _update_register_descriptors (self, arch):
+ if (self._last_arch != arch):
+ TestUnwinder.AMD64_RIP = arch.registers ().find ("rip")
+ self._last_arch = arch
def _read_word(self, address):
return address.cast(self.char_ptr_ptr_t).dereference()
if (inf_arch != frame_arch):
raise gdb.GdbError ("architecture mismatch")
+ self._update_register_descriptors (frame_arch)
+
try:
# NOTE: the registers in Unwinder API can be referenced
# either by name or by number. The code below uses both