gdb
authorTom Tromey <tromey@redhat.com>
Wed, 19 Jan 2011 17:21:39 +0000 (17:21 +0000)
committerTom Tromey <tromey@redhat.com>
Wed, 19 Jan 2011 17:21:39 +0000 (17:21 +0000)
PR mi/8618:
* thread.c (free_thread): Free 'name'.
(print_thread_info): Emit thread name.  Change CLI output.
(thread_name_command): New function.
(do_captured_thread_select): Emit newline.
(_initialize_thread): Register 'thread name' command.
* target.h (struct target_ops) <to_thread_name>: New field.
(target_thread_name): New macro.
* target.c (update_current_target): Handle to_thread_name.
* python/py-infthread.c (thpy_get_name): New function.
(thpy_set_name): Likewise.
(thread_object_getset): Add "name".
* linux-nat.c (linux_nat_thread_name): New function.
(linux_nat_add_target): Set to_thread_name.
* gdbthread.h (struct thread_info) <name>: New field.
gdb/doc
* gdb.texinfo (Threads): Document thread name output and `thread
name' command.
(Threads In Python): Document Thread.name attribute.
(GDB/MI Thread Commands): Document thread attributes.
gdb/testsuite
* gdb.python/py-infthread.exp: Add thread tests.

13 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbthread.h
gdb/linux-nat.c
gdb/python/py-infthread.c
gdb/target.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-infthread.exp
gdb/testsuite/gdb.threads/manythreads.exp
gdb/thread.c

index 1a99d78f02402dd06f2f1063462dbd35752d82a2..f5cf4007812ede2f43644d4ca3f261c2d08a41d1 100644 (file)
@@ -1,3 +1,21 @@
+2011-01-19  Tom Tromey  <tromey@redhat.com>
+
+       PR mi/8618:
+       * thread.c (free_thread): Free 'name'.
+       (print_thread_info): Emit thread name.  Change CLI output.
+       (thread_name_command): New function.
+       (do_captured_thread_select): Emit newline.
+       (_initialize_thread): Register 'thread name' command.
+       * target.h (struct target_ops) <to_thread_name>: New field.
+       (target_thread_name): New macro.
+       * target.c (update_current_target): Handle to_thread_name.
+       * python/py-infthread.c (thpy_get_name): New function.
+       (thpy_set_name): Likewise.
+       (thread_object_getset): Add "name".
+       * linux-nat.c (linux_nat_thread_name): New function.
+       (linux_nat_add_target): Set to_thread_name.
+       * gdbthread.h (struct thread_info) <name>: New field.
+
 2011-01-18  Joel Brobecker  <brobecker@adacore.com>
 
        * ada-valprint.c (ada_print_scalar): Remove unsigned char downcast.
index 9283e15f47cee56b164754d64a78150e05d87c4b..2539eb7de627caf3be3c478d0c974d50303b9a3b 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
   It is like the "dir" command except that it replaces the
   source path list instead of augmenting it.
 
+* GDB now understands thread names.
+
+  On GNU/Linux, "info threads" will display the thread name as set by
+  prctl or pthread_setname_np.
+
+  There is also a new command, "thread name", which can be used to
+  assign a name internally for GDB to display.
+
 * OpenCL C
   Initial support for the OpenCL C language (http://www.khronos.org/opencl)
   has been integrated into GDB.
@@ -49,6 +57,9 @@
   ** New function gdb.newest_frame returns the newest frame in the
      selected thread.
 
+  ** The gdb.InferiorThread class has a new "name" attribute.  This
+     holds the thread's name.
+
 * C++ Improvements:
 
   ** GDB now puts template parameters in scope when debugging in an
index 8863d0ee55cd4299ad49a1355c78a8e35ce1adfb..25c47589fb1702fd67881f374e178bed567fd5cd 100644 (file)
@@ -1,3 +1,10 @@
+2011-01-19  Tom Tromey  <tromey@redhat.com>
+
+       * gdb.texinfo (Threads): Document thread name output and `thread
+       name' command.
+       (Threads In Python): Document Thread.name attribute.
+       (GDB/MI Thread Commands): Document thread attributes.
+
 2011-01-12  Andrew Burgess  <aburgess@broadcom.com>
 
        * gdb.texinfo (GDB/MI Data Manipulation): Update to reflect
index d48d95cb215e9eec5da208a69e7eb9ed10529b1c..4da4e55f207bd27697b3d2e4bc93b1953c759827 100644 (file)
@@ -2717,6 +2717,11 @@ the thread number assigned by @value{GDBN}
 @item
 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.
+
 @item
 the current stack frame summary for that thread
 @end enumerate
@@ -2787,6 +2792,19 @@ shown in the first field of the @samp{info threads} display; or it
 could be a range of thread numbers, as in @code{2-4}.  To apply a
 command to all threads, type @kbd{thread apply all @var{command}}.
 
+@kindex thread name
+@cindex name a thread
+@item thread name [@var{name}]
+This command assigns a name to the current thread.  If no argument is
+given, any existing user-specified name is removed.  The thread name
+appears in the @samp{info threads} display.
+
+On some systems, such as @sc{gnu}/Linux, @value{GDBN} is able to
+determine the name of the thread as given by the OS.  On these
+systems, a name specified with @samp{thread name} will override the
+system-give name, and removing the user-specified name will cause
+@value{GDBN} to once again display the system-specified name.
+
 @kindex set print thread-events
 @cindex print messages on thread start and exit
 @item set print thread-events
@@ -21865,6 +21883,17 @@ is no selected thread, this will return @code{None}.
 A @code{gdb.InferiorThread} object has the following attributes:
 
 @table @code
+@defivar InferiorThread name
+The name of the thread.  If the user specified a name using
+@code{thread name}, then this returns that name.  Otherwise, if an
+OS-supplied name is available, then it is returned.  Otherwise, this
+returns @code{None}.
+
+This attribute can be assigned to.  The new value must be a string
+object, which sets the new name, or @code{None}, which removes any
+user-specified thread name.
+@end defivar
+
 @defivar InferiorThread num
 ID of the thread, as assigned by GDB.
 @end defivar
@@ -25753,21 +25782,38 @@ also reports the current thread.
 The @samp{info thread} command prints the same information
 about all threads.
 
-@subsubheading Example
+@subsubheading Result
 
-@smallexample
--thread-info
-^done,threads=[
-@{id="2",target-id="Thread 0xb7e14b90 (LWP 21257)",
-   frame=@{level="0",addr="0xffffe410",func="__kernel_vsyscall",args=[]@},state="running"@},
-@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
-   frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
-           file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}],
-current-thread-id="1"
-(gdb)
-@end smallexample
+The result is a list of threads.  The following attributes are
+defined for a given thread:
+
+@table @samp
+@item current
+This field exists only for the current thread.  It has the value @samp{*}.
 
-The @samp{state} field may have the following values:
+@item id
+The identifier that @value{GDBN} uses to refer to the thread.
+
+@item target-id
+The identifier that the target uses to refer to the thread.
+
+@item details
+Extra information about the thread, in a target-specific format.  This
+field is optional.
+
+@item name
+The name of the thread.  If the user specified a name using the
+@code{thread name} command, then this name is given.  Otherwise, if
+@value{GDBN} can extract the thread name from the target, then that
+name is given.  If @value{GDBN} cannot find the thread name, then this
+field is omitted.
+
+@item frame
+The stack frame currently executing in the thread.
+
+@item state
+The thread's state.  The @samp{state} field may have the following
+values:
 
 @table @code
 @item stopped
@@ -25780,6 +25826,29 @@ threads.
 
 @end table
 
+@item core
+If @value{GDBN} can find the CPU core on which this thread is running,
+then this field is the core identifier.  This field is optional.
+
+@end table
+
+@subsubheading Example
+
+@smallexample
+-thread-info
+^done,threads=[
+@{id="2",target-id="Thread 0xb7e14b90 (LWP 21257)",
+   frame=@{level="0",addr="0xffffe410",func="__kernel_vsyscall",
+           args=[]@},state="running"@},
+@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
+   frame=@{level="0",addr="0x0804891f",func="foo",
+           args=[@{name="i",value="10"@}],
+           file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},
+           state="running"@}],
+current-thread-id="1"
+(gdb)
+@end smallexample
+
 @subheading The @code{-thread-list-ids} Command
 @findex -thread-list-ids
 
index 9e122f691bf9f58d65cab2c96bda145d44551a2e..ddb7b0f66cd1d96933081ccac9fd420aa5cecc74 100644 (file)
@@ -125,6 +125,10 @@ struct thread_info
                                    kernel thread id, etc.  */
   int num;                     /* Convenient handle (GDB thread id) */
 
