Patchwork [1/2] Add interface for implementing thread parent operation in Python

login
register
mail settings
Submitter Kevin Buettner
Date Nov. 30, 2017, 4:54 a.m.
Message ID <20171129215401.1f91afe0@pinnacle.lan>
Download mbox | patch
Permalink /patch/24623/
State New
Headers show

Comments

Kevin Buettner - Nov. 30, 2017, 4:54 a.m.
Add interface for implementing thread parent operation in Python

With this patch in place, it is possible to define a method in python
named gdb.thread_parent which will be called when gdb wants to know
which (if any) thread created the thread passed to gdb.thread_parent.

The thread parent operation will be used to help find certain variables
which are currently not accessible to the GDB user when debugging
OpenMP programs.

An implementation of gdb.thread_parent might look something like
this:

def thr_parent (thr):
    try:
    h = gdb.parse_and_eval("gomp_tls_data->parent->pthread_id")
    parent = gdb.selected_inferior().thread_from_thread_handle(h)
    if thr == parent:
	return None
    else:
	return parent
    except:
    return None

gdb.thread_parent = thr_parent

It is expected that this will code be placed in a file implementing
a plugin associated with the library - in this case libgomp - for
which the thread parent operation is required.  For example, I've
been placing code similar to that shown above in a file named
libgomp.so.1.0.0-gdb.py which is located in the same directory as
the libgomp library, in this case named libgomp.so.1.0.0.

Please note that the above example might not necessarily (ever) work
since the details of the libgomp implementation have not yet been
decided.  To be honest, it no longer works even in my own local tree,
though it did at one time.  I chose to show this implementation because
it's fairly straightforward and also provided a complete implementation
of the thread parent operation.

At the moment, a very simple and perhaps even naive registration
mechanism is used.  In the future, a more robust registration
mechanism might be implemented which could handle multiple thread
parent implementations.  However, at least for OpenMP, it's not clear
what the ultimate solution will be.  It may be that functionality from
a library similar to libthread_db (OMPD) will be used to determine
thread ancestory.  As such, there may not be a long term need for this
mechanism and (therefore) no need for a more robust registration
interface.

