+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.
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.
** 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
+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
@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
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
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
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
@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
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
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. */
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;
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)
{
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 },
/* 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);
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);
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)
{
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);
#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.
+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.
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"
}
}
+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 $" {
xfree (tp->private);
}
+ xfree (tp->name);
xfree (tp);
}
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 ();
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);
}
}
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");
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
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);