@@ -3,6 +3,46 @@
*** 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. See also $_gthread below.
+
+ 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)
+
+* You can use "info threads -gid" to display the global thread ID of
+ all threads.
+
+* The new convenience variable $_gthread holds the global number of
+ the current thread.
+
+* The new convenience variable $_inferior holds the number of the
+ current inferior.
+
* Record btrace now supports non-stop mode.
* Support for tracepoints on aarch64-linux was added in GDBserver.
@@ -148,6 +188,15 @@ show remote exec-event-feature-packet
format. It outputs data in hexadecimal format with zero-padding on the
left.
+* Python Scripting
+
+ ** gdb.InferiorThread objects have a new attribute "global_num",
+ which refers to the thread's global thread ID. The existing
+ "num" attribute 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.
+
*** Changes in GDB 7.10
* Support for process record-replay and reverse debugging on aarch64*-linux*
@@ -1102,7 +1102,7 @@ print_ada_task_info (struct ui_out *uiout,
/* 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);
@@ -3144,7 +3144,7 @@ insert_breakpoint_locations (void)
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);
@@ -3244,13 +3244,13 @@ remove_threaded_breakpoints (struct thread_info *tp, int silent)
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;
printf_filtered (_("\
-Thread-specific breakpoint %d deleted - thread %d no longer in the thread list.\n"),
- b->number, tp->num);
+Thread-specific breakpoint %d deleted - thread %s no longer in the thread list.\n"),
+ b->number, print_thread_id (tp));
/* Hide it from the user. */
b->number = 0;
@@ -5447,7 +5447,7 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
/* 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)))
{
@@ -6516,7 +6516,14 @@ print_one_breakpoint_location (struct breakpoint *b,
{
/* FIXME should make an annotation for this. */
ui_out_text (uiout, "\tstop only in thread ");
- ui_out_field_int (uiout, "thread", b->thread);
+ 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");
}
@@ -7572,7 +7579,7 @@ void
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
@@ -7641,7 +7648,7 @@ set_longjmp_breakpoint_for_call_dummy (void)
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. */
@@ -7671,7 +7678,7 @@ check_longjmp_breakpoint_for_call_dummy (struct thread_info *tp)
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;
@@ -8891,7 +8898,7 @@ set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal,
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);
@@ -9565,14 +9572,6 @@ check_fast_tracepoint_sals (struct gdbarch *gdbarch,
}
}
-/* 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.
@@ -9621,14 +9620,14 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
}
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)
@@ -11133,25 +11132,23 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
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);
- /* 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"))
{
@@ -11692,7 +11689,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
resolve_sal_pc (&sal);
tp = inferior_thread ();
- thread = tp->num;
+ thread = tp->global_num;
old_chain = make_cleanup (null_cleanup, NULL);
@@ -11742,7 +11739,8 @@ until_break_command (char *arg, int from_tty, int anywhere)
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);
@@ -13364,7 +13362,7 @@ momentary_bkpt_print_mention (struct breakpoint *b)
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;
@@ -14545,7 +14543,7 @@ breakpoint_re_set_thread (struct breakpoint *b)
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
@@ -15056,7 +15054,7 @@ insert_single_step_breakpoint (struct gdbarch *gdbarch,
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);
@@ -1043,7 +1043,8 @@ btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
if (!target_supports_btrace (conf->format))
error (_("Target does not support branch tracing."));
- DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+ DEBUG ("enable thread %s (%s)", print_thread_id (tp),
+ target_pid_to_str (tp->ptid));
tp->btrace.target = target_enable_btrace (tp->ptid, conf);
@@ -1075,7 +1076,8 @@ btrace_disable (struct thread_info *tp)
if (btp->target == NULL)
return;
- DEBUG ("disable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+ DEBUG ("disable thread %s (%s)", print_thread_id (tp),
+ target_pid_to_str (tp->ptid));
target_disable_btrace (btp->target);
btp->target = NULL;
@@ -1094,7 +1096,8 @@ btrace_teardown (struct thread_info *tp)
if (btp->target == NULL)
return;
- DEBUG ("teardown thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+ DEBUG ("teardown thread %s (%s)", print_thread_id (tp),
+ target_pid_to_str (tp->ptid));
target_teardown_btrace (btp->target);
btp->target = NULL;
@@ -1268,7 +1271,8 @@ btrace_fetch (struct thread_info *tp)
struct cleanup *cleanup;
int errcode;
- DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+ DEBUG ("fetch thread %s (%s)", print_thread_id (tp),
+ target_pid_to_str (tp->ptid));
btinfo = &tp->btrace;
tinfo = btinfo->target;
@@ -1340,7 +1344,8 @@ btrace_clear (struct thread_info *tp)
struct btrace_thread_info *btinfo;
struct btrace_function *it, *trash;
- DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+ DEBUG ("clear thread %s (%s)", print_thread_id (tp),
+ target_pid_to_str (tp->ptid));
/* Make sure btrace frames that may hold a pointer into the branch
trace data are destroyed. */
@@ -23,16 +23,9 @@
#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 */
@@ -193,6 +186,18 @@ get_number_or_range (struct get_number_or_range_state *state)
return state->last_retval;
}
+/* See documentation in cli-utils.h. */
+
+int
+number_is_in_remaining_range (struct get_number_or_range_state *state,
+ int number)
+{
+ gdb_assert (state->in_range);
+
+ return (state->last_retval < number
+ && number <= state->end_value);
+}
+
/* Accept a number and a string-form list of numbers such as is
accepted by get_number_or_range. Return TRUE if the number is
in the list.
@@ -20,11 +20,18 @@
#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 **);
@@ -83,6 +90,14 @@ extern void init_number_or_range (struct get_number_or_range_state *state,
extern int get_number_or_range (struct get_number_or_range_state *state);
+/* Assuming STATE just parsed a number range, check whether NUMBER is
+ within the range being processed. IOW, returns true if
+ get_number_or_range would eventually return NUMBER. */
+
+extern int number_is_in_remaining_range
+ (struct get_number_or_range_state *state,
+ int number);
+
/* Accept a number and a string-form list of numbers such as is
accepted by get_number_or_range. Return TRUE if the number is
in the list.
@@ -21,15 +21,15 @@
#include "print-utils.h"
/* Temporary storage using circular buffer. */
+/* Number of cells in the circular buffer. */
#define NUMCELLS 16
-#define CELLSIZE 50
/* Return the next entry in the circular buffer. */
-static char *
-get_cell (void)
+char *
+get_print_cell (void)
{
- static char buf[NUMCELLS][CELLSIZE];
+ static char buf[NUMCELLS][PRINT_CELL_SIZE];
static int cell = 0;
if (++cell >= NUMCELLS)
@@ -43,7 +43,7 @@ decimal2str (char *sign, ULONGEST addr, int width)
/* Steal code from valprint.c:print_decimal(). Should this worry
about the real size of addr as the above does? */
unsigned long temp[3];
- char *str = get_cell ();
+ char *str = get_print_cell ();
int i = 0;
do
@@ -62,14 +62,14 @@ decimal2str (char *sign, ULONGEST addr, int width)
switch (i)
{
case 1:
- xsnprintf (str, CELLSIZE, "%s%0*lu", sign, width, temp[0]);
+ xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu", sign, width, temp[0]);
break;
case 2:
- xsnprintf (str, CELLSIZE, "%s%0*lu%09lu", sign, width,
+ xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu%09lu", sign, width,
temp[1], temp[0]);
break;
case 3:
- xsnprintf (str, CELLSIZE, "%s%0*lu%09lu%09lu", sign, width,
+ xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu%09lu%09lu", sign, width,
temp[2], temp[1], temp[0]);
break;
default:
@@ -84,7 +84,7 @@ static char *
octal2str (ULONGEST addr, int width)
{
unsigned long temp[3];
- char *str = get_cell ();
+ char *str = get_print_cell ();
int i = 0;
do
@@ -104,15 +104,15 @@ octal2str (ULONGEST addr, int width)
{
case 1:
if (temp[0] == 0)
- xsnprintf (str, CELLSIZE, "%*o", width, 0);
+ xsnprintf (str, PRINT_CELL_SIZE, "%*o", width, 0);
else
- xsnprintf (str, CELLSIZE, "0%0*lo", width, temp[0]);
+ xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo", width, temp[0]);
break;
case 2:
- xsnprintf (str, CELLSIZE, "0%0*lo%010lo", width, temp[1], temp[0]);
+ xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo%010lo", width, temp[1], temp[0]);
break;
case 3:
- xsnprintf (str, CELLSIZE, "0%0*lo%010lo%010lo", width,
+ xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo%010lo%010lo", width,
temp[2], temp[1], temp[0]);
break;
default:
@@ -155,18 +155,18 @@ phex (ULONGEST l, int sizeof_l)
switch (sizeof_l)
{
case 8:
- str = get_cell ();
- xsnprintf (str, CELLSIZE, "%08lx%08lx",
+ str = get_print_cell ();
+ xsnprintf (str, PRINT_CELL_SIZE, "%08lx%08lx",
(unsigned long) (l >> thirty_two),
(unsigned long) (l & 0xffffffff));
break;
case 4:
- str = get_cell ();
- xsnprintf (str, CELLSIZE, "%08lx", (unsigned long) l);
+ str = get_print_cell ();
+ xsnprintf (str, PRINT_CELL_SIZE, "%08lx", (unsigned long) l);
break;
case 2:
- str = get_cell ();
- xsnprintf (str, CELLSIZE, "%04x", (unsigned short) (l & 0xffff));
+ str = get_print_cell ();
+ xsnprintf (str, PRINT_CELL_SIZE, "%04x", (unsigned short) (l & 0xffff));
break;
default:
str = phex (l, sizeof (l));
@@ -189,22 +189,22 @@ phex_nz (ULONGEST l, int sizeof_l)
{
unsigned long high = (unsigned long) (l >> thirty_two);
- str = get_cell ();
+ str = get_print_cell ();
if (high == 0)
- xsnprintf (str, CELLSIZE, "%lx",
+ xsnprintf (str, PRINT_CELL_SIZE, "%lx",
(unsigned long) (l & 0xffffffff));
else
- xsnprintf (str, CELLSIZE, "%lx%08lx", high,
+ xsnprintf (str, PRINT_CELL_SIZE, "%lx%08lx", high,
(unsigned long) (l & 0xffffffff));
break;
}
case 4:
- str = get_cell ();
- xsnprintf (str, CELLSIZE, "%lx", (unsigned long) l);
+ str = get_print_cell ();
+ xsnprintf (str, PRINT_CELL_SIZE, "%lx", (unsigned long) l);
break;
case 2:
- str = get_cell ();
- xsnprintf (str, CELLSIZE, "%x", (unsigned short) (l & 0xffff));
+ str = get_print_cell ();
+ xsnprintf (str, PRINT_CELL_SIZE, "%x", (unsigned short) (l & 0xffff));
break;
default:
str = phex_nz (l, sizeof (l));
@@ -219,9 +219,9 @@ phex_nz (ULONGEST l, int sizeof_l)
char *
hex_string (LONGEST num)
{
- char *result = get_cell ();
+ char *result = get_print_cell ();
- xsnprintf (result, CELLSIZE, "0x%s", phex_nz (num, sizeof (num)));
+ xsnprintf (result, PRINT_CELL_SIZE, "0x%s", phex_nz (num, sizeof (num)));
return result;
}
@@ -230,14 +230,14 @@ hex_string (LONGEST num)
char *
hex_string_custom (LONGEST num, int width)
{
- char *result = get_cell ();
- char *result_end = result + CELLSIZE - 1;
+ char *result = get_print_cell ();
+ char *result_end = result + PRINT_CELL_SIZE - 1;
const char *hex = phex_nz (num, sizeof (num));
int hex_len = strlen (hex);
if (hex_len > width)
width = hex_len;
- if (width + 2 >= CELLSIZE)
+ if (width + 2 >= PRINT_CELL_SIZE)
internal_error (__FILE__, __LINE__, _("\
hex_string_custom: insufficient space to store result"));
@@ -294,7 +294,7 @@ int_string (LONGEST val, int radix, int is_signed, int width,
const char *
core_addr_to_string (const CORE_ADDR addr)
{
- char *str = get_cell ();
+ char *str = get_print_cell ();
strcpy (str, "0x");
strcat (str, phex (addr, sizeof (addr)));
@@ -306,7 +306,7 @@ core_addr_to_string (const CORE_ADDR addr)
const char *
core_addr_to_string_nz (const CORE_ADDR addr)
{
- char *str = get_cell ();
+ char *str = get_print_cell ();
strcpy (str, "0x");
strcat (str, phex_nz (addr, sizeof (addr)));
@@ -318,8 +318,9 @@ core_addr_to_string_nz (const CORE_ADDR addr)
const char *
host_address_to_string_1 (const void *addr)
{
- char *str = get_cell ();
+ char *str = get_print_cell ();
- xsnprintf (str, CELLSIZE, "0x%s", phex_nz ((uintptr_t) addr, sizeof (addr)));
+ xsnprintf (str, PRINT_CELL_SIZE, "0x%s",
+ phex_nz ((uintptr_t) addr, sizeof (addr)));
return str;
}
@@ -20,6 +20,10 @@
#ifndef COMMON_CELLS_H
#define COMMON_CELLS_H
+/* How many characters (including the terminating null byte) fit in a
+ cell. */
+#define PRINT_CELL_SIZE 50
+
/* %d for LONGEST. The result is stored in a circular static buffer,
NUMCELLS deep. */
@@ -71,4 +75,8 @@ extern const char *host_address_to_string_1 (const void *addr);
#define host_address_to_string(ADDR) \
host_address_to_string_1 ((const void *) (ADDR))
+/* Return the next entry in the circular print buffer. */
+
+extern char *get_print_cell (void);
+
#endif /* COMMON_CELLS_H */
@@ -2658,6 +2658,12 @@ Make inferior number @var{infno} the current inferior. The argument
in the first field of the @samp{info inferiors} display.
@end table
+@vindex $_inferior@r{, convenience variable}
+The debugger convenience variable @samp{$_inferior} contains the
+number of the current inferior. 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.
You can get multiple executables into a debugging session via the
@code{add-inferior} and @w{@code{clone-inferior}} commands. On some
@@ -2832,9 +2838,9 @@ programs:
@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}] [@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
@@ -2880,26 +2886,81 @@ further qualifier.
@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.
+The debugger convenience variables @samp{$_thread} and
+@samp{$_gthread} contain, respectively, the per-inferior thread number
+and the global 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 global thread number assigned by @value{GDBN}, if the @samp{-gid}
+option was specified
@item
the target system's thread identifier (@var{systag})
@@ -2930,6 +2991,22 @@ For example,
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.
+
+If you specify the @samp{-gid} option, @value{GDBN} displays a column
+indicating each thread's global thread ID:
+
+@smallexample
+(@value{GDBP}) info threads
+ Id GId Target Id Frame
+ 1.1 1 process 35 thread 13 main (argc=1, argv=0x7ffffff8)
+ 1.2 3 process 35 thread 23 0x34e5 in sigpause ()
+ 1.3 4 process 35 thread 27 0x34e5 in sigpause ()
+* 2.1 2 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:
@@ -2941,13 +3018,15 @@ Display info on Solaris user threads.
@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
@@ -2961,23 +3040,14 @@ As with the @samp{[New @dots{}]} message, the form of the text after
@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}}.
@@ -3977,7 +4047,7 @@ slow down the running of your program.
@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
@@ -3987,9 +4057,9 @@ to watch the value of a single variable:
(@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.
@@ -4021,12 +4091,12 @@ Examples:
@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.
@@ -6045,25 +6115,25 @@ breakpoints on all threads, or on a particular thread.
@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
@@ -10382,6 +10452,16 @@ gdbserver that supports the @code{qGetTIBAddr} request.
@xref{General Query Packets}.
This variable contains the address of the thread information block.
+@item $_inferior
+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}.
+
+@item $_gthread
+The global number of the current thread. @xref{global thread numbers}.
+
@end table
@node Convenience Funs
@@ -16124,7 +16204,7 @@ This command prints the ID of the current task.
@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.
@@ -25469,8 +25549,8 @@ increases the risk that by relying on implicitly selected thread, the
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
@@ -26023,15 +26103,16 @@ The following is the list of possible async records:
@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
@@ -26085,8 +26166,9 @@ The inferior called @code{exec}. This is reported when @code{catch exec}
(@pxref{Set Catchpoints}) has been used.
@end table
-The @var{id} field identifies the thread that directly caused the 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
@@ -26122,7 +26204,7 @@ only when the inferior exited with some code.
@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}"
@@ -26383,7 +26465,7 @@ uses a tuple with the following fields:
@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
@@ -26856,7 +26938,8 @@ Make the breakpoint conditional on @var{condition}.
@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
@@ -26946,7 +27029,8 @@ Make the breakpoint conditional on @var{condition}.
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
@@ -27596,10 +27680,11 @@ The corresponding @value{GDBN} command is @samp{pwd}.
-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
@@ -27616,7 +27701,7 @@ defined for a given thread:
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.
@@ -27682,8 +27767,9 @@ current-thread-id="1"
-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.
@@ -27709,11 +27795,12 @@ current-thread-id="1",number-of-threads="3"
@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.
@@ -27782,7 +27869,8 @@ The identifier that @value{GDBN} uses to refer to the Ada task.
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
@@ -28998,7 +29086,7 @@ would be printed by the @value{GDBN} CLI. If @samp{print object}
@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
@@ -29179,8 +29267,8 @@ The type of the child. If @samp{print object}
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.
@@ -3127,13 +3127,14 @@ At present, @var{count} must be zero.
@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
@@ -2995,7 +2995,11 @@ user-specified thread name.
@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.global_num
+The global ID of the thread, as assigned by GDB.
@end defvar
@defvar InferiorThread.ptid
@@ -4638,9 +4642,9 @@ first command is @code{silent}. This is not reported by the
@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
@@ -130,7 +130,7 @@ pop_dummy_frame_bpt (struct breakpoint *b, void *dummy_voidp)
{
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)
@@ -902,7 +902,7 @@ elf_gnu_ifunc_resolver_stop (struct 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);
@@ -30,6 +30,7 @@ struct symtab;
#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),... */
@@ -187,7 +188,46 @@ struct thread_info
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,
+ Python/Scheme, visible with "info threads -gid", and is also what
+ the $_gthread convenience variable is bound to. */
+ 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. */
@@ -353,27 +393,136 @@ extern int thread_has_single_step_breakpoint_here (struct thread_info *tp,
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 integer thread id (GDB's homegrown id, not the system's). */
-extern int pid_to_thread_id (ptid_t ptid);
+/* 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);
+
+/* 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);
+
+/* 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_number1> 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_number1>, in which
+ case GDB infers the inferior number from the current inferior.
+
+ While processing a thread ID range list, this function is called
+ iteratively; At each call it will return (in the INF_NUM and
+ THR_NUM output parameters) the next thread in the range.
+
+ 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>. */
+extern void tid_range_parser_get_tid (struct tid_range_parser *parser,
+ int *inf_num, int *thr_num);
+
+/* 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);
/* 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. */
@@ -395,6 +544,10 @@ void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
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 all threads, except those that have THREAD_EXITED
state. */
@@ -500,7 +653,14 @@ extern void thread_command (char *tidstr, int from_tty);
`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);
@@ -745,7 +745,7 @@ gdbscm_set_breakpoint_thread_x (SCM self, SCM newvalue)
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"));
@@ -1273,14 +1273,14 @@ Set the breakpoint's \"hit\" count. The value must be zero.\n\
{ "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),
"\
@@ -960,7 +960,7 @@ step_command_fsm_prepare (struct step_command_fsm *sm,
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)
@@ -1032,7 +1032,7 @@ static int
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)
{
@@ -1316,8 +1316,8 @@ signal_command (char *signum_exp, int from_tty)
{
if (!must_confirm)
printf_unfiltered (_("Note:\n"));
- printf_unfiltered (_(" Thread %d previously stopped with signal %s, %s.\n"),
- tp->num,
+ printf_unfiltered (_(" Thread %s previously stopped with signal %s, %s.\n"),
+ print_thread_id (tp),
gdb_signal_to_name (tp->suspend.stop_signal),
gdb_signal_to_string (tp->suspend.stop_signal));
must_confirm = 1;
@@ -1325,10 +1325,10 @@ signal_command (char *signum_exp, int from_tty)
}
if (must_confirm
- && !query (_("Continuing thread %d (the current thread) with specified signal will\n"
+ && !query (_("Continuing thread %s (the current thread) with specified signal will\n"
"still deliver the signals noted above to their respective threads.\n"
"Continue anyway? "),
- inferior_thread ()->num))
+ print_thread_id (inferior_thread ())))
error (_("Not confirmed."));
}
@@ -1478,7 +1478,7 @@ until_next_command (int from_tty)
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;
@@ -1520,12 +1520,11 @@ until_next_command (int from_tty)
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
@@ -1774,7 +1773,7 @@ finish_command_fsm_should_stop (struct thread_fsm *self)
{
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,
@@ -1967,7 +1966,7 @@ finish_command (char *arg, int from_tty)
tp = inferior_thread ();
- sm = new_finish_command_fsm (tp->num);
+ sm = new_finish_command_fsm (tp->global_num);
tp->thread_fsm = &sm->thread_fsm;
@@ -2715,7 +2714,8 @@ attach_post_wait (char *args, int from_tty, enum attach_post_wait_mode mode)
{
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;
}
}
@@ -748,8 +748,8 @@ inferior_command (char *args, int from_tty)
switch_to_thread (tp->ptid);
}
- printf_filtered (_("[Switching to thread %d (%s)] "),
- pid_to_thread_id (inferior_ptid),
+ printf_filtered (_("[Switching to thread %s (%s)] "),
+ print_thread_id (inferior_thread ()),
target_pid_to_str (inferior_ptid));
}
else
@@ -1008,6 +1008,26 @@ show_print_inferior_events (struct ui_file *file, int from_tty,
fprintf_filtered (file, _("Printing of inferior events is %s.\n"), value);
}
+/* Return a new value for the selected inferior's id. */
+
+static struct value *
+inferior_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
+ void *ignore)
+{
+ struct inferior *inf = current_inferior ();
+
+ return value_from_longest (builtin_type (gdbarch)->builtin_int, inf->num);
+}
+
+/* Implementation of `$_inferior' variable. */
+
+static const struct internalvar_funcs inferior_funcs =
+{
+ inferior_id_make_value,
+ NULL,
+ NULL
+};
+
void
@@ -1071,4 +1091,5 @@ Show printing of inferior events (e.g., inferior start and exit)."), NULL,
show_print_inferior_events,
&setprintlist, &showprintlist);
+ create_internalvar_type_lazy ("_inferior", &inferior_funcs, NULL);
}
@@ -304,6 +304,9 @@ struct inferior
/* 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;
@@ -5702,7 +5702,7 @@ handle_signal_stop (struct execution_control_state *ecs)
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. */
@@ -7510,7 +7510,7 @@ insert_exception_resume_breakpoint (struct thread_info *tp,
/* 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;
}
}
@@ -7547,7 +7547,7 @@ insert_exception_resume_from_probe (struct thread_info *tp,
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;
}
@@ -7905,7 +7905,7 @@ print_signal_received_reason (struct ui_out *uiout, enum gdb_signal siggnal)
ui_out_text (uiout, "\n[");
ui_out_field_string (uiout, "thread-name",
target_pid_to_str (t->ptid));
- ui_out_field_fmt (uiout, "thread-id", "] #%d", t->num);
+ ui_out_field_fmt (uiout, "thread-id", "] #%d", t->global_num);
ui_out_text (uiout, " stopped");
}
else
@@ -662,7 +662,7 @@ mi_cmd_var_update_iter (struct varobj *var, void *data_pointer)
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);
@@ -362,7 +362,7 @@ mi_new_thread (struct thread_info *t)
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);
}
@@ -383,7 +383,7 @@ mi_thread_exit (struct thread_info *t, int silent)
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);
@@ -617,15 +617,14 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
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
@@ -859,7 +858,7 @@ mi_output_running_pid (struct thread_info *info, void *arg)
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;
}
@@ -925,7 +924,8 @@ mi_on_resume (ptid_t ptid)
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)
@@ -2165,7 +2165,7 @@ mi_execute_command (const char *cmd, int from_tty)
{
struct thread_info *ti = inferior_thread ();
- report_change = (ti->num != command->thread);
+ report_change = (ti->global_num != command->thread);
}
if (report_change)
@@ -2175,7 +2175,7 @@ mi_execute_command (const char *cmd, int from_tty)
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-selected,id=\"%d\"",
- ti->num);
+ ti->global_num);
gdb_flush (mi->event_channel);
}
}
@@ -2226,7 +2226,7 @@ mi_cmd_execute (struct mi_parse *parse)
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);
@@ -203,7 +203,7 @@ bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
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."));
@@ -221,7 +221,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
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,
@@ -115,6 +115,8 @@ thpy_set_name (PyObject *self, PyObject *newvalue, void *ignore)
return 0;
}
+/* Getter for InferiorThread.num. */
+
static PyObject *
thpy_get_num (PyObject *self, void *closure)
{
@@ -122,7 +124,19 @@ 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.global_num. */
+
+static PyObject *
+thpy_get_global_num (PyObject *self, void *closure)
+{
+ thread_object *thread_obj = (thread_object *) self;
+
+ THPY_REQUIRE_VALID (thread_obj);
+
+ return PyLong_FromLong (thread_obj->thread->global_num);
}
/* Getter for InferiorThread.ptid -> (pid, lwp, tid).
@@ -140,6 +154,18 @@ thpy_get_ptid (PyObject *self, void *closure)
return gdbpy_create_ptid_object (thread_obj->thread->ptid);
}
+/* Getter for InferiorThread.inferior -> Inferior. */
+
+static PyObject *
+thpy_get_inferior (PyObject *self, void *ignore)
+{
+ thread_object *thread_obj = (thread_object *) self;
+
+ THPY_REQUIRE_VALID (thread_obj);
+
+ return inferior_to_inferior_object (thread_obj->thread->inf);
+}
+
/* Implementation of InferiorThread.switch ().
Makes this the GDB selected thread. */
@@ -282,9 +308,14 @@ 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 },
+ { "num", thpy_get_num, NULL,
+ "Per-inferior number of the thread, as assigned by GDB.", NULL },
+ { "global_num", thpy_get_global_num, NULL,
+ "Global 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,
+ "The Inferior object this thread belongs to.", NULL },
{ NULL }
};
@@ -218,7 +218,7 @@ record_btrace_open (const char *args, int from_tty)
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);
@@ -438,8 +438,8 @@ record_btrace_info (struct target_ops *self)
}
printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) "
- "for thread %d (%s).\n"), insns, calls, gaps,
- tp->num, target_pid_to_str (tp->ptid));
+ "for thread %s (%s).\n"), insns, calls, gaps,
+ print_thread_id (tp), target_pid_to_str (tp->ptid));
if (btrace_is_replaying (tp))
printf_unfiltered (_("Replay in progress. At instruction %u.\n"),
@@ -1864,7 +1864,7 @@ record_btrace_resume_thread (struct thread_info *tp,
{
struct btrace_thread_info *btinfo;
- DEBUG ("resuming thread %d (%s): %x (%s)", tp->num,
+ DEBUG ("resuming thread %s (%s): %x (%s)", print_thread_id (tp),
target_pid_to_str (tp->ptid), flag, btrace_thread_flag_to_str (flag));
btinfo = &tp->btrace;
@@ -2131,7 +2131,8 @@ record_btrace_cancel_resume (struct thread_info *tp)
if (flags == 0)
return;
- DEBUG ("cancel resume thread %d (%s): %x (%s)", tp->num,
+ DEBUG ("cancel resume thread %s (%s): %x (%s)",
+ print_thread_id (tp),
target_pid_to_str (tp->ptid), flags,
btrace_thread_flag_to_str (flags));
@@ -2354,7 +2355,7 @@ record_btrace_step_thread (struct thread_info *tp)
flags = btinfo->flags & (BTHR_MOVE | BTHR_STOP);
btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
- DEBUG ("stepping thread %d (%s): %x (%s)", tp->num,
+ DEBUG ("stepping thread %s (%s): %x (%s)", print_thread_id (tp),
target_pid_to_str (tp->ptid), flags,
btrace_thread_flag_to_str (flags));
@@ -2563,7 +2564,8 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
/* We moved the replay position but did not update registers. */
registers_changed_ptid (eventing->ptid);
- DEBUG ("wait ended by thread %d (%s): %s", eventing->num,
+ DEBUG ("wait ended by thread %s (%s): %s",
+ print_thread_id (eventing),
target_pid_to_str (eventing->ptid),
target_waitstatus_to_string (status));
@@ -3881,7 +3881,9 @@ process_initial_stop_replies (int from_tty)
&& 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)
@@ -2157,6 +2157,8 @@ target_pre_inferior (int from_tty)
the inferior was attached to. */
current_inferior ()->attach_flag = 0;
+ current_inferior ()->highest_thread_num = 0;
+
agent_capability_invalidate ();
}
@@ -550,7 +550,7 @@ gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
"thread-specific breakpoint on non-existent thread disallowed"
gdb_test "break $bp_location12 thread foo" \
- "Junk after thread keyword.*" \
+ "Bad thread spec.*" \
"thread-specific breakpoint on bogus thread ID disallowed"
# Verify that GDB responds gracefully to a breakpoint command with
@@ -587,6 +587,8 @@ set show_conv_list \
{$_sdata = void} \
{$_siginfo = void} \
{$_thread = 0} \
+ {$_gthread = 0} \
+ {$_inferior = 1} \
{$_exception = <error: No frame selected>} \
{$_probe_argc = <error: No frame selected>} \
{$_probe_arg0 = <error: No frame selected>} \
@@ -341,7 +341,7 @@ gdb_test "hbreak $bp_location12 thread 999" "Unknown thread 999.*" \
"thread-specific hardware breakpoint on non-existent thread disallowed"
gdb_test "hbreak $bp_location12 thread foo" \
- "Junk after thread keyword.*" \
+ "Bad thread spec.*" \
"thread-specific hardware breakpoint on bogus thread ID disallowed"
# Verify that GDB responds gracefully to a breakpoint command with
@@ -361,7 +361,7 @@ gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
"thread-specific breakpoint on non-existent thread disallowed"
gdb_test "break $bp_location12 thread foo" \
- "Junk after thread keyword.*" \
+ "Bad thread spec.*" \
"thread-specific breakpoint on bogus thread ID disallowed"
# Verify that GDB responds gracefully to a breakpoint command with
@@ -46,7 +46,7 @@ if { ![runto main] } then {
return
}
-gdb_test "watch shared_var thread 0" "Unknown thread 0\." "Watchpoint on invalid thread"
+gdb_test "watch shared_var thread 0" "Bad thread spec.*" "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"]
@@ -54,16 +54,16 @@ with_test_prefix "trailing whitespace" {
# 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" "Bad thread spec '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" "Bad thread spec 'thread'"
+gdb_test "break thread task" "Bad thread spec 'task'"
+gdb_test "break thread if" "Bad thread spec '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\\."
@@ -44,12 +44,16 @@ if { [build_executable ${testfile}.exp ${exec3} "${srcfile3}" {debug}] == -1 } {
clean_restart ${exec1}
+gdb_test {print $_inferior} " = 1"
+
# Add an empty inferior, switch to it, and load a main executable into
# it.
gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2"
gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
gdb_test "file ${binfile2}" ".*" "load ${exec2} file in inferior 2"
+gdb_test {print $_inferior} " = 2" "print \$_inferior after switching"
+
# Add a new inferior and load a main executable into it in one
# command.
gdb_test "add-inferior -exec ${binfile3}" \
@@ -36,4 +36,4 @@ gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
# "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.*"
new file mode 100644
@@ -0,0 +1,52 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2015 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);
+}
+
+void *
+thread_function1 (void *arg)
+{
+ pthread_create (&child_thread[1], NULL, thread_function2, NULL);
+
+ while (1)
+ sleep (1);
+}
+
+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;
+}
new file mode 100644
@@ -0,0 +1,309 @@
+# Copyright 2015 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 1" 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
+ }
+ set cmd "$cmd p 1234"
+
+ verbose -log "r=\"$r\""
+ gdb_test $cmd $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 both "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'd 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'd 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"
+
+ # Same, but show the global ID.
+ gdb_test "info threads -gid" \
+ [multi_line \
+ " 1\.1 +1 +.*" \
+ "\\* 1\.2 +4 +.* thread_function1 .* at .*$srcfile:.*" \
+ " 2\.1 +2 +.*" \
+ " 2\.2 +3 +.* thread_function1 .* at .*$srcfile:.*"]
+
+ # Confirm the convenience variables show the expected numbers.
+ gdb_test "p \$_thread == 2" " = 1"
+ gdb_test "p \$_gthread == 4" " = 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 Python is configured, check that InferiorThread.global_num
+ # returns the expected number.
+ if { ![skip_python_tests] } {
+ 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"
+ gdb_test "python print ('result = %s' % t0.global_num)" " = 6" \
+ "test InferiorThread.global_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, it's 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"
+}
@@ -41,9 +41,13 @@ if ![runto_main] then {
gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
gdb_test "python print (t0)" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>" "verify InferiorThread object"
-gdb_test "python print ('result = %s' % t0.num)" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print ('result = %s' % t0.num)" " = 1" "test InferiorThread.num"
+gdb_test "python print ('result = %s' % t0.global_num)" " = 1" "test InferiorThread.global_num"
gdb_test "python print ('result = %s' % str (t0.ptid))" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
+gdb_py_test_silent_cmd "python i0 = t0.inferior" "test InferiorThread.inferior" 1
+gdb_test "python print ('result = %s' % i0.num)" " = 1" "test Inferior.num"
+
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'" \
@@ -276,9 +276,9 @@ gdb_test_multiple "info threads 3-3" "info threads 3-3" {
# 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"
@@ -66,7 +66,10 @@ clean_restart ${binfile}
gdb_test_no_output "set print sevenbit-strings"
gdb_test_no_output "set width 0"
+# As this test only runs a single inferior, $_thread and $_gthread
+# should match throughout.
gdb_test {print $_thread} ".* = 0" "thread var when not running"
+gdb_test {print $_gthread} ".* = 0" "gthread var when not running"
runto_main
@@ -82,6 +85,7 @@ if {[llength $threads] == 0} {
}
gdb_test {print $_thread} ".* = [lindex $threads 0]" "thread var in main"
+gdb_test {print $_gthread} ".* = [lindex $threads 0]" "gthread var in main"
gdb_test_multiple "break $line thread [lindex $threads 0]" \
"breakpoint $line main thread" {
@@ -120,6 +124,7 @@ if { $this_breakpoint != -1 } {
if { $this_thread != -1 } {
gdb_test {print $_thread} ".* = $this_thread" "thread var at break"
+ gdb_test {print $_gthread} ".* = $this_thread" "gthread var at break"
} else {
untested "thread var at break"
}
@@ -182,7 +182,7 @@ clear_thread_inferior_resources (struct thread_info *tp)
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);
@@ -230,12 +230,18 @@ init_thread_list (void)
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;
@@ -260,6 +266,8 @@ struct thread_info *
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)
@@ -277,7 +285,7 @@ add_thread_silent (ptid_t ptid)
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;
@@ -301,7 +309,7 @@ add_thread_silent (ptid_t ptid)
delete_thread (ptid);
}
- tp = new_thread (ptid);
+ tp = new_thread (inf, ptid);
observer_notify_new_thread (tp);
return tp;
@@ -481,12 +489,24 @@ delete_thread_silent (ptid_t ptid)
}
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->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->num == num)
+ if (tp->inf == inf && tp->per_inf_num == thr_num)
return tp;
return NULL;
@@ -548,38 +568,38 @@ thread_count (void)
}
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
@@ -604,7 +624,7 @@ first_thread_of_process (int pid)
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;
@@ -688,10 +708,10 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
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);
@@ -1105,25 +1125,62 @@ pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread)
&& 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;
@@ -1142,13 +1199,8 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
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;
@@ -1165,34 +1217,39 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
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)
+ /* TODO: Re-indent before push. Kept as is to ease review. */
+ ALL_INFERIORS (inf)
+ {
+
+ ALL_THREADS (tp)
{
struct cleanup *chain2;
int core;
- if (!number_is_in_list (requested_threads, tp->num))
+ if (inf != tp->inf)
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);
@@ -1213,7 +1270,11 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
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
@@ -1282,6 +1343,7 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
do_cleanups (chain2);
}
+ }
/* Restores the current thread and the frame selected before
the "info threads" command. */
@@ -1289,27 +1351,35 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
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);
- if (current_thread != -1 && is_exited (current_ptid))
+ gdb_assert (num != 0);
+ ui_out_field_int (uiout, "current-thread-id", num);
+ }
+
+ 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
@@ -1318,7 +1388,16 @@ No selected thread. See `help thread'.\n");
static void
info_threads_command (char *arg, int from_tty)
{
- print_thread_info (current_uiout, arg, -1);
+ int show_global_ids = 0;
+
+ if (arg != NULL
+ && check_for_argument (&arg, "-gid", sizeof ("-gid") - 1))
+ {
+ arg = skip_spaces (arg);
+ show_global_ids = 1;
+ }
+
+ print_thread_info_1 (current_uiout, arg, 0, -1, show_global_ids);
}
/* See gdbthread.h. */
@@ -1593,18 +1672,24 @@ make_cleanup_restore_current_thread (void)
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));
}
@@ -1678,8 +1763,8 @@ thread_apply_all_command (char *cmd, int from_tty)
if (thread_alive (tp_array[k]))
{
switch_to_thread (tp_array[k]->ptid);
- printf_filtered (_("\nThread %d (%s):\n"),
- tp_array[k]->num,
+ printf_filtered (_("\nThread %s (%s):\n"),
+ print_thread_id (tp_array[k]),
target_pid_to_str (inferior_ptid));
execute_command (cmd, from_tty);
@@ -1691,13 +1776,40 @@ thread_apply_all_command (char *cmd, int from_tty)
do_cleanups (old_chain);
}
+/* 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. */
+
+static 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 ();
+
+ 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;
+}
+
+
+/* Implementation of the "thread apply" command. */
+
static void
thread_apply_command (char *tidlist, int from_tty)
{
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"));
@@ -1712,33 +1824,44 @@ thread_apply_command (char *tidlist, int from_tty)
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);
@@ -1757,13 +1880,15 @@ thread_command (char *tidstr, int from_tty)
if (target_has_stack)
{
+ struct thread_info *tp = inferior_thread ();
+
if (is_exited (inferior_ptid))
- printf_filtered (_("[Current thread is %d (%s) (exited)]\n"),
- pid_to_thread_id (inferior_ptid),
+ printf_filtered (_("[Current thread is %s (%s) (exited)]\n"),
+ print_thread_id (tp),
target_pid_to_str (inferior_ptid));
else
- printf_filtered (_("[Current thread is %d (%s)]\n"),
- pid_to_thread_id (inferior_ptid),
+ printf_filtered (_("[Current thread is %s (%s)]\n"),
+ print_thread_id (tp),
target_pid_to_str (inferior_ptid));
}
else
@@ -1812,32 +1937,32 @@ thread_find_command (char *arg, int from_tty)
{
if (tp->name != NULL && re_exec (tp->name))
{
- printf_filtered (_("Thread %d has name '%s'\n"),
- tp->num, tp->name);
+ printf_filtered (_("Thread %s has name '%s'\n"),
+ print_thread_id (tp), tp->name);
match++;
}
tmp = target_thread_name (tp);
if (tmp != NULL && re_exec (tmp))
{
- printf_filtered (_("Thread %d has target name '%s'\n"),
- tp->num, tmp);
+ printf_filtered (_("Thread %s has target name '%s'\n"),
+ print_thread_id (tp), tmp);
match++;
}
tmp = target_pid_to_str (tp->ptid);
if (tmp != NULL && re_exec (tmp))
{
- printf_filtered (_("Thread %d has target id '%s'\n"),
- tp->num, tmp);
+ printf_filtered (_("Thread %s has target id '%s'\n"),
+ print_thread_id (tp), tmp);
match++;
}
tmp = target_extra_thread_info (tp);
if (tmp != NULL && re_exec (tmp))
{
- printf_filtered (_("Thread %d has extra info '%s'\n"),
- tp->num, tmp);
+ printf_filtered (_("Thread %s has extra info '%s'\n"),
+ print_thread_id (tp), tmp);
match++;
}
}
@@ -1856,31 +1981,266 @@ show_print_thread_events (struct ui_file *file, int from_tty,
value);
}
-static int
-do_captured_thread_select (struct ui_out *uiout, void *tidstr)
+/* See gdbthread.h. */
+
+struct thread_info *
+parse_thread_id (const char *tidstr, const char **end)
{
- int num;
+ const char *number = tidstr;
+ const char *dot, *p1;
struct thread_info *tp;
+ struct inferior *inf;
+ int thr_num;
+ int explicit_inf_id = 0;
- num = value_as_long (parse_and_eval ((const char *) tidstr));
+ dot = strchr (number, '.');
- tp = find_thread_id (num);
+ if (dot != NULL)
+ {
+ /* Parse number to the left of the dot. */
+ int inf_num;
- if (!tp)
- error (_("Thread ID %d not known."), num);
+ p1 = number;
+ inf_num = get_number_trailer (&p1, '.');
+ if (inf_num == 0)
+ error (_("Bad thread spec '%s'"), 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)
+ error (_("Bad thread spec '%s'"), 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 gdbthread.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 gdbthread.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 gdbthread.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 gdbthread.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 gdbthread.h. */
+
+int
+tid_range_parser_qualified (struct tid_range_parser *parser)
+{
+ return parser->qualified;
+}
+
+/* See gdbthread.h. */
+
+void
+tid_range_parser_get_tid (struct tid_range_parser *parser, int *inf_num,
+ int *thr_num)
+{
+ 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)
+ error (_("Invalid thread ID: %s"), parser->string);
+
+ parser->qualified = 1;
+ p = dot + 1;
+
+ if (isspace (*p))
+ error (_("Invalid thread ID: %s"), 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_num = get_number_or_range (&parser->range_parser);
+ if (*thr_num == 0)
+ error (_("Invalid thread ID: %s"), 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;
+ }
+}
+
+/* See gdbthread.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;
+
+ tid_range_parser_get_tid (&parser, &tmp_inf, &tmp_thr);
+ if (tmp_inf == 0 || tmp_thr == 0)
+ error (_("Invalid thread ID: %s"), parser.string);
+ if (tmp_inf == inf_num && tmp_thr == thr_num)
+ return 1;
+
+ /* If we parsed a range, short-circuit iterating over each
+ number in the range. */
+ if (parser.state == TID_RANGE_STATE_THREAD_RANGE)
+ {
+ if (parser.inf_num == inf_num
+ && number_is_in_remaining_range (&parser.range_parser, thr_num))
+ return 1;
+
+ tid_range_parser_skip (&parser);
+ }
+ }
+ return 0;
+}
+
+static int
+do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
+{
+ const char *tidstr = tidstr_v;
+ struct thread_info *tp;
+
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ int num = value_as_long (parse_and_eval (tidstr));
+
+ 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_int (uiout, "new-thread-id", pid_to_thread_id (inferior_ptid));
- 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_text (uiout, print_thread_id (inferior_thread ()));
+ 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. */
@@ -1934,17 +2294,45 @@ update_thread_list (void)
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 id. Return a value of
+ 0 if no thread is selected. If GLOBAL is true, return the thread's
+ global number. Otherwise return the per-inferior number. */
static struct value *
-thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
- void *ignore)
+thread_num_make_value_helper (struct gdbarch *gdbarch, int global)
{
struct thread_info *tp = find_thread_ptid (inferior_ptid);
+ int int_val;
- return value_from_longest (builtin_type (gdbarch)->builtin_int,
- (tp ? tp->num : 0));
+ if (tp == NULL)
+ int_val = 0;
+ else if (global)
+ int_val = tp->global_num;
+ else
+ int_val = tp->per_inf_num;
+
+ return value_from_longest (builtin_type (gdbarch)->builtin_int, int_val);
+}
+
+/* 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_per_inf_num_make_value (struct gdbarch *gdbarch, struct internalvar *var,
+ void *ignore)
+{
+ return thread_num_make_value_helper (gdbarch, 0);
+}
+
+/* Return a new value for the selected thread's global id. Return a
+ value of 0 if no thread is selected, or no threads exist. */
+
+static struct value *
+global_thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
+ void *ignore)
+{
+ return thread_num_make_value_helper (gdbarch, 1);
}
/* Commands with a prefix of `thread'. */
@@ -1954,7 +2342,16 @@ struct cmd_list_element *thread_cmd_list = NULL;
static const struct internalvar_funcs thread_funcs =
{
- thread_id_make_value,
+ thread_id_per_inf_num_make_value,
+ NULL,
+ NULL
+};
+
+/* Implementation of `gthread' variable. */
+
+static const struct internalvar_funcs gthread_funcs =
+{
+ global_thread_id_make_value,
NULL,
NULL
};
@@ -1966,9 +2363,10 @@ _initialize_thread (void)
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."));
+Usage: info threads [-gid] [ID]...\n\
+-gid: Show global thread IDs.\n\
+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\
@@ -2011,6 +2409,7 @@ Show printing of thread events (such as thread start and exit)."), NULL,
&setprintlist, &showprintlist);
create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
+ create_internalvar_type_lazy ("_gthread", >hread_funcs, NULL);
observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
}
@@ -78,7 +78,7 @@ struct varobj_root
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
@@ -380,7 +380,7 @@ varobj_create (char *objname,
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);
}
@@ -2363,8 +2363,9 @@ value_of_root_1 (struct varobj **var_handle)
}
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);