From: Aaron Gamble <agamble@google.com>
To: gdb-patches@sourceware.org
Subject: [RFC] Thread Name Printers
Date: Tue, 21 Aug 2012 20:59:00 -0000 [thread overview]
Message-ID: <CAHX8C+LvZKv2_98u+38Y7P08pgK0_a9uRd3KJ7HiU9zehgQiUQ@mail.gmail.com> (raw)
[-- 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
next reply other threads:[~2012-08-21 20:59 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-21 20:59 Aaron Gamble [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAHX8C+LvZKv2_98u+38Y7P08pgK0_a9uRd3KJ7HiU9zehgQiUQ@mail.gmail.com \
--to=agamble@google.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox