From patchwork Thu Nov 30 04:54:01 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Buettner X-Patchwork-Id: 24623 Received: (qmail 40484 invoked by alias); 30 Nov 2017 04:54:07 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 40471 invoked by uid 89); 30 Nov 2017 04:54:06 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.7 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KB_WAM_FROM_NAME_SINGLEWORD, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=printers, sk:extensi, 10306 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 30 Nov 2017 04:54:04 +0000 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DA2D27CB84 for ; Thu, 30 Nov 2017 04:54:03 +0000 (UTC) Received: from pinnacle.lan (ovpn-121-48.rdu2.redhat.com [10.10.121.48]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 5C657600D5 for ; Thu, 30 Nov 2017 04:54:03 +0000 (UTC) Date: Wed, 29 Nov 2017 21:54:01 -0700 From: Kevin Buettner To: gdb-patches@sourceware.org Subject: [PATCH 1/2] Add interface for implementing thread parent operation in Python Message-ID: <20171129215401.1f91afe0@pinnacle.lan> In-Reply-To: <20171129215103.093cc881@pinnacle.lan> References: <20171129215103.093cc881@pinnacle.lan> MIME-Version: 1.0 X-IsSubscribed: yes 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); } +/* 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 #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", >hread_funcs, NULL); }