diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 106dfcdac81..234dc240786 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,18 @@ +2020-07-28 Andrew Burgess + + * 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 * python/py-registers.c: Add 'user-regs.h' include. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 1074511bcbb..76a2d9e724a 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2020-07-28 Andrew Burgess + + * 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 * python.texi (Registers In Python): Document new find function. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index c9dc1ff3b71..9bb9f3c2a6b 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2458,12 +2458,11 @@ provides a method to read frame's registers: @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. @@ -2532,8 +2531,8 @@ create a @code{gdb.UnwindInfo} instance. Use the following method to 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 @@ -4687,10 +4686,29 @@ Return the frame's symtab and line object. @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{]}) diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c index 3fd1e7fabcc..e121afb222d 100644 --- a/gdb/python/py-frame.c +++ b/gdb/python/py-frame.c @@ -27,7 +27,6 @@ #include "python-internal.h" #include "symfile.h" #include "objfiles.h" -#include "user-regs.h" typedef struct { PyObject_HEAD @@ -242,12 +241,11 @@ frapy_pc (PyObject *self, PyObject *args) 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; @@ -255,14 +253,18 @@ frapy_read_register (PyObject *self, PyObject *args) 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) { diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c index fffe3ecb1e6..516d43d7116 100644 --- a/gdb/python/py-registers.c +++ b/gdb/python/py-registers.c @@ -370,6 +370,59 @@ register_descriptor_iter_find (PyObject *self, PyObject *args, PyObject *kw) 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 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 diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 1cef491cedf..55d6a310c93 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -27,7 +27,6 @@ #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); } @@ -101,37 +100,6 @@ static unsigned int pyuw_debug = 0; 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 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. */ @@ -275,7 +243,7 @@ unwind_infopy_add_saved_register (PyObject *self, PyObject *args) 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; @@ -376,7 +344,7 @@ pending_framepy_read_register (PyObject *self, PyObject *args) } 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; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 1e6dcf3dbb0..6874543441b 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -776,4 +776,23 @@ struct Py_buffer_deleter /* A unique_ptr specialization for Py_buffer. */ typedef std::unique_ptr 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 */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index a4b8dfeb8da..5ae258820e6 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2020-07-28 Andrew Burgess + + * gdb.python/py-unwind.py: Update to make use of a register + descriptor. + 2020-07-28 Andrew Burgess * gdb.python/py-arch-reg-names.exp: Add additional tests. diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index d01da80f25b..59602d69028 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -33,12 +33,19 @@ class FrameId(object): 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() @@ -77,6 +84,8 @@ class TestUnwinder(Unwinder): 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