]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/python: Add gdb.InferiorThread.__dict__ attribute
authorAndrew Burgess <aburgess@redhat.com>
Fri, 5 Jan 2024 11:05:51 +0000 (11:05 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Fri, 12 Jan 2024 11:21:31 +0000 (11:21 +0000)
The gdb.Objfile, gdb.Progspace, gdb.Type, and gdb.Inferior 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.InferiorThread
type.

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

  (gdb) pi
  >>> t = gdb.selected_thread()
  >>> t._user_attribute = 123
  >>> t._user_attribute
  123
  >>>

There's a new test included.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
gdb/NEWS
gdb/doc/python.texi
gdb/python/py-infthread.c
gdb/python/python-internal.h
gdb/testsuite/gdb.python/py-inferior.exp

index 92e7f32ab8c61b00da50345484aefec3d580cf49..bab300e36b85b7c0a24ee70ace095c8857810178 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -104,6 +104,10 @@ show remote thread-options-packet
      these will be stored in the object's new Inferior.__dict__
      attribute.
 
+  ** User defined attributes can be added to a gdb.InferiorThread
+     object, these will be stored in the object's new
+     InferiorThread.__dict__ attribute.
+
 * Debugger Adapter Protocol changes
 
   ** GDB now emits the "process" event.
index 2fd8a9b5af8c225bbced6a96f589439261b6f7be..674ec565b0d65f24dac5850bb58c59a05aa0de35 100644 (file)
@@ -4184,6 +4184,41 @@ the Python @code{bytes} representation of the handle and @var{type} is
 a @code{gdb.Type} for the handle type.
 @end defun
 
+One may add arbitrary attributes to @code{gdb.InferiorThread} objects
+in the usual Python way.  This is useful if, for example, one needs to
+do some extra record keeping associated with the thread.
+
+In this contrived example we record the time when a thread 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()
+    thread._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_thread()._last_stop_time)
+2024-01-04 14:48:41.347036
+@end group
+@end smallexample
+
 @node Recordings In Python
 @subsubsection Recordings In Python
 @cindex recordings in python
index b5887c7942d0c35c32faf2642dc25953e63b0200..421158455e7a09e448a3b3928cc13758d0d3787e 100644 (file)
@@ -51,6 +51,9 @@ create_thread_object (struct thread_info *tp)
 
   thread_obj->thread = tp;
   thread_obj->inf_obj = (PyObject *) inf_obj.release ();
+  thread_obj->dict = PyDict_New ();
+  if (thread_obj->dict == nullptr)
+    return nullptr;
 
   return thread_obj;
 }
@@ -58,7 +61,13 @@ create_thread_object (struct thread_info *tp)
 static void
 thpy_dealloc (PyObject *self)
 {
-  Py_DECREF (((thread_object *) self)->inf_obj);
+  thread_object *thr_obj = (thread_object *) self;
+
+  gdb_assert (thr_obj->inf_obj != nullptr);
+
+  Py_DECREF (thr_obj->inf_obj);
+  Py_XDECREF (thr_obj->dict);
+
   Py_TYPE (self)->tp_free (self);
 }
 
@@ -418,6 +427,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_thread);
 
 static gdb_PyGetSetDef thread_object_getset[] =
 {
+  { "__dict__", gdb_py_generic_dict, nullptr,
+    "The __dict__ for this thread.", &thread_object_type },
   { "name", thpy_get_name, thpy_set_name,
     "The name of the thread, as set by the user or the OS.", NULL },
   { "details", thpy_get_details, NULL,
@@ -498,7 +509,7 @@ PyTypeObject thread_object_type =
   0,                             /* tp_dict */
   0,                             /* tp_descr_get */
   0,                             /* tp_descr_set */
-  0,                             /* tp_dictoffset */
+  offsetof (thread_object, dict), /* tp_dictoffset */
   0,                             /* tp_init */
   0                              /* tp_alloc */
 };
index 8ff9af650c2160e799ce57b9f8fe806b10a8e300..e01557edeb7e4d4372d0e3200da959a9603e6a40 100644 (file)
@@ -356,6 +356,10 @@ struct thread_object
 
   /* The Inferior object to which this thread belongs.  */
   PyObject *inf_obj;
+
+  /* Dictionary holding user-added attributes.  This is the __dict__
+     attribute of the object.  */
+  PyObject *dict;
 };
 
 struct inferior_object;
index 0e00636fa1cc0e62b898c85917e3a6c6b29da049..d1cd29f734b31e54690ff7e4842d1ed1e605b65b 100644 (file)
@@ -107,6 +107,19 @@ gdb_test "python print(last_thread)" \
     "<gdb.InferiorThread id=${decimal}\\.${decimal} target-id=\"\[^\r\n\]*\">" \
     "test repr of a valid thread"
 
+# Add a user defined attribute to this thread, check the attribute can
+# be read back, and check the attribute is not present on other
+# threads.
+gdb_test_no_output "python last_thread._user_attribute = 123" \
+    "add user defined attribute to InferiorThread object"
+gdb_test "python print(last_thread._user_attribute)" "123" \
+    "read back user defined attribute"
+gdb_test "python print(i0.threads ()\[0\]._user_attribute)" \
+    [multi_line \
+        "AttributeError: 'gdb\\.InferiorThread' object has no attribute '_user_attribute'" \
+        "Error while executing Python code\\."] \
+    "attempt to read non-existent user defined attribute"
+
 # Proceed to the next test.
 
 gdb_breakpoint [gdb_get_line_number "Break here."]
@@ -117,6 +130,10 @@ gdb_test "python print(last_thread)" \
     "<gdb.InferiorThread \\(invalid\\)>" \
     "test repr of an invalid thread"
 
+# Check the user defined attribute is still present on the invalid thread object.
+gdb_test "python print(last_thread._user_attribute)" "123" \
+    "check user defined attribute on an invalid InferiorThread object"
+
 # Test memory read and write operations.
 
 gdb_py_test_silent_cmd "python addr = gdb.selected_frame ().read_var ('str')" \