gdb/ChangeLog:
    
    	* extension-priv.h (struct extension_language_ops): Add new
    	field `thread_parent'.
    	* extension.c (ext_thread_parent): New function.
    	* extension.h (ext_thread_parent): Declare.
    	* gdbthread.h (thread_parent): Declare.
    	* python.c (gdbpy_thread_parent): New function.
    	(python_extension_ops): Add method gdbpy_thread_parent.
    	* thread.c (extension.h): Include.
    	(thread_parent): New function.
    	(maintenance_print_thread_parent): New function.
    	(_initialize_thread): Add new maintenance print command for
    	printing the thread parent.
---
 gdb/extension-priv.h |  5 +++++
 gdb/extension.c      | 21 ++++++++++++++++++
 gdb/extension.h      |  1 +
 gdb/gdbthread.h      |  3 +++
 gdb/python/python.c  | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 gdb/thread.c         | 35 +++++++++++++++++++++++++++++
 6 files changed, 126 insertions(+), 1 deletion(-)

Patch

diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
index 4d16ac5..efc9aae 100644
--- a/gdb/extension-priv.h
+++ b/gdb/extension-priv.h
@@ -318,6 +318,11 @@  struct extension_language_ops
      struct value *object,
      struct value **args,
      int nargs);
+
+  /* Fetch a thread's parent.  */
+  struct thread_info * (*thread_parent)
+    (const struct extension_language_defn *extlang,
+    struct thread_info *thread);
 };
 
 /* State necessary to restore a signal handler to its previous value.  */
diff --git a/gdb/extension.c b/gdb/extension.c
index 4ffad03..25cc3dc 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -1030,6 +1030,27 @@  free_xmethod_worker_vec (void *vec)
   VEC_free (xmethod_worker_ptr, v);
 }
 
+/* Fetch the parent thread of THREAD.  */
+struct thread_info *
+ext_thread_parent (struct thread_info *thread)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+  struct thread_info *rv;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      enum ext_lang_rc rc;
+
+      if (extlang->ops->thread_parent == NULL)
+	continue;
+      rv = extlang->ops->thread_parent (extlang, thread);
+
+      return rv;
+    }
+  return NULL;
+}
+
 /* Called via an observer before gdb prints its prompt.
    Iterate over the extension languages giving them a chance to
    change the prompt.  The first one to change the prompt wins,
diff --git a/gdb/extension.h b/gdb/extension.h
index 2c79411..83cc5fc 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -264,4 +264,5 @@  extern struct type *get_xmethod_result_type (struct xmethod_worker *,
 					     struct value *object,
 					     struct value **args, int nargs);
 
+extern struct thread_info *ext_thread_parent (struct thread_info *thread);
 #endif /* EXTENSION_H */
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 49fc80f..007fafb 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -452,6 +452,9 @@  struct thread_info *find_thread_global_id (int global_id);
 struct thread_info *find_thread_by_handle (struct value *thread_handle,
 					   struct inferior *inf);
 
+/* Find thread parent.  */
+struct thread_info * thread_parent (struct thread_info *thread);
+
 /* Finds the first thread of the inferior given by PID.  If PID is -1,
    returns the first thread in the list.  */
 struct thread_info *first_thread_of_process (int pid);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 5f15261..d9578cc 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -152,6 +152,8 @@  static void gdbpy_set_quit_flag (const struct extension_language_defn *);
 static int gdbpy_check_quit_flag (const struct extension_language_defn *);
 static enum ext_lang_rc gdbpy_before_prompt_hook
   (const struct extension_language_defn *, const char *current_gdb_prompt);
+static struct thread_info * gdbpy_thread_parent
+  (const struct extension_language_defn *, struct thread_info *);
 
 /* The interface between gdb proper and loading of python scripts.  */
 
@@ -195,7 +197,9 @@  const struct extension_language_ops python_extension_ops =
   gdbpy_get_matching_xmethod_workers,
   gdbpy_get_xmethod_arg_types,
   gdbpy_get_xmethod_result_type,
-  gdbpy_invoke_xmethod
+  gdbpy_invoke_xmethod,
+
+  gdbpy_thread_parent
 };
 
 /* Architecture and language to be used in callbacks from
@@ -1573,6 +1577,62 @@  gdbpy_free_type_printers (const struct extension_language_defn *extlang,
   Py_DECREF (printers);
 }
 
+
+static struct thread_info *
+gdbpy_thread_parent (const struct extension_language_defn *extlang,
+                     struct thread_info *thread)
+{
+  struct cleanup *cleanup;
+  struct thread_info *rv = NULL;
+
+  if (!gdb_python_initialized)
+    return NULL;
+
+  gdbpy_enter enter_py (get_current_arch (), current_language);
+
+  if (gdb_python_module
+      && PyObject_HasAttrString (gdb_python_module, "thread_parent"))
+    {
+      gdbpy_ref<> hook
+        (PyObject_GetAttrString (gdb_python_module, "thread_parent"));
+
+      if (hook == NULL)
+	return NULL;
+
+      if (PyCallable_Check (hook.get ()))
+	{
+	  thread_object *thread_obj = find_thread_object (thread->ptid);
+	  gdbpy_ref<> result (PyObject_CallFunctionObjArgs (hook.get (),
+	                                                    thread_obj, NULL));
+
+	  if (result == NULL)
+	    {
+	      gdbpy_print_stack ();
+	      return NULL;
+	    }
+
+	  if (result == Py_None)
+	    rv = NULL;
+	  else if (PyObject_IsInstance
+	             (result.get (), (PyObject *) &thread_object_type) == 1)
+	    {
+	      thread_object *thread_obj = (thread_object *) result.get ();
+	      rv = thread_obj->thread;
+	    }
+	  else
+	    {
+	      PyErr_Format (PyExc_RuntimeError,
+			    _("Return from thread_parent must " \
+			      "be a thread object"));
+	      gdbpy_print_stack ();
+	      return NULL;
+	    }
+	}
+    }
+
+  return rv;
+}
+
 #else /* HAVE_PYTHON */
 
 /* Dummy implementation of the gdb "python-interactive" and "python"
diff --git a/gdb/thread.c b/gdb/thread.c
index d71568e..27cc417 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -45,6 +45,7 @@ 
 #include "tid-parse.h"
 #include <algorithm>
 #include "common/gdb_optional.h"
+#include "extension.h"
 
 /* Definition of struct thread_info exported to gdbthread.h.  */
 
@@ -1914,6 +1915,34 @@  thread_find_command (const char *arg, int from_tty)
     printf_filtered (_("No threads match '%s'\n"), arg);
 }
 
+/* Return the parent thread of THREAD.  */
+struct thread_info *
+thread_parent (struct thread_info *thread)
+{
+  return ext_thread_parent (thread);
+}
+
+static void
+maintenance_print_thread_parent (const char *arg, int from_tty)
+{
+  struct thread_info *info, *parent;
+
+  if (ptid_equal (inferior_ptid, null_ptid))
+    error (_("No thread selected"));
+
+  arg = skip_spaces (arg);
+
+  info = inferior_thread ();
+
+  parent = thread_parent (info);
+
+  if (parent == NULL)
+     printf_filtered (_("No parent found for thread %d\n"), info->global_num);
+  else
+     printf_filtered (_("Parent of thread %d is %d\n"),
+                      info->global_num, parent->global_num);
+}
+
 /* Print notices when new threads are attached and detached.  */
 int print_thread_events = 1;
 static void
@@ -2125,6 +2154,12 @@  Show printing of thread events (such as thread start and exit)."), NULL,
 			   show_print_thread_events,
 			   &setprintlist, &showprintlist);
 
+  add_cmd ("thread-parent", class_maintenance, maintenance_print_thread_parent, _("\
+Display parent of current thread.\n\
+Usage: thread parent\n\
+Thread parent is not always available.\n"),
+           &maintenanceprintlist);
+
   create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
   create_internalvar_type_lazy ("_gthread", &gthread_funcs, NULL);
 }