From 3ebdf3e05d01f1c48ec9dee57f412ccb25a90a81 Mon Sep 17 00:00:00 2001
From: Aaron Gamble <agamble@google.com>
Date: Fri, 24 Aug 2012 14:48:07 -0700
Subject: [PATCH] thread name printers

---
 gdb/data-directory/Makefile.in                    |    2 +
 gdb/doc/gdb.texinfo                               |   89 ++++++++++++++-
 gdb/gdbthread.h                                   |    5 +
 gdb/python/lib/gdb/__init__.py                    |    8 ++
 gdb/python/lib/gdb/command/thread_printers.py     |   72 ++++++++++++
 gdb/python/lib/gdb/threads.py                     |  127 +++++++++++++++++++++
 gdb/python/py-inferior.c                          |    2 +
 gdb/python/py-infthread.c                         |   96 ++++++++++++++++
 gdb/python/python-internal.h                      |    2 +
 gdb/python/python.c                               |    6 +
 gdb/python/python.h                               |    7 +
 gdb/testsuite/gdb.python/Makefile.in              |    2 +-
 gdb/testsuite/gdb.python/py-threadprettyprint.c   |  122 ++++++++++++++++++++
 gdb/testsuite/gdb.python/py-threadprettyprint.exp |   86 ++++++++++++++
 gdb/thread.c                                      |   23 ++++-
 15 files changed, 645 insertions(+), 4 deletions(-)
 create mode 100644 gdb/python/lib/gdb/command/thread_printers.py
 create mode 100644 gdb/python/lib/gdb/threads.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..3a5bb05 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -55,9 +55,11 @@ PYTHON_FILES = \
 	gdb/__init__.py \
 	gdb/types.py \
 	gdb/printing.py \
+	gdb/threads.py \
 	gdb/prompt.py \
 	gdb/command/__init__.py \
 	gdb/command/pretty_printers.py \