+  /* The name of the thread, as specified by the user.  This is NULL
+     if the thread does not have a user-given name.  */
+  char *name;
+
   /* Non-zero means the thread is executing.  Note: this is different
      from saying that there is an active target and we are stopped at
      a breakpoint, for instance.  This is a real indicator whether the
index 62a453879bbb63bfc4b3ff8bd1e7c058fc514610..a855219b53a6d78af72bc2a8a85996ad0918cc14 100644 (file)
@@ -4078,6 +4078,43 @@ linux_nat_pid_to_str (struct target_ops *ops, ptid_t ptid)
   return normal_pid_to_str (ptid);
 }
 
+static char *
+linux_nat_thread_name (struct thread_info *thr)
+{
+  int pid = ptid_get_pid (thr->ptid);
+  long lwp = ptid_get_lwp (thr->ptid);
+#define FORMAT "/proc/%d/task/%ld/comm"
+  char buf[sizeof (FORMAT) + 30];
+  FILE *comm_file;
+  char *result = NULL;
+
+  snprintf (buf, sizeof (buf), FORMAT, pid, lwp);
+  comm_file = fopen (buf, "r");
+  if (comm_file)
+    {
+      /* Not exported by the kernel, so we define it here.  */
+#define COMM_LEN 16
+      static char line[COMM_LEN + 1];
+
+      if (fgets (line, sizeof (line), comm_file))
+       {
+         char *nl = strchr (line, '\n');
+
+         if (nl)
+           *nl = '\0';
+         if (*line != '\0')
+           result = line;
+       }
+
+      fclose (comm_file);
+    }
+
+#undef COMM_LEN
+#undef FORMAT
+
+  return result;
+}
+
 /* Accepts an integer PID; Returns a string representing a file that
    can be opened to get the symbols for the child process.  */
 
@@ -5683,6 +5720,7 @@ linux_nat_add_target (struct target_ops *t)
   t->to_mourn_inferior = linux_nat_mourn_inferior;
   t->to_thread_alive = linux_nat_thread_alive;
   t->to_pid_to_str = linux_nat_pid_to_str;
+  t->to_thread_name = linux_nat_thread_name;
   t->to_has_thread_control = tc_schedlock;
   t->to_thread_address_space = linux_nat_thread_address_space;
   t->to_stopped_by_watchpoint = linux_nat_stopped_by_watchpoint;
index 46637d09fc0c6b8d8e179aa59c7a44b1d9aaa277..059422d5786e204b4a2f96fe13a5384ffc180e16 100644 (file)
@@ -63,6 +63,63 @@ thpy_dealloc (PyObject *self)
   self->ob_type->tp_free (self);
 }
 
+static PyObject *
+thpy_get_name (PyObject *self, void *ignore)
+{
+  thread_object *thread_obj = (thread_object *) self;
+  char *name;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  name = thread_obj->thread->name;
+  if (name == NULL)
+    name = target_thread_name (thread_obj->thread);
+
+  if (name == NULL)
+    Py_RETURN_NONE;
+
+  return PyString_FromString (name);
+}
+
+static int
+thpy_set_name (PyObject *self, PyObject *newvalue, void *ignore)
+{
+  thread_object *thread_obj = (thread_object *) self;
+  char *name;
+
+  if (! thread_obj->thread)
+    {
+      PyErr_SetString (PyExc_RuntimeError, _("Thread no longer exists."));
+      return -1;
+    }
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+                      _("Cannot delete `name' attribute."));
+      return -1;
+    }
+  else if (newvalue == Py_None)
+    name = NULL;
+  else if (! gdbpy_is_string (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+                      _("The value of `name' must be a string."));
+      return -1;
+    }
+  else
+    {
+      name = python_string_to_host_string (newvalue);
+      if (! name)
+       return -1;
+    }
+
+  xfree (thread_obj->thread->name);
+  thread_obj->thread->name = name;
+
+  return 0;
+}
+
 static PyObject *
 thpy_get_num (PyObject *self, void *closure)
 {
@@ -201,6 +258,8 @@ gdbpy_initialize_thread (void)
 
 static PyGetSetDef thread_object_getset[] =
 {
+  { "name", thpy_get_name, thpy_set_name,
+    "The name of the thread, as set by the user or the OS.", NULL },
   { "num", thpy_get_num, NULL, "ID of the thread, as assigned by GDB.", NULL },
   { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
     NULL },
index 32da4dd0e6cde079c45add2659011072a15f7a65..710af02c7dc30c00f00ffa89438929f1d57997df 100644 (file)
@@ -634,6 +634,7 @@ update_current_target (void)
       /* Do not inherit to_find_new_threads.  */
       /* Do not inherit to_pid_to_str.  */
       INHERIT (to_extra_thread_info, t);
+      INHERIT (to_thread_name, t);
       INHERIT (to_stop, t);
       /* Do not inherit to_xfer_partial.  */
       INHERIT (to_rcmd, t);
@@ -804,6 +805,9 @@ update_current_target (void)
   de_fault (to_extra_thread_info,
            (char *(*) (struct thread_info *))
            return_zero);
+  de_fault (to_thread_name,
+           (char *(*) (struct thread_info *))
+           return_zero);
   de_fault (to_stop,
            (void (*) (ptid_t))
            target_ignore);
@@ -2407,6 +2411,20 @@ target_pid_to_str (ptid_t ptid)
   return normal_pid_to_str (ptid);
 }
 
+char *
+target_thread_name (struct thread_info *info)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    {
+      if (t->to_thread_name != NULL)
+       return (*t->to_thread_name) (info);
+    }
+
+  return NULL;
+}
+
 void
 target_resume (ptid_t ptid, int step, enum target_signal signal)
 {
index c9f9ef076be2dc0eb8399ab542ae10e32abde8c8..4625d520e52b00fb8580c15d103146e87d23966b 100644 (file)
@@ -497,6 +497,7 @@ struct target_ops
     void (*to_find_new_threads) (struct target_ops *);
     char *(*to_pid_to_str) (struct target_ops *, ptid_t);
     char *(*to_extra_thread_info) (struct thread_info *);
+    char *(*to_thread_name) (struct thread_info *);
     void (*to_stop) (ptid_t);
     void (*to_rcmd) (char *command, struct ui_file *output);
     char *(*to_pid_to_exec_file) (int pid);
@@ -1246,6 +1247,11 @@ extern char *normal_pid_to_str (ptid_t ptid);
 #define target_extra_thread_info(TP) \
      (current_target.to_extra_thread_info (TP))
 
+/* Return the thread's name.  A NULL result means that the target
+   could not determine this thread's name.  */
+
+extern char *target_thread_name (struct thread_info *);
+
 /* Attempts to find the pathname of the executable file
    that was run to create a specified process.
 
index f6f51212de6c62a3c92d58773bbf0401142cb738..77828ab5a6412d1a2df47bd4420367a87610a911 100644 (file)
@@ -1,3 +1,7 @@
+2011-01-19  Tom Tromey  <tromey@redhat.com>
+
+       * gdb.python/py-infthread.exp: Add thread tests.
+
 2011-01-14  Joel Brobecker  <brobecker@adacore.com>
 
        * gdb.base/wchar.c, gdb.base/wchar.exp: New testcases.
index 7b0c5896220b2f7cc3e70a26700b3059db38abfb..bbec4eca664dd87f02b7c2d9583d84f1b5be273b 100644 (file)
@@ -50,6 +50,17 @@ gdb_test "python print t0" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>
 gdb_test "python print 'result =', t0.num" " = \[0-9\]+" "test Inferior.num"
 gdb_test "python print 'result =', t0.ptid" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
 
+gdb_py_test_silent_cmd "python name = gdb.selected_thread().name" \
+    "get supplied name of current thread" 1
+gdb_py_test_silent_cmd "python gdb.selected_thread().name = 'hibob'" \
+    "set name of current thread" 1
+gdb_test "python print gdb.selected_thread().name" "hibob" \
+    "check name of current thread"
+gdb_py_test_silent_cmd "python gdb.selected_thread().name = None" \
+    "reset name of current thread" 1
+gdb_test "python print gdb.selected_thread().name == name" "True" \
+    "check name of current thread again"
+
 gdb_test "python print 'result =', t0.is_stopped ()" " = True" "test InferiorThread.is_stopped"
 gdb_test "python print 'result =', t0.is_running ()" " = False" "test InferiorThread.is_running"
 gdb_test "python print 'result =', t0.is_exited ()" " = False" "test InferiorThread.is_exited"
index 1bd3e8dcf9569e035aaca7696157418f4dcba9d1..12a25b95ba119fd62c0a602fa33051af075bf9b2 100644 (file)
@@ -99,6 +99,9 @@ gdb_test_multiple $cmd $cmd {
     }
 }
 
+gdb_test_no_output "thread name zardoz" "give a name to the thread"
+gdb_test "info threads" ".*zardoz.*" "check thread name"
+
 set message "second continue"
 gdb_test_multiple "continue" "second continue" {
   -re "error:.*$gdb_prompt $" {
index 5ced1fa6c9161b14ce1825f00e78364e9850b914..62455c2a495ac6cbd7e1e04967fd6217f3323189 100644 (file)
@@ -141,6 +141,7 @@ free_thread (struct thread_info *tp)
        xfree (tp->private);
     }
 
+  xfree (tp->name);
   xfree (tp);
 }
 
@@ -769,7 +770,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
   struct thread_info *tp;
   ptid_t current_ptid;
   struct cleanup *old_chain;
-  char *extra_info;
+  char *extra_info, *name, *target_id;
   int current_thread = -1;
 
   update_thread_list ();
@@ -811,12 +812,11 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
          return;
        }
 
-      make_cleanup_ui_out_table_begin_end (uiout, 5, n_threads, "threads");
+      make_cleanup_ui_out_table_begin_end (uiout, 4, n_threads, "threads");
 
       ui_out_table_header (uiout, 1, ui_left, "current", "");
       ui_out_table_header (uiout, 4, ui_left, "id", "Id");
       ui_out_table_header (uiout, 17, ui_left, "target-id", "Target Id");
-      ui_out_table_header (uiout, 1, ui_noalign, "details", "");
       ui_out_table_header (uiout, 1, ui_left, "frame", "Frame");
       ui_out_table_body (uiout);
     }
@@ -861,17 +861,45 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
        }
 
       ui_out_field_int (uiout, "id", tp->num);
-      ui_out_field_string (uiout, "target-id", target_pid_to_str (tp->ptid));
 
+      /* For the CLI, we stuff everything into the target-id field.
+        This is a gross hack to make the output come out looking
+        correct.  The underlying problem here is that ui-out has no
+        way to specify that a field's space allocation should be
+        shared by several fields.  For MI, we do the right thing
+        instead.  */
+
+      target_id = target_pid_to_str (tp->ptid);
       extra_info = target_extra_thread_info (tp);
-      if (extra_info)
+      name = tp->name ? tp->name : target_thread_name (tp);
+
+      if (ui_out_is_mi_like_p (uiout))
        {
-         ui_out_text (uiout, " (");
-         ui_out_field_string (uiout, "details", extra_info);
-         ui_out_text (uiout, ")");
+         ui_out_field_string (uiout, "target-id", target_id);
+         if (extra_info)
+           ui_out_field_string (uiout, "details", extra_info);
+         if (name)
+           ui_out_field_string (uiout, "name", name);
+       }
+      else
+       {
+         struct cleanup *str_cleanup;
+         char *contents;
+
+         if (extra_info && name)
+           contents = xstrprintf ("%s \"%s\" (%s)", target_id,
+                                  name, extra_info);
+         else if (extra_info)
+           contents = xstrprintf ("%s (%s)", target_id, extra_info);
+         else if (name)
+           contents = xstrprintf ("%s \"%s\"", target_id, name);
+         else
+           contents = xstrdup (target_id);
+         str_cleanup = make_cleanup (xfree, contents);
+
+         ui_out_field_string (uiout, "target-id", contents);
+         do_cleanups (str_cleanup);
        }
-      else if (! ui_out_is_mi_like_p (uiout))
-       ui_out_field_skip (uiout, "details");
 
       if (tp->state_ == THREAD_RUNNING)
        ui_out_text (uiout, "(running)\n");
@@ -1267,6 +1295,24 @@ thread_command (char *tidstr, int from_tty)
   gdb_thread_select (uiout, tidstr, NULL);
 }
 
+/* Implementation of `thread name'.  */
+
+static void
+thread_name_command (char *arg, int from_tty)
+{
+  struct thread_info *info;
+
+  if (ptid_equal (inferior_ptid, null_ptid))
+    error (_("No thread selected"));
+
+  while (arg && isspace (*arg))
+    ++arg;
+
+  info = inferior_thread ();
+  xfree (info->name);
+  info->name = arg ? xstrdup (arg) : NULL;
+}
+
 /* Print notices when new threads are attached and detached.  */
 int print_thread_events = 1;
 static void
@@ -1372,6 +1418,11 @@ The new thread ID must be currently known."),
   add_cmd ("all", class_run, thread_apply_all_command,
           _("Apply a command to all threads."), &thread_apply_list);
 
+  add_cmd ("name", class_run, thread_name_command,
+          _("Set the current thread's name.\n\
+Usage: thread name [NAME]\n\
+If NAME is not given, then any existing name is removed."), &thread_cmd_list);
+
   if (!xdb_commands)
     add_com_alias ("t", "thread", class_run, 1);