diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index c74d586ef39..a4fd0095c43 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2882,8 +2882,8 @@ Use @code{PendingFrame.create_unwind_info} method described above to 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, for a description of the acceptable +@defun gdb.UnwindInfo.add_saved_register (register, value) +@var{register} 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 diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 409dbd3a470..2e13b84eb12 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -292,7 +292,7 @@ pyuw_create_unwind_info (PyObject *pyo_pending_frame, gdb.UnwindInfo.add_saved_register (REG, VALUE) -> None. */ static PyObject * -unwind_infopy_add_saved_register (PyObject *self, PyObject *args) +unwind_infopy_add_saved_register (PyObject *self, PyObject *args, PyObject *kw) { unwind_info_object *unwind_info = (unwind_info_object *) self; pending_frame_object *pending_frame @@ -305,11 +305,15 @@ unwind_infopy_add_saved_register (PyObject *self, PyObject *args) { PyErr_SetString (PyExc_ValueError, "UnwindInfo instance refers to a stale PendingFrame"); - return NULL; + return nullptr; } - if (!PyArg_UnpackTuple (args, "previous_frame_register", 2, 2, - &pyo_reg_id, &pyo_reg_value)) - return NULL; + + static const char *keywords[] = { "register", "value", nullptr }; + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OO!", keywords, + &pyo_reg_id, &value_object_type, + &pyo_reg_value)) + return nullptr; + if (!gdbpy_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num)) return nullptr; @@ -332,43 +336,38 @@ unwind_infopy_add_saved_register (PyObject *self, PyObject *args) } } - { - struct value *value; - size_t data_size; + /* The argument parsing above guarantees that PYO_REG_VALUE will be a + gdb.Value object, as a result the value_object_to_value call should + succeed. */ + gdb_assert (pyo_reg_value != nullptr); + struct value *value = value_object_to_value (pyo_reg_value); + gdb_assert (value != nullptr); + + ULONGEST reg_size = register_size (pending_frame->gdbarch, regnum); + if (reg_size != value->type ()->length ()) + { + PyErr_Format (PyExc_ValueError, + "The value of the register returned by the Python " + "sniffer has unexpected size: %s instead of %s.", + pulongest (value->type ()->length ()), + pulongest (reg_size)); + return nullptr; + } + + gdbpy_ref<> new_value = gdbpy_ref<>::new_reference (pyo_reg_value); + bool found = false; + for (saved_reg ® : *unwind_info->saved_regs) + { + if (regnum == reg.number) + { + found = true; + reg.value = std::move (new_value); + break; + } + } + if (!found) + unwind_info->saved_regs->emplace_back (regnum, std::move (new_value)); - if (pyo_reg_value == NULL - || (value = value_object_to_value (pyo_reg_value)) == NULL) - { - PyErr_SetString (PyExc_ValueError, "Bad register value"); - return NULL; - } - data_size = register_size (pending_frame->gdbarch, regnum); - if (data_size != value->type ()->length ()) - { - PyErr_Format ( - PyExc_ValueError, - "The value of the register returned by the Python " - "sniffer has unexpected size: %u instead of %u.", - (unsigned) value->type ()->length (), - (unsigned) data_size); - return NULL; - } - } - { - gdbpy_ref<> new_value = gdbpy_ref<>::new_reference (pyo_reg_value); - bool found = false; - for (saved_reg ® : *unwind_info->saved_regs) - { - if (regnum == reg.number) - { - found = true; - reg.value = std::move (new_value); - break; - } - } - if (!found) - unwind_info->saved_regs->emplace_back (regnum, std::move (new_value)); - } Py_RETURN_NONE; } @@ -1087,7 +1086,8 @@ PyTypeObject pending_frame_object_type = static PyMethodDef unwind_info_object_methods[] = { { "add_saved_register", - unwind_infopy_add_saved_register, METH_VARARGS, + (PyCFunction) unwind_infopy_add_saved_register, + METH_VARARGS | METH_KEYWORDS, "add_saved_register (REG, VALUE) -> None\n" "Set the value of the REG in the previous frame to VALUE." }, { NULL } /* Sentinel */ diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index d0a1960058b..e7c4c23dae2 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -131,10 +131,17 @@ gdb_test "disable unwinder global \"test unwinder\"" \ check_for_broken_backtrace "stack is broken after command disabling" check_info_unwinder "info unwinder after command disabling" off -# Check that invalid register names cause errors. -gdb_test "python print(add_saved_register_error)" "True" \ - "add_saved_register error" -gdb_test "python print(read_register_error)" "True" \ +# Check that invalid register names and values cause errors. +gdb_test "python print(add_saved_register_errors\[\"unknown_name\"\])" \ + "Bad register" \ + "add_saved_register error when an unknown register name is used" +gdb_test "python print(add_saved_register_errors\[\"unknown_number\"\])" \ + "Bad register" \ + "add_saved_register error when an unknown register number is used" +gdb_test "python print(add_saved_register_errors\[\"bad_value\"\])" \ + "argument 2 must be gdb.Value, not int" \ + "add_saved_register error when invalid register value is used" +gdb_test "python print(read_register_error)" "Bad register" \ "read_register error" # Try to create an unwinder object with a non-string name. diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index 4e110c51e3b..5853abc7486 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -18,7 +18,7 @@ from gdb.unwinder import Unwinder, FrameId # These are set to test whether invalid register names cause an error. -add_saved_register_error = False +add_saved_register_errors = {} read_register_error = False @@ -94,9 +94,9 @@ class TestUnwinder(Unwinder): try: pending_frame.read_register("nosuchregister") - except ValueError: + except ValueError as ve: global read_register_error - read_register_error = True + read_register_error = str(ve) frame_id = FrameId( pending_frame.read_register(TestUnwinder.AMD64_RSP), @@ -104,13 +104,25 @@ class TestUnwinder(Unwinder): ) unwind_info = pending_frame.create_unwind_info(frame_id) unwind_info.add_saved_register(TestUnwinder.AMD64_RBP, previous_bp) - unwind_info.add_saved_register("rip", previous_ip) - unwind_info.add_saved_register("rsp", previous_sp) + unwind_info.add_saved_register(value=previous_ip, register="rip") + unwind_info.add_saved_register(register="rsp", value=previous_sp) + + global add_saved_register_errors try: unwind_info.add_saved_register("nosuchregister", previous_sp) - except ValueError: - global add_saved_register_error - add_saved_register_error = True + except ValueError as ve: + add_saved_register_errors["unknown_name"] = str(ve) + + try: + unwind_info.add_saved_register(999, previous_sp) + except ValueError as ve: + add_saved_register_errors["unknown_number"] = str(ve) + + try: + unwind_info.add_saved_register("rsp", 1234) + except TypeError as ve: + add_saved_register_errors["bad_value"] = str(ve) + return unwind_info except (gdb.error, RuntimeError): return None