+	gdb/command/thread_printers.py \
 	gdb/command/prompt.py \
 	gdb/command/explore.py \
 	gdb/function/__init__.py \
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 08ba92d..51d9072 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,90 @@ 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 inherit from
+gdb.thread_printing.ThreadNamePrinter in thread_printing.py and define
+the following 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 can be instantiated and added to the global
+list of printers as follows:
+@code{my_printer_class().add_printer("my printer alias")}
+
+@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 the printer may gather any information and store it internally
+for use when printing thread names.
+@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
+
+@smallexample
+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, thread_info):
+        """Return a new thread name or None.
+
+        Printers must define this function, which takes a InveriorThread object,
+        and, if the thread matches the printer (usually a ptid check) returns
+        a new string for the threads name.
+
+        Parameters:
+            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
+
+    def cleanup(self):
+        """Printers can use this method to do any cleanup they may wish to do
+           after all threads have been printed.
+        """
+        pass
+
+ExamplePrinter().add_printer("Example Printer")
+@end smallexample
+
 @node Commands In Python
 @subsubsection Commands In Python
 
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 0250555..58fac60 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -28,6 +28,7 @@ struct symtab;
 #include "frame.h"
 #include "ui-out.h"
 #include "inferior.h"
+#include "python/python-internal.h"
 
 /* Frontend view of the thread state.  Possible extensions: stepping,
    finishing, until(ling),...  */
@@ -227,6 +228,10 @@ struct thread_info
   /* Function that is called to free PRIVATE.  If this is NULL, then
      xfree will be called on PRIVATE.  */
   void (*private_dtor) (struct private_thread_info *);
+
+  /* Pointer to the corresponding Python PyObject for this thread.
+     This is set in python/py-inferior.c:add_thread_object.  */
+  thread_object *thread_object;
 };
 
 /* Create an empty thread list, or empty the existing one.  */
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index a82e495..6f33bce 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -39,3 +39,11 @@ for module, location in module_dict.iteritems():
          exec('import ' + py_file)
        except:
          print >> sys.stderr, traceback.format_exc()
+
+try:
+    from gdb.threads import ThreadNamePrinter
+    gdb.prepare_thread_name_printers = ThreadNamePrinter.prepare_thread_name_printers
+    gdb.apply_thread_name_printers = ThreadNamePrinter.apply_thread_name_printers
+    gdb.cleanup_thread_name_printers = ThreadNamePrinter.cleanup_thread_name_printers
+except:
+    print >> sys.stderr, "Unable to setup thread name pretty printing"
diff --git a/gdb/python/lib/gdb/command/thread_printers.py b/gdb/python/lib/gdb/command/thread_printers.py
new file mode 100644
index 0000000..40f472f
--- /dev/null
+++ b/gdb/python/lib/gdb/command/thread_printers.py
@@ -0,0 +1,72 @@
+# 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/>.
+
+"""GDB commands for working with thread-name-printers."""
+
+import gdb
+from gdb.threads import ThreadNamePrinter
+
+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)
+
+EnableThreadNamePrinter()
+DisableThreadNamePrinter()
+InfoThreadNamePrinter()
diff --git a/gdb/python/lib/gdb/threads.py b/gdb/python/lib/gdb/threads.py
new file mode 100644
index 0000000..2a7eedb
--- /dev/null
+++ b/gdb/python/lib/gdb/threads.py
@@ -0,0 +1,127 @@
+# 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.
+
+    Thread name printers must inherit from this base class
+    and implement the methods
+        -prepare()
+            Called once per info threads command prior to printing
+            any threads.
+
+        -print_thread(thread_info)
+            Called per thread. Return a new string name for any
+            thread that applies.
+
+        -cleanup() (OPTIONAL)
+            Called after all threads have been printed. Allows for
+            thread prints to do any cleanup they may need.
+    """
+
+    # This is the global list of thread printers
+    printers = []
+
+    def add_printer(self, alias, replace = False):
+        if not hasattr(self, "prepare"):
+            raise TypeError("printer missing attribute: prepare")
+        if not hasattr(self, "print_thread"):
+            raise TypeError("printer missing attribute: print_thread")
+
+        if not hasattr(self, "enabled"):
+            self.enabled = True
+
+        self.alias = alias
+
+        for i, p in enumerate(ThreadNamePrinter.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)
+        ThreadNamePrinter.printers.append(self)
+
+    @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 threads' code.
+        """
+        for p in cls.printers:
+            p.prepare()
+        return cls.printers
+
+    @classmethod
+    def apply_thread_name_printers(cls, thread_info):
+        """Apply thread name printers to a particular thread.
+
+        If a new name is returned by any printer, update the name of the thread
+        and return.
+
+        This is called by GDB in the 'info threads' code.
+        """
+        for p in [ p for p in cls.printers if p.enabled == True ]:
+            new_name = p.print_thread(thread_info)
+            if new_name:
+                thread_info.name = new_name
+                break
+        return
+
+    @classmethod
+    def cleanup_thread_name_printers(cls):
+        """Call the cleanup method for each printer."""
+        for p in [ p for p in cls.printers if p.enabled == True ]:
+            if hasattr(p.cleanup):
+                p.cleanup()
+
+    @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
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..747c138 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,96 @@ static PyTypeObject thread_object_type =
   0,				  /* tp_init */
   0				  /* tp_alloc */
 };
+
+#ifdef HAVE_PYTHON
+
+/* Calls prepare for each printer in the the list of thread name printers.  */
+
+void
+prepare_thread_name_printers (void)
+{
+  PyObject *ret;
+  struct cleanup *cleanups;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  /* Prepare each thread printer.  */
+  if (PyObject_HasAttrString (gdb_module, "prepare_thread_name_printers"))
+    {
+      ret = PyObject_CallMethod (gdb_module, "prepare_thread_name_printers",
+				 NULL);
+      if (ret == NULL)
+        gdbpy_print_stack();
+      else
+	Py_DECREF (ret);
+    }
+  do_cleanups (cleanups);
+}
+
+/* Called per thread by print_thread_info to check if a thread name printer
+   applies to a thread to give it a new name. If a new name is returned for
+   a thread, the name is updated and the loop terminates.  */
+
+void
+apply_thread_name_pretty_printers (struct thread_info *info)
+{
+  struct cleanup *cleanups;
+  PyObject *ret;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  if (PyObject_HasAttr (gdb_module, gdbpy_apply_thread_printers_cst))
+    {
+      ret = PyObject_CallMethodObjArgs (gdb_module,
+					gdbpy_apply_thread_printers_cst,
+					(PyObject *) info->thread_object, NULL);
+      if (ret == NULL)
+	  gdbpy_print_stack();
+      else
+	Py_DECREF (ret);
+    }
+
+  do_cleanups (cleanups);
+}
+
+/* Called after all threads have been printed. Allows for thread name printers
+   to release any objects or do any cleanups.  */
+
+void
+cleanup_thread_name_pretty_printers (void *unused)
+{
+  struct cleanup *cleanups;
+  PyObject *ret;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  if (PyObject_HasAttr (gdb_module, gdbpy_cleanup_thread_printers_cst))
+    {
+      ret = PyObject_CallMethodObjArgs (gdb_module,
+					gdbpy_cleanup_thread_printers_cst,
+					NULL);
+      if (ret == NULL)
+	  gdbpy_print_stack();
+      else
+	Py_DECREF (ret);
+    }
+
+  do_cleanups (cleanups);
+}
+
+#else // HAVE_PYTHON
+void
+prepare_thread_name_printers (void)
+{
+}
+
+void
+apply_thread_name_pretty_printers (struct thread_info *info)
+{
+}
+
+void
+cleanup_thread_name_pretty_printers (void *unused)
+{
+}
+#endif
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index bae61c2..c5e412e 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -328,6 +328,8 @@ 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_apply_thread_printers_cst;
+extern PyObject *gdbpy_cleanup_thread_printers_cst;
 
 /* Exception types.  */
 extern PyObject *gdbpy_gdb_error;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index c66efe4..f25dab6 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -82,6 +82,8 @@ PyObject *gdbpy_display_hint_cst;
 PyObject *gdbpy_doc_cst;
 PyObject *gdbpy_enabled_cst;
 PyObject *gdbpy_value_cst;
+PyObject *gdbpy_apply_thread_printers_cst;
+PyObject *gdbpy_cleanup_thread_printers_cst;
 
 /* The GdbError exception.  */
 PyObject *gdbpy_gdberror_exc;
@@ -1305,6 +1307,10 @@ 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_apply_thread_printers_cst
+    = PyString_FromString ("apply_thread_name_printers");
+  gdbpy_cleanup_thread_printers_cst
+    = PyString_FromString ("cleanup_thread_name_printers");
 
   /* Release the GIL while gdb runs.  */
   PyThreadState_Swap (NULL);
diff --git a/gdb/python/python.h b/gdb/python/python.h
index dd7066f..9f6beed 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,12 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
 			      const struct value_print_options *options,
 			      const struct language_defn *language);
 
+void prepare_thread_name_printers (void);
+
+void apply_thread_name_pretty_printers (struct thread_info *info);
+
+void cleanup_thread_name_pretty_printers (void *unused);
+
 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..09e2756
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-threadprettyprint.exp
@@ -0,0 +1,86 @@
+# 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" "" \
+  "from gdb.threads import ThreadNamePrinter" "" \
+  "class test_thread_printer (ThreadNamePrinter):" "" \
+  "  def prepare(self):" "" \
+  "    pass" "" \
+  "  def print_thread(self, thread):" "" \
+  "    return \"test_name\" + str(thread.ptid\[1\])" "" \
+  "test_thread_printer().add_printer('test_printer')" "" \
+  "class bad_no_methods(ThreadNamePrinter):" "" \
+  "  pass" "" \
+  "class bad_no_print_thread(ThreadNamePrinter):" "" \
+  "  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 bad_no_methods().add_printer('bad printer')" \
+         ".*TypeError: printer missing attribute: prepare.*" \
+         "Catch bad printers - missing attribute prepare"
+
+gdb_test "python bad_no_print_thread().add_printer('bad printer')" \
+         ".*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..fae901a 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -34,6 +34,7 @@
 #include "regcache.h"
 #include "gdb.h"
 #include "gdb_string.h"
+#include "python/python.h"
 
 #include <ctype.h>
 #include <sys/types.h>
@@ -774,6 +775,10 @@ 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 ();
 
+  /* Prepare the list of thread printers.  */
+  prepare_thread_name_printers ();
+  make_cleanup (cleanup_thread_name_pretty_printers, NULL);
+
   /* For backward compatibility, we make a list for MI.  A table is
      preferable for the CLI, though, because it shows table
      headers.  */
@@ -867,7 +872,16 @@ 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);
-      name = tp->name ? tp->name : target_thread_name (tp);
+
+      if (tp->name != NULL)
+	name = tp->name;
+      else
+	{
+	  /* Potentially update tp->name with a pretty printer.  */
+	  apply_thread_name_pretty_printers (tp);
+	  if (tp->name != NULL)
+	    name = tp->name;
+	}
 
       if (ui_out_is_mi_like_p (uiout))
 	{
@@ -1469,6 +1483,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

