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

* Re: [RFC] Thread Name Printers
  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
  0 siblings, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2012-08-22 19:39 UTC (permalink / raw)
  To: Aaron Gamble; +Cc: gdb-patches

>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes:

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

Thanks.

Could you say why you implemented thread name printers instead of just
having Python code just directly set thread names?  This is already
possible by just assigning to the thread's 'name' attribute.

Maybe it is so you can enable or disable them dynamically?

More on this below.

Aaron> +Once a printer has been defined, it may be added to the global list
Aaron> +of printers in gdb.thread_printing.ThreadNamePrinter by using
Aaron> +gdb.thread_printing.ThreadNamePrinter.add_printer(@var{alias}

I tend to think this is one too many names.
How about just 'gdb.threads.add_printer'?
Or just have the base class constructor do it, and make it private?

Aaron> +@defun thread_name_printer.prepare (@var{self})
Aaron> +@value{GDBN} will call this method for each printer at the beginning of the
Aaron> +'info threads' command.
Aaron> +
Aaron> +In this method printers may gather any information and store it internally
Aaron> +for use when printing thread names. 

I think it should read "In this method the printer may ...".

Aaron> Access to gdb internal data is provided
Aaron> +by the gdb module functions.

I think this last sentence isn't needed.

Aaron> +    gdb.prepare_thread_name_printers =  ThreadNamePrinter.prepare_thread_name_printers

Extra space after the '='.

Aaron> +class ExamplePrinter(ThreadNamePrinter):
Aaron> +    """Reference class for thread name printers.

I'd prefer to have this in the documentation.

Aaron> +def register_thread_name_printer_commands():
Aaron> +    EnableThreadNamePrinter()
Aaron> +    DisableThreadNamePrinter()
Aaron> +    InfoThreadNamePrinter()
Aaron> +
Aaron> +register_thread_name_printer_commands()

No need for a separate function, just invoke them directly.

The commands probably be in python/commands/ rather than in the base
directory.  The base directory is really for library-ish functionality,
not commands.

Aaron> +  tp->thread_object = thread_obj;

I didn't see a patch to gdbthread.h.

However, if you do this, then you probably ought to change how thread
object lifetimes are handled more generally, getting rid of
threadlist_entry, etc.

Aaron> +/* Initializes the list of thread name printers.
Aaron> +   Returns a list of printers or NULL.
Aaron> +   FIXME: Returns a PyObject to non-python code
Aaron> +   Perhaps do this another way.  */
Aaron> +
Aaron> +PyObject *
Aaron> +prepare_thread_name_printers (void)

Yeah, I'm not very fond of this approach.

What is the rationale for the 'prepare' step, anyway?

Aaron> +      printers = PyObject_CallMethod (gdb_module,
Aaron> +                                      "prepare_thread_name_printers", NULL);
Aaron> +      if (printers != NULL && (printers == Py_None
Aaron> +                               || (!PyList_Check (printers)
Aaron> +                               || PyList_Size (printers) == 0)))
Aaron> +        {

If you are explicitly ignoring a Python exception, you have to clear the
exception state.

In gdb, though, it is more normal to do this with gdbpy_print_stack.

Aaron> +/* Returns TRUE if the given printer PyObject is enabled.
Aaron> +   Returns FALSE otherwise.  */
Aaron> +
Aaron> +static int
Aaron> +thread_printer_enabled (PyObject *printer)
Aaron> +{

If you're making a list of printers in the .py code, you might as well
filter there instead of having this.  It's a lot easier to do it there.

Aaron> +  PyObject *enabled = PyObject_GetAttrString(printer, "enabled");
Aaron> +  int ret = FALSE;
Aaron> +  if (enabled)
Aaron> +    {

For example, this does the wrong thing with exceptions.

Aaron> +      if (PyObject_IsTrue(enabled))

Likewise.

Aaron> +/* Gets a new thread name by applying each printer in a list of printers
Aaron> +   to a thread. If a printer returns a new name, a pointer to this name
Aaron> +   is stored in the threads name member. The name exists in the heap and
Aaron> +   will be freed when the thread is destroyed.  */
Aaron> +
Aaron> +char *
Aaron> +apply_thread_name_pretty_printers (PyObject *printers,
Aaron> +                                   char *name, struct thread_info *info)

Given that contract, I think this should take and return 'const char *'.
But see below.

Aaron> +  TRY_CATCH (except, RETURN_MASK_ALL)
Aaron> +  {
Aaron> +    py_name = PyString_FromString (name);
Aaron> +    seq = PySequence_Fast (printers, "expected sequence");
Aaron> +    len = PySequence_Size (printers);

Exception handling is missing, here and elsewhere.

I think all the code in this TRY_CATCH is calling into Python, not into gdb.
So, you don't need TRY_CATCH at all.

Aaron> +            /* This newly allocated name will be freed in free_thread.  */
Aaron> +            xfree (info->name);
Aaron> +            info->name = name;

This seems weird to me.

If a printer sets the thread's name, then this seems like a lot of code
for what amounts to an assignment.  It seems like you could already
achieve all this with code to listen to thread-creation events and set
thread names appropriately.

Aaron> diff --git a/gdb/thread.c b/gdb/thread.c
Aaron> index 7e8eec5..3147384 100644
Aaron> --- a/gdb/thread.c
Aaron> +++ b/gdb/thread.c
Aaron> @@ -34,6 +34,8 @@
Aaron>  #include "regcache.h"
Aaron>  #include "gdb.h"
Aaron>  #include "gdb_string.h"
Aaron> +#include "python/python.h"
Aaron> +#include "python/python-internal.h"

python-internal.h shouldn't generally be included in gdb.
varobj does, but it is kind of an exception.
I'd entertain other exceptions, of course, but I think in this case one
isn't needed.

Aaron> +#ifdef HAVE_PYTHON
Aaron> +  /* Get list of printers, they will initialize themselves here.  */
Aaron> +  printers = prepare_thread_name_printers ();
Aaron> +  if (printers != NULL)
Aaron> +    make_cleanup_py_decref (printers);
Aaron> +#endif

The normal approach in cases like this is to avoid #ifdef, and instead
define a dummy function that does nothing in the no-Python case.
Then, declare these interfaces in python.h.

For example, you could have prepare_thread_name_printers which just
iterates and calls the 'prepare' methods.  (If that is really needed.)
This could just be:

    void prepare_thread_name_printers (void);

Then you could have apply_thread_name_pretty_printers, just dropping the
PyObject* argument:

    char *apply_thread_name_pretty_printers (char *, struct thread_info *);

Tom


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

* Re: [RFC] Thread Name Printers
  2012-08-22 19:39 ` Tom Tromey
@ 2012-08-23  0:29   ` Aaron Gamble
  2012-08-23 16:18     ` Tom Tromey
  0 siblings, 1 reply; 7+ messages in thread
From: Aaron Gamble @ 2012-08-23  0:29 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Once again, thanks for the feedback.

On Wed, Aug 22, 2012 at 12:38 PM, Tom Tromey <tromey@redhat.com> wrote:
>
>>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes:
>
> Aaron> This is a patch that adds thread name printers to GDB. Like
> Aaron> pretty-printers, they are Python classes that augment the output of
> Aaron> thread names.
>
> Thanks.
>
> Could you say why you implemented thread name printers instead of just
> having Python code just directly set thread names?  This is already
> possible by just assigning to the thread's 'name' attribute.
>
> Maybe it is so you can enable or disable them dynamically?
>
> More on this below.
>
> Aaron> +Once a printer has been defined, it may be added to the global list
> Aaron> +of printers in gdb.thread_printing.ThreadNamePrinter by using
> Aaron> +gdb.thread_printing.ThreadNamePrinter.add_printer(@var{alias}
>
> I tend to think this is one too many names.
> How about just 'gdb.threads.add_printer'?
> Or just have the base class constructor do it, and make it private?
How about gdb.thread_printing.add_printer? It also seems fine to me to
have a method in the base class.
e.g.
myclass().add_printer("my printer")
# instantiates class and adds it to list of printers with alias "my printer"

>
> Aaron> +@defun thread_name_printer.prepare (@var{self})
> Aaron> +@value{GDBN} will call this method for each printer at the beginning of the
> Aaron> +'info threads' command.
> Aaron> +
> Aaron> +In this method printers may gather any information and store it internally
> Aaron> +for use when printing thread names.
>
> I think it should read "In this method the printer may ...".
>
> Aaron> Access to gdb internal data is provided
> Aaron> +by the gdb module functions.
>
> I think this last sentence isn't needed.
>
> Aaron> +    gdb.prepare_thread_name_printers =  ThreadNamePrinter.prepare_thread_name_printers
>
> Extra space after the '='.
>
> Aaron> +class ExamplePrinter(ThreadNamePrinter):
> Aaron> +    """Reference class for thread name printers.
>
> I'd prefer to have this in the documentation.
>
> Aaron> +def register_thread_name_printer_commands():
> Aaron> +    EnableThreadNamePrinter()
> Aaron> +    DisableThreadNamePrinter()
> Aaron> +    InfoThreadNamePrinter()
> Aaron> +
> Aaron> +register_thread_name_printer_commands()
>
> No need for a separate function, just invoke them directly.
>
> The commands probably be in python/commands/ rather than in the base
> directory.  The base directory is really for library-ish functionality,
> not commands.
>
> Aaron> +  tp->thread_object = thread_obj;
>
> I didn't see a patch to gdbthread.h.
Ah, this was missed out by accident.
>
> However, if you do this, then you probably ought to change how thread
> object lifetimes are handled more generally, getting rid of
> threadlist_entry, etc.
Not entirely sure what you mean here, but I will look into it. My
unfamiliarity with the codebase is to blame.
>
> Aaron> +/* Initializes the list of thread name printers.
> Aaron> +   Returns a list of printers or NULL.
> Aaron> +   FIXME: Returns a PyObject to non-python code
> Aaron> +   Perhaps do this another way.  */
> Aaron> +
> Aaron> +PyObject *
> Aaron> +prepare_thread_name_printers (void)
>
> Yeah, I'm not very fond of this approach.
>
> What is the rationale for the 'prepare' step, anyway?

This is for the printers to do any one time setup before printing a
list of threads. A common case I can see is if the printer needs to
examine memory and traverse something like a linked list. Without a
call like this, or an indicator in print_thread, there is no way for a
printer to know the different between multiple calls to info threads.


>
> Aaron> +      printers = PyObject_CallMethod (gdb_module,
> Aaron> +                                      "prepare_thread_name_printers", NULL);
> Aaron> +      if (printers != NULL && (printers == Py_None
> Aaron> +                               || (!PyList_Check (printers)
> Aaron> +                               || PyList_Size (printers) == 0)))
> Aaron> +        {
>
> If you are explicitly ignoring a Python exception, you have to clear the
> exception state.
>
> In gdb, though, it is more normal to do this with gdbpy_print_stack.

Ah, sorry, I'm unfamiliar with the Python C api. I will add a call to
gdbpy_print_stack when printers == NULL. Are you also worried about
the potential exceptions raised in PyList_Check and PyList_Size? I
suppose in that case just having a call to gdbpy_print_stack at the
end of this function or in each case of printers == NULL would be
sufficient.

>
> Aaron> +/* Returns TRUE if the given printer PyObject is enabled.
> Aaron> +   Returns FALSE otherwise.  */
> Aaron> +
> Aaron> +static int
> Aaron> +thread_printer_enabled (PyObject *printer)
> Aaron> +{
>
> If you're making a list of printers in the .py code, you might as well
> filter there instead of having this.  It's a lot easier to do it there.

The thing is that we do not create a new list. The same list is
returned instead. I suppose if we add the check to
prepare_thread_name_printers and just return a new list, that would be
fine as well.

>
> Aaron> +  PyObject *enabled = PyObject_GetAttrString(printer, "enabled");
> Aaron> +  int ret = FALSE;
> Aaron> +  if (enabled)
> Aaron> +    {
>
> For example, this does the wrong thing with exceptions.
>
> Aaron> +      if (PyObject_IsTrue(enabled))
>
> Likewise.
>
> Aaron> +/* Gets a new thread name by applying each printer in a list of printers
> Aaron> +   to a thread. If a printer returns a new name, a pointer to this name
> Aaron> +   is stored in the threads name member. The name exists in the heap and
> Aaron> +   will be freed when the thread is destroyed.  */
> Aaron> +
> Aaron> +char *
> Aaron> +apply_thread_name_pretty_printers (PyObject *printers,
> Aaron> +                                   char *name, struct thread_info *info)
>
> Given that contract, I think this should take and return 'const char *'.
> But see below.
>
> Aaron> +  TRY_CATCH (except, RETURN_MASK_ALL)
> Aaron> +  {
> Aaron> +    py_name = PyString_FromString (name);
> Aaron> +    seq = PySequence_Fast (printers, "expected sequence");
> Aaron> +    len = PySequence_Size (printers);
>
> Exception handling is missing, here and elsewhere.
>
> I think all the code in this TRY_CATCH is calling into Python, not into gdb.
> So, you don't need TRY_CATCH at all.
>
> Aaron> +            /* This newly allocated name will be freed in free_thread.  */
> Aaron> +            xfree (info->name);
> Aaron> +            info->name = name;
>
> This seems weird to me.
>
> If a printer sets the thread's name, then this seems like a lot of code
> for what amounts to an assignment.  It seems like you could already
> achieve all this with code to listen to thread-creation events and set
> thread names appropriately.
The problem with only setting names on thread-creation events is that
a library managing threads and assigning internal thread names we may
wish to print will potentially not have set the name at the time the
thread is created.
e.g.
1. thread created.
2. thread-created event in gdb. (no name available yet)
3. library sets internal name for thread.

The thread name is assigned here because I think it is safe to assume
that once a thread has a name, that name will not change. Also, if a
user assigns a name via 'thread name foo', they would want that name
to override any thread name printer.

>
> Aaron> diff --git a/gdb/thread.c b/gdb/thread.c
> Aaron> index 7e8eec5..3147384 100644
> Aaron> --- a/gdb/thread.c
> Aaron> +++ b/gdb/thread.c
> Aaron> @@ -34,6 +34,8 @@
> Aaron>  #include "regcache.h"
> Aaron>  #include "gdb.h"
> Aaron>  #include "gdb_string.h"
> Aaron> +#include "python/python.h"
> Aaron> +#include "python/python-internal.h"
>
> python-internal.h shouldn't generally be included in gdb.
> varobj does, but it is kind of an exception.
> I'd entertain other exceptions, of course, but I think in this case one
> isn't needed.
>
> Aaron> +#ifdef HAVE_PYTHON
> Aaron> +  /* Get list of printers, they will initialize themselves here.  */
> Aaron> +  printers = prepare_thread_name_printers ();
> Aaron> +  if (printers != NULL)
> Aaron> +    make_cleanup_py_decref (printers);
> Aaron> +#endif
>
> The normal approach in cases like this is to avoid #ifdef, and instead
> define a dummy function that does nothing in the no-Python case.
> Then, declare these interfaces in python.h.
>
> For example, you could have prepare_thread_name_printers which just
> iterates and calls the 'prepare' methods.  (If that is really needed.)
> This could just be:
>
>     void prepare_thread_name_printers (void);
>
> Then you could have apply_thread_name_pretty_printers, just dropping the
> PyObject* argument:
>
>     char *apply_thread_name_pretty_printers (char *, struct thread_info *);
>
> Tom
Ok. I'll use a global static variable for the list of printers in
python/py-infthread.c and use dummy functions for the #ifdef stuff.


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

* Re: [RFC] Thread Name Printers
  2012-08-23  0:29   ` Aaron Gamble
@ 2012-08-23 16:18     ` Tom Tromey
  2012-08-24 22:00       ` Aaron Gamble
  0 siblings, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2012-08-23 16:18 UTC (permalink / raw)
  To: Aaron Gamble; +Cc: gdb-patches

>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes:

Tom> How about just 'gdb.threads.add_printer'?
Tom> Or just have the base class constructor do it, and make it private?

Aaron> How about gdb.thread_printing.add_printer? It also seems fine to me to
Aaron> have a method in the base class.
Aaron> e.g.
Aaron> myclass().add_printer("my printer")
Aaron> # instantiates class and adds it to list of printers with alias "my printer"

It's sort of a norm in gdb to have the instantiation of the class
install the CLI bits as a side effect.  E.g., Command and Parameter do
this.  I don't insist on it, though.

I like the name gdb.threads over gdb.thread_printing, just because I
assume we'll want to have more thread utility code, and this would
provide a spot to put it.

Tom> However, if you do this, then you probably ought to change how thread
Tom> object lifetimes are handled more generally, getting rid of
Tom> threadlist_entry, etc.

Aaron> Not entirely sure what you mean here, but I will look into it. My
Aaron> unfamiliarity with the codebase is to blame.

No worries.

The current code is written in a way to let us associate a gdb.Thread
object with a thread_info in an indirect way.  However, this probably
doesn't perform extremely well if there are many threads.

So, your approach is superior.  However, I think it is best to do the
conversion completely.  This could be a separate refactoring patch --
just rip out the old threadlist_entry stuff and replace it with your
approach.  You can see how we handled this in breakpoint.h (search for
struct breakpoint_object) to make it work without including python.h
everywhere.

Tom> What is the rationale for the 'prepare' step, anyway?

Aaron> This is for the printers to do any one time setup before printing a
Aaron> list of threads. A common case I can see is if the printer needs to
Aaron> examine memory and traverse something like a linked list. Without a
Aaron> call like this, or an indicator in print_thread, there is no way for a
Aaron> printer to know the different between multiple calls to info threads.

Ok, I see.
Should there also be an "unprepare" step so that these objects can
release resources after "info threads" exits?

Aaron> Ah, sorry, I'm unfamiliar with the Python C api. I will add a call to
Aaron> gdbpy_print_stack when printers == NULL. Are you also worried about
Aaron> the potential exceptions raised in PyList_Check and PyList_Size? I
Aaron> suppose in that case just having a call to gdbpy_print_stack at the
Aaron> end of this function or in each case of printers == NULL would be
Aaron> sufficient.

Nearly all Python functions have to have their result checked for error.
This often leads to spaghetti code, unfortunately, but that's how it is.

Exactly how to handle the error depends on the situation.

In "Python-facing" code, the typical thing to do is propagate the error
-- release locally-acquired resources and return NULL (or -1 or whatever
it is for the current context).

In "gdb-facing" code, usually we call gdbpy_print_stack.  This isn't
ideal, since in many situations I think it would be preferable to
convert the Python exception to a gdb exception.

In your particular case I think it is friendliest to the user to call
gdbpy_print_stack, even assuming we implement real exception handling,
just because this approach means that some bad Python code won't make
"info threads" fail.

Tom> If you're making a list of printers in the .py code, you might as well
Tom> filter there instead of having this.  It's a lot easier to do it there.

Aaron> The thing is that we do not create a new list. The same list is
Aaron> returned instead. I suppose if we add the check to
Aaron> prepare_thread_name_printers and just return a new list, that would be
Aaron> fine as well.

I got a little lost here.

If there is a single list, where disabled items are filtered when
iterating over it, then don't bother returning a list at all, just use
the global one from the .py code.

Aaron> The problem with only setting names on thread-creation events is that
Aaron> a library managing threads and assigning internal thread names we may
Aaron> wish to print will potentially not have set the name at the time the
Aaron> thread is created.

Ok, makes sense.

Aaron> e.g.
Aaron> 1. thread created.
Aaron> 2. thread-created event in gdb. (no name available yet)
Aaron> 3. library sets internal name for thread.

In this scenario I was picturing that the python code would set a
breakpoint to capture the interesting event.  But I can accept that this
may not always be desirable.

Aaron> The thread name is assigned here because I think it is safe to assume
Aaron> that once a thread has a name, that name will not change. Also, if a
Aaron> user assigns a name via 'thread name foo', they would want that name
Aaron> to override any thread name printer.

Ok, thanks.

Aaron> Ok. I'll use a global static variable for the list of printers in
Aaron> python/py-infthread.c and use dummy functions for the #ifdef stuff.

IIUC it could all just be in the python code.

Tom


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

* Re: [RFC] Thread Name Printers
  2012-08-23 16:18     ` Tom Tromey
@ 2012-08-24 22:00       ` Aaron Gamble
  2012-08-24 22:02         ` Aaron Gamble
  0 siblings, 1 reply; 7+ messages in thread
From: Aaron Gamble @ 2012-08-24 22:00 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

New patch. See inline comments.

(FYI today is the last day of my internship, so this e-mail address
will be dead very soon. I can be reached at invalidfunction or
jaarongamble both at gmail.com)

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'


On Thu, Aug 23, 2012 at 9:17 AM, Tom Tromey <tromey@redhat.com> wrote:
>
>>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes:
>
> Tom> How about just 'gdb.threads.add_printer'?
> Tom> Or just have the base class constructor do it, and make it private?
>
> Aaron> How about gdb.thread_printing.add_printer? It also seems fine to me to
> Aaron> have a method in the base class.
> Aaron> e.g.
> Aaron> myclass().add_printer("my printer")
> Aaron> # instantiates class and adds it to list of printers with alias "my printer"
>
> It's sort of a norm in gdb to have the instantiation of the class
> install the CLI bits as a side effect.  E.g., Command and Parameter do
> this.  I don't insist on it, though.
I just stuck with myclass().add_printer("my alias") for now.
>
> I like the name gdb.threads over gdb.thread_printing, just because I
> assume we'll want to have more thread utility code, and this would
> provide a spot to put it.
Done. changed to gdb.threads.


> Tom> However, if you do this, then you probably ought to change how thread
> Tom> object lifetimes are handled more generally, getting rid of
> Tom> threadlist_entry, etc.
>
> Aaron> Not entirely sure what you mean here, but I will look into it. My
> Aaron> unfamiliarity with the codebase is to blame.
>
> No worries.
>
> The current code is written in a way to let us associate a gdb.Thread
> object with a thread_info in an indirect way.  However, this probably
> doesn't perform extremely well if there are many threads.
>
> So, your approach is superior.  However, I think it is best to do the
> conversion completely.  This could be a separate refactoring patch --
> just rip out the old threadlist_entry stuff and replace it with your
> approach.  You can see how we handled this in breakpoint.h (search for
> struct breakpoint_object) to make it work without including python.h
> everywhere.
I will leave this for dje@ to do.


> Tom> What is the rationale for the 'prepare' step, anyway?
>
> Aaron> This is for the printers to do any one time setup before printing a
> Aaron> list of threads. A common case I can see is if the printer needs to
> Aaron> examine memory and traverse something like a linked list. Without a
> Aaron> call like this, or an indicator in print_thread, there is no way for a
> Aaron> printer to know the different between multiple calls to info threads.
>
> Ok, I see.
> Should there also be an "unprepare" step so that these objects can
> release resources after "info threads" exits?
That's a good point. I've added a cleanup step.

> Aaron> Ah, sorry, I'm unfamiliar with the Python C api. I will add a call to
> Aaron> gdbpy_print_stack when printers == NULL. Are you also worried about
> Aaron> the potential exceptions raised in PyList_Check and PyList_Size? I
> Aaron> suppose in that case just having a call to gdbpy_print_stack at the
> Aaron> end of this function or in each case of printers == NULL would be
> Aaron> sufficient.
>
> Nearly all Python functions have to have their result checked for error.
> This often leads to spaghetti code, unfortunately, but that's how it is.
>
> Exactly how to handle the error depends on the situation.
>
> In "Python-facing" code, the typical thing to do is propagate the error
> -- release locally-acquired resources and return NULL (or -1 or whatever
> it is for the current context).
>
> In "gdb-facing" code, usually we call gdbpy_print_stack.  This isn't
> ideal, since in many situations I think it would be preferable to
> convert the Python exception to a gdb exception.
>
> In your particular case I think it is friendliest to the user to call
> gdbpy_print_stack, even assuming we implement real exception handling,
> just because this approach means that some bad Python code won't make
> "info threads" fail.
Added stack trace printing to "handle" exceptions.

> Tom> If you're making a list of printers in the .py code, you might as well
> Tom> filter there instead of having this.  It's a lot easier to do it there.
>
> Aaron> The thing is that we do not create a new list. The same list is
> Aaron> returned instead. I suppose if we add the check to
> Aaron> prepare_thread_name_printers and just return a new list, that would be
> Aaron> fine as well.
>
> I got a little lost here.
>
> If there is a single list, where disabled items are filtered when
> iterating over it, then don't bother returning a list at all, just use
> the global one from the .py code.
>
> Aaron> The problem with only setting names on thread-creation events is that
> Aaron> a library managing threads and assigning internal thread names we may
> Aaron> wish to print will potentially not have set the name at the time the
> Aaron> thread is created.
>
> Ok, makes sense.
>
> Aaron> e.g.
> Aaron> 1. thread created.
> Aaron> 2. thread-created event in gdb. (no name available yet)
> Aaron> 3. library sets internal name for thread.
>
> In this scenario I was picturing that the python code would set a
> breakpoint to capture the interesting event.  But I can accept that this
> may not always be desirable.
>
> Aaron> The thread name is assigned here because I think it is safe to assume
> Aaron> that once a thread has a name, that name will not change. Also, if a
> Aaron> user assigns a name via 'thread name foo', they would want that name
> Aaron> to override any thread name printer.
>
> Ok, thanks.
>
> Aaron> Ok. I'll use a global static variable for the list of printers in
> Aaron> python/py-infthread.c and use dummy functions for the #ifdef stuff.
>
> IIUC it could all just be in the python code.
You are entirely right. I changed this to just keep the printer in the
Python world, along with some other changes that cleans things up.


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

* Re: [RFC] Thread Name Printers
  2012-08-24 22:00       ` Aaron Gamble
@ 2012-08-24 22:02         ` Aaron Gamble
  2012-08-25  5:28           ` Eli Zaretskii
  0 siblings, 1 reply; 7+ messages in thread
From: Aaron Gamble @ 2012-08-24 22:02 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

Oops. Forgot to actually attach patch.

On Fri, Aug 24, 2012 at 3:00 PM, Aaron Gamble <agamble@google.com> wrote:
> New patch. See inline comments.
>
> (FYI today is the last day of my internship, so this e-mail address
> will be dead very soon. I can be reached at invalidfunction or
> jaarongamble both at gmail.com)
>
> 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'
>
>
> On Thu, Aug 23, 2012 at 9:17 AM, Tom Tromey <tromey@redhat.com> wrote:
>>
>>>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes:
>>
>> Tom> How about just 'gdb.threads.add_printer'?
>> Tom> Or just have the base class constructor do it, and make it private?
>>
>> Aaron> How about gdb.thread_printing.add_printer? It also seems fine to me to
>> Aaron> have a method in the base class.
>> Aaron> e.g.
>> Aaron> myclass().add_printer("my printer")
>> Aaron> # instantiates class and adds it to list of printers with alias "my printer"
>>
>> It's sort of a norm in gdb to have the instantiation of the class
>> install the CLI bits as a side effect.  E.g., Command and Parameter do
>> this.  I don't insist on it, though.
> I just stuck with myclass().add_printer("my alias") for now.
>>
>> I like the name gdb.threads over gdb.thread_printing, just because I
>> assume we'll want to have more thread utility code, and this would
>> provide a spot to put it.
> Done. changed to gdb.threads.
>
>
>> Tom> However, if you do this, then you probably ought to change how thread
>> Tom> object lifetimes are handled more generally, getting rid of
>> Tom> threadlist_entry, etc.
>>
>> Aaron> Not entirely sure what you mean here, but I will look into it. My
>> Aaron> unfamiliarity with the codebase is to blame.
>>
>> No worries.
>>
>> The current code is written in a way to let us associate a gdb.Thread
>> object with a thread_info in an indirect way.  However, this probably
>> doesn't perform extremely well if there are many threads.
>>
>> So, your approach is superior.  However, I think it is best to do the
>> conversion completely.  This could be a separate refactoring patch --
>> just rip out the old threadlist_entry stuff and replace it with your
>> approach.  You can see how we handled this in breakpoint.h (search for
>> struct breakpoint_object) to make it work without including python.h
>> everywhere.
> I will leave this for dje@ to do.
>
>
>> Tom> What is the rationale for the 'prepare' step, anyway?
>>
>> Aaron> This is for the printers to do any one time setup before printing a
>> Aaron> list of threads. A common case I can see is if the printer needs to
>> Aaron> examine memory and traverse something like a linked list. Without a
>> Aaron> call like this, or an indicator in print_thread, there is no way for a
>> Aaron> printer to know the different between multiple calls to info threads.
>>
>> Ok, I see.
>> Should there also be an "unprepare" step so that these objects can
>> release resources after "info threads" exits?
> That's a good point. I've added a cleanup step.
>
>> Aaron> Ah, sorry, I'm unfamiliar with the Python C api. I will add a call to
>> Aaron> gdbpy_print_stack when printers == NULL. Are you also worried about
>> Aaron> the potential exceptions raised in PyList_Check and PyList_Size? I
>> Aaron> suppose in that case just having a call to gdbpy_print_stack at the
>> Aaron> end of this function or in each case of printers == NULL would be
>> Aaron> sufficient.
>>
>> Nearly all Python functions have to have their result checked for error.
>> This often leads to spaghetti code, unfortunately, but that's how it is.
>>
>> Exactly how to handle the error depends on the situation.
>>
>> In "Python-facing" code, the typical thing to do is propagate the error
>> -- release locally-acquired resources and return NULL (or -1 or whatever
>> it is for the current context).
>>
>> In "gdb-facing" code, usually we call gdbpy_print_stack.  This isn't
>> ideal, since in many situations I think it would be preferable to
>> convert the Python exception to a gdb exception.
>>
>> In your particular case I think it is friendliest to the user to call
>> gdbpy_print_stack, even assuming we implement real exception handling,
>> just because this approach means that some bad Python code won't make
>> "info threads" fail.
> Added stack trace printing to "handle" exceptions.
>
>> Tom> If you're making a list of printers in the .py code, you might as well
>> Tom> filter there instead of having this.  It's a lot easier to do it there.
>>
>> Aaron> The thing is that we do not create a new list. The same list is
>> Aaron> returned instead. I suppose if we add the check to
>> Aaron> prepare_thread_name_printers and just return a new list, that would be
>> Aaron> fine as well.
>>
>> I got a little lost here.
>>
>> If there is a single list, where disabled items are filtered when
>> iterating over it, then don't bother returning a list at all, just use
>> the global one from the .py code.
>>
>> Aaron> The problem with only setting names on thread-creation events is that
>> Aaron> a library managing threads and assigning internal thread names we may
>> Aaron> wish to print will potentially not have set the name at the time the
>> Aaron> thread is created.
>>
>> Ok, makes sense.
>>
>> Aaron> e.g.
>> Aaron> 1. thread created.
>> Aaron> 2. thread-created event in gdb. (no name available yet)
>> Aaron> 3. library sets internal name for thread.
>>
>> In this scenario I was picturing that the python code would set a
>> breakpoint to capture the interesting event.  But I can accept that this
>> may not always be desirable.
>>
>> Aaron> The thread name is assigned here because I think it is safe to assume
>> Aaron> that once a thread has a name, that name will not change. Also, if a
>> Aaron> user assigns a name via 'thread name foo', they would want that name
>> Aaron> to override any thread name printer.
>>
>> Ok, thanks.
>>
>> Aaron> Ok. I'll use a global static variable for the list of printers in
>> Aaron> python/py-infthread.c and use dummy functions for the #ifdef stuff.
>>
>> IIUC it could all just be in the python code.
> You are entirely right. I changed this to just keep the printer in the
> Python world, along with some other changes that cleans things up.

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

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


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

* Re: [RFC] Thread Name Printers
  2012-08-24 22:02         ` Aaron Gamble
@ 2012-08-25  5:28           ` Eli Zaretskii
  0 siblings, 0 replies; 7+ messages in thread
From: Eli Zaretskii @ 2012-08-25  5:28 UTC (permalink / raw)
  To: Aaron Gamble; +Cc: tromey, gdb-patches

> Date: Fri, 24 Aug 2012 15:02:04 -0700
> From: Aaron Gamble <agamble@google.com>
> Cc: gdb-patches@sourceware.org
> 
> New patch. See inline comments.

Thanks.  I have comments about the documentation part.

> +Thread name printers are registered and may be disabled and enabled by the
> +provided alias when the printer was registered.

What does it mean "enabled by the provided alias"?

> +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")}

The last line should be in @smallexample block.

> +@defun thread_name_printer.prepare (@var{self})
> +@value{GDBN} will call this method for each printer at the beginning of the
> +'info threads' command.

"info threads" should be in @code and without the quotes.

> +method in each enabled printer until a string is returned. It is up to each thread printer
                                                            ^^
Two spaces here.

> +@var{thread_info} is the InferiorThread as defined in @pxref{Threads In Python}.

@pxref is for text in parentheses.  You want @ref here.


^ 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