public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/2] OpenMP: Search ancestor threads for variables
@ 2017-11-30  4:51 Kevin Buettner
  2017-11-30  4:54 ` [PATCH 1/2] Add interface for implementing thread parent operation in Python Kevin Buettner
  2017-11-30  4:55 ` [PATCH 2/2] OpenMP: Search ancestor threads for variables Kevin Buettner
  0 siblings, 2 replies; 3+ messages in thread
From: Kevin Buettner @ 2017-11-30  4:51 UTC (permalink / raw)
  To: gdb-patches

This is a two part series which adds support for accessing certain
variables which cannot currently be accessed in OpenMP programs. 

This work is not yet complete:  Changes to libgomp as well as the
python plugin implementing the thread parent operation for libgomp
are still under development.

Kevin

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] Add interface for implementing thread parent operation in Python
  2017-11-30  4:51 [PATCH 0/2] OpenMP: Search ancestor threads for variables Kevin Buettner
@ 2017-11-30  4:54 ` Kevin Buettner
  2017-11-30  4:55 ` [PATCH 2/2] OpenMP: Search ancestor threads for variables Kevin Buettner
  1 sibling, 0 replies; 3+ messages in thread
From: Kevin Buettner @ 2017-11-30  4:54 UTC (permalink / raw)
  To: gdb-patches

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(-)

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);
 }
 \f
+/* 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;
+}
+\f
 /* 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);
 }

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH 2/2] OpenMP: Search ancestor threads for variables
  2017-11-30  4:51 [PATCH 0/2] OpenMP: Search ancestor threads for variables Kevin Buettner
  2017-11-30  4:54 ` [PATCH 1/2] Add interface for implementing thread parent operation in Python Kevin Buettner
@ 2017-11-30  4:55 ` Kevin Buettner
  1 sibling, 0 replies; 3+ messages in thread
From: Kevin Buettner @ 2017-11-30  4:55 UTC (permalink / raw)
  To: gdb-patches

Consider this simple OpenMP program:

    void foo (int a1) {}

    int
    main (void)
    {
      static int s1 = -41;
      int i1 = 11, i2;

      for (i2 = 1; i2 <= 2; i2++)
	{
	  int pass = i2;
    #pragma omp parallel num_threads (2) firstprivate (i1)
	  {
	    foo (i1);
	  }
	  foo(pass);
	}
      foo (s1); foo (i2);
    }

When using a recent (development version) of GCC, GDB is now able to
access variables i2, and pass in the master thread.  (s1, due
to being a static variable is accessible from either thread.
Accessing i1 has been possible for a long while now.)

This patch makes variables such as i2 and pass accessible in
all other threads as well.  (Well, it does so long as a suitable
"thread parent" plugin is installed for libgomp.)

gdb/ChangeLog:
    
    	findvar.c (gdbthread.h): Include.
    	(get_hosting_frame): Add new parameter, errorp. Delete code
    	which generates an error and set errorp instead.
    	(default_read_var_value): Call get_hosting_frame() for
    	ancestor threads in addition to current thread.  Print errors
    	formerly printed by get_hosting_frame().
---
 gdb/findvar.c | 55 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 42 insertions(+), 13 deletions(-)

diff --git a/gdb/findvar.c b/gdb/findvar.c
index 2bc2095..81cac7d 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -33,6 +33,7 @@
 #include "language.h"
 #include "dwarf2loc.h"
 #include "selftest.h"
+#include "gdbthread.h"
 
 /* Basic byte-swapping routines.  All 'extract' functions return a
    host-format integer from a target-format integer at ADDR which is
@@ -474,10 +475,12 @@ follow_static_link (struct frame_info *frame,
 
 static struct frame_info *
 get_hosting_frame (struct symbol *var, const struct block *var_block,
-		   struct frame_info *frame)
+		   struct frame_info *frame, bool *errorp)
 {
   const struct block *frame_block = NULL;
 
+  *errorp = false;
+
   if (!symbol_read_needs_frame (var))
     return NULL;
 
@@ -564,17 +567,7 @@ get_hosting_frame (struct symbol *var, const struct block *var_block,
   if (frame == NULL)
     {
       frame = block_innermost_frame (var_block);
-      if (frame == NULL)
-	{
-	  if (BLOCK_FUNCTION (var_block)
-	      && !block_inlined_p (var_block)
-	      && SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block)))
-	    error (_("No frame is currently executing in block %s."),
-		   SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block)));
-	  else
-	    error (_("No frame is currently executing in specified"
-		     " block"));
-	}
+      *errorp = true;
     }
 
   return frame;
@@ -605,8 +598,44 @@ default_read_var_value (struct symbol *var, const struct block *var_block,
   else if (sym_need == SYMBOL_NEEDS_REGISTERS && !target_has_registers)
     error (_("Cannot read `%s' without registers"), SYMBOL_PRINT_NAME (var));
 
+  scoped_restore_current_thread restore_thread;
+
   if (frame != NULL)
-    frame = get_hosting_frame (var, var_block, frame);
+    {
+      struct frame_info *frame0 = frame;
+      bool errorp;
+
+      frame = get_hosting_frame (var, var_block, frame0, &errorp);
+
+      /* If the hosting frame could not be determined for the current thread,
+         try again using ancestor threads.  This allows GDB to find variables
+	 in outlined OpenMP functions.  */
+      while (frame == NULL)
+        {
+	  struct thread_info *thr_parent = thread_parent (inferior_thread ());
+
+	  if (thr_parent == NULL)
+	    break;
+
+	  switch_to_thread (thr_parent->ptid);
+	  /* frame0 will not be on the stack after switching threads. So,
+	     instead, search using the current frame.  */
+	  frame0 = get_current_frame ();
+	  frame = get_hosting_frame (var, var_block, frame0, &errorp);
+	}
+
+      if (frame == NULL && errorp)
+	{
+	  if (BLOCK_FUNCTION (var_block)
+	      && !block_inlined_p (var_block)
+	      && SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block)))
+	    error (_("No frame is currently executing in block %s."),
+		   SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block)));
+	  else
+	    error (_("No frame is currently executing in specified"
+		     " block"));
+	}
+    }
 
   if (SYMBOL_COMPUTED_OPS (var) != NULL)
     return SYMBOL_COMPUTED_OPS (var)->read_variable (var, frame);

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2017-11-30  4:55 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-30  4:51 [PATCH 0/2] OpenMP: Search ancestor threads for variables Kevin Buettner
2017-11-30  4:54 ` [PATCH 1/2] Add interface for implementing thread parent operation in Python Kevin Buettner
2017-11-30  4:55 ` [PATCH 2/2] OpenMP: Search ancestor threads for variables Kevin Buettner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).