gdb/python: Add gdb.Inferior.__dict__ attribute

The gdb.Objfile, gdb.Progspace, and gdb.Type Python types already have
a __dict__ attribute, which allows users to create user defined
attributes within the objects.  This is useful if the user wants to
cache information within an object.

This commit adds the same functionality to the gdb.Inferior type.

After this commit there is a new gdb.Inferior.__dict__ attribute,
which is a dictionary.  A user can, for example, do this:

  (gdb) pi
  >>> i = gdb.selected_inferior()
  >>> i._user_attribute = 123
  >>> i._user_attribute
  123
  >>>

There's a new test included.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
Andrew Burgess 2024-01-04 16:46:40 +00:00
parent 2f47f48fe5
commit 13cd9bceea
4 changed files with 71 additions and 1 deletions

View File

@ -100,6 +100,10 @@ show remote thread-options-packet
objects can still be obtained through calling other API
functions, for example 'gdb.current_progspace()'.
** User defined attributes can be added to a gdb.Inferior object,
these will be stored in the object's new Inferior.__dict__
attribute.
* Debugger Adapter Protocol changes
** GDB now emits the "process" event.

View File

@ -3667,6 +3667,42 @@ Unset the environment variable @var{name}. @var{name} must be a
string.
@end defun
One may add arbitrary attributes to @code{gdb.Inferior} objects in the
usual Python way. This is useful if, for example, one needs to do
some extra record keeping associated with the inferior.
In this contrived example we record the time when an inferior last
stopped:
@smallexample
@group
(@value{GDBP}) python
import datetime
def thread_stopped(event):
if event.inferior_thread is not None:
thread = event.inferior_thread
else:
thread = gdb.selected_thread()
inferior = thread.inferior
inferior._last_stop_time = datetime.datetime.today()
gdb.events.stop.connect(thread_stopped)
@end group
@group
(@value{GDBP}) file /tmp/hello
Reading symbols from /tmp/hello...
(@value{GDBP}) start
Temporary breakpoint 1 at 0x401198: file /tmp/hello.c, line 18.
Starting program: /tmp/hello
Temporary breakpoint 1, main () at /tmp/hello.c:18
18 printf ("Hello World\n");
(@value{GDBP}) python print(gdb.selected_inferior()._last_stop_time)
2024-01-04 14:48:41.347036
@end group
@end smallexample
@node Events In Python
@subsubsection Events In Python
@cindex inferior events in Python

View File

@ -46,6 +46,10 @@ struct inferior_object
/* thread_object instances under this inferior. This owns a
reference to each object it contains. */
thread_map_t *threads;
/* Dictionary holding user-added attributes.
This is the __dict__ attribute of the object. */
PyObject *dict;
};
extern PyTypeObject inferior_object_type
@ -241,6 +245,9 @@ inferior_to_inferior_object (struct inferior *inferior)
inf_obj->inferior = inferior;
inf_obj->threads = new thread_map_t ();
inf_obj->dict = PyDict_New ();
if (inf_obj->dict == nullptr)
return nullptr;
/* PyObject_New initializes the new object with a refcount of 1. This
counts for the reference we are keeping in the inferior data. */
@ -981,6 +988,8 @@ infpy_dealloc (PyObject *obj)
function is called. */
gdb_assert (inf_obj->inferior == nullptr);
Py_XDECREF (inf_obj->dict);
Py_TYPE (obj)->tp_free (obj);
}
@ -1038,6 +1047,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_inferior);
static gdb_PyGetSetDef inferior_object_getset[] =
{
{ "__dict__", gdb_py_generic_dict, nullptr,
"The __dict__ for this inferior.", &inferior_object_type },
{ "arguments", infpy_get_args, infpy_set_args,
"Arguments to this program.", nullptr },
{ "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
@ -1135,7 +1146,7 @@ PyTypeObject inferior_object_type =
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
offsetof (inferior_object, dict), /* tp_dictoffset */
0, /* tp_init */
0 /* tp_alloc */
};

View File

@ -85,6 +85,15 @@ gdb_test "python print (i0.threads ())" \
gdb_test "python print (i0.progspace)" "<gdb.Progspace object at $hex>"
gdb_test "python print (i0.progspace == gdb.progspaces()\[0\])" "True"
# Add a user defined attribute to the inferior, and check the
# attribute can be read back.
gdb_test_no_output "python i0._user_attr = 123" \
"add a user defined attribute to the inferior object"
gdb_test "python print(i0._user_attr)" \
"123" "read back user defined attribute from i0"
gdb_test "python print(gdb.inferiors()\[0\]._user_attr)" \
"123" "read back user defined attribute from gdb.inferiors"
# Test the number of inferior threads.
gdb_breakpoint check_threads
@ -127,6 +136,16 @@ gdb_test "print str" " = \"hallo, testsuite\"" \
# correct inferior.
set num [add_inferior]
# Confirm the new inferior doesn't have the user defined attribute,
# but that the first inferior does still have the attribute.
gdb_test "python print(gdb.inferiors()\[1\]._user_attr)" \
[multi_line \
"AttributeError: 'gdb\\.Inferior' object has no attribute '_user_attr'" \
"Error while executing Python code\\."] \
"check new inferior doesn't have user defined attribute"
gdb_test "python print(gdb.inferiors()\[0\]._user_attr)" \
"123" "read back user defined attribute again"
# Test memory search.
set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}