Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFC] Thread Name Printers
@ 2012-08-21 20:59 Aaron Gamble
  2012-08-22 19:39 ` Tom Tromey
  0 siblings, 1 reply; 7+ messages in thread
From: Aaron Gamble @ 2012-08-21 20:59 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 2199 bytes --]

Hi all,

This is a patch that adds thread name printers to GDB. Like
pretty-printers, they are Python classes that augment the output of
thread names.

gdb/ChangeLog:
    * data-directory/Makefile.in: Add gdb/thread_printing.py.
    * doc/gdb.texinfo: Add section about thread name printers in Python section.
    Add reference to thread name printers in 'info threads' section.
    * python/lib/gdb/__init__.py: Import ThreadNamePrinter.
    * python/lib/gdb/thread_printing.py: new file.
    (class ThreadNamePrinter): base class, contains helper functions
and list of registered printers.
    (class ExamplePrinter): example printer class for reference.
    (class EnableThreadNamePrinter, DisableThreadNamePrinter,
InfoThreadNamePrinter):
        gdb.commands for managing and getting information about thread
name printers.
    (register_thread_name_printer_commands): Registers thread name
printer commands.
    * python/py-inferior.c (add_thread_object): Save pointer to
thread_object in thread_info.
    * python/py-infthread.c (prepare_thread_name_printers): Prepares
all the registered thread name printers
        and returns a list of the printers. Called once by
thread.c:print_thread_info.
    (thread_printer_enabled): Returns TRUE if printer enabled.
    (apply_thread_name_pretty_printers): Iterates through the list of
printers calling print_thread until one returns a string.
        Called for each thread by thread.c:print_thread_info
    * python/python-internal.h: Extern gdbpy_print_thread_cst PyObject
string object.
    * pyhon/python.c: Declare and assign gdbpy_print_thread_cst.
    * testsuite/gdb.python/Makefile.in: add py-threadprettyprint.
    * testsuite/gdb.python/py-threadprettyprint.c: Basic threaded
program for testing thread name printing.
    * testsuite/gdb.python/py-threadprettyprint.exp: Tests for thread
name printers.
    * thread.c (print_thread_info): Calls prepare_thread_name_printers
to initialize printers and get list of printers.
        Calls apply_thread_name_pretty_printers for each thread if no
name for thread is already stored.
    (_initialize_thread): Add aliases for 'info threads' to
disambiguate between 'info thread-name-printer'

[-- Attachment #2: thread_name_printer.patch --]
[-- Type: application/octet-stream, Size: 28117 bytes --]

From ea94351d1ffaeb2f0e513f183afa0f74e7afde59 Mon Sep 17 00:00:00 2001
From: Aaron Gamble <agamble@google.com>
Date: Tue, 21 Aug 2012 11:50:38 -0700
Subject: [PATCH] thread name printers

---
 gdb/data-directory/Makefile.in                    |    1 +
 gdb/doc/gdb.texinfo                               |   38 ++++-
 gdb/python/lib/gdb/__init__.py                    |    6 +
 gdb/python/lib/gdb/thread_printing.py             |  200 +++++++++++++++++++++
 gdb/python/py-inferior.c                          |    2 +
 gdb/python/py-infthread.c                         |  116 ++++++++++++
 gdb/python/python-internal.h                      |    1 +
 gdb/python/python.c                               |    2 +
 gdb/python/python.h                               |    6 +
 gdb/testsuite/gdb.python/Makefile.in              |    2 +-
 gdb/testsuite/gdb.python/py-threadprettyprint.c   |  122 +++++++++++++
 gdb/testsuite/gdb.python/py-threadprettyprint.exp |   87 +++++++++
 gdb/thread.c                                      |   27 +++
 13 files changed, 607 insertions(+), 3 deletions(-)
 create mode 100644 gdb/python/lib/gdb/thread_printing.py
 create mode 100644 gdb/testsuite/gdb.python/py-threadprettyprint.c
 create mode 100644 gdb/testsuite/gdb.python/py-threadprettyprint.exp

diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 41947c6..30f5e88 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -55,6 +55,7 @@ PYTHON_FILES = \
 	gdb/__init__.py \
 	gdb/types.py \
 	gdb/printing.py \
+	gdb/thread_printing.py \
 	gdb/prompt.py \
 	gdb/command/__init__.py \
 	gdb/command/pretty_printers.py \
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 08ba92d..e98a3b5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2766,8 +2766,8 @@ the target system's thread identifier (@var{systag})
 
 @item
 the thread's name, if one is known.  A thread can either be named by
-the user (see @code{thread name}, below), or, in some cases, by the
-program itself.
+the user (see @code{thread name}, below), by Thread Name Printers
+(@pxref{Thread Name Printers}), or, in some cases, by the program itself.
 
 @item
 the current stack frame summary for that thread
@@ -22585,6 +22585,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Inferiors In Python::         Python representation of inferiors (processes)
 * Events In Python::            Listening for events from @value{GDBN}.
 * Threads In Python::           Accessing inferior threads from Python.
+* Thread Name Printers::        Custom thread name printing from Python.
 * Commands In Python::          Implementing new commands in Python.
 * Parameters In Python::        Adding new @value{GDBN} parameters.
 * Functions In Python::         Writing new convenience functions.
@@ -24175,6 +24176,39 @@ Return a Boolean indicating whether the thread is exited.
 @end defun
 @end table
 
+@node Thread Name Printers
+@subsubsection Thread Name Printers
+@cindex thread name printers
+
+Thread name printers are used to print custom thread names. They are
+implemented as Python classes that define two methods: @code{prepare} and @code{print_thread}.
+
+Thread name printers are registered and may be disabled and enabled by the
+provided alias when the printer was registered.
+
+Once a printer has been defined, it may be added to the global list
+of printers in gdb.thread_printing.ThreadNamePrinter by using
+gdb.thread_printing.ThreadNamePrinter.add_printer(@var{alias}, @var{printer})
+
+@defun thread_name_printer.prepare (@var{self})
+@value{GDBN} will call this method for each printer at the beginning of the
+'info threads' command.
+
+In this method printers may gather any information and store it internally
+for use when printing thread names. Access to gdb internal data is provided
+by the gdb module functions.
+@end defun
+
+@defun thread_name_printer.print_thread (@var{self}, @var{name}, @var{thread_info})
+For each thread, @value{GDBN} will iterate through the registered printers and call this
+method in each enabled printer until a string is returned. It is up to each thread printer
+to determine whether it can or should provide a thread name.
+
+@var{name} is the default name for the thread.
+
+@var{thread_info} is the InferiorThread as defined in @pxref{Threads In Python}.
+@end defun
+
 @node Commands In Python
 @subsubsection Commands In Python
 
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index a82e495..0cafdeb 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -39,3 +39,9 @@ for module, location in module_dict.iteritems():
          exec('import ' + py_file)
        except:
          print >> sys.stderr, traceback.format_exc()
+
+try:
+    from gdb.thread_printing import ThreadNamePrinter
+    gdb.prepare_thread_name_printers =  ThreadNamePrinter.prepare_thread_name_printers
+except:
+    print >> sys.stderr, "Unable to setup thread name pretty printing"
diff --git a/gdb/python/lib/gdb/thread_printing.py b/gdb/python/lib/gdb/thread_printing.py
new file mode 100644
index 0000000..bc3d72c
--- /dev/null
+++ b/gdb/python/lib/gdb/thread_printing.py
@@ -0,0 +1,200 @@
+# Pretty-printer utilities.
+# Copyright (C) 2010-2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Utilities and class definitions for thread name printers."""
+
+import gdb
+
+class ThreadNamePrinter(object):
+    """Base class for thread name printers.
+
+    See the ExamplePrinter(ThreadNamePrinter) below for reference.
+    The example printer would be used as such:
+      ThreadNamePrinter.add_printer("example printer", ExamplePrinter)
+
+    A printer is a class that is registered with the add_printer command.
+
+    When a printer class is added, it is first instantiated and then added
+    to the global list of printers.
+    """
+
+    # This is the global list of thread printers
+    printers = []
+
+    @classmethod
+    def prepare_thread_name_printers(cls):
+        """Run for every 'info threads' command.
+        Initialize all the printers and return the list.
+
+        This is called by GDB in the 'info thread' code.
+        """
+        for p in cls.printers:
+            p.prepare()
+        return cls.printers
+
+    @classmethod
+    def add_printer(cls, alias, printer, replace = False):
+        if not hasattr(printer, "prepare"):
+            raise TypeError("printer missing attribute: prepare")
+        if not hasattr(printer, "print_thread"):
+            raise TypeError("printer missing attribute: print_thread")
+
+        if not hasattr(printer, "enabled"):
+            printer.enabled = True
+
+        printer_instance = printer()
+        printer_instance.alias = alias
+
+        for i, p in enumerate(cls.printers):
+            if hasattr(p, "alias") and p.alias == alias:
+                if replace:
+                    del cls.printers[i]
+                    break # assumption: there will only one in the list.
+                else:
+                    raise RuntimeError(
+                        "Thread printer with alias %s already registered" % alias)
+        cls.printers.append(printer_instance)
+
+    @classmethod
+    def remove_printer(cls, alias):
+        for i, p in enumerate(cls.printers):
+            if p.alias == alias:
+                del cls.printers[i]
+                return
+        print "Printer '%s' not found" % alias
+
+    @classmethod
+    def disable_printer(cls, alias):
+        for p in cls.printers:
+            if p.alias == alias:
+              cls.disable_printer_instance(p)
+              return
+        print "Printer '%s' not found" % alias
+
+    @classmethod
+    def enable_printer(cls, alias):
+        for p in cls.printers:
+            if p.alias == alias:
+                cls.enable_printer_instance(p)
+                return
+        print "Printer '%s' not found" % alias
+
+    @staticmethod
+    def disable_printer_instance(p):
+        p.enabled = False
+
+    @staticmethod
+    def enable_printer_instance(p):
+        p.enabled = True
+
+class ExamplePrinter(ThreadNamePrinter):
+    """Reference class for thread name printers.
+
+    Thread name printers must implement the two methods below.
+    In order for a printer to be used it needs to be instantiated and
+    added to the gdb.thread_printers list.
+    Use the add_printer(alias, printer) function for this task.
+    This function will instantiate the printer, set the alias of the instance
+    and add the instance to the printer list.
+    """
+
+    def prepare(self):
+        """Called once for each 'info threads' command before the threads
+           are printed. Printers may re-define this method to initialize
+           their internal data.
+        """
+        # Usually we will have some map of threads we own.
+        # If necessary, We should discover them here.
+        # For this dummy example, it will be empty.
+        self.my_threads = {}
+
+    def print_thread(self, name, thread_info):
+        """Prints a thread name.
+
+        Printers must define this function, which takes a thread name and info,
+        and, if the thread matches the printer (usually a ptid check) returns
+        a new string for the threads name.
+
+        Parameters:
+            name: Existing name for the thread.
+            thread_info: A gdb.inferior_thread object for the thread.
+
+        Returns:
+            -A string, if the thread matches the printer.
+            -None, if the thread does not match.
+        """
+        # We only return a string name if we own the thread.
+        if thread_info.ptid[0] in self.my_thread:
+          return "some interesting name"
+        else:
+          return None
+
+class EnableThreadNamePrinter (gdb.Command):
+    """GDB command to enable the specified thread printer.
+
+    Usage: enable thread-name-printer [alias]
+
+    ALIAS is the name given to the printer when it was registered.
+    """
+
+    def __init__(self):
+        super(EnableThreadNamePrinter, self).__init__(
+                "enable thread-name-printer",
+                gdb.COMMAND_DATA)
+
+    def invoke(self, arg, from_tty):
+        ThreadNamePrinter.enable_printer(arg)
+
+class DisableThreadNamePrinter (gdb.Command):
+    """GDB command to disable the specified thread printer.
+
+    Usage: disable thread-name-printer [alias]
+
+    ALIAS is the name given to the printer when it was registered.
+    """
+
+    def __init__(self):
+        super(DisableThreadNamePrinter, self).__init__(
+                "disable thread-name-printer",
+                gdb.COMMAND_DATA)
+
+    def invoke(self, arg, from_tty):
+        ThreadNamePrinter.disable_printer(arg)
+
+class InfoThreadNamePrinter (gdb.Command):
+    """GDB command to list all the registered thread printers.
+
+    Usage: info thread-name-printer
+    """
+
+    def __init__(self):
+        super(InfoThreadNamePrinter, self).__init__(
+                "info thread-name-printer",
+                gdb.COMMAND_DATA)
+
+    def invoke(self, arg, from_tty):
+        print "Thread name pretty-printers:"
+        for p in ThreadNamePrinter.printers:
+            print "[%s] %s" % ("x" if p.enabled else " ",
+                               p.alias)
+
+
+def register_thread_name_printer_commands():
+    EnableThreadNamePrinter()
+    DisableThreadNamePrinter()
+    InfoThreadNamePrinter()
+
+register_thread_name_printer_commands()
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 907b73e..f087d3c 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -239,6 +239,8 @@ add_thread_object (struct thread_info *tp)
       return;
     }
 
+  tp->thread_object = thread_obj;
+
   inf_obj = (inferior_object *) thread_obj->inf_obj;
 
   entry = xmalloc (sizeof (struct threadlist_entry));
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 8821f20..2b972c6 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -21,7 +21,10 @@
 #include "exceptions.h"
 #include "gdbthread.h"
 #include "inferior.h"
+#include "python/python.h"
 #include "python-internal.h"
+#include "arch-utils.h"
+#include "language.h"
 
 static PyTypeObject thread_object_type;
 
@@ -340,3 +343,116 @@ static PyTypeObject thread_object_type =
   0,				  /* tp_init */
   0				  /* tp_alloc */
 };
+
+/* Initializes the list of thread name printers.
+   Returns a list of printers or NULL.
+   FIXME: Returns a PyObject to non-python code
+   Perhaps do this another way.  */
+
+PyObject *
+prepare_thread_name_printers (void)
+{
+  PyObject *printers = NULL;
+  struct cleanup *cleanups;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  /* Fetch and prepare each thread printer.  */
+  if (PyObject_HasAttrString (gdb_module, "prepare_thread_name_printers"))
+    {
+      printers = PyObject_CallMethod (gdb_module,
+                                      "prepare_thread_name_printers", NULL);
+      if (printers != NULL && (printers == Py_None
+                               || (!PyList_Check (printers)
+                               || PyList_Size (printers) == 0)))
+        {
+          Py_DECREF (printers);
+          printers = NULL;
+        }
+    }
+  do_cleanups (cleanups);
+  return printers;
+}
+
+/* Returns TRUE if the given printer PyObject is enabled.
+   Returns FALSE otherwise.  */
+
+static int
+thread_printer_enabled (PyObject *printer)
+{
+  PyObject *enabled = PyObject_GetAttrString(printer, "enabled");
+  int ret = FALSE;
+  if (enabled)
+    {
+      if (PyObject_IsTrue(enabled))
+        ret = TRUE;
+      else
+        ret = FALSE;
+
+      Py_DECREF (enabled);
+    }
+  return ret;
+}
+
+/* Gets a new thread name by applying each printer in a list of printers
+   to a thread. If a printer returns a new name, a pointer to this name
+   is stored in the threads name member. The name exists in the heap and
+   will be freed when the thread is destroyed.  */
+
+char *
+apply_thread_name_pretty_printers (PyObject *printers,
+                                   char *name, struct thread_info *info)
+{
+  struct cleanup *cleanups;
+  volatile struct gdb_exception except;
+  PyObject *ret_str;
+  PyObject *py_name = NULL;
+  PyObject *py_thread;
+  PyObject *seq = NULL;
+  PyObject *printer;
+  int len, i;
+
+  if (printers == NULL)
+    return name;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  py_thread = (PyObject*) info->thread_object;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+  {
+    py_name = PyString_FromString (name);
+    seq = PySequence_Fast (printers, "expected sequence");
+    len = PySequence_Size (printers);
+
+    for (i = 0; i < len; i++)
+      {
+        printer = PySequence_Fast_GET_ITEM (seq, i);
+        if (!thread_printer_enabled (printer))
+            continue;
+
+        ret_str = PyObject_CallMethodObjArgs (printer, gdbpy_print_thread_cst,
+                                              py_name, py_thread, NULL);
+        if (ret_str
+            && (gdbpy_is_string (ret_str) || gdbpy_is_lazy_string (ret_str)))
+          {
+            /* This printer matched the thread and returned a new name.  */
+            name = python_string_to_host_string (ret_str);
+            /* This newly allocated name will be freed in free_thread.  */
+            xfree (info->name);
+            info->name = name;
+            Py_DECREF (ret_str);
+            break;
+          }
+
+        /* ret_str should be None.  */
+        Py_XDECREF (ret_str);
+      }
+  }
+
+  Py_XDECREF (seq);
+  Py_XDECREF (py_name);
+
+  do_cleanups (cleanups);
+  return name;
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index bae61c2..c46d061 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -328,6 +328,7 @@ extern PyObject *gdbpy_to_string_cst;
 extern PyObject *gdbpy_display_hint_cst;
 extern PyObject *gdbpy_enabled_cst;
 extern PyObject *gdbpy_value_cst;
+extern PyObject *gdbpy_print_thread_cst;
 
 /* Exception types.  */
 extern PyObject *gdbpy_gdb_error;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c66efe4..387594b 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -82,6 +82,7 @@ PyObject *gdbpy_display_hint_cst;
 PyObject *gdbpy_doc_cst;
 PyObject *gdbpy_enabled_cst;
 PyObject *gdbpy_value_cst;
+PyObject *gdbpy_print_thread_cst;
 
 /* The GdbError exception.  */
 PyObject *gdbpy_gdberror_exc;
@@ -1305,6 +1306,7 @@ message == an error message without a stack will be printed."),
   gdbpy_doc_cst = PyString_FromString ("__doc__");
   gdbpy_enabled_cst = PyString_FromString ("enabled");
   gdbpy_value_cst = PyString_FromString ("value");
+  gdbpy_print_thread_cst = PyString_FromString ("print_thread");
 
   /* Release the GIL while gdb runs.  */
   PyThreadState_Swap (NULL);
diff --git a/gdb/python/python.h b/gdb/python/python.h
index dd7066f..f7e5476 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -21,6 +21,7 @@
 #define GDB_PYTHON_H
 
 #include "value.h"
+#include "gdbthread.h"
 
 struct breakpoint_object;
 
@@ -37,6 +38,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
 			      const struct value_print_options *options,
 			      const struct language_defn *language);
 
+PyObject *prepare_thread_name_printers (void);
+
+char *apply_thread_name_pretty_printers (PyObject *printers,
+                                         char *name, struct thread_info *info);
+
 void preserve_python_values (struct objfile *objfile, htab_t copied_types);
 
 void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);
diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index 4e286b5..9155eac 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -6,7 +6,7 @@ EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
 	py-shared python lib-types py-events py-evthreads py-frame \
 	py-mi py-pp-maint py-progspace py-section-script py-objfile \
 	py-finish-breakpoint py-finish-breakpoint2 py-value-cc py-explore \
-	py-explore-cc
+	py-explore-cc py-threadprettyprint
 
 MISCELLANEOUS = py-shared-sl.sl py-events-shlib.so py-events-shlib-nodebug.so 
 
diff --git a/gdb/testsuite/gdb.python/py-threadprettyprint.c b/gdb/testsuite/gdb.python/py-threadprettyprint.c
new file mode 100644
index 0000000..1cba2d6
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-threadprettyprint.c
@@ -0,0 +1,122 @@
+/* Test cases for thread name printers.
+
+   Copyright 2008, 2010-2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NR_THREADS
+#define NR_THREADS 4
+#endif
+
+int thread_count;
+
+pthread_mutex_t thread_count_mutex;
+
+pthread_cond_t thread_count_condvar;
+
+void
+incr_thread_count (void)
+{
+  pthread_mutex_lock (&thread_count_mutex);
+  ++thread_count;
+  if (thread_count == NR_THREADS)
+    pthread_cond_signal (&thread_count_condvar);
+  pthread_mutex_unlock (&thread_count_mutex);
+}
+
+void
+cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut)
+{
+  pthread_mutex_lock (mut);
+  pthread_cond_wait (cond, mut);
+  pthread_mutex_unlock (mut);
+}
+
+void
+noreturn (void)
+{
+  pthread_mutex_t mut;
+  pthread_cond_t cond;
+
+  pthread_mutex_init (&mut, NULL);
+  pthread_cond_init (&cond, NULL);
+
+  /* Wait for a condition that will never be signaled, so we effectively
+     block the thread here.  */
+  cond_wait (&cond, &mut);
+}
+
+void *
+forever_pthread (void *unused)
+{
+  incr_thread_count ();
+  noreturn ();
+}
+
+/* Wait until all threads are running.  */
+
+void
+wait_all_threads_running (void)
+{
+  pthread_mutex_lock (&thread_count_mutex);
+  if (thread_count == NR_THREADS)
+    {
+      pthread_mutex_unlock (&thread_count_mutex);
+      return;
+    }
+  pthread_cond_wait (&thread_count_condvar, &thread_count_mutex);
+  if (thread_count == NR_THREADS)
+    {
+      pthread_mutex_unlock (&thread_count_mutex);
+      return;
+    }
+  pthread_mutex_unlock (&thread_count_mutex);
+  printf ("failed waiting for all threads to start\n");
+  abort ();
+}
+
+/* Called when all threads are running.
+   Easy place for a breakpoint.  */
+
+void
+all_threads_running (void)
+{
+}
+
+int
+main (void)
+{
+  pthread_t forever[NR_THREADS];
+  int i;
+
+  pthread_mutex_init (&thread_count_mutex, NULL);
+  pthread_cond_init (&thread_count_condvar, NULL);
+
+  for (i = 0; i < NR_THREADS; ++i)
+    pthread_create (&forever[i], NULL, forever_pthread, NULL);
+
+  wait_all_threads_running ();
+  all_threads_running ();
+
+  return 0;
+}
+
diff --git a/gdb/testsuite/gdb.python/py-threadprettyprint.exp b/gdb/testsuite/gdb.python/py-threadprettyprint.exp
new file mode 100644
index 0000000..a60ef74
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-threadprettyprint.exp
@@ -0,0 +1,87 @@
+# Copyright (C) 2004, 2007-2008, 2010-2012 Free Software Foundation,
+# Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test thread name pretty printer functionality.
+
+set NR_THREADS 4
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Start with a fresh gdb.
+clean_restart ${binfile}
+
+if { [skip_python_tests] } { continue }
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "additional_flags=-DNR_THREADS=$NR_THREADS"]] != "" } {
+    return -1
+}
+
+if { ![runto_main] } {
+    fail "Can't run to main"
+    return 0
+}
+
+gdb_test "break all_threads_running" \
+         "Breakpoint 2 at .*: file .*${srcfile}, line .*" \
+         "breakpoint on all_threads_running"
+
+# Run the program and make sure GDB reports that we stopped after
+# hitting breakpoint 2 in all_threads_running().
+
+gdb_test "continue" \
+         ".*Breakpoint 2, all_threads_running ().*" \
+         "run to all_threads_running"
+
+gdb_py_test_multiple "define some thread printers" \
+  "python" "" \
+  "import gdb.thread_printing" "" \
+  "class test_thread_printer (gdb.thread_printing.ThreadNamePrinter):" "" \
+  "  def prepare(self):" "" \
+  "    pass" "" \
+  "  def print_thread(self, name, thread):" "" \
+  "    return \"test_name\" + str(thread.ptid\[1\])" "" \
+  "gdb.thread_printing.ThreadNamePrinter.add_printer('test_printer'," "" \
+  "                                                  test_thread_printer)" "" \
+  "class bad_no_methods():" "" \
+  "  pass" "" \
+  "class bad_no_print_thread():" "" \
+  "  def prepare(self): " "" \
+  "    pass" "" \
+  "end" ""
+
+gdb_test "info thread-name-printer" \
+         "Thread name pretty-printers:\r\n\\\[x\\\] test_printer" \
+         "print registered thread name printers"
+
+# Check we are correctly rejecting bad printers.
+# These "printers" were definied in the "define some thread printers" test above.
+gdb_test "python gdb.thread_printing.ThreadNamePrinter.add_printer('bad printer', bad_no_methods)" \
+         ".*TypeError: printer missing attribute: prepare.*" \
+         "Catch bad printers - missing attribute prepare"
+
+gdb_test "python gdb.thread_printing.ThreadNamePrinter.add_printer('bad printer', bad_no_print_thread)" \
+         ".*TypeError: printer missing attribute: print_thread.*" \
+         "Catch bad printers - missing attribute print_thread"
+
+
+# We want to make sure the thread list has printed all the thread names to include the ptid as our
+# test printer was defined.
+# Note: the \\2 backreference is 2 because gdb_test contains the first matching group
+gdb_test "info threads" \
+         " +Id +Target +Id +Frame(?:.*Thread 0x\[A-Fa-f0-9\]+ \\\(LWP (\\\d+)\\\) \"test_name\\2.*)+" \
+         "thread name printer test"
diff --git a/gdb/thread.c b/gdb/thread.c
index 7e8eec5..3147384 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -34,6 +34,8 @@
 #include "regcache.h"
 #include "gdb.h"
 #include "gdb_string.h"
+#include "python/python.h"
+#include "python/python-internal.h"
 
 #include <ctype.h>
 #include <sys/types.h>
@@ -767,6 +769,9 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
   struct cleanup *old_chain;
   char *extra_info, *name, *target_id;
   int current_thread = -1;
+#ifdef HAVE_PYTHON
+  PyObject *printers = NULL;
+#endif
 
   update_thread_list ();
   current_ptid = inferior_ptid;
@@ -774,6 +779,13 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
   /* We'll be switching threads temporarily.  */
   old_chain = make_cleanup_restore_current_thread ();
 
+#ifdef HAVE_PYTHON
+  /* Get list of printers, they will initialize themselves here.  */
+  printers = prepare_thread_name_printers ();
+  if (printers != NULL)
+    make_cleanup_py_decref (printers);
+#endif
+
   /* For backward compatibility, we make a list for MI.  A table is
      preferable for the CLI, though, because it shows table
      headers.  */
@@ -867,7 +879,15 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 
       target_id = target_pid_to_str (tp->ptid);
       extra_info = target_extra_thread_info (tp);
+#ifdef HAVE_PYTHON
+      name = (tp->name
+              ? tp->name
+              : apply_thread_name_pretty_printers (printers,
+                                                   target_thread_name (tp),
+                                                   tp));
+#else
       name = tp->name ? tp->name : target_thread_name (tp);
+#endif
 
       if (ui_out_is_mi_like_p (uiout))
 	{
@@ -1469,6 +1489,13 @@ _initialize_thread (void)
 Usage: info threads [ID]...\n\
 Optional arguments are thread IDs with spaces between.\n\
 If no arguments, all threads are displayed."));
+  /* Keep these abbreviations of "info threads", "info thread-name-printer"
+     makes them ambigous otherwise.  */
+  add_info_alias ("th", "threads" , 1);
+  add_info_alias ("thr", "threads" , 1);
+  add_info_alias ("thre", "threads" , 1);
+  add_info_alias ("threa", "threads" , 1);
+  add_info_alias ("thread", "threads" , 1);
 
   add_prefix_cmd ("thread", class_run, thread_command, _("\
 Use this command to switch between threads.\n\
-- 
1.7.7.3


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

end of thread, other threads:[~2012-08-25  5:28 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-21 20:59 [RFC] Thread Name Printers Aaron Gamble
2012-08-22 19:39 ` Tom Tromey
2012-08-23  0:29   ` Aaron Gamble
2012-08-23 16:18     ` Tom Tromey
2012-08-24 22:00       ` Aaron Gamble
2012-08-24 22:02         ` Aaron Gamble
2012-08-25  5:28           ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox