gdb/python: have UnwindInfo.add_saved_register accept named args
Update gdb.UnwindInfo.add_saved_register to accept named keyword arguments. As part of this update we now use gdb_PyArg_ParseTupleAndKeywords instead of PyArg_UnpackTuple to parse the function arguments. By switching to gdb_PyArg_ParseTupleAndKeywords, we can now use 'O!' as the argument format for the function's value argument. This means that we can check the argument type (is gdb.Value) as part of the argument processing rather than manually performing the check later in the function. One result of this is that we now get a better error message (at least, I think so). Previously we would get something like: ValueError: Bad register value Now we get: TypeError: argument 2 must be gdb.Value, not XXXX It's unfortunate that the exception type changed, but I think the new exception type actually makes more sense. My preference for argument names is to use full words where that's not too excessive. As such, I've updated the name of the argument from 'reg' to 'register' in the documentation, which is the argument name I've made GDB look for here. For existing unwinder code that doesn't throw any exceptions nothing should change with this commit. It is possible that a user has some code that throws and catches the ValueError, and this code will break after this commit, but I think this is going to be sufficiently rare that we can take the risk here. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Reviewed-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
parent
cf141dd8cc
commit
d2d62da62e
4 changed files with 75 additions and 56 deletions
|
@ -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
|
create a @code{gdb.UnwindInfo} instance. Use the following method to
|
||||||
specify caller registers that have been saved in this frame:
|
specify caller registers that have been saved in this frame:
|
||||||
|
|
||||||
@defun gdb.UnwindInfo.add_saved_register (reg, value)
|
@defun gdb.UnwindInfo.add_saved_register (register, value)
|
||||||
@var{reg} identifies the register, for a description of the acceptable
|
@var{register} identifies the register, for a description of the acceptable
|
||||||
values see @ref{gdbpy_frame_read_register,,Frame.read_register}.
|
values see @ref{gdbpy_frame_read_register,,Frame.read_register}.
|
||||||
@var{value} is a register value (a @code{gdb.Value} object).
|
@var{value} is a register value (a @code{gdb.Value} object).
|
||||||
@end defun
|
@end defun
|
||||||
|
|
|
@ -292,7 +292,7 @@ pyuw_create_unwind_info (PyObject *pyo_pending_frame,
|
||||||
gdb.UnwindInfo.add_saved_register (REG, VALUE) -> None. */
|
gdb.UnwindInfo.add_saved_register (REG, VALUE) -> None. */
|
||||||
|
|
||||||
static PyObject *
|
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;
|
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
||||||
pending_frame_object *pending_frame
|
pending_frame_object *pending_frame
|
||||||
|
@ -305,11 +305,15 @@ unwind_infopy_add_saved_register (PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyErr_SetString (PyExc_ValueError,
|
PyErr_SetString (PyExc_ValueError,
|
||||||
"UnwindInfo instance refers to a stale PendingFrame");
|
"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))
|
static const char *keywords[] = { "register", "value", nullptr };
|
||||||
return NULL;
|
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))
|
if (!gdbpy_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -332,43 +336,38 @@ unwind_infopy_add_saved_register (PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
/* The argument parsing above guarantees that PYO_REG_VALUE will be a
|
||||||
struct value *value;
|
gdb.Value object, as a result the value_object_to_value call should
|
||||||
size_t data_size;
|
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;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1087,7 +1086,8 @@ PyTypeObject pending_frame_object_type =
|
||||||
static PyMethodDef unwind_info_object_methods[] =
|
static PyMethodDef unwind_info_object_methods[] =
|
||||||
{
|
{
|
||||||
{ "add_saved_register",
|
{ "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"
|
"add_saved_register (REG, VALUE) -> None\n"
|
||||||
"Set the value of the REG in the previous frame to VALUE." },
|
"Set the value of the REG in the previous frame to VALUE." },
|
||||||
{ NULL } /* Sentinel */
|
{ NULL } /* Sentinel */
|
||||||
|
|
|
@ -131,10 +131,17 @@ gdb_test "disable unwinder global \"test unwinder\"" \
|
||||||
check_for_broken_backtrace "stack is broken after command disabling"
|
check_for_broken_backtrace "stack is broken after command disabling"
|
||||||
check_info_unwinder "info unwinder after command disabling" off
|
check_info_unwinder "info unwinder after command disabling" off
|
||||||
|
|
||||||
# Check that invalid register names cause errors.
|
# Check that invalid register names and values cause errors.
|
||||||
gdb_test "python print(add_saved_register_error)" "True" \
|
gdb_test "python print(add_saved_register_errors\[\"unknown_name\"\])" \
|
||||||
"add_saved_register error"
|
"Bad register" \
|
||||||
gdb_test "python print(read_register_error)" "True" \
|
"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"
|
"read_register error"
|
||||||
|
|
||||||
# Try to create an unwinder object with a non-string name.
|
# Try to create an unwinder object with a non-string name.
|
||||||
|
|
|
@ -18,7 +18,7 @@ from gdb.unwinder import Unwinder, FrameId
|
||||||
|
|
||||||
|
|
||||||
# These are set to test whether invalid register names cause an error.
|
# 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
|
read_register_error = False
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ class TestUnwinder(Unwinder):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pending_frame.read_register("nosuchregister")
|
pending_frame.read_register("nosuchregister")
|
||||||
except ValueError:
|
except ValueError as ve:
|
||||||
global read_register_error
|
global read_register_error
|
||||||
read_register_error = True
|
read_register_error = str(ve)
|
||||||
|
|
||||||
frame_id = FrameId(
|
frame_id = FrameId(
|
||||||
pending_frame.read_register(TestUnwinder.AMD64_RSP),
|
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 = pending_frame.create_unwind_info(frame_id)
|
||||||
unwind_info.add_saved_register(TestUnwinder.AMD64_RBP, previous_bp)
|
unwind_info.add_saved_register(TestUnwinder.AMD64_RBP, previous_bp)
|
||||||
unwind_info.add_saved_register("rip", previous_ip)
|
unwind_info.add_saved_register(value=previous_ip, register="rip")
|
||||||
unwind_info.add_saved_register("rsp", previous_sp)
|
unwind_info.add_saved_register(register="rsp", value=previous_sp)
|
||||||
|
|
||||||
|
global add_saved_register_errors
|
||||||
try:
|
try:
|
||||||
unwind_info.add_saved_register("nosuchregister", previous_sp)
|
unwind_info.add_saved_register("nosuchregister", previous_sp)
|
||||||
except ValueError:
|
except ValueError as ve:
|
||||||
global add_saved_register_error
|
add_saved_register_errors["unknown_name"] = str(ve)
|
||||||
add_saved_register_error = True
|
|
||||||
|
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
|
return unwind_info
|
||||||
except (gdb.error, RuntimeError):
|
except (gdb.error, RuntimeError):
|
||||||
return None
|
return None
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue