+2016-01-13 Pedro Alves <palves@redhat.com>
+
+ * NEWS: Mention that thread IDs are now per inferior and global
+ thread IDs.
+ * Makefile.in (SFILES): Add tid-parse.c.
+ (COMMON_OBS): Add tid-parse.o.
+ (HFILES_NO_SRCDIR): Add tid-parse.h.
+ * ada-tasks.c: Adjust to use ptid_to_global_thread_id.
+ * breakpoint.c (insert_breakpoint_locations)
+ (remove_threaded_breakpoints, bpstat_check_breakpoint_conditions)
+ (print_one_breakpoint_location, set_longjmp_breakpoint)
+ (check_longjmp_breakpoint_for_call_dummy)
+ (set_momentary_breakpoint): Adjust to use global IDs.
+ (find_condition_and_thread, watch_command_1): Use parse_thread_id.
+ (until_break_command, longjmp_bkpt_dtor)
+ (breakpoint_re_set_thread, insert_single_step_breakpoint): Adjust
+ to use global IDs.
+ * dummy-frame.c (pop_dummy_frame_bpt): Adjust to use
+ ptid_to_global_thread_id.
+ * elfread.c (elf_gnu_ifunc_resolver_stop): Likewise.
+ * gdbthread.h (struct thread_info): Rename field 'num' to
+ 'global_num. Add new fields 'per_inf_num' and 'inf'.
+ (thread_id_to_pid): Rename thread_id_to_pid to
+ global_thread_id_to_ptid.
+ (pid_to_thread_id): Rename to ...
+ (ptid_to_global_thread_id): ... this.
+ (valid_thread_id): Rename to ...
+ (valid_global_thread_id): ... this.
+ (find_thread_id): Rename to ...
+ (find_thread_global_id): ... this.
+ (ALL_THREADS, ALL_THREADS_BY_INFERIOR): Declare.
+ (print_thread_info): Add comment.
+ * tid-parse.h: New file.
+ * tid-parse.c: New file.
+ * infcmd.c (step_command_fsm_prepare)
+ (step_command_fsm_should_stop): Adjust to use the global thread
+ ID.
+ (until_next_command, until_next_command)
+ (finish_command_fsm_should_stop): Adjust to use the global thread
+ ID.
+ (attach_post_wait): Adjust to check the inferior number too.
+ * inferior.h (struct inferior) <highest_thread_num>: New field.
+ * infrun.c (handle_signal_stop)
+ (insert_exception_resume_breakpoint)
+ (insert_exception_resume_from_probe): Adjust to use the global
+ thread ID.
+ * record-btrace.c (record_btrace_open): Use global thread IDs.
+ * remote.c (process_initial_stop_replies): Also consider the
+ inferior number.
+ * target.c (target_pre_inferior): Clear the inferior's highest
+ thread num.
+ * thread.c (clear_thread_inferior_resources): Adjust to use the
+ global thread ID.
+ (new_thread): New inferior parameter. Adjust to use it. Set both
+ the thread's global ID and the thread's per-inferior ID.
+ (add_thread_silent): Adjust.
+ (find_thread_global_id): New.
+ (find_thread_id): Make static. Adjust to rename.
+ (valid_thread_id): Rename to ...
+ (valid_global_thread_id): ... this.
+ (pid_to_thread_id): Rename to ...
+ (ptid_to_global_thread_id): ... this.
+ (thread_id_to_pid): Rename to ...
+ (global_thread_id_to_ptid): ... this. Adjust.
+ (first_thread_of_process): Adjust.
+ (do_captured_list_thread_ids): Adjust to use global thread IDs.
+ (should_print_thread): New function.
+ (print_thread_info): Rename to ...
+ (print_thread_info_1): ... this, and add new show_global_ids
+ parameter. Handle it. Iterate over inferiors.
+ (print_thread_info): Reimplement as wrapper around
+ print_thread_info_1.
+ (show_inferior_qualified_tids): New function.
+ (print_thread_id): Use it.
+ (tp_array_compar): Compare inferior numbers too.
+ (thread_apply_command): Use tid_range_parser.
+ (do_captured_thread_select): Use parse_thread_id.
+ (thread_id_make_value): Adjust.
+ (_initialize_thread): Adjust "info threads" help string.
+ * varobj.c (struct varobj_root): Update comment.
+ (varobj_create): Adjust to use global thread IDs.
+ (value_of_root_1): Adjust to use global_thread_id_to_ptid.
+ * windows-tdep.c (display_tib): No longer accept an argument.
+ * cli/cli-utils.c (get_number_trailer): Make extern.
+ * cli/cli-utils.h (get_number_trailer): Declare.
+ (get_number_const): Adjust documentation.
+ * mi/mi-cmd-var.c (mi_cmd_var_update_iter): Adjust to use global
+ thread IDs.
+ * mi/mi-interp.c (mi_new_thread, mi_thread_exit)
+ (mi_on_normal_stop, mi_output_running_pid, mi_on_resume):
+ * mi/mi-main.c (mi_execute_command, mi_cmd_execute): Likewise.
+ * guile/scm-breakpoint.c (gdbscm_set_breakpoint_thread_x):
+ Likewise.
+ * python/py-breakpoint.c (bppy_set_thread): Likewise.
+ * python/py-finishbreakpoint.c (bpfinishpy_init): Likewise.
+ * python/py-infthread.c (thpy_get_num): Add comment and return the
+ per-inferior thread ID.
+ (thread_object_getset): Update comment of "num".
+
2016-01-13 Pedro Alves <palves@redhat.com>
* breakpoint.c (remove_threaded_breakpoints)
stabsread.c stack.c probe.c stap-probe.c std-regs.c \
symfile.c symfile-debug.c symfile-mem.c symmisc.c symtab.c \
target.c target-dcache.c target-descriptions.c target-memory.c \
- thread.c top.c tracepoint.c \
+ tid-parse.c thread.c top.c tracepoint.c \
trad-frame.c \
tramp-frame.c \
typeprint.c \
common/common-exceptions.h target/target.h common/symbol.h \
common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
-nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h
+nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \
+tid-parse.h
# Header files that already have srcdir in them, or which are in objdir.
linespec.o dictionary.o namespace.o \
location.o infcall.o \
infcmd.o infrun.o \
- expprint.o environ.o stack.o thread.o thread-fsm.o \
+ expprint.o environ.o stack.o tid-parse.o thread.o thread-fsm.o \
exceptions.o \
extension.o \
filesystem.o \
*** Changes since GDB 7.10
+* Per-inferior thread numbers
+
+ Thread numbers are now per inferior instead of global. If you're
+ debugging multiple inferiors, GDB displays thread IDs using a
+ qualified INF_NUM.THR_NUM form. For example:
+
+ (gdb) info threads
+ Id Target Id Frame
+ 1.1 Thread 0x7ffff7fc2740 (LWP 8155) (running)
+ 1.2 Thread 0x7ffff7fc1700 (LWP 8168) (running)
+ * 2.1 Thread 0x7ffff7fc2740 (LWP 8157) (running)
+ 2.2 Thread 0x7ffff7fc1700 (LWP 8190) (running)
+
+ As consequence, thread numbers as visible in the $_thread
+ convenience variable and in Python's InferiorThread.num attribute
+ are no longer unique between inferiors.
+
+ GDB now maintains a second thread ID per thread, referred to as the
+ global thread ID, which is the new equivalent of thread numbers in
+ previous releases.
+
+ For backwards compatibility, MI's thread IDs always refer to global
+ IDs.
+
+* Commands that accept thread IDs now accept the qualified
+ INF_NUM.THR_NUM form as well. For example:
+
+ (gdb) thread 2.1
+ [Switching to thread 2.1 (Thread 0x7ffff7fc2740 (LWP 8157))] (running)
+ (gdb)
+
* The new convenience variable $_inferior holds the number of the
current inferior.
* Python Scripting
+ ** The "num" attribute of gdb.InferiorThread objects now refers to
+ the thread's per-inferior number. See "Per-inferior thread
+ numbers" above.
** gdb.InferiorThread objects have a new attribute "inferior", which
is the Inferior object the thread belongs to.
/* Print the associated Thread ID. */
if (ui_out_is_mi_like_p (uiout))
{
- const int thread_id = pid_to_thread_id (task_info->ptid);
+ const int thread_id = ptid_to_global_thread_id (task_info->ptid);
if (thread_id != 0)
ui_out_field_int (uiout, "thread-id", thread_id);
#include "format.h"
#include "location.h"
#include "thread-fsm.h"
+#include "tid-parse.h"
/* readline include files */
#include "readline/readline.h"
the thread no longer exists. ALL_BP_LOCATIONS bp_location
has BL->OWNER always non-NULL. */
if (bl->owner->thread != -1
- && !valid_thread_id (bl->owner->thread))
+ && !valid_global_thread_id (bl->owner->thread))
continue;
switch_to_program_space_and_thread (bl->pspace);
ALL_BREAKPOINTS_SAFE (b, b_tmp)
{
- if (b->thread == tp->num && user_breakpoint_p (b))
+ if (b->thread == tp->global_num && user_breakpoint_p (b))
{
b->disposition = disp_del_at_next_stop;
/* If this is a thread/task-specific breakpoint, don't waste cpu
evaluating the condition if this isn't the specified
thread/task. */
- if ((b->thread != -1 && b->thread != pid_to_thread_id (ptid))
+ if ((b->thread != -1 && b->thread != ptid_to_global_thread_id (ptid))
|| (b->task != 0 && b->task != ada_get_task_number (ptid)))
{
if (!part_of_multiple && b->thread != -1)
{
- struct thread_info *thr = find_thread_id (b->thread);
-
/* FIXME should make an annotation for this. */
ui_out_text (uiout, "\tstop only in thread ");
- ui_out_field_string (uiout, "thread", print_thread_id (thr));
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_int (uiout, "thread", b->thread);
+ else
+ {
+ struct thread_info *thr = find_thread_global_id (b->thread);
+
+ ui_out_field_string (uiout, "thread", print_thread_id (thr));
+ }
ui_out_text (uiout, "\n");
}
set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
{
struct breakpoint *b, *b_tmp;
- int thread = tp->num;
+ int thread = tp->global_num;
/* To avoid having to rescan all objfile symbols at every step,
we maintain a list of continually-inserted but always disabled
new_b = momentary_breakpoint_from_master (b, bp_longjmp_call_dummy,
&momentary_breakpoint_ops,
1);
- new_b->thread = pid_to_thread_id (inferior_ptid);
+ new_b->thread = ptid_to_global_thread_id (inferior_ptid);
/* Link NEW_B into the chain of RETVAL breakpoints. */
struct breakpoint *b, *b_tmp;
ALL_BREAKPOINTS_SAFE (b, b_tmp)
- if (b->type == bp_longjmp_call_dummy && b->thread == tp->num)
+ if (b->type == bp_longjmp_call_dummy && b->thread == tp->global_num)
{
struct breakpoint *dummy_b = b->related_breakpoint;
momentary breakpoints to be active in only a single thread of
control. */
if (in_thread_list (inferior_ptid))
- b->thread = pid_to_thread_id (inferior_ptid);
+ b->thread = ptid_to_global_thread_id (inferior_ptid);
update_global_location_list_nothrow (UGLL_MAY_INSERT);
}
}
-/* Issue an invalid thread ID error. */
-
-static void ATTRIBUTE_NORETURN
-invalid_thread_id_error (int id)
-{
- error (_("Unknown thread %d."), id);
-}
-
/* Given TOK, a string specification of condition and thread, as
accepted by the 'break' command, extract the condition
string and thread number and set *COND_STRING and *THREAD.
}
else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
{
- char *tmptok;
+ const char *tmptok;
+ struct thread_info *thr;
tok = end_tok + 1;
- *thread = strtol (tok, &tmptok, 0);
+ thr = parse_thread_id (tok, &tmptok);
if (tok == tmptok)
error (_("Junk after thread keyword."));
- if (!valid_thread_id (*thread))
- invalid_thread_id_error (*thread);
+ *thread = thr->global_num;
tok = tmptok;
}
else if (toklen >= 1 && strncmp (tok, "task", toklen) == 0)
if (toklen == 6 && startswith (tok, "thread"))
{
+ struct thread_info *thr;
/* At this point we've found a "thread" token, which means
the user is trying to set a watchpoint that triggers
only in a specific thread. */
- char *endp;
+ const char *endp;
if (thread != -1)
error(_("You can specify only one thread."));
/* Extract the thread ID from the next token. */
- thread = strtol (value_start, &endp, 0);
+ thr = parse_thread_id (value_start, &endp);
- /* Check if the user provided a valid numeric value for the
- thread ID. */
+ /* Check if the user provided a valid thread ID. */
if (*endp != ' ' && *endp != '\t' && *endp != '\0')
- error (_("Invalid thread ID specification %s."), value_start);
+ invalid_thread_id_error (value_start);
- /* Check if the thread actually exists. */
- if (!valid_thread_id (thread))
- invalid_thread_id_error (thread);
+ thread = thr->global_num;
}
else if (toklen == 4 && startswith (tok, "mask"))
{
resolve_sal_pc (&sal);
tp = inferior_thread ();
- thread = tp->num;
+ thread = tp->global_num;
old_chain = make_cleanup (null_cleanup, NULL);
stack_frame_id, bp_until);
make_cleanup_delete_breakpoint (location_breakpoint);
- sm = new_until_break_fsm (tp->num, location_breakpoint, caller_breakpoint);
+ sm = new_until_break_fsm (tp->global_num,
+ location_breakpoint, caller_breakpoint);
tp->thread_fsm = &sm->thread_fsm;
discard_cleanups (old_chain);
static void
longjmp_bkpt_dtor (struct breakpoint *self)
{
- struct thread_info *tp = find_thread_id (self->thread);
+ struct thread_info *tp = find_thread_global_id (self->thread);
if (tp)
tp->initiating_frame = null_frame_id;
if (b->thread != -1)
{
if (in_thread_list (inferior_ptid))
- b->thread = pid_to_thread_id (inferior_ptid);
+ b->thread = ptid_to_global_thread_id (inferior_ptid);
/* We're being called after following a fork. The new fork is
selected as current, and unless this was a vfork will have a
if (tp->control.single_step_breakpoints == NULL)
{
tp->control.single_step_breakpoints
- = new_single_step_breakpoint (tp->num, gdbarch);
+ = new_single_step_breakpoint (tp->global_num, gdbarch);
}
sal = find_pc_line (pc, 0);
#include <ctype.h>
-/* *PP is a string denoting a number. Get the number of the. Advance
- *PP after the string and any trailing whitespace.
-
- Currently the string can either be a number, or "$" followed by the
- name of a convenience variable, or ("$" or "$$") followed by digits.
-
- TRAILER is a character which can be found after the number; most
- commonly this is `-'. If you don't want a trailer, use \0. */
+/* See documentation in cli-utils.h. */
-static int
+int
get_number_trailer (const char **pp, int trailer)
{
int retval = 0; /* default */
#ifndef CLI_UTILS_H
#define CLI_UTILS_H
-/* *PP is a string denoting a number. Get the number of the. Advance
- *PP after the string and any trailing whitespace.
+/* *PP is a string denoting a number. Get the number. Advance *PP
+ after the string and any trailing whitespace.
- Currently the string can either be a number, or "$" followed by the
- name of a convenience variable, or ("$" or "$$") followed by digits. */
+ The string can either be a number, or "$" followed by the name of a
+ convenience variable, or ("$" or "$$") followed by digits.
+
+ TRAILER is a character which can be found after the number; most
+ commonly this is `-'. If you don't want a trailer, use \0. */
+
+extern int get_number_trailer (const char **pp, int trailer);
+
+/* Convenience. Like get_number_trailer, but with no TRAILER. */
extern int get_number_const (const char **);
+2016-01-07 Pedro Alves <palves@redhat.com>
+
+ * gdb.texinfo (Threads): Document per-inferior thread IDs,
+ qualified thread IDs, global thread IDs and thread ID lists.
+ (Set Watchpoints, Thread-Specific Breakpoints): Adjust to refer to
+ thread IDs.
+ (Convenience Vars): Document the $_thread convenience variable.
+ (Ada Tasks): Adjust to refer to thread IDs.
+ (GDB/MI Async Records, GDB/MI Thread Commands, GDB/MI Ada Tasking
+ Commands, GDB/MI Variable Objects): Update to mention global
+ thread IDs.
+ * guile.texi (Breakpoints In Guile)
+ <breakpoint-thread/set-breakpoint-thread breakpoint>: Mention
+ global thread IDs instead of thread IDs.
+ * python.texi (Threads In Python): Adjust documentation of
+ InferiorThread.num.
+ (Breakpoint.thread): Mention global thread IDs instead of thread
+ IDs.
+
2016-01-13 Pedro Alves <palves@redhat.com>
* python.texi (Threads In Python): Document
@itemize @bullet
@item automatic notification of new threads
-@item @samp{thread @var{threadno}}, a command to switch among threads
+@item @samp{thread @var{thread-id}}, a command to switch among threads
@item @samp{info threads}, a command to inquire about existing threads
-@item @samp{thread apply [@var{threadno}] [@var{all}] @var{args}},
+@item @samp{thread apply [@var{thread-id-list}] [@var{all}] @var{args}},
a command to apply a command to a list of threads
@item thread-specific breakpoints
@item @samp{set print thread-events}, which controls printing of
@c multithread systems permit starting a program with multiple
@c threads ab initio?
-@cindex thread number
+@anchor{thread numbers}
+@cindex thread number, per inferior
@cindex thread identifier (GDB)
-For debugging purposes, @value{GDBN} associates its own thread
-number---always a single integer---with each thread in your program.
+For debugging purposes, @value{GDBN} associates its own thread number
+---always a single integer---with each thread of an inferior. This
+number is unique between all threads of an inferior, but not unique
+between threads of different inferiors.
+
+@cindex qualified thread ID
+You can refer to a given thread in an inferior using the qualified
+@var{inferior-num}.@var{thread-num} syntax, also known as
+@dfn{qualified thread ID}, with @var{inferior-num} being the inferior
+number and @var{thread-num} being the thread number of the given
+inferior. For example, thread @code{2.3} refers to thread number 3 of
+inferior 2. If you omit @var{inferior-num} (e.g., @code{thread 3}),
+then @value{GDBN} infers you're referring to a thread of the current
+inferior.
+
+Until you create a second inferior, @value{GDBN} does not show the
+@var{inferior-num} part of thread IDs, even though you can always use
+the full @var{inferior-num}.@var{thread-num} form to refer to threads
+of inferior 1, the initial inferior.
+
+@anchor{thread ID lists}
+@cindex thread ID lists
+Some commands accept a space-separated @dfn{thread ID list} as
+argument. A list element can be a thread ID as shown in the first
+field of the @samp{info threads} display, with or without an inferior
+qualifier (e.g., @samp{2.1} or @samp{1}); or can be a range of thread
+numbers, again with or without an inferior qualifier, as in
+@var{inf1}.@var{thr1}-@var{thr2} or @var{thr1}-@var{thr2} (e.g.,
+@samp{1.2-4} or @samp{2-4}). For example, if the current inferior is
+1, the thread list @samp{1 2-3 4.5 6.7-9} includes threads 1 to 3 of
+inferior 1, thread 5 of inferior 4 and threads 7 to 9 of inferior 6.
+That is, in expanded qualified form, the same as @samp{1.1 1.2 1.3 4.5
+6.7 6.8 6.9}.
+
+@anchor{global thread numbers}
+@cindex global thread number
+@cindex global thread identifier (GDB)
+In addition to a @emph{per-inferior} number, each thread is also
+assigned a unique @emph{global} number, also known as @dfn{global
+thread ID}, a single integer. Unlike the thread number component of
+the thread ID, no two threads have the same global ID, even when
+you're debugging multiple inferiors.
From @value{GDBN}'s perspective, a process always has at least one
thread. In other words, @value{GDBN} assigns a thread number to the
program's ``main thread'' even if the program is not multi-threaded.
+@vindex $_thread@r{, convenience variable}
+The debugger convenience variable @samp{$_thread} contains the
+per-inferior thread number of the current thread. You may find this
+useful in writing breakpoint conditional expressions, command scripts,
+and so forth. @xref{Convenience Vars,, Convenience Variables}, for
+general information on convenience variables.
+
@table @code
@kindex info threads
-@item info threads @r{[}@var{id}@dots{}@r{]}
-Display a summary of all threads currently in your program. Optional
-argument @var{id}@dots{} is one or more thread ids separated by spaces, and
-means to print information only about the specified thread or threads.
+@item info threads @r{[}@var{thread-id-list}@r{]}
+
+Display information about one or more threads. With no arguments
+displays information about all threads. You can specify the list of
+threads that you want to display using the thread ID list syntax
+(@pxref{thread ID lists}).
+
@value{GDBN} displays for each thread (in this order):
@enumerate
@item
-the thread number assigned by @value{GDBN}
+the per-inferior thread number assigned by @value{GDBN}
@item
the target system's thread identifier (@var{systag})
at threadtest.c:68
@end smallexample
+If you're debugging multiple inferiors, @value{GDBN} displays thread
+IDs using the qualified @var{inferior-num}.@var{thread-num} format.
+Otherwise, only @var{thread-num} is shown:
+
+@smallexample
+(@value{GDBP}) info threads
+ Id Target Id Frame
+ 1.1 process 35 thread 13 main (argc=1, argv=0x7ffffff8)
+ 1.2 process 35 thread 23 0x34e5 in sigpause ()
+ 1.3 process 35 thread 27 0x34e5 in sigpause ()
+* 2.1 process 65 thread 1 main (argc=1, argv=0x7ffffff8)
+@end smallexample
+
On Solaris, you can display more information about user threads with a
Solaris-specific command:
@end table
@table @code
-@kindex thread @var{threadno}
-@item thread @var{threadno}
-Make thread number @var{threadno} the current thread. The command
-argument @var{threadno} is the internal @value{GDBN} thread number, as
-shown in the first field of the @samp{info threads} display.
-@value{GDBN} responds by displaying the system identifier of the thread
-you selected, and its current stack frame summary:
+@kindex thread @var{thread-id}
+@item thread @var{thread-id}
+Make thread ID @var{thread-id} the current thread. The command
+argument @var{thread-id} is the @value{GDBN} thread ID, as shown in
+the first field of the @samp{info threads} display, with or without an
+inferior qualifier (e.g., @samp{2.1} or @samp{1}).
+
+@value{GDBN} responds by displaying the system identifier of the
+thread you selected, and its current stack frame summary:
@smallexample
(@value{GDBP}) thread 2
@samp{Switching to} depends on your system's conventions for identifying
threads.
-@vindex $_thread@r{, convenience variable}
-The debugger convenience variable @samp{$_thread} contains the number
-of the current thread. You may find this useful in writing breakpoint
-conditional expressions, command scripts, and so forth. See
-@xref{Convenience Vars,, Convenience Variables}, for general
-information on convenience variables.
-
@kindex thread apply
@cindex apply command to several threads
-@item thread apply [@var{threadno} | all [-ascending]] @var{command}
+@item thread apply [@var{thread-id-list} | all [-ascending]] @var{command}
The @code{thread apply} command allows you to apply the named
-@var{command} to one or more threads. Specify the numbers of the
-threads that you want affected with the command argument
-@var{threadno}. It can be a single thread number, one of the numbers
-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 in descending order, type @kbd{thread apply all
+@var{command} to one or more threads. Specify the threads that you
+want affected using the thread ID list syntax (@pxref{thread ID
+lists}), or specify @code{all} to apply to all threads. To apply a
+command to all threads in descending order, type @kbd{thread apply all
@var{command}}. To apply a command to all threads in ascending order,
type @kbd{thread apply all -ascending @var{command}}.
@table @code
@kindex watch
-@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint for an expression. @value{GDBN} will break when the
expression @var{expr} is written into by the program and its value
changes. The simplest (and the most popular) use of this command is
(@value{GDBP}) watch foo
@end smallexample
-If the command includes a @code{@r{[}thread @var{threadnum}@r{]}}
+If the command includes a @code{@r{[}thread @var{thread-id}@r{]}}
argument, @value{GDBN} breaks only when the thread identified by
-@var{threadnum} changes the value of @var{expr}. If any other threads
+@var{thread-id} changes the value of @var{expr}. If any other threads
change the value of @var{expr}, @value{GDBN} will not break. Note
that watchpoints restricted to a single thread in this way only work
with Hardware Watchpoints.
@end smallexample
@kindex rwatch
-@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint that will break when the value of @var{expr} is read
by the program.
@kindex awatch
-@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint that will break when @var{expr} is either read from
or written into by the program.
@table @code
@cindex breakpoints and threads
@cindex thread breakpoints
-@kindex break @dots{} thread @var{threadno}
-@item break @var{location} thread @var{threadno}
-@itemx break @var{location} thread @var{threadno} if @dots{}
+@kindex break @dots{} thread @var{thread-id}
+@item break @var{location} thread @var{thread-id}
+@itemx break @var{location} thread @var{thread-id} if @dots{}
@var{location} specifies source lines; there are several ways of
writing them (@pxref{Specify Location}), but the effect is always to
specify some source line.
-Use the qualifier @samp{thread @var{threadno}} with a breakpoint command
+Use the qualifier @samp{thread @var{thread-id}} with a breakpoint command
to specify that you only want @value{GDBN} to stop the program when a
-particular thread reaches this breakpoint. The @var{threadno} specifier
-is one of the numeric thread identifiers assigned by @value{GDBN}, shown
+particular thread reaches this breakpoint. The @var{thread-id} specifier
+is one of the thread identifiers assigned by @value{GDBN}, shown
in the first column of the @samp{info threads} display.
-If you do not specify @samp{thread @var{threadno}} when you set a
+If you do not specify @samp{thread @var{thread-id}} when you set a
breakpoint, the breakpoint applies to @emph{all} threads of your
program.
You can use the @code{thread} qualifier on conditional breakpoints as
-well; in this case, place @samp{thread @var{threadno}} before or
+well; in this case, place @samp{thread @var{thread-id}} before or
after the breakpoint condition, like this:
@smallexample
The number of the current inferior. @xref{Inferiors and
Programs, ,Debugging Multiple Inferiors and Programs}.
+@item $_thread
+The thread number of the current thread. @xref{thread numbers}.
+
@end table
@node Convenience Funs
@item task @var{taskno}
@cindex Ada task switching
-This command is like the @code{thread @var{threadno}}
+This command is like the @code{thread @var{thread-id}}
command (@pxref{Threads}). It switches the context of debugging
from the current task to the given task.
frontend may be operating on a wrong one. Therefore, each MI command
should explicitly specify which thread and frame to operate on. To
make it possible, each MI command accepts the @samp{--thread} and
-@samp{--frame} options, the value to each is @value{GDBN} identifier
-for thread and frame to operate on.
+@samp{--frame} options, the value to each is @value{GDBN} global
+identifier for thread and frame to operate on.
Usually, each top-level window in a frontend allows the user to select
a thread and a frame, and remembers the user selection for further
@table @code
@item *running,thread-id="@var{thread}"
-The target is now running. The @var{thread} field tells which
-specific thread is now running, and can be @samp{all} if all threads
-are running. The frontend should assume that no interaction with a
-running thread is possible after this notification is produced.
-The frontend should not assume that this notification is output
-only once for any command. @value{GDBN} may emit this notification
-several times, either for different threads, because it cannot resume
-all threads together, or even for a single thread, if the thread must
-be stepped though some code before letting it run freely.
+The target is now running. The @var{thread} field can be the global
+thread ID of the the thread that is now running, and it can be
+@samp{all} if all threads are running. The frontend should assume
+that no interaction with a running thread is possible after this
+notification is produced. The frontend should not assume that this
+notification is output only once for any command. @value{GDBN} may
+emit this notification several times, either for different threads,
+because it cannot resume all threads together, or even for a single
+thread, if the thread must be stepped though some code before letting
+it run freely.
@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
(@pxref{Set Catchpoints}) has been used.
@end table
-The @var{id} field identifies the thread that directly caused the stop
--- for example by hitting a breakpoint. Depending on whether all-stop
+The @var{id} field identifies the global thread ID of the thread
+that directly caused the stop -- for example by hitting a breakpoint.
+Depending on whether all-stop
mode is in effect (@pxref{All-Stop Mode}), @value{GDBN} may either
stop all threads, or only the thread that directly triggered the stop.
If all threads are stopped, the @var{stopped} field will have the
@item =thread-created,id="@var{id}",group-id="@var{gid}"
@itemx =thread-exited,id="@var{id}",group-id="@var{gid}"
A thread either was created, or has exited. The @var{id} field
-contains the @value{GDBN} identifier of the thread. The @var{gid}
+contains the global @value{GDBN} identifier of the thread. The @var{gid}
field identifies the thread group this thread belongs to.
@item =thread-selected,id="@var{id}"
@table @code
@item id
-The numeric id assigned to the thread by @value{GDBN}. This field is
+The global numeric id assigned to the thread by @value{GDBN}. This field is
always present.
@item target-id
@item -i @var{ignore-count}
Initialize the @var{ignore-count}.
@item -p @var{thread-id}
-Restrict the breakpoint to the specified @var{thread-id}.
+Restrict the breakpoint to the thread with the specified global
+@var{thread-id}.
@end table
@subsubheading Result
Set the ignore count of the breakpoint (@pxref{Conditions, ignore count})
to @var{ignore-count}.
@item -p @var{thread-id}
-Restrict the breakpoint to the specified @var{thread-id}.
+Restrict the breakpoint to the thread with the specified global
+@var{thread-id}.
@end table
@subsubheading Result
-thread-info [ @var{thread-id} ]
@end smallexample
-Reports information about either a specific thread, if
-the @var{thread-id} parameter is present, or about all
-threads. When printing information about all threads,
-also reports the current thread.
+Reports information about either a specific thread, if the
+@var{thread-id} parameter is present, or about all threads.
+@var{thread-id} is the thread's global thread ID. When printing
+information about all threads, also reports the global ID of the
+current thread.
@subsubheading @value{GDBN} Command
This field exists only for the current thread. It has the value @samp{*}.
@item id
-The identifier that @value{GDBN} uses to refer to the thread.
+The global identifier that @value{GDBN} uses to refer to the thread.
@item target-id
The identifier that the target uses to refer to the thread.
-thread-list-ids
@end smallexample
-Produces a list of the currently known @value{GDBN} thread ids. At the
-end of the list it also prints the total number of such threads.
+Produces a list of the currently known global @value{GDBN} thread ids.
+At the end of the list it also prints the total number of such
+threads.
This command is retained for historical reasons, the
@code{-thread-info} command should be used instead.
@subsubheading Synopsis
@smallexample
- -thread-select @var{threadnum}
+ -thread-select @var{thread-id}
@end smallexample
-Make @var{threadnum} the current thread. It prints the number of the new
-current thread, and the topmost frame for that thread.
+Make thread with global thread number @var{thread-id} the current
+thread. It prints the number of the new current thread, and the
+topmost frame for that thread.
This command is deprecated in favor of explicitly using the
@samp{--thread} option to each command.
The identifier that the target uses to refer to the Ada task.
@item thread-id
-The identifier of the thread corresponding to the Ada task.
+The global thread identifier of the thread corresponding to the Ada
+task.
This field should always exist, as Ada tasks are always implemented
on top of a thread. But if @value{GDBN} cannot find this corresponding
@item thread-id
If a variable object is bound to a specific thread, then this is the
-thread's identifier.
+thread's global identifier.
@item has_more
For a dynamic varobj, this indicates whether there appear to be any
If values were requested, this is the value.
@item thread-id
-If this variable object is associated with a thread, this is the thread id.
-Otherwise this result is not present.
+If this variable object is associated with a thread, this is the
+thread's global thread id. Otherwise this result is not present.
@item frozen
If the variable object is frozen, this variable will be present with a value of 1.
@end deffn
@deffn {Scheme Procedure} breakpoint-thread breakpoint
-Return the thread-id for thread-specific breakpoint @var{breakpoint}.
-Return #f if @var{breakpoint} is not thread-specific.
+Return the global-thread-id for thread-specific breakpoint
+@var{breakpoint}. Return #f if @var{breakpoint} is not
+thread-specific.
@end deffn
-@deffn {Scheme Procedure} set-breakpoint-thread! breakpoint thread-id|#f
-Set the thread-id for @var{breakpoint} to @var{thread-id}.
-If set to @code{#f}, the breakpoint is no longer thread-specific.
+@deffn {Scheme Procedure} set-breakpoint-thread! breakpoint global-thread-id|#f
+Set the thread-id for @var{breakpoint} to @var{global-thread-id} If
+set to @code{#f}, the breakpoint is no longer thread-specific.
@end deffn
@deffn {Scheme Procedure} breakpoint-task breakpoint
@end defvar
@defvar InferiorThread.num
-ID of the thread, as assigned by GDB.
+The per-inferior number of the thread, as assigned by GDB.
@end defvar
@defvar InferiorThread.ptid
@end defvar
@defvar Breakpoint.thread
-If the breakpoint is thread-specific, this attribute holds the thread
-id. If the breakpoint is not thread-specific, this attribute is
-@code{None}. This attribute is writable.
+If the breakpoint is thread-specific, this attribute holds the
+thread's global id. If the breakpoint is not thread-specific, this
+attribute is @code{None}. This attribute is writable.
@end defvar
@defvar Breakpoint.task
{
struct dummy_frame *dummy = (struct dummy_frame *) dummy_voidp;
- if (b->thread == pid_to_thread_id (dummy->id.ptid)
+ if (b->thread == ptid_to_global_thread_id (dummy->id.ptid)
&& b->disposition == disp_del && frame_id_eq (b->frame_id, dummy->id.id))
{
while (b->related_breakpoint != b)
struct frame_info *prev_frame = get_prev_frame (get_current_frame ());
struct frame_id prev_frame_id = get_stack_frame_id (prev_frame);
CORE_ADDR prev_pc = get_frame_pc (prev_frame);
- int thread_id = pid_to_thread_id (inferior_ptid);
+ int thread_id = ptid_to_global_thread_id (inferior_ptid);
gdb_assert (b->type == bp_gnu_ifunc_resolver);
#include "btrace.h"
#include "common/vec.h"
#include "target/waitstatus.h"
+#include "cli/cli-utils.h"
/* Frontend view of the thread state. Possible extensions: stepping,
finishing, until(ling),... */
ptid_t ptid; /* "Actual process id";
In fact, this may be overloaded with
kernel thread id, etc. */
- int num; /* Convenient handle (GDB thread id) */
+
+ /* Each thread has two GDB IDs.
+
+ a) The thread ID (Id). This consists of the pair of:
+
+ - the number of the thread's inferior and,
+
+ - the thread's thread number in its inferior, aka, the
+ per-inferior thread number. This number is unique in the
+ inferior but not unique between inferiors.
+
+ b) The global ID (GId). This is a a single integer unique
+ between all inferiors.
+
+ E.g.:
+
+ (gdb) info threads -gid
+ Id GId Target Id Frame
+ * 1.1 1 Thread A 0x16a09237 in foo () at foo.c:10
+ 1.2 3 Thread B 0x15ebc6ed in bar () at foo.c:20
+ 1.3 5 Thread C 0x15ebc6ed in bar () at foo.c:20
+ 2.1 2 Thread A 0x16a09237 in foo () at foo.c:10
+ 2.2 4 Thread B 0x15ebc6ed in bar () at foo.c:20
+ 2.3 6 Thread C 0x15ebc6ed in bar () at foo.c:20
+
+ Above, both inferiors 1 and 2 have threads numbered 1-3, but each
+ thread has its own unique global ID. */
+
+ /* The thread's global GDB thread number. This is exposed to MI and
+ Python/Scheme. */
+ int global_num;
+
+ /* The per-inferior thread number. This is unique in the inferior
+ the thread belongs to, but not unique between inferiors. This is
+ what the $_thread convenience variable is bound to. */
+ int per_inf_num;
+
+ /* The inferior this thread belongs to. */
+ struct inferior *inf;
/* The name of the thread, as specified by the user. This is NULL
if the thread does not have a user-given name. */
struct address_space *aspace,
CORE_ADDR addr);
-/* Translate the integer thread id (GDB's homegrown id, not the system's)
- into a "pid" (which may be overloaded with extra thread information). */
-extern ptid_t thread_id_to_pid (int);
+/* Translate the global integer thread id (GDB's homegrown id, not the
+ system's) into a "pid" (which may be overloaded with extra thread
+ information). */
+extern ptid_t global_thread_id_to_ptid (int num);
+
+/* Translate a 'pid' (which may be overloaded with extra thread
+ information) into the global integer thread id (GDB's homegrown id,
+ not the system's). */
+extern int ptid_to_global_thread_id (ptid_t ptid);
-/* Translate a 'pid' (which may be overloaded with extra thread information)
- into the integer thread id (GDB's homegrown id, not the system's). */
-extern int pid_to_thread_id (ptid_t ptid);
+/* Returns whether to show inferior-qualified thread IDs, or plain
+ thread numbers. Inferior-qualified IDs are shown whenever we have
+ multiple inferiors, or the only inferior left has number > 1. */
+extern int show_inferior_qualified_tids (void);
-/* Return a string version of THR's thread ID. The result is stored
- in a circular static buffer, NUMCELLS deep. */
+/* Return a string version of THR's thread ID. If there are multiple
+ inferiors, then this prints the inferior-qualifier form, otherwise
+ it only prints the thread number. The result is stored in a
+ circular static buffer, NUMCELLS deep. */
const char *print_thread_id (struct thread_info *thr);
/* Boolean test for an already-known pid (which may be overloaded with
extra thread information). */
extern int in_thread_list (ptid_t ptid);
-/* Boolean test for an already-known thread id (GDB's homegrown id,
- not the system's). */
-extern int valid_thread_id (int thread);
+/* Boolean test for an already-known global thread id (GDB's homegrown
+ global id, not the system's). */
+extern int valid_global_thread_id (int global_id);
/* Search function to lookup a thread by 'pid'. */
extern struct thread_info *find_thread_ptid (ptid_t ptid);
-/* Find thread by GDB user-visible thread number. */
-struct thread_info *find_thread_id (int num);
+/* Find thread by GDB global thread ID. */
+struct thread_info *find_thread_global_id (int global_id);
/* Finds the first thread of the inferior given by PID. If PID is -1,
returns the first thread in the list. */
typedef int (*thread_callback_func) (struct thread_info *, void *);
extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
+/* Traverse all threads. */
+#define ALL_THREADS(T) \
+ for (T = thread_list; T; T = T->next) \
+
+/* Traverse over all threads, sorted by inferior. */
+#define ALL_THREADS_BY_INFERIOR(inf, tp) \
+ ALL_INFERIORS (inf) \
+ ALL_THREADS (tp) \
+ if (inf == tp->inf)
+
/* Traverse all threads, except those that have THREAD_EXITED
state. */
`set print thread-events'. */
extern int print_thread_events;
-extern void print_thread_info (struct ui_out *uiout, char *threads,
+/* Prints the list of threads and their details on UIOUT. If
+ REQUESTED_THREADS, a list of GDB ids/ranges, is not NULL, only
+ print threads whose ID is included in the list. If PID is not -1,
+ only print threads from the process PID. Otherwise, threads from
+ all attached PIDs are printed. If both REQUESTED_THREADS is not
+ NULL and PID is not -1, then the thread is printed if it belongs to
+ the specified process. Otherwise, an error is raised. */
+extern void print_thread_info (struct ui_out *uiout, char *requested_threads,
int pid);
extern struct cleanup *make_cleanup_restore_current_thread (void);
if (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX))
{
id = scm_to_long (newvalue);
- if (! valid_thread_id (id))
+ if (!valid_global_thread_id (id))
{
gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
_("invalid thread id"));
{ "breakpoint-thread", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_thread),
"\
-Return the breakpoint's thread id or #f if there isn't one." },
+Return the breakpoint's global thread id or #f if there isn't one." },
{ "set-breakpoint-thread!", 2, 0, 0,
as_a_scm_t_subr (gdbscm_set_breakpoint_thread_x),
"\
-Set the thread id for this breakpoint.\n\
+Set the global thread id for this breakpoint.\n\
\n\
- Arguments: <gdb:breakpoint> thread-id" },
+ Arguments: <gdb:breakpoint> global-thread-id" },
{ "breakpoint-task", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_task),
"\
sm->skip_subroutines = skip_subroutines;
sm->single_inst = single_inst;
sm->count = count;
- sm->thread = thread->num;
+ sm->thread = thread->global_num;
/* Leave the si command alone. */
if (!sm->single_inst || sm->skip_subroutines)
step_command_fsm_should_stop (struct thread_fsm *self)
{
struct step_command_fsm *sm = (struct step_command_fsm *) self;
- struct thread_info *tp = find_thread_id (sm->thread);
+ struct thread_info *tp = find_thread_global_id (sm->thread);
if (tp->control.stop_step)
{
struct symbol *func;
struct symtab_and_line sal;
struct thread_info *tp = inferior_thread ();
- int thread = tp->num;
+ int thread = tp->global_num;
struct cleanup *old_chain;
struct until_next_fsm *sm;
set_longjmp_breakpoint (tp, get_frame_id (frame));
old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
- sm = new_until_next_fsm (tp->num);
+ sm = new_until_next_fsm (tp->global_num);
tp->thread_fsm = &sm->thread_fsm;
discard_cleanups (old_chain);
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
-
}
static void
{
struct finish_command_fsm *f = (struct finish_command_fsm *) self;
struct return_value_info *rv = &f->return_value;
- struct thread_info *tp = find_thread_id (f->thread);
+ struct thread_info *tp = find_thread_global_id (f->thread);
if (f->function != NULL
&& bpstat_find_breakpoint (tp->control.stop_bpstat,
tp = inferior_thread ();
- sm = new_finish_command_fsm (tp->num);
+ sm = new_finish_command_fsm (tp->global_num);
tp->thread_fsm = &sm->thread_fsm;
{
if (ptid_get_pid (thread->ptid) == pid)
{
- if (thread->num < lowest->num)
+ if (thread->inf->num < lowest->inf->num
+ || thread->per_inf_num < lowest->per_inf_num)
lowest = thread;
}
}
/* True if the PID was actually faked by GDB. */
int fake_pid_p;
+ /* The highest thread number this inferior ever had. */
+ int highest_thread_num;
+
/* State of GDB control of inferior process execution.
See `struct inferior_control_state'. */
struct inferior_control_state control;
context_switch (ecs->ptid);
if (deprecated_context_hook)
- deprecated_context_hook (pid_to_thread_id (ecs->ptid));
+ deprecated_context_hook (ptid_to_global_thread_id (ecs->ptid));
}
/* At this point, get hold of the now-current thread's frame. */
/* set_momentary_breakpoint_at_pc invalidates FRAME. */
frame = NULL;
- bp->thread = tp->num;
+ bp->thread = tp->global_num;
inferior_thread ()->control.exception_resume_breakpoint = bp;
}
}
bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
handler, bp_exception_resume);
- bp->thread = tp->num;
+ bp->thread = tp->global_num;
inferior_thread ()->control.exception_resume_breakpoint = bp;
}
thread_stopped = 1;
else
{
- struct thread_info *tp = find_thread_id (thread_id);
+ struct thread_info *tp = find_thread_global_id (thread_id);
if (tp)
thread_stopped = is_stopped (tp->ptid);
fprintf_unfiltered (mi->event_channel,
"thread-created,id=\"%d\",group-id=\"i%d\"",
- t->num, inf->num);
+ t->global_num, inf->num);
gdb_flush (mi->event_channel);
}
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-exited,id=\"%d\",group-id=\"i%d\"",
- t->num, inf->num);
+ t->global_num, inf->num);
gdb_flush (mi->event_channel);
do_cleanups (old_chain);
print_stop_event (mi->cli_uiout);
}
- ui_out_field_int (mi_uiout, "thread-id",
- pid_to_thread_id (inferior_ptid));
+ tp = inferior_thread ();
+ ui_out_field_int (mi_uiout, "thread-id", tp->global_num);
if (non_stop)
{
struct cleanup *back_to = make_cleanup_ui_out_list_begin_end
(mi_uiout, "stopped-threads");
- ui_out_field_int (mi_uiout, NULL,
- pid_to_thread_id (inferior_ptid));
+ ui_out_field_int (mi_uiout, NULL, tp->global_num);
do_cleanups (back_to);
}
else
if (ptid_get_pid (*ptid) == ptid_get_pid (info->ptid))
fprintf_unfiltered (raw_stdout,
"*running,thread-id=\"%d\"\n",
- info->num);
+ info->global_num);
return 0;
}
struct thread_info *ti = find_thread_ptid (ptid);
gdb_assert (ti);
- fprintf_unfiltered (raw_stdout, "*running,thread-id=\"%d\"\n", ti->num);
+ fprintf_unfiltered (raw_stdout, "*running,thread-id=\"%d\"\n",
+ ti->global_num);
}
if (!running_result_record_printed && mi_proceeded)
{
struct thread_info *ti = inferior_thread ();
- report_change = (ti->num != command->thread);
+ report_change = (ti->global_num != command->thread);
}
if (report_change)
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-selected,id=\"%d\"",
- ti->num);
+ ti->global_num);
gdb_flush (mi->event_channel);
}
}
if (parse->thread != -1)
{
- struct thread_info *tp = find_thread_id (parse->thread);
+ struct thread_info *tp = find_thread_global_id (parse->thread);
if (!tp)
error (_("Invalid thread id: %d"), parse->thread);
if (! gdb_py_int_as_long (newvalue, &id))
return -1;
- if (! valid_thread_id (id))
+ if (!valid_global_thread_id (id))
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid thread ID."));
if (PyErr_Occurred ())
return -1;
- thread = pid_to_thread_id (inferior_ptid);
+ thread = ptid_to_global_thread_id (inferior_ptid);
if (thread == 0)
{
PyErr_SetString (PyExc_ValueError,
return 0;
}
+/* Getter for InferiorThread.num. */
+
static PyObject *
thpy_get_num (PyObject *self, void *closure)
{
THPY_REQUIRE_VALID (thread_obj);
- return PyLong_FromLong (thread_obj->thread->num);
+ return PyLong_FromLong (thread_obj->thread->per_inf_num);
}
/* Getter for InferiorThread.ptid -> (pid, lwp, tid).
{
{ "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 },
+ { "num", thpy_get_num, NULL,
+ "Per-inferior number of the thread, as assigned by GDB.", NULL },
{ "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
NULL },
{ "inferior", thpy_get_inferior, NULL,
disable_chain = make_cleanup (null_cleanup, NULL);
ALL_NON_EXITED_THREADS (tp)
- if (args == NULL || *args == 0 || number_is_in_list (args, tp->num))
+ if (args == NULL || *args == 0 || number_is_in_list (args, tp->global_num))
{
btrace_enable (tp, &record_btrace_conf);
&& thread->suspend.waitstatus_pending_p)
selected = thread;
- if (lowest_stopped == NULL || thread->num < lowest_stopped->num)
+ if (lowest_stopped == NULL
+ || thread->inf->num < lowest_stopped->inf->num
+ || thread->per_inf_num < lowest_stopped->per_inf_num)
lowest_stopped = thread;
if (non_stop)
the inferior was attached to. */
current_inferior ()->attach_flag = 0;
+ current_inferior ()->highest_thread_num = 0;
+
agent_capability_invalidate ();
}
+2016-01-07 Pedro Alves <palves@redhat.com>
+
+ * gdb.base/break.exp: Adjust to output changes.
+ * gdb.base/hbreak2.exp: Likewise.
+ * gdb.base/sepdebug.exp: Likewise.
+ * gdb.base/watch_thread_num.exp: Likewise.
+ * gdb.linespec/keywords.exp: Likewise.
+ * gdb.multi/info-threads.exp: Likewise.
+ * gdb.threads/thread-find.exp: Likewise.
+ * gdb.multi/tids.c: New file.
+ * gdb.multi/tids.exp: New file.
+
2016-01-13 Pedro Alves <palves@redhat.com>
* gdb.python/py-infthread.exp: Test InferiorThread.inferior.
"thread-specific breakpoint on non-existent thread disallowed"
gdb_test "break $bp_location12 thread foo" \
- "Junk after thread keyword.*" \
+ "Invalid thread ID: foo" \
"thread-specific breakpoint on bogus thread ID disallowed"
# Verify that GDB responds gracefully to a breakpoint command with
"thread-specific hardware breakpoint on non-existent thread disallowed"
gdb_test "hbreak $bp_location12 thread foo" \
- "Junk after thread keyword.*" \
+ "Invalid thread ID: foo" \
"thread-specific hardware breakpoint on bogus thread ID disallowed"
# Verify that GDB responds gracefully to a breakpoint command with
"thread-specific breakpoint on non-existent thread disallowed"
gdb_test "break $bp_location12 thread foo" \
- "Junk after thread keyword.*" \
+ "Invalid thread ID: foo" \
"thread-specific breakpoint on bogus thread ID disallowed"
# Verify that GDB responds gracefully to a breakpoint command with
return
}
-gdb_test "watch shared_var thread 0" "Unknown thread 0\." "Watchpoint on invalid thread"
+gdb_test "watch shared_var thread 0" "Invalid thread ID: 0" "Watchpoint on invalid thread"
gdb_test "watch shared_var thread" "A syntax error in expression, near `thread'\." "Invalid watch syntax"
set bpexitline [gdb_get_line_number "all threads started"]
# break {thread,task} NUMBER --> invalid thread/task
# break {thread,task} STUFF --> "junk" after keyword (STUFF is not numeric)
gdb_test "break thread 123" "Unknown thread 123\\."
-gdb_test "break thread foo" "Junk after thread keyword\\."
+gdb_test "break thread foo" "Invalid thread ID: foo"
gdb_test "break task 123" "Unknown task 123\\."
gdb_test "break task foo" "Junk after task keyword\\."
gdb_breakpoint "thread if 0" "message"
# These are also NULL locations, but using a subsequent keyword
# as the "junk".
-gdb_test "break thread thread" "Junk after thread keyword\\."
-gdb_test "break thread task" "Junk after thread keyword\\."
-gdb_test "break thread if" "Junk after thread keyword\\."
+gdb_test "break thread thread" "Invalid thread ID: thread"
+gdb_test "break thread task" "Invalid thread ID: task"
+gdb_test "break thread if" "Invalid thread ID: if"
gdb_test "break task task" "Junk after task keyword\\."
gdb_test "break task thread" "Junk after task keyword\\."
gdb_test "break task if" "Junk after task keyword\\."
# "info threads" while inferior 1 has execution and inferior 2 is not
# running yet should show inferior 1's thread, and give no error.
-gdb_test "info threads" "1 .* main .* at .*$srcfile:.*No selected thread.*"
+gdb_test "info threads" "1\.1 .* main .* at .*$srcfile:.*No selected thread.*"
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2015-2016 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/>. */
+
+#include <unistd.h>
+#include <pthread.h>
+
+pthread_t child_thread[2];
+
+void *
+thread_function2 (void *arg)
+{
+ while (1)
+ sleep (1);
+
+ return NULL;
+}
+
+void *
+thread_function1 (void *arg)
+{
+ pthread_create (&child_thread[1], NULL, thread_function2, NULL);
+
+ while (1)
+ sleep (1);
+
+ return NULL;
+}
+
+int
+main (void)
+{
+ int i;
+
+ alarm (300);
+
+ pthread_create (&child_thread[0], NULL, thread_function1, NULL);
+
+ for (i = 0; i < 2; i++)
+ pthread_join (child_thread[i], NULL);
+
+ return 0;
+}
--- /dev/null
+# Copyright 2015-2016 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 ID parsing and display.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Multiple inferiors are needed, therefore both native and extended
+# gdbserver modes are supported. Only non-extended gdbserver is not
+# supported.
+if [target_info exists use_gdb_stub] {
+ untested ${testfile}.exp
+ return
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {pthreads debug}] } {
+ return -1
+}
+
+clean_restart ${testfile}
+
+if { ![runto_main] } then {
+ return -1
+}
+
+# Issue "thread apply TID_LIST p 1234" and expect EXP_TID_LIST (a list
+# of thread ids) to be displayed.
+proc thread_apply {tid_list exp_tid_list {message ""}} {
+ global decimal
+ set any "\[^\r\n\]*"
+ set expected [string_to_regexp $exp_tid_list]
+
+ set r ""
+ foreach tid $expected {
+ append r "\[\r\n\]+"
+ append r "Thread $tid $any:\r\n"
+ append r "\\$$decimal = 1234"
+ }
+
+ set cmd "thread apply $tid_list"
+ if {$message == ""} {
+ set message $cmd
+ }
+ gdb_test "$cmd p 1234" $r $message
+}
+
+# Issue "info threads TID_LIST" and expect EXP_TID_LIST (a list of
+# thread ids) to be displayed.
+proc info_threads {tid_list exp_tid_list {message ""}} {
+ set any "\[^\r\n\]*"
+ set expected [string_to_regexp $exp_tid_list]
+ set r [join $expected " ${any}\r\n${any} "]
+ set r "${any} $r ${any}"
+ set cmd "info threads $tid_list"
+ if {$message == ""} {
+ set message $cmd
+ }
+ gdb_test $cmd $r $message
+}
+
+# Issue "info threads TID_LIST" and expect INFO_THR output. Then
+# issue "thread apply TID_LIST" and expect THR_APPLY output. If
+# THR_APPLY is omitted, INFO_THR is expected instead.
+proc thr_apply_info_thr {tid_list info_thr {thr_apply ""}} {
+ if {$thr_apply == ""} {
+ set thr_apply $info_thr
+ }
+
+ info_threads $tid_list $info_thr
+ thread_apply $tid_list $thr_apply
+}
+
+# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and
+# expect both commands to error out with EXP_ERROR.
+proc thr_apply_info_thr_error {tid_list exp_error} {
+ gdb_test "info threads $tid_list" \
+ $exp_error
+
+ gdb_test "thread apply $tid_list p 1234" \
+ $exp_error \
+ "thread apply $tid_list"
+}
+
+# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and
+# expect the command to error out with "Invalid thread ID: $EXPECTED".
+# EXPECTED is a literal string, not a regexp.
+proc thr_apply_info_thr_invalid {tid_list expected} {
+ set expected [string_to_regexp $expected]
+ gdb_test "info threads $tid_list" \
+ "Invalid thread ID: $expected"
+
+ gdb_test "thread apply $tid_list p 1234" \
+ "Invalid thread ID: $expected p 1234" \
+ "thread apply $tid_list"
+}
+
+# "info threads" while there's only inferior 1 should show
+# single-number thread IDs.
+with_test_prefix "single inferior" {
+ info_threads "" "1"
+
+ gdb_test "thread" "Current thread is 1 .*"
+}
+
+# "info threads" while there are multiple inferiors should show
+# qualified thread IDs.
+with_test_prefix "two inferiors" {
+ # Add another inferior.
+ gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2"
+
+ # Now that we've added another inferior, thread IDs now show the
+ # inferior number.
+ info_threads "" "1.1"
+
+ gdb_test "thread" "Current thread is 1\.1 .*"
+
+ gdb_test "inferior 2" "Switching to inferior 2 .*" "switch to inferior 2"
+ gdb_test "file ${binfile}" ".*" "load file in inferior 2"
+
+ runto_main
+
+ # Now that we've added another inferior, thread IDs now show the
+ # inferior number.
+ info_threads "" "1.1 2.1" \
+ "info threads show inferior numbers"
+
+ gdb_test "thread" "Current thread is 2\.1 .*" \
+ "switch to thread using extended thread ID"
+
+ gdb_breakpoint "thread_function1"
+
+ gdb_continue_to_breakpoint "once"
+ gdb_test "inferior 1" "Switching to inferior 1 .*"
+ gdb_continue_to_breakpoint "twice"
+
+ info_threads "" "1.1 1.2 2.1 2.2" \
+ "info threads again"
+
+ # Confirm the convenience variable show the expected number.
+ gdb_test "p \$_thread == 2" " = 1"
+
+ # Without an explicit inferior component, GDB defaults to the
+ # current inferior. Make sure we don't refer to a thread by
+ # global ID by mistake.
+ gdb_test "thread 4" "Unknown thread 1.4\\."
+
+ # Test thread ID list parsing. Test qualified and unqualified
+ # IDs; qualified and unqualified ranges; invalid IDs and invalid
+ # ranges.
+
+ # First spawn a couple more threads so ranges includes more than
+ # two threads.
+ with_test_prefix "more threads" {
+ gdb_breakpoint "thread_function2"
+
+ gdb_test "inferior 2" "Switching to inferior 2 .*"
+ gdb_continue_to_breakpoint "once"
+
+ gdb_test "inferior 1" "Switching to inferior 1 .*"
+ gdb_continue_to_breakpoint "twice"
+ }
+
+ thr_apply_info_thr "1 2 3" \
+ "1.1 1.2 1.3"
+
+ # Same, but with qualified thread IDs.
+ thr_apply_info_thr "1.1 1.2 1.3 2.1 2.2" \
+ "1.1 1.2 1.3 2.1 2.2"
+
+ # Test a thread number range.
+ thr_apply_info_thr "1-3" \
+ "1.1 1.2 1.3"
+
+ # Same, but using a qualified range.
+ thr_apply_info_thr "1.1-3" \
+ "1.1 1.2 1.3"
+
+ # A mix of qualified and unqualified thread IDs/ranges.
+ thr_apply_info_thr "1.1 2-3" \
+ "1.1 1.2 1.3"
+
+ thr_apply_info_thr "1 1.2-3" \
+ "1.1 1.2 1.3"
+
+ # Likewise, but mix inferiors too.
+ thr_apply_info_thr "2.1 2-3" \
+ "1.2 1.3 2.1" \
+ "2.1 1.2 1.3"
+
+ # Multiple ranges with mixed explicit inferiors.
+ thr_apply_info_thr "1.1-2 2.2-3" \
+ "1.1 1.2 2.2 2.3"
+
+ # Now test a set of invalid thread IDs/ranges.
+
+ thr_apply_info_thr_invalid "1." \
+ "1."
+
+ thr_apply_info_thr_invalid "1-3 1." \
+ "1."
+
+ thr_apply_info_thr_invalid "1.1.1" \
+ "1.1.1"
+
+ thr_apply_info_thr_invalid "2 1.1.1" \
+ "1.1.1"
+
+ thr_apply_info_thr_invalid "1.1.1 2" \
+ "1.1.1 2"
+
+ thr_apply_info_thr_invalid "1-2.1" \
+ "1-2.1"
+
+ thr_apply_info_thr_error "1-0" "inverted range"
+ thr_apply_info_thr_error "1.1-0" "inverted range"
+
+ thr_apply_info_thr_error "1-" "inverted range"
+ thr_apply_info_thr_error "1.1-" "inverted range"
+
+ thr_apply_info_thr_error "2-1" "inverted range"
+ thr_apply_info_thr_error "1.2-1" "inverted range"
+
+ thr_apply_info_thr_error "-1" "negative value"
+ thr_apply_info_thr_error "1.-1" "negative value"
+
+ # Check that we do parse the inferior number and don't confuse it.
+ gdb_test "info threads 3.1" \
+ "No threads match '3.1'\."
+}
+
+if { ![skip_python_tests] } {
+ with_test_prefix "python" {
+ # Check that InferiorThread.num returns the expected number.
+ gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" \
+ "test gdb.selected_thread" 1
+ gdb_test "python print ('result = %s' % t0.num)" " = 3" \
+ "test InferiorThread.num"
+ }
+}
+
+# Remove the second inferior and confirm that GDB goes back to showing
+# single-number thread IDs.
+with_test_prefix "back to one inferior" {
+ gdb_test "kill inferior 2" "" "kill inferior 2" "Kill the program being debugged.*" "y"
+ gdb_test "thread 1.1" "Switching to thread 1\.1 .*"
+ gdb_test "remove-inferior 2" ".*" "remove inferior 2"
+
+ # "info threads" while there's only inferior 1 should show
+ # single-number thread IDs.
+ info_threads "" "1 2 3"
+
+ gdb_test "thread" "Current thread is 1 .*"
+}
+
+# Add another inferior and remove inferior 1. Since even though
+# there's a single inferior, its number is not 1, GDB should show
+# inferior-qualified thread IDs.
+with_test_prefix "single-inferior but not initial" {
+ # Add another inferior.
+ gdb_test "add-inferior" "Added inferior 3.*" "add empty inferior"
+
+ # Now that we'd added another inferior, thread IDs should show the
+ # inferior number.
+ info_threads "" "1.1 1.2 1.3" \
+ "info threads with multiple inferiors"
+
+ gdb_test "thread" "Current thread is 1\.1 .*"
+
+ gdb_test "inferior 3" "Switching to inferior 3 .*" "switch to inferior 3"
+ gdb_test "file ${binfile}" ".*" "load file in inferior 3"
+
+ runto_main
+
+ gdb_test "remove-inferior 1" ".*" "remove inferior 1"
+
+ # Even though we have a single inferior, its number is > 1, so
+ # thread IDs should include the inferior number.
+ info_threads "" "3.1" \
+ "info threads with single inferior"
+
+ gdb_test "thread" "Current thread is 3\.1 .*" "thread again"
+}
# Test bad input
gdb_test "info thread foo" \
- "Args must be numbers or '.' variables." \
+ "Invalid thread ID: foo" \
"info thread foo"
gdb_test "info thread foo -1" \
- "Args must be numbers or '.' variables." \
+ "Invalid thread ID: foo -1" \
"info thread foo -1"
#include "gdb_regex.h"
#include "cli/cli-utils.h"
#include "thread-fsm.h"
+#include "tid-parse.h"
/* Definition of struct thread_info exported to gdbthread.h. */
delete_at_next_stop (&tp->control.exception_resume_breakpoint);
delete_at_next_stop (&tp->control.single_step_breakpoints);
- delete_longjmp_breakpoint_at_next_stop (tp->num);
+ delete_longjmp_breakpoint_at_next_stop (tp->global_num);
bpstat_clear (&tp->control.stop_bpstat);
threads_executing = 0;
}
-/* Allocate a new thread with target id PTID and add it to the thread
- list. */
+/* Allocate a new thread of inferior INF with target id PTID and add
+ it to the thread list. */
static struct thread_info *
-new_thread (ptid_t ptid)
+new_thread (struct inferior *inf, ptid_t ptid)
{
- struct thread_info *tp = XCNEW (struct thread_info);
+ struct thread_info *tp;
+
+ gdb_assert (inf != NULL);
+
+ tp = XCNEW (struct thread_info);
tp->ptid = ptid;
- tp->num = ++highest_thread_num;
+ tp->global_num = ++highest_thread_num;
+ tp->inf = inf;
+ tp->per_inf_num = ++inf->highest_thread_num;
if (thread_list == NULL)
thread_list = tp;
add_thread_silent (ptid_t ptid)
{
struct thread_info *tp;
+ struct inferior *inf = find_inferior_ptid (ptid);
+ gdb_assert (inf != NULL);
tp = find_thread_ptid (ptid);
if (tp)
if (ptid_equal (inferior_ptid, ptid))
{
- tp = new_thread (null_ptid);
+ tp = new_thread (inf, null_ptid);
/* Make switch_to_thread not read from the thread. */
tp->state = THREAD_EXITED;
delete_thread (ptid);
}
- tp = new_thread (ptid);
+ tp = new_thread (inf, ptid);
observer_notify_new_thread (tp);
return tp;
}
struct thread_info *
-find_thread_id (int num)
+find_thread_global_id (int global_id)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
- if (tp->num == num)
+ if (tp->global_num == global_id)
+ return tp;
+
+ return NULL;
+}
+
+static struct thread_info *
+find_thread_id (struct inferior *inf, int thr_num)
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->inf == inf && tp->per_inf_num == thr_num)
return tp;
return NULL;
}
int
-valid_thread_id (int num)
+valid_global_thread_id (int global_id)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
- if (tp->num == num)
+ if (tp->global_num == global_id)
return 1;
return 0;
}
int
-pid_to_thread_id (ptid_t ptid)
+ptid_to_global_thread_id (ptid_t ptid)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if (ptid_equal (tp->ptid, ptid))
- return tp->num;
+ return tp->global_num;
return 0;
}
ptid_t
-thread_id_to_pid (int num)
+global_thread_id_to_ptid (int global_id)
{
- struct thread_info *thread = find_thread_id (num);
+ struct thread_info *thread = find_thread_global_id (global_id);
if (thread)
return thread->ptid;
else
- return pid_to_ptid (-1);
+ return minus_one_ptid;
}
int
for (tp = thread_list; tp; tp = tp->next)
if (pid == -1 || ptid_get_pid (tp->ptid) == pid)
- if (ret == NULL || tp->num < ret->num)
+ if (ret == NULL || tp->global_num < ret->global_num)
ret = tp;
return ret;
continue;
if (ptid_equal (tp->ptid, inferior_ptid))
- current_thread = tp->num;
+ current_thread = tp->global_num;
num++;
- ui_out_field_int (uiout, "thread-id", tp->num);
+ ui_out_field_int (uiout, "thread-id", tp->global_num);
}
do_cleanups (cleanup_chain);
&& pc < thread->control.step_range_end);
}
-/* Prints the list of threads and their details on UIOUT.
- This is a version of 'info_threads_command' suitable for
- use from MI.
- If REQUESTED_THREAD is not -1, it's the GDB id of the thread
- that should be printed. Otherwise, all threads are
- printed.
- If PID is not -1, only print threads from the process PID.
- Otherwise, threads from all attached PIDs are printed.
- If both REQUESTED_THREAD and PID are not -1, then the thread
- is printed if it belongs to the specified process. Otherwise,
- an error is raised. */
-void
-print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
+/* Helper for print_thread_info. Returns true if THR should be
+ printed. If REQUESTED_THREADS, a list of GDB ids/ranges, is not
+ NULL, only print THR if its ID is included in the list. GLOBAL_IDS
+ is true if REQUESTED_THREADS is list of global IDs, false if a list
+ of per-inferior thread ids. If PID is not -1, only print THR if it
+ is a thread from the process PID. Otherwise, threads from all
+ attached PIDs are printed. If both REQUESTED_THREADS is not NULL
+ and PID is not -1, then the thread is printed if it belongs to the
+ specified process. Otherwise, an error is raised. */
+
+static int
+should_print_thread (const char *requested_threads, int default_inf_num,
+ int global_ids, int pid, struct thread_info *thr)
+{
+ if (requested_threads != NULL && *requested_threads != '\0')
+ {
+ int in_list;
+
+ if (global_ids)
+ in_list = number_is_in_list (requested_threads, thr->global_num);
+ else
+ in_list = tid_is_in_list (requested_threads, default_inf_num,
+ thr->inf->num, thr->per_inf_num);
+ if (!in_list)
+ return 0;
+ }
+
+ if (pid != -1 && ptid_get_pid (thr->ptid) != pid)
+ {
+ if (requested_threads != NULL && *requested_threads != '\0')
+ error (_("Requested thread not found in requested process"));
+ return 0;
+ }
+
+ if (thr->state == THREAD_EXITED)
+ return 0;
+
+ return 1;
+}
+
+/* Like print_thread_info, but in addition, GLOBAL_IDS indicates
+ whether REQUESTED_THREADS is a list of global or per-inferior
+ thread ids. */
+
+static void
+print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
+ int global_ids, int pid,
+ int show_global_ids)
{
struct thread_info *tp;
ptid_t current_ptid;
struct cleanup *old_chain;
const char *extra_info, *name, *target_id;
int current_thread = -1;
+ struct inferior *inf;
+ int default_inf_num = current_inferior ()->num;
update_thread_list ();
current_ptid = inferior_ptid;
for (tp = thread_list; tp; tp = tp->next)
{
- if (!number_is_in_list (requested_threads, tp->num))
- continue;
-
- if (pid != -1 && ptid_get_pid (tp->ptid) != pid)
- continue;
-
- if (tp->state == THREAD_EXITED)
+ if (!should_print_thread (requested_threads, default_inf_num,
+ global_ids, pid, tp))
continue;
++n_threads;
return;
}
- make_cleanup_ui_out_table_begin_end (uiout, 4, n_threads, "threads");
+ if (show_global_ids || ui_out_is_mi_like_p (uiout))
+ make_cleanup_ui_out_table_begin_end (uiout, 5, n_threads, "threads");
+ else
+ 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");
+
+ if (!ui_out_is_mi_like_p (uiout))
+ ui_out_table_header (uiout, 4, ui_left, "id-in-tg", "Id");
+ if (show_global_ids || ui_out_is_mi_like_p (uiout))
+ ui_out_table_header (uiout, 4, ui_left, "id", "GId");
ui_out_table_header (uiout, 17, ui_left, "target-id", "Target Id");
ui_out_table_header (uiout, 1, ui_left, "frame", "Frame");
ui_out_table_body (uiout);
}
- for (tp = thread_list; tp; tp = tp->next)
+ ALL_THREADS_BY_INFERIOR (inf, tp)
{
struct cleanup *chain2;
int core;
- if (!number_is_in_list (requested_threads, tp->num))
- continue;
-
- if (pid != -1 && ptid_get_pid (tp->ptid) != pid)
- {
- if (requested_threads != NULL && *requested_threads != '\0')
- error (_("Requested thread not found in requested process"));
- continue;
- }
-
if (ptid_equal (tp->ptid, current_ptid))
- current_thread = tp->num;
+ current_thread = tp->global_num;
- if (tp->state == THREAD_EXITED)
+ if (!should_print_thread (requested_threads, default_inf_num,
+ global_ids, pid, tp))
continue;
chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_skip (uiout, "current");
}
- ui_out_field_int (uiout, "id", tp->num);
+ if (!ui_out_is_mi_like_p (uiout))
+ ui_out_field_string (uiout, "id-in-tg", print_thread_id (tp));
+
+ if (show_global_ids || ui_out_is_mi_like_p (uiout))
+ ui_out_field_int (uiout, "id", tp->global_num);
/* For the CLI, we stuff everything into the target-id field.
This is a gross hack to make the output come out looking
if (pid == -1 && requested_threads == NULL)
{
- gdb_assert (current_thread != -1
- || !thread_list
- || ptid_equal (inferior_ptid, null_ptid));
- if (current_thread != -1 && ui_out_is_mi_like_p (uiout))
- ui_out_field_int (uiout, "current-thread-id", current_thread);
+ if (ui_out_is_mi_like_p (uiout)
+ && !ptid_equal (inferior_ptid, null_ptid))
+ {
+ int num = ptid_to_global_thread_id (inferior_ptid);
+
+ gdb_assert (num != 0);
+ ui_out_field_int (uiout, "current-thread-id", num);
+ }
- if (current_thread != -1 && is_exited (current_ptid))
+ if (!ptid_equal (inferior_ptid, null_ptid) && is_exited (inferior_ptid))
ui_out_message (uiout, 0, "\n\
-The current thread <Thread ID %d> has terminated. See `help thread'.\n",
- current_thread);
- else if (thread_list
- && current_thread == -1
- && ptid_equal (current_ptid, null_ptid))
+The current thread <Thread ID %s> has terminated. See `help thread'.\n",
+ print_thread_id (inferior_thread ()));
+ else if (thread_list != NULL
+ && ptid_equal (inferior_ptid, null_ptid))
ui_out_message (uiout, 0, "\n\
No selected thread. See `help thread'.\n");
}
}
-/* Print information about currently known threads
+/* See gdbthread.h. */
- Optional ARG is a thread id, or list of thread ids.
+void
+print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
+{
+ print_thread_info_1 (uiout, requested_threads, 1, pid, 0);
+}
+
+/* Implementation of the "info threads" command.
Note: this has the drawback that it _really_ switches
threads, which frees the frame cache. A no-side
static void
info_threads_command (char *arg, int from_tty)
{
- print_thread_info (current_uiout, arg, -1);
+ print_thread_info_1 (current_uiout, arg, 0, -1, 0);
}
/* See gdbthread.h. */
/* See gdbthread.h. */
+int
+show_inferior_qualified_tids (void)
+{
+ return (inferior_list->next != NULL || inferior_list->num != 1);
+}
+
+/* See gdbthread.h. */
+
const char *
print_thread_id (struct thread_info *thr)
{
char *s = get_print_cell ();
- xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->num);
+ if (show_inferior_qualified_tids ())
+ xsnprintf (s, PRINT_CELL_SIZE, "%d.%d", thr->inf->num, thr->per_inf_num);
+ else
+ xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->per_inf_num);
return s;
}
static int tp_array_compar_ascending;
-/* Sort an array for struct thread_info pointers by their NUM, order is
- determined by TP_ARRAY_COMPAR_ASCENDING. */
+/* Sort an array for struct thread_info pointers by thread ID (first
+ by inferior number, and then by per-inferior thread number). The
+ order is determined by TP_ARRAY_COMPAR_ASCENDING. */
static int
tp_array_compar (const void *ap_voidp, const void *bp_voidp)
{
- const struct thread_info *const *ap
- = (const struct thread_info * const*) ap_voidp;
- const struct thread_info *const *bp
- = (const struct thread_info * const*) bp_voidp;
+ const struct thread_info *a = *(const struct thread_info * const *) ap_voidp;
+ const struct thread_info *b = *(const struct thread_info * const *) bp_voidp;
+
+ if (a->inf->num != b->inf->num)
+ {
+ return ((a->inf->num > b->inf->num) - (a->inf->num < b->inf->num)
+ * (tp_array_compar_ascending ? +1 : -1));
+ }
- return ((((*ap)->num > (*bp)->num) - ((*ap)->num < (*bp)->num))
+ return (((a->per_inf_num > b->per_inf_num)
+ - (a->per_inf_num < b->per_inf_num))
* (tp_array_compar_ascending ? +1 : -1));
}
char *cmd;
struct cleanup *old_chain;
char *saved_cmd;
- struct get_number_or_range_state state;
+ struct tid_range_parser parser;
if (tidlist == NULL || *tidlist == '\000')
error (_("Please specify a thread ID list"));
saved_cmd = xstrdup (cmd);
old_chain = make_cleanup (xfree, saved_cmd);
- init_number_or_range (&state, tidlist);
- while (!state.finished && state.string < cmd)
- {
- struct thread_info *tp;
- int start;
-
- start = get_number_or_range (&state);
+ make_cleanup_restore_current_thread ();
- make_cleanup_restore_current_thread ();
+ tid_range_parser_init (&parser, tidlist, current_inferior ()->num);
+ while (!tid_range_parser_finished (&parser)
+ && tid_range_parser_string (&parser) < cmd)
+ {
+ struct thread_info *tp = NULL;
+ struct inferior *inf;
+ int inf_num, thr_num;
- tp = find_thread_id (start);
+ tid_range_parser_get_tid (&parser, &inf_num, &thr_num);
+ inf = find_inferior_id (inf_num);
+ if (inf != NULL)
+ tp = find_thread_id (inf, thr_num);
+ if (tp == NULL)
+ {
+ if (show_inferior_qualified_tids ()
+ || tid_range_parser_qualified (&parser))
+ warning (_("Unknown thread %d.%d"), inf_num, thr_num);
+ else
+ warning (_("Unknown thread %d"), thr_num);
+ continue;
+ }
- if (!tp)
- warning (_("Unknown thread %d."), start);
- else if (!thread_alive (tp))
- warning (_("Thread %d has terminated."), start);
- else
+ if (!thread_alive (tp))
{
- switch_to_thread (tp->ptid);
+ warning (_("Thread %s has terminated."), print_thread_id (tp));
+ continue;
+ }
- printf_filtered (_("\nThread %d (%s):\n"), tp->num,
- target_pid_to_str (inferior_ptid));
- execute_command (cmd, from_tty);
+ switch_to_thread (tp->ptid);
- /* Restore exact command used previously. */
- strcpy (cmd, saved_cmd);
- }
+ printf_filtered (_("\nThread %s (%s):\n"), print_thread_id (tp),
+ target_pid_to_str (inferior_ptid));
+ execute_command (cmd, from_tty);
+
+ /* Restore exact command used previously. */
+ strcpy (cmd, saved_cmd);
}
do_cleanups (old_chain);
}
static int
-do_captured_thread_select (struct ui_out *uiout, void *tidstr)
+do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
{
- int num;
+ const char *tidstr = tidstr_v;
struct thread_info *tp;
- num = value_as_long (parse_and_eval ((const char *) tidstr));
-
- tp = find_thread_id (num);
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ int num = value_as_long (parse_and_eval (tidstr));
- if (!tp)
- error (_("Thread ID %d not known."), num);
+ tp = find_thread_global_id (num);
+ if (tp == NULL)
+ error (_("Thread ID %d not known."), num);
+ }
+ else
+ {
+ tp = parse_thread_id (tidstr, NULL);
+ gdb_assert (tp != NULL);
+ }
if (!thread_alive (tp))
- error (_("Thread ID %d has terminated."), num);
+ error (_("Thread ID %s has terminated."), tidstr);
switch_to_thread (tp->ptid);
annotate_thread_changed ();
- ui_out_text (uiout, "[Switching to thread ");
- ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp));
- ui_out_text (uiout, " (");
- ui_out_text (uiout, target_pid_to_str (inferior_ptid));
- ui_out_text (uiout, ")]");
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_int (uiout, "new-thread-id", inferior_thread ()->global_num);
+ else
+ {
+ ui_out_text (uiout, "[Switching to thread ");
+ ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp));
+ ui_out_text (uiout, " (");
+ ui_out_text (uiout, target_pid_to_str (inferior_ptid));
+ ui_out_text (uiout, ")]");
+ }
/* Note that we can't reach this with an exited thread, due to the
thread_alive check above. */
update_threads_executing ();
}
-/* Return a new value for the selected thread's id. Return a value of 0 if
- no thread is selected, or no threads exist. */
+/* Return a new value for the selected thread's per-inferior thread
+ number. Return a value of 0 if no thread is selected, or no
+ threads exist. */
static struct value *
thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
struct thread_info *tp = find_thread_ptid (inferior_ptid);
return value_from_longest (builtin_type (gdbarch)->builtin_int,
- (tp ? tp->num : 0));
+ (tp ? tp->per_inf_num : 0));
}
/* Commands with a prefix of `thread'. */
add_info ("threads", info_threads_command,
_("Display currently known threads.\n\
Usage: info threads [ID]...\n\
-Optional arguments are thread IDs with spaces between.\n\
-If no arguments, all threads are displayed."));
+If ID is given, it is a space-separated list of IDs of threads to display.\n\
+Otherwise, all threads are displayed."));
add_prefix_cmd ("thread", class_run, thread_command, _("\
Use this command to switch between threads.\n\
--- /dev/null
+/* TID parsing for GDB, the GNU debugger.
+
+ Copyright (C) 2015-2016 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 "defs.h"
+#include "tid-parse.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include <ctype.h>
+
+/* See tid-parse.h. */
+
+void ATTRIBUTE_NORETURN
+invalid_thread_id_error (const char *string)
+{
+ error (_("Invalid thread ID: %s"), string);
+}
+
+/* See tid-parse.h. */
+
+struct thread_info *
+parse_thread_id (const char *tidstr, const char **end)
+{
+ const char *number = tidstr;
+ const char *dot, *p1;
+ struct thread_info *tp;
+ struct inferior *inf;
+ int thr_num;
+ int explicit_inf_id = 0;
+
+ dot = strchr (number, '.');
+
+ if (dot != NULL)
+ {
+ /* Parse number to the left of the dot. */
+ int inf_num;
+
+ p1 = number;
+ inf_num = get_number_trailer (&p1, '.');
+ if (inf_num == 0)
+ invalid_thread_id_error (number);
+
+ inf = find_inferior_id (inf_num);
+ if (inf == NULL)
+ error (_("No inferior number '%d'"), inf_num);
+
+ explicit_inf_id = 1;
+ p1 = dot + 1;
+ }
+ else
+ {
+ inf = current_inferior ();
+
+ p1 = number;
+ }
+
+ thr_num = get_number_const (&p1);
+ if (thr_num == 0)
+ invalid_thread_id_error (number);
+
+ ALL_THREADS (tp)
+ {
+ if (ptid_get_pid (tp->ptid) == inf->pid
+ && tp->per_inf_num == thr_num)
+ break;
+ }
+
+ if (tp == NULL)
+ {
+ if (show_inferior_qualified_tids () || explicit_inf_id)
+ error (_("Unknown thread %d.%d."), inf->num, thr_num);
+ else
+ error (_("Unknown thread %d."), thr_num);
+ }
+
+ if (end != NULL)
+ *end = p1;
+
+ return tp;
+}
+
+/* See tid-parse.h. */
+
+void
+tid_range_parser_init (struct tid_range_parser *parser, const char *tidlist,
+ int default_inferior)
+{
+ parser->state = TID_RANGE_STATE_INFERIOR;
+ parser->string = tidlist;
+ parser->inf_num = 0;
+ parser->qualified = 0;
+ parser->default_inferior = default_inferior;
+}
+
+/* See tid-parse.h. */
+
+int
+tid_range_parser_finished (struct tid_range_parser *parser)
+{
+ switch (parser->state)
+ {
+ case TID_RANGE_STATE_INFERIOR:
+ return *parser->string == '\0';
+ case TID_RANGE_STATE_THREAD_RANGE:
+ return parser->range_parser.finished;
+ }
+
+ gdb_assert_not_reached (_("unhandled state"));
+}
+
+/* See tid-parse.h. */
+
+const char *
+tid_range_parser_string (struct tid_range_parser *parser)
+{
+ switch (parser->state)
+ {
+ case TID_RANGE_STATE_INFERIOR:
+ return parser->string;
+ case TID_RANGE_STATE_THREAD_RANGE:
+ return parser->range_parser.string;
+ }
+
+ gdb_assert_not_reached (_("unhandled state"));
+}
+
+/* See tid-parse.h. */
+
+void
+tid_range_parser_skip (struct tid_range_parser *parser)
+{
+ gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE)
+ && parser->range_parser.in_range);
+
+ tid_range_parser_init (parser, parser->range_parser.end_ptr,
+ parser->default_inferior);
+}
+
+/* See tid-parse.h. */
+
+int
+tid_range_parser_qualified (struct tid_range_parser *parser)
+{
+ return parser->qualified;
+}
+
+/* Helper for tid_range_parser_get_tid and
+ tid_range_parser_get_tid_range. Return the next range if THR_END
+ is non-NULL, return a single thread ID otherwise. */
+
+static int
+get_tid_or_range (struct tid_range_parser *parser, int *inf_num,
+ int *thr_start, int *thr_end)
+{
+ if (parser->state == TID_RANGE_STATE_INFERIOR)
+ {
+ const char *p;
+ const char *space;
+
+ space = skip_to_space (parser->string);
+
+ p = parser->string;
+ while (p < space && *p != '.')
+ p++;
+ if (p < space)
+ {
+ const char *dot = p;
+
+ /* Parse number to the left of the dot. */
+ p = parser->string;
+ parser->inf_num = get_number_trailer (&p, '.');
+ if (parser->inf_num == 0)
+ invalid_thread_id_error (parser->string);
+
+ parser->qualified = 1;
+ p = dot + 1;
+
+ if (isspace (*p))
+ invalid_thread_id_error (parser->string);
+ }
+ else
+ {
+ parser->inf_num = parser->default_inferior;
+ parser->qualified = 0;
+ p = parser->string;
+ }
+
+ init_number_or_range (&parser->range_parser, p);
+ parser->state = TID_RANGE_STATE_THREAD_RANGE;
+ }
+
+ *inf_num = parser->inf_num;
+ *thr_start = get_number_or_range (&parser->range_parser);
+ if (*thr_start == 0)
+ invalid_thread_id_error (parser->string);
+
+ /* If we successfully parsed a thread number or finished parsing a
+ thread range, switch back to assuming the next TID is
+ inferior-qualified. */
+ if (parser->range_parser.end_ptr == NULL
+ || parser->range_parser.string == parser->range_parser.end_ptr)
+ {
+ parser->state = TID_RANGE_STATE_INFERIOR;
+ parser->string = parser->range_parser.string;
+
+ if (thr_end != NULL)
+ *thr_end = *thr_start;
+ }
+
+ /* If we're midway through a range, and the caller wants the end
+ value, return it and skip to the end of the range. */
+ if (thr_end != NULL && parser->state == TID_RANGE_STATE_THREAD_RANGE)
+ {
+ *thr_end = parser->range_parser.end_value;
+ tid_range_parser_skip (parser);
+ }
+
+ return (*inf_num != 0 && *thr_start != 0);
+}
+
+/* See tid-parse.h. */
+
+int
+tid_range_parser_get_tid_range (struct tid_range_parser *parser, int *inf_num,
+ int *thr_start, int *thr_end)
+{
+ gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL);
+
+ return get_tid_or_range (parser, inf_num, thr_start, thr_end);
+}
+
+/* See tid-parse.h. */
+
+int
+tid_range_parser_get_tid (struct tid_range_parser *parser,
+ int *inf_num, int *thr_num)
+{
+ gdb_assert (inf_num != NULL && thr_num != NULL);
+
+ return get_tid_or_range (parser, inf_num, thr_num, NULL);
+}
+
+/* See tid-parse.h. */
+
+int
+tid_is_in_list (const char *list, int default_inferior,
+ int inf_num, int thr_num)
+{
+ struct tid_range_parser parser;
+
+ if (list == NULL || *list == '\0')
+ return 1;
+
+ tid_range_parser_init (&parser, list, default_inferior);
+ while (!tid_range_parser_finished (&parser))
+ {
+ int tmp_inf, tmp_thr_start, tmp_thr_end;
+
+ if (!tid_range_parser_get_tid_range (&parser, &tmp_inf,
+ &tmp_thr_start, &tmp_thr_end))
+ invalid_thread_id_error (parser.string);
+ if (tmp_inf == inf_num
+ && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/* TID parsing for GDB, the GNU debugger.
+
+ Copyright (C) 2015-2016 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/>. */
+
+#ifndef TID_PARSE_H
+#define TID_PARSE_H
+
+#include "cli/cli-utils.h"
+
+struct thread_info;
+
+/* Issue an invalid thread ID error, pointing at STRING, the invalid
+ ID. */
+extern void ATTRIBUTE_NORETURN invalid_thread_id_error (const char *string);
+
+/* Parse TIDSTR as a per-inferior thread ID, in either INF_NUM.THR_NUM
+ or THR_NUM form. In the latter case, the missing INF_NUM is filled
+ in from the current inferior. If ENDPTR is not NULL,
+ parse_thread_id stores the address of the first character after the
+ thread ID. Either a valid thread is returned, or an error is
+ thrown. */
+struct thread_info *parse_thread_id (const char *tidstr, const char **end);
+
+/* The possible states of the tid range parser's state machine. */
+enum tid_range_state
+{
+ /* Parsing the inferior number. */
+ TID_RANGE_STATE_INFERIOR,
+
+ /* Parsing the thread number or thread number range. */
+ TID_RANGE_STATE_THREAD_RANGE,
+};
+
+/* An object of this type is passed to tid_range_parser_get_tid. It
+ must be initialized by calling tid_range_parser_init. This type is
+ defined here so that it can be stack-allocated, but all members
+ should be treated as opaque. */
+struct tid_range_parser
+{
+ /* What sub-component are we expecting. */
+ enum tid_range_state state;
+
+ /* The string being parsed. When parsing has finished, this points
+ past the last parsed token. */
+ const char *string;
+
+ /* The range parser state when we're parsing the thread number
+ sub-component. */
+ struct get_number_or_range_state range_parser;
+
+ /* Last inferior number returned. */
+ int inf_num;
+
+ /* True if the TID last parsed was explicitly inferior-qualified.
+ IOW, whether the spec specified an inferior number
+ explicitly. */
+ int qualified;
+
+ /* The inferior number to assume if the TID is not qualified. */
+ int default_inferior;
+};
+
+/* Initialize a tid_range_parser for use with
+ tid_range_parser_get_tid. TIDLIST is the string to be parsed.
+ DEFAULT_INFERIOR is the inferior number to assume if a
+ non-qualified thread ID is found. */
+extern void tid_range_parser_init (struct tid_range_parser *parser,
+ const char *tidlist,
+ int default_inferior);
+
+/* Parse a thread ID or a thread range list.
+
+ A range will be of the form
+
+ <inferior_num>.<thread_number1>-<thread_number2>
+
+ and will represent all the threads of inferior INFERIOR_NUM with
+ number between THREAD_NUMBER1 and THREAD_NUMBER2, inclusive.
+ <inferior_num> can also be omitted, as in
+
+ <thread_number1>-<thread_number2>
+
+ in which case GDB infers the inferior number from the default
+ passed to the tid_range_parser_init function.
+
+ This function is designed to be called iteratively. While
+ processing a thread ID range list, at each call it will return (in
+ the INF_NUM and THR_NUM output parameters) the next thread ID in
+ the range (irrespective of whether the thread actually exists).
+
+ At the beginning of parsing a thread range, the char pointer
+ PARSER->string will be advanced past <thread_number1> and left
+ pointing at the '-' token. Subsequent calls will not advance the
+ pointer until the range is completed. The call that completes the
+ range will advance the pointer past <thread_number2>.
+
+ This function advances through the input string for as long you
+ call it. Once the end of the input string is reached, a call to
+ tid_range_parser_finished returns false (see below).
+
+ E.g., with list: "1.2 3.4-6":
+
+ 1st call: *INF_NUM=1; *THR_NUM=2 (finished==0)
+ 2nd call: *INF_NUM=3; *THR_NUM=4 (finished==0)
+ 3rd call: *INF_NUM=3; *THR_NUM=5 (finished==0)
+ 4th call: *INF_NUM=3; *THR_NUM=6 (finished==1)
+
+ Returns true if parsed a thread/range successfully, false
+ otherwise. */
+extern int tid_range_parser_get_tid (struct tid_range_parser *parser,
+ int *inf_num, int *thr_num);
+
+/* Like tid_range_parser_get_tid, but return a thread ID range per
+ call, rather then a single thread ID.
+
+ If the next element in the list is a single thread ID, then
+ *THR_START and *THR_END are set to the same value.
+
+ E.g.,. with list: "1.2 3.4-6"
+
+ 1st call: *INF_NUM=1; *THR_START=2; *THR_END=2 (finished==0)
+ 2nd call: *INF_NUM=3; *THR_START=4; *THR_END=6 (finished==1)
+
+ Returns true if parsed a thread/range successfully, false
+ otherwise. */
+extern int tid_range_parser_get_tid_range (struct tid_range_parser *parser,
+ int *inf_num,
+ int *thr_start, int *thr_end);
+
+/* Returns non-zero if parsing has completed. */
+extern int tid_range_parser_finished (struct tid_range_parser *parser);
+
+/* Return the string being parsed. When parsing has finished, this
+ points past the last parsed token. */
+const char *tid_range_parser_string (struct tid_range_parser *parser);
+
+/* When parsing a range, advance past the final token in the range. */
+extern void tid_range_parser_skip (struct tid_range_parser *parser);
+
+/* True if the TID last parsed was explicitly inferior-qualified.
+ IOW, whether the spec specified an inferior number explicitly. */
+extern int tid_range_parser_qualified (struct tid_range_parser *parser);
+
+/* Accept a string-form list of thread IDs such as is accepted by
+ tid_range_parser_get_tid. Return true if the INF_NUM.THR.NUM
+ thread is in the list. DEFAULT_INFERIOR is the inferior number to
+ assume if a non-qualified thread ID is found in the list.
+
+ By definition, an empty list includes all threads. This is to be
+ interpreted as typing a command such as "info threads" with no
+ arguments. */
+extern int tid_is_in_list (const char *list, int default_inferior,
+ int inf_num, int thr_num);
+
+#endif /* TID_PARSE_H */
not NULL. */
struct frame_id frame;
- /* The thread ID that this varobj_root belong to. This field
+ /* The global thread ID that this varobj_root belongs to. This field
is only valid if valid_block is not NULL.
When not 0, indicates which thread 'frame' belongs to.
When 0, indicates that the thread list was empty when the varobj_root
error (_("Failed to find the specified frame"));
var->root->frame = get_frame_id (fi);
- var->root->thread_id = pid_to_thread_id (inferior_ptid);
+ var->root->thread_id = ptid_to_global_thread_id (inferior_ptid);
old_id = get_frame_id (get_selected_frame (NULL));
select_frame (fi);
}
}
else
{
- ptid_t ptid = thread_id_to_pid (var->root->thread_id);
- if (in_thread_list (ptid))
+ ptid_t ptid = global_thread_id_to_ptid (var->root->thread_id);
+
+ if (!ptid_equal (minus_one_ptid, ptid))
{
switch_to_thread (ptid);
within_scope = check_scope (var);
return 1;
}
-/* Display thread information block of a thread specified by ARGS.
- If ARGS is empty, display thread information block of current_thread
- if current_thread is non NULL.
- Otherwise ARGS is parsed and converted to a integer that should
- be the windows ThreadID (not the internal GDB thread ID). */
+/* Display thread information block of the current thread. */
static void
display_tib (char * args, int from_tty)
{
- if (args)
- {
- struct thread_info *tp;
- int gdb_id = value_as_long (parse_and_eval (args));
-
- tp = find_thread_id (gdb_id);
-
- if (!tp)
- error (_("Thread ID %d not known."), gdb_id);
-
- if (!target_thread_alive (tp->ptid))
- error (_("Thread ID %d has terminated."), gdb_id);
-
- display_one_tib (tp->ptid);
- }
- else if (!ptid_equal (inferior_ptid, null_ptid))
+ if (!ptid_equal (inferior_ptid, null_ptid))
display_one_tib (inferior_ptid);
}