diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 960a560d74..c10644bafd 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,102 @@ +2016-01-13 Pedro Alves + + * NEWS: Mention that thread IDs are now per inferior and global + thread IDs. + * Makefile.in (SFILES): Add tid-parse.c. + (COMMON_OBS): Add tid-parse.o. + (HFILES_NO_SRCDIR): Add tid-parse.h. + * ada-tasks.c: Adjust to use ptid_to_global_thread_id. + * breakpoint.c (insert_breakpoint_locations) + (remove_threaded_breakpoints, bpstat_check_breakpoint_conditions) + (print_one_breakpoint_location, set_longjmp_breakpoint) + (check_longjmp_breakpoint_for_call_dummy) + (set_momentary_breakpoint): Adjust to use global IDs. + (find_condition_and_thread, watch_command_1): Use parse_thread_id. + (until_break_command, longjmp_bkpt_dtor) + (breakpoint_re_set_thread, insert_single_step_breakpoint): Adjust + to use global IDs. + * dummy-frame.c (pop_dummy_frame_bpt): Adjust to use + ptid_to_global_thread_id. + * elfread.c (elf_gnu_ifunc_resolver_stop): Likewise. + * gdbthread.h (struct thread_info): Rename field 'num' to + 'global_num. Add new fields 'per_inf_num' and 'inf'. + (thread_id_to_pid): Rename thread_id_to_pid to + global_thread_id_to_ptid. + (pid_to_thread_id): Rename to ... + (ptid_to_global_thread_id): ... this. + (valid_thread_id): Rename to ... + (valid_global_thread_id): ... this. + (find_thread_id): Rename to ... + (find_thread_global_id): ... this. + (ALL_THREADS, ALL_THREADS_BY_INFERIOR): Declare. + (print_thread_info): Add comment. + * tid-parse.h: New file. + * tid-parse.c: New file. + * infcmd.c (step_command_fsm_prepare) + (step_command_fsm_should_stop): Adjust to use the global thread + ID. + (until_next_command, until_next_command) + (finish_command_fsm_should_stop): Adjust to use the global thread + ID. + (attach_post_wait): Adjust to check the inferior number too. + * inferior.h (struct inferior) : New field. + * infrun.c (handle_signal_stop) + (insert_exception_resume_breakpoint) + (insert_exception_resume_from_probe): Adjust to use the global + thread ID. + * record-btrace.c (record_btrace_open): Use global thread IDs. + * remote.c (process_initial_stop_replies): Also consider the + inferior number. + * target.c (target_pre_inferior): Clear the inferior's highest + thread num. + * thread.c (clear_thread_inferior_resources): Adjust to use the + global thread ID. + (new_thread): New inferior parameter. Adjust to use it. Set both + the thread's global ID and the thread's per-inferior ID. + (add_thread_silent): Adjust. + (find_thread_global_id): New. + (find_thread_id): Make static. Adjust to rename. + (valid_thread_id): Rename to ... + (valid_global_thread_id): ... this. + (pid_to_thread_id): Rename to ... + (ptid_to_global_thread_id): ... this. + (thread_id_to_pid): Rename to ... + (global_thread_id_to_ptid): ... this. Adjust. + (first_thread_of_process): Adjust. + (do_captured_list_thread_ids): Adjust to use global thread IDs. + (should_print_thread): New function. + (print_thread_info): Rename to ... + (print_thread_info_1): ... this, and add new show_global_ids + parameter. Handle it. Iterate over inferiors. + (print_thread_info): Reimplement as wrapper around + print_thread_info_1. + (show_inferior_qualified_tids): New function. + (print_thread_id): Use it. + (tp_array_compar): Compare inferior numbers too. + (thread_apply_command): Use tid_range_parser. + (do_captured_thread_select): Use parse_thread_id. + (thread_id_make_value): Adjust. + (_initialize_thread): Adjust "info threads" help string. + * varobj.c (struct varobj_root): Update comment. + (varobj_create): Adjust to use global thread IDs. + (value_of_root_1): Adjust to use global_thread_id_to_ptid. + * windows-tdep.c (display_tib): No longer accept an argument. + * cli/cli-utils.c (get_number_trailer): Make extern. + * cli/cli-utils.h (get_number_trailer): Declare. + (get_number_const): Adjust documentation. + * mi/mi-cmd-var.c (mi_cmd_var_update_iter): Adjust to use global + thread IDs. + * mi/mi-interp.c (mi_new_thread, mi_thread_exit) + (mi_on_normal_stop, mi_output_running_pid, mi_on_resume): + * mi/mi-main.c (mi_execute_command, mi_cmd_execute): Likewise. + * guile/scm-breakpoint.c (gdbscm_set_breakpoint_thread_x): + Likewise. + * python/py-breakpoint.c (bppy_set_thread): Likewise. + * python/py-finishbreakpoint.c (bpfinishpy_init): Likewise. + * python/py-infthread.c (thpy_get_num): Add comment and return the + per-inferior thread ID. + (thread_object_getset): Update comment of "num". + 2016-01-13 Pedro Alves * breakpoint.c (remove_threaded_breakpoints) diff --git a/gdb/Makefile.in b/gdb/Makefile.in index d1e0b00367..895ece6866 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -876,7 +876,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ stabsread.c stack.c probe.c stap-probe.c std-regs.c \ symfile.c symfile-debug.c symfile-mem.c symmisc.c symtab.c \ target.c target-dcache.c target-descriptions.c target-memory.c \ - thread.c top.c tracepoint.c \ + tid-parse.c thread.c top.c tracepoint.c \ trad-frame.c \ tramp-frame.c \ typeprint.c \ @@ -987,7 +987,8 @@ common/common-debug.h common/cleanups.h common/gdb_setjmp.h \ common/common-exceptions.h target/target.h common/symbol.h \ common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \ common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \ -nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h +nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \ +tid-parse.h # Header files that already have srcdir in them, or which are in objdir. @@ -1029,7 +1030,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ linespec.o dictionary.o namespace.o \ location.o infcall.o \ infcmd.o infrun.o \ - expprint.o environ.o stack.o thread.o thread-fsm.o \ + expprint.o environ.o stack.o tid-parse.o thread.o thread-fsm.o \ exceptions.o \ extension.o \ filesystem.o \ diff --git a/gdb/NEWS b/gdb/NEWS index ed38754782..4e0ffd3a71 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,37 @@ *** Changes since GDB 7.10 +* Per-inferior thread numbers + + Thread numbers are now per inferior instead of global. If you're + debugging multiple inferiors, GDB displays thread IDs using a + qualified INF_NUM.THR_NUM form. For example: + + (gdb) info threads + Id Target Id Frame + 1.1 Thread 0x7ffff7fc2740 (LWP 8155) (running) + 1.2 Thread 0x7ffff7fc1700 (LWP 8168) (running) + * 2.1 Thread 0x7ffff7fc2740 (LWP 8157) (running) + 2.2 Thread 0x7ffff7fc1700 (LWP 8190) (running) + + As consequence, thread numbers as visible in the $_thread + convenience variable and in Python's InferiorThread.num attribute + are no longer unique between inferiors. + + GDB now maintains a second thread ID per thread, referred to as the + global thread ID, which is the new equivalent of thread numbers in + previous releases. + + For backwards compatibility, MI's thread IDs always refer to global + IDs. + +* Commands that accept thread IDs now accept the qualified + INF_NUM.THR_NUM form as well. For example: + + (gdb) thread 2.1 + [Switching to thread 2.1 (Thread 0x7ffff7fc2740 (LWP 8157))] (running) + (gdb) + * The new convenience variable $_inferior holds the number of the current inferior. @@ -177,6 +208,9 @@ show remote catch-syscall-packet * Python Scripting + ** The "num" attribute of gdb.InferiorThread objects now refers to + the thread's per-inferior number. See "Per-inferior thread + numbers" above. ** gdb.InferiorThread objects have a new attribute "inferior", which is the Inferior object the thread belongs to. diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c index 062c3722ad..c067ae6672 100644 --- a/gdb/ada-tasks.c +++ b/gdb/ada-tasks.c @@ -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); diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index b2a3c47772..72da4ef7aa 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -69,6 +69,7 @@ #include "format.h" #include "location.h" #include "thread-fsm.h" +#include "tid-parse.h" /* readline include files */ #include "readline/readline.h" @@ -3144,7 +3145,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,7 +3245,7 @@ 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; @@ -5447,7 +5448,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))) { @@ -6514,11 +6515,16 @@ print_one_breakpoint_location (struct breakpoint *b, if (!part_of_multiple && b->thread != -1) { - struct thread_info *thr = find_thread_id (b->thread); - /* FIXME should make an annotation for this. */ ui_out_text (uiout, "\tstop only in thread "); - ui_out_field_string (uiout, "thread", print_thread_id (thr)); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_int (uiout, "thread", b->thread); + else + { + struct thread_info *thr = find_thread_global_id (b->thread); + + ui_out_field_string (uiout, "thread", print_thread_id (thr)); + } ui_out_text (uiout, "\n"); } @@ -7574,7 +7580,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 @@ -7643,7 +7649,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. */ @@ -7673,7 +7679,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; @@ -8893,7 +8899,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); @@ -9567,14 +9573,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. @@ -9623,14 +9621,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) @@ -11135,25 +11133,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); + invalid_thread_id_error (value_start); - /* Check if the thread actually exists. */ - if (!valid_thread_id (thread)) - invalid_thread_id_error (thread); + thread = thr->global_num; } else if (toklen == 4 && startswith (tok, "mask")) { @@ -11694,7 +11690,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); @@ -11744,7 +11740,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); @@ -13366,7 +13363,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; @@ -14547,7 +14544,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 @@ -15058,7 +15055,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); diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c index 08b099fbec..a68b67d5ff 100644 --- a/gdb/cli/cli-utils.c +++ b/gdb/cli/cli-utils.c @@ -23,16 +23,9 @@ #include -/* *PP is a string denoting a number. Get the number of the. Advance - *PP after the string and any trailing whitespace. +/* See documentation in cli-utils.h. */ - 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. */ - -static int +int get_number_trailer (const char **pp, int trailer) { int retval = 0; /* default */ diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h index ace46b4b19..ebf11f28b9 100644 --- a/gdb/cli/cli-utils.h +++ b/gdb/cli/cli-utils.h @@ -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 **); diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index be16225555..46207f68df 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,22 @@ +2016-01-07 Pedro Alves + + * gdb.texinfo (Threads): Document per-inferior thread IDs, + qualified thread IDs, global thread IDs and thread ID lists. + (Set Watchpoints, Thread-Specific Breakpoints): Adjust to refer to + thread IDs. + (Convenience Vars): Document the $_thread convenience variable. + (Ada Tasks): Adjust to refer to thread IDs. + (GDB/MI Async Records, GDB/MI Thread Commands, GDB/MI Ada Tasking + Commands, GDB/MI Variable Objects): Update to mention global + thread IDs. + * guile.texi (Breakpoints In Guile) + : Mention + global thread IDs instead of thread IDs. + * python.texi (Threads In Python): Adjust documentation of + InferiorThread.num. + (Breakpoint.thread): Mention global thread IDs instead of thread + IDs. + 2016-01-13 Pedro Alves * python.texi (Threads In Python): Document diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index d4452a96db..786556e854 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2838,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-list}] [@var{all}] @var{args}}, a command to apply a command to a list of threads @item thread-specific breakpoints @item @samp{set print thread-events}, which controls printing of @@ -2886,26 +2886,77 @@ 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. +@vindex $_thread@r{, convenience variable} +The debugger convenience variable @samp{$_thread} contains the +per-inferior thread number of the current thread. You may find this +useful in writing breakpoint conditional expressions, command scripts, +and so forth. @xref{Convenience Vars,, Convenience Variables}, for +general information on convenience variables. + @table @code @kindex info threads -@item info threads @r{[}@var{id}@dots{}@r{]} -Display a summary of all threads currently in your program. Optional -argument @var{id}@dots{} is one or more thread ids separated by spaces, and -means to print information only about the specified thread or threads. +@item info threads @r{[}@var{thread-id-list}@r{]} + +Display information about one or more threads. With no arguments +displays information about all threads. You can specify the list of +threads that you want to display using the thread ID list syntax +(@pxref{thread ID lists}). + @value{GDBN} displays for each thread (in this order): @enumerate @item -the thread number assigned by @value{GDBN} +the per-inferior thread number assigned by @value{GDBN} @item the target system's thread identifier (@var{systag}) @@ -2936,6 +2987,19 @@ 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: + +@smallexample +(@value{GDBP}) info threads + Id Target Id Frame + 1.1 process 35 thread 13 main (argc=1, argv=0x7ffffff8) + 1.2 process 35 thread 23 0x34e5 in sigpause () + 1.3 process 35 thread 27 0x34e5 in sigpause () +* 2.1 process 65 thread 1 main (argc=1, argv=0x7ffffff8) +@end smallexample + On Solaris, you can display more information about user threads with a Solaris-specific command: @@ -2947,13 +3011,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 @@ -2967,23 +3033,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}}. @@ -3983,7 +4040,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 @@ -3993,9 +4050,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. @@ -4027,12 +4084,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. @@ -6051,25 +6108,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 @@ -10392,6 +10449,9 @@ This variable contains the address of the thread information block. The number of the current inferior. @xref{Inferiors and Programs, ,Debugging Multiple Inferiors and Programs}. +@item $_thread +The thread number of the current thread. @xref{thread numbers}. + @end table @node Convenience Funs @@ -16134,7 +16194,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. @@ -25483,8 +25543,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 @@ -26037,15 +26097,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 @@ -26099,8 +26160,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 --- for example by hitting a breakpoint. Depending on whether all-stop +The @var{id} field identifies the global thread ID of the thread +that directly caused the stop -- for example by hitting a breakpoint. +Depending on whether all-stop mode is in effect (@pxref{All-Stop Mode}), @value{GDBN} may either stop all threads, or only the thread that directly triggered the stop. If all threads are stopped, the @var{stopped} field will have the @@ -26136,7 +26198,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}" @@ -26397,7 +26459,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 @@ -26870,7 +26932,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 @@ -26960,7 +27023,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 @@ -27610,10 +27674,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 @@ -27630,7 +27695,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. @@ -27696,8 +27761,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. @@ -27723,11 +27789,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. @@ -27796,7 +27863,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 @@ -29012,7 +29080,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 @@ -29193,8 +29261,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. diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi index fa1ad53d95..840defcccb 100644 --- a/gdb/doc/guile.texi +++ b/gdb/doc/guile.texi @@ -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 diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 855da44fed..f9f9e5b907 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2995,7 +2995,7 @@ 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.ptid @@ -4643,9 +4643,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 diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c index 6e9dc2e3bd..606fb852cc 100644 --- a/gdb/dummy-frame.c +++ b/gdb/dummy-frame.c @@ -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) diff --git a/gdb/elfread.c b/gdb/elfread.c index 1eb341b563..55674f355a 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -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); diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 972517c812..5ff38bf084 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -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,45 @@ 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 and + Python/Scheme. */ + int global_num; + + /* The per-inferior thread number. This is unique in the inferior + the thread belongs to, but not unique between inferiors. This is + what the $_thread convenience variable is bound to. */ + int per_inf_num; + + /* The inferior this thread belongs to. */ + struct inferior *inf; /* The name of the thread, as specified by the user. This is NULL if the thread does not have a user-given name. */ @@ -353,31 +392,40 @@ 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. The result is stored - in a circular static buffer, NUMCELLS deep. */ +/* Returns whether to show inferior-qualified thread IDs, or plain + thread numbers. Inferior-qualified IDs are shown whenever we have + multiple inferiors, or the only inferior left has number > 1. */ +extern int show_inferior_qualified_tids (void); + +/* Return a string version of THR's thread ID. If there are multiple + inferiors, then this prints the inferior-qualifier form, otherwise + it only prints the thread number. The result is stored in a + circular static buffer, NUMCELLS deep. */ const char *print_thread_id (struct thread_info *thr); /* Boolean test for an already-known pid (which may be overloaded with extra thread information). */ extern int in_thread_list (ptid_t ptid); -/* Boolean test for an already-known thread id (GDB's homegrown id, - not the system's). */ -extern int valid_thread_id (int thread); +/* Boolean test for an already-known global thread id (GDB's homegrown + global id, not the system's). */ +extern int valid_global_thread_id (int global_id); /* Search function to lookup a thread by 'pid'. */ extern struct thread_info *find_thread_ptid (ptid_t ptid); -/* Find thread by GDB user-visible thread number. */ -struct thread_info *find_thread_id (int num); +/* Find thread by GDB global thread ID. */ +struct thread_info *find_thread_global_id (int global_id); /* Finds the first thread of the inferior given by PID. If PID is -1, returns the first thread in the list. */ @@ -399,6 +447,16 @@ 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 over all threads, sorted by inferior. */ +#define ALL_THREADS_BY_INFERIOR(inf, tp) \ + ALL_INFERIORS (inf) \ + ALL_THREADS (tp) \ + if (inf == tp->inf) + /* Traverse all threads, except those that have THREAD_EXITED state. */ @@ -504,7 +562,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); diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c index 46f9918715..716fe4cbe8 100644 --- a/gdb/guile/scm-breakpoint.c +++ b/gdb/guile/scm-breakpoint.c @@ -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: thread-id" }, + Arguments: global-thread-id" }, { "breakpoint-task", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_task), "\ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index d46e8a7c7b..df13896a11 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -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) { @@ -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; } } diff --git a/gdb/inferior.h b/gdb/inferior.h index 67ac9e8dea..571d26a1f8 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -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; diff --git a/gdb/infrun.c b/gdb/infrun.c index 48b0576234..64c729e927 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -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; } diff --git a/gdb/mi/mi-cmd-var.c b/gdb/mi/mi-cmd-var.c index e95ded795a..04f326c718 100644 --- a/gdb/mi/mi-cmd-var.c +++ b/gdb/mi/mi-cmd-var.c @@ -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); diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 91ca8b8625..7f42367820 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -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) diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index ab8eae379c..e25eedf987 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -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); diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index 640ec1bc01..85b17d5aa5 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -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.")); diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c index 38cf2932c5..bff6dba6cb 100644 --- a/gdb/python/py-finishbreakpoint.c +++ b/gdb/python/py-finishbreakpoint.c @@ -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, diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c index 5075071f00..a9dd5cb3a4 100644 --- a/gdb/python/py-infthread.c +++ b/gdb/python/py-infthread.c @@ -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,7 @@ thpy_get_num (PyObject *self, void *closure) THPY_REQUIRE_VALID (thread_obj); - return PyLong_FromLong (thread_obj->thread->num); + return PyLong_FromLong (thread_obj->thread->per_inf_num); } /* Getter for InferiorThread.ptid -> (pid, lwp, tid). @@ -294,7 +296,8 @@ 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 }, { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.", NULL }, { "inferior", thpy_get_inferior, NULL, diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index 9fee3df862..77b51801e6 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -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); diff --git a/gdb/remote.c b/gdb/remote.c index e825d27d84..4a42da8ec2 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -3969,7 +3969,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) diff --git a/gdb/target.c b/gdb/target.c index e88d60c506..ac66a3a899 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -2165,6 +2165,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 (); } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ec2a0674b3..d9bafe133d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2016-01-07 Pedro Alves + + * gdb.base/break.exp: Adjust to output changes. + * gdb.base/hbreak2.exp: Likewise. + * gdb.base/sepdebug.exp: Likewise. + * gdb.base/watch_thread_num.exp: Likewise. + * gdb.linespec/keywords.exp: Likewise. + * gdb.multi/info-threads.exp: Likewise. + * gdb.threads/thread-find.exp: Likewise. + * gdb.multi/tids.c: New file. + * gdb.multi/tids.exp: New file. + 2016-01-13 Pedro Alves * gdb.python/py-infthread.exp: Test InferiorThread.inferior. diff --git a/gdb/testsuite/gdb.base/break.exp b/gdb/testsuite/gdb.base/break.exp index 3ec74bff8f..2b4587e9e6 100644 --- a/gdb/testsuite/gdb.base/break.exp +++ b/gdb/testsuite/gdb.base/break.exp @@ -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.*" \ + "Invalid thread ID: foo" \ "thread-specific breakpoint on bogus thread ID disallowed" # Verify that GDB responds gracefully to a breakpoint command with diff --git a/gdb/testsuite/gdb.base/hbreak2.exp b/gdb/testsuite/gdb.base/hbreak2.exp index dbf55f32f6..3a303ec99b 100644 --- a/gdb/testsuite/gdb.base/hbreak2.exp +++ b/gdb/testsuite/gdb.base/hbreak2.exp @@ -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.*" \ + "Invalid thread ID: foo" \ "thread-specific hardware breakpoint on bogus thread ID disallowed" # Verify that GDB responds gracefully to a breakpoint command with diff --git a/gdb/testsuite/gdb.base/sepdebug.exp b/gdb/testsuite/gdb.base/sepdebug.exp index ad872695a4..3ad3669e46 100644 --- a/gdb/testsuite/gdb.base/sepdebug.exp +++ b/gdb/testsuite/gdb.base/sepdebug.exp @@ -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.*" \ + "Invalid thread ID: foo" \ "thread-specific breakpoint on bogus thread ID disallowed" # Verify that GDB responds gracefully to a breakpoint command with diff --git a/gdb/testsuite/gdb.base/watch_thread_num.exp b/gdb/testsuite/gdb.base/watch_thread_num.exp index b3ce27399b..eff9fbde28 100644 --- a/gdb/testsuite/gdb.base/watch_thread_num.exp +++ b/gdb/testsuite/gdb.base/watch_thread_num.exp @@ -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" "Invalid thread ID: 0" "Watchpoint on invalid thread" gdb_test "watch shared_var thread" "A syntax error in expression, near `thread'\." "Invalid watch syntax" set bpexitline [gdb_get_line_number "all threads started"] diff --git a/gdb/testsuite/gdb.linespec/keywords.exp b/gdb/testsuite/gdb.linespec/keywords.exp index 03f662ca96..11e2c4ce28 100644 --- a/gdb/testsuite/gdb.linespec/keywords.exp +++ b/gdb/testsuite/gdb.linespec/keywords.exp @@ -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" "Invalid thread ID: foo" gdb_test "break task 123" "Unknown task 123\\." gdb_test "break task foo" "Junk after task keyword\\." gdb_breakpoint "thread if 0" "message" # These are also NULL locations, but using a subsequent keyword # as the "junk". -gdb_test "break thread thread" "Junk after thread keyword\\." -gdb_test "break thread task" "Junk after thread keyword\\." -gdb_test "break thread if" "Junk after thread keyword\\." +gdb_test "break thread thread" "Invalid thread ID: thread" +gdb_test "break thread task" "Invalid thread ID: task" +gdb_test "break thread if" "Invalid thread ID: if" gdb_test "break task task" "Junk after task keyword\\." gdb_test "break task thread" "Junk after task keyword\\." gdb_test "break task if" "Junk after task keyword\\." diff --git a/gdb/testsuite/gdb.multi/info-threads.exp b/gdb/testsuite/gdb.multi/info-threads.exp index 7ae60d79e9..f8d9c6d48b 100644 --- a/gdb/testsuite/gdb.multi/info-threads.exp +++ b/gdb/testsuite/gdb.multi/info-threads.exp @@ -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.*" diff --git a/gdb/testsuite/gdb.multi/tids.c b/gdb/testsuite/gdb.multi/tids.c new file mode 100644 index 0000000000..8a53d43d49 --- /dev/null +++ b/gdb/testsuite/gdb.multi/tids.c @@ -0,0 +1,56 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2015-2016 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include + +pthread_t child_thread[2]; + +void * +thread_function2 (void *arg) +{ + while (1) + sleep (1); + + return NULL; +} + +void * +thread_function1 (void *arg) +{ + pthread_create (&child_thread[1], NULL, thread_function2, NULL); + + while (1) + sleep (1); + + return NULL; +} + +int +main (void) +{ + int i; + + alarm (300); + + pthread_create (&child_thread[0], NULL, thread_function1, NULL); + + for (i = 0; i < 2; i++) + pthread_join (child_thread[i], NULL); + + return 0; +} diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp new file mode 100644 index 0000000000..70a1d81486 --- /dev/null +++ b/gdb/testsuite/gdb.multi/tids.exp @@ -0,0 +1,296 @@ +# Copyright 2015-2016 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test thread ID parsing and display. + +load_lib gdb-python.exp + +standard_testfile + +# Multiple inferiors are needed, therefore both native and extended +# gdbserver modes are supported. Only non-extended gdbserver is not +# supported. +if [target_info exists use_gdb_stub] { + untested ${testfile}.exp + return +} + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {pthreads debug}] } { + return -1 +} + +clean_restart ${testfile} + +if { ![runto_main] } then { + return -1 +} + +# Issue "thread apply TID_LIST p 1234" and expect EXP_TID_LIST (a list +# of thread ids) to be displayed. +proc thread_apply {tid_list exp_tid_list {message ""}} { + global decimal + set any "\[^\r\n\]*" + set expected [string_to_regexp $exp_tid_list] + + set r "" + foreach tid $expected { + append r "\[\r\n\]+" + append r "Thread $tid $any:\r\n" + append r "\\$$decimal = 1234" + } + + set cmd "thread apply $tid_list" + if {$message == ""} { + set message $cmd + } + gdb_test "$cmd p 1234" $r $message +} + +# Issue "info threads TID_LIST" and expect EXP_TID_LIST (a list of +# thread ids) to be displayed. +proc info_threads {tid_list exp_tid_list {message ""}} { + set any "\[^\r\n\]*" + set expected [string_to_regexp $exp_tid_list] + set r [join $expected " ${any}\r\n${any} "] + set r "${any} $r ${any}" + set cmd "info threads $tid_list" + if {$message == ""} { + set message $cmd + } + gdb_test $cmd $r $message +} + +# Issue "info threads TID_LIST" and expect INFO_THR output. Then +# issue "thread apply TID_LIST" and expect THR_APPLY output. If +# THR_APPLY is omitted, INFO_THR is expected instead. +proc thr_apply_info_thr {tid_list info_thr {thr_apply ""}} { + if {$thr_apply == ""} { + set thr_apply $info_thr + } + + info_threads $tid_list $info_thr + thread_apply $tid_list $thr_apply +} + +# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and +# expect both commands to error out with EXP_ERROR. +proc thr_apply_info_thr_error {tid_list exp_error} { + gdb_test "info threads $tid_list" \ + $exp_error + + gdb_test "thread apply $tid_list p 1234" \ + $exp_error \ + "thread apply $tid_list" +} + +# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and +# expect the command to error out with "Invalid thread ID: $EXPECTED". +# EXPECTED is a literal string, not a regexp. +proc thr_apply_info_thr_invalid {tid_list expected} { + set expected [string_to_regexp $expected] + gdb_test "info threads $tid_list" \ + "Invalid thread ID: $expected" + + gdb_test "thread apply $tid_list p 1234" \ + "Invalid thread ID: $expected p 1234" \ + "thread apply $tid_list" +} + +# "info threads" while there's only inferior 1 should show +# single-number thread IDs. +with_test_prefix "single inferior" { + info_threads "" "1" + + gdb_test "thread" "Current thread is 1 .*" +} + +# "info threads" while there are multiple inferiors should show +# qualified thread IDs. +with_test_prefix "two inferiors" { + # Add another inferior. + gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2" + + # Now that we've added another inferior, thread IDs now show the + # inferior number. + info_threads "" "1.1" + + gdb_test "thread" "Current thread is 1\.1 .*" + + gdb_test "inferior 2" "Switching to inferior 2 .*" "switch to inferior 2" + gdb_test "file ${binfile}" ".*" "load file in inferior 2" + + runto_main + + # Now that we've added another inferior, thread IDs now show the + # inferior number. + info_threads "" "1.1 2.1" \ + "info threads show inferior numbers" + + gdb_test "thread" "Current thread is 2\.1 .*" \ + "switch to thread using extended thread ID" + + gdb_breakpoint "thread_function1" + + gdb_continue_to_breakpoint "once" + gdb_test "inferior 1" "Switching to inferior 1 .*" + gdb_continue_to_breakpoint "twice" + + info_threads "" "1.1 1.2 2.1 2.2" \ + "info threads again" + + # Confirm the convenience variable show the expected number. + gdb_test "p \$_thread == 2" " = 1" + + # Without an explicit inferior component, GDB defaults to the + # current inferior. Make sure we don't refer to a thread by + # global ID by mistake. + gdb_test "thread 4" "Unknown thread 1.4\\." + + # Test thread ID list parsing. Test qualified and unqualified + # IDs; qualified and unqualified ranges; invalid IDs and invalid + # ranges. + + # First spawn a couple more threads so ranges includes more than + # two threads. + with_test_prefix "more threads" { + gdb_breakpoint "thread_function2" + + gdb_test "inferior 2" "Switching to inferior 2 .*" + gdb_continue_to_breakpoint "once" + + gdb_test "inferior 1" "Switching to inferior 1 .*" + gdb_continue_to_breakpoint "twice" + } + + thr_apply_info_thr "1 2 3" \ + "1.1 1.2 1.3" + + # Same, but with qualified thread IDs. + thr_apply_info_thr "1.1 1.2 1.3 2.1 2.2" \ + "1.1 1.2 1.3 2.1 2.2" + + # Test a thread number range. + thr_apply_info_thr "1-3" \ + "1.1 1.2 1.3" + + # Same, but using a qualified range. + thr_apply_info_thr "1.1-3" \ + "1.1 1.2 1.3" + + # A mix of qualified and unqualified thread IDs/ranges. + thr_apply_info_thr "1.1 2-3" \ + "1.1 1.2 1.3" + + thr_apply_info_thr "1 1.2-3" \ + "1.1 1.2 1.3" + + # Likewise, but mix inferiors too. + thr_apply_info_thr "2.1 2-3" \ + "1.2 1.3 2.1" \ + "2.1 1.2 1.3" + + # Multiple ranges with mixed explicit inferiors. + thr_apply_info_thr "1.1-2 2.2-3" \ + "1.1 1.2 2.2 2.3" + + # Now test a set of invalid thread IDs/ranges. + + thr_apply_info_thr_invalid "1." \ + "1." + + thr_apply_info_thr_invalid "1-3 1." \ + "1." + + thr_apply_info_thr_invalid "1.1.1" \ + "1.1.1" + + thr_apply_info_thr_invalid "2 1.1.1" \ + "1.1.1" + + thr_apply_info_thr_invalid "1.1.1 2" \ + "1.1.1 2" + + thr_apply_info_thr_invalid "1-2.1" \ + "1-2.1" + + thr_apply_info_thr_error "1-0" "inverted range" + thr_apply_info_thr_error "1.1-0" "inverted range" + + thr_apply_info_thr_error "1-" "inverted range" + thr_apply_info_thr_error "1.1-" "inverted range" + + thr_apply_info_thr_error "2-1" "inverted range" + thr_apply_info_thr_error "1.2-1" "inverted range" + + thr_apply_info_thr_error "-1" "negative value" + thr_apply_info_thr_error "1.-1" "negative value" + + # Check that we do parse the inferior number and don't confuse it. + gdb_test "info threads 3.1" \ + "No threads match '3.1'\." +} + +if { ![skip_python_tests] } { + with_test_prefix "python" { + # Check that InferiorThread.num returns the expected number. + gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" \ + "test gdb.selected_thread" 1 + gdb_test "python print ('result = %s' % t0.num)" " = 3" \ + "test InferiorThread.num" + } +} + +# Remove the second inferior and confirm that GDB goes back to showing +# single-number thread IDs. +with_test_prefix "back to one inferior" { + gdb_test "kill inferior 2" "" "kill inferior 2" "Kill the program being debugged.*" "y" + gdb_test "thread 1.1" "Switching to thread 1\.1 .*" + gdb_test "remove-inferior 2" ".*" "remove inferior 2" + + # "info threads" while there's only inferior 1 should show + # single-number thread IDs. + info_threads "" "1 2 3" + + gdb_test "thread" "Current thread is 1 .*" +} + +# Add another inferior and remove inferior 1. Since even though +# there's a single inferior, its number is not 1, GDB should show +# inferior-qualified thread IDs. +with_test_prefix "single-inferior but not initial" { + # Add another inferior. + gdb_test "add-inferior" "Added inferior 3.*" "add empty inferior" + + # Now that we'd added another inferior, thread IDs should show the + # inferior number. + info_threads "" "1.1 1.2 1.3" \ + "info threads with multiple inferiors" + + gdb_test "thread" "Current thread is 1\.1 .*" + + gdb_test "inferior 3" "Switching to inferior 3 .*" "switch to inferior 3" + gdb_test "file ${binfile}" ".*" "load file in inferior 3" + + runto_main + + gdb_test "remove-inferior 1" ".*" "remove inferior 1" + + # Even though we have a single inferior, its number is > 1, so + # thread IDs should include the inferior number. + info_threads "" "3.1" \ + "info threads with single inferior" + + gdb_test "thread" "Current thread is 3\.1 .*" "thread again" +} diff --git a/gdb/testsuite/gdb.threads/thread-find.exp b/gdb/testsuite/gdb.threads/thread-find.exp index bd90c57f47..6d885a732e 100644 --- a/gdb/testsuite/gdb.threads/thread-find.exp +++ b/gdb/testsuite/gdb.threads/thread-find.exp @@ -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" diff --git a/gdb/thread.c b/gdb/thread.c index 0ad26fc9a8..dd8f7da0d6 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -43,6 +43,7 @@ #include "gdb_regex.h" #include "cli/cli-utils.h" #include "thread-fsm.h" +#include "tid-parse.h" /* Definition of struct thread_info exported to gdbthread.h. */ @@ -182,7 +183,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); @@ -226,16 +227,22 @@ init_thread_list (void) threads_executing = 0; } -/* Allocate a new thread with target id PTID and add it to the thread - list. */ +/* Allocate a new thread of inferior INF with target id PTID and add + it to the thread list. */ static struct thread_info * -new_thread (ptid_t ptid) +new_thread (struct inferior *inf, ptid_t ptid) { - struct thread_info *tp = XCNEW (struct thread_info); + struct thread_info *tp; + + gdb_assert (inf != NULL); + + tp = XCNEW (struct thread_info); tp->ptid = ptid; - tp->num = ++highest_thread_num; + tp->global_num = ++highest_thread_num; + tp->inf = inf; + tp->per_inf_num = ++inf->highest_thread_num; if (thread_list == NULL) thread_list = tp; @@ -260,6 +267,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 +286,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 +310,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 +490,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->num == num) + if (tp->global_num == global_id) + return tp; + + return NULL; +} + +static struct thread_info * +find_thread_id (struct inferior *inf, int thr_num) +{ + struct thread_info *tp; + + for (tp = thread_list; tp; tp = tp->next) + if (tp->inf == inf && tp->per_inf_num == thr_num) return tp; return NULL; @@ -548,38 +569,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 +625,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 +709,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); @@ -1127,25 +1148,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; @@ -1164,13 +1222,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; @@ -1187,34 +1240,32 @@ 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) + ALL_THREADS_BY_INFERIOR (inf, tp) { struct cleanup *chain2; int core; - if (!number_is_in_list (requested_threads, tp->num)) - continue; - - if (pid != -1 && ptid_get_pid (tp->ptid) != pid) - { - if (requested_threads != NULL && *requested_threads != '\0') - error (_("Requested thread not found in requested process")); - continue; - } - if (ptid_equal (tp->ptid, current_ptid)) - current_thread = tp->num; + current_thread = tp->global_num; - if (tp->state == THREAD_EXITED) + if (!should_print_thread (requested_threads, default_inf_num, + global_ids, pid, tp)) continue; chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); @@ -1235,7 +1286,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 @@ -1311,27 +1366,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 has terminated. See `help thread'.\n", - current_thread); - else if (thread_list - && current_thread == -1 - && ptid_equal (current_ptid, null_ptid)) +The current thread 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 @@ -1340,7 +1403,7 @@ No selected thread. See `help thread'.\n"); static void info_threads_command (char *arg, int from_tty) { - print_thread_info (current_uiout, arg, -1); + print_thread_info_1 (current_uiout, arg, 0, -1, 0); } /* See gdbthread.h. */ @@ -1612,12 +1675,23 @@ make_cleanup_restore_current_thread (void) /* See gdbthread.h. */ +int +show_inferior_qualified_tids (void) +{ + return (inferior_list->next != NULL || inferior_list->num != 1); +} + +/* See gdbthread.h. */ + const char * print_thread_id (struct thread_info *thr) { char *s = get_print_cell (); - xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->num); + if (show_inferior_qualified_tids ()) + xsnprintf (s, PRINT_CELL_SIZE, "%d.%d", thr->inf->num, thr->per_inf_num); + else + xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->per_inf_num); return s; } @@ -1626,18 +1700,24 @@ print_thread_id (struct thread_info *thr) 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; - return ((((*ap)->num > (*bp)->num) - ((*ap)->num < (*bp)->num)) + 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 (((a->per_inf_num > b->per_inf_num) + - (a->per_inf_num < b->per_inf_num)) * (tp_array_compar_ascending ? +1 : -1)); } @@ -1732,7 +1812,7 @@ 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")); @@ -1747,33 +1827,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) + 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; - int start; + struct thread_info *tp = NULL; + struct inferior *inf; + int inf_num, thr_num; - start = get_number_or_range (&state); - - make_cleanup_restore_current_thread (); - - tp = find_thread_id (start); - - if (!tp) - warning (_("Unknown thread %d."), start); - else if (!thread_alive (tp)) - warning (_("Thread %d has terminated."), start); - else + 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) { - switch_to_thread (tp->ptid); - - printf_filtered (_("\nThread %d (%s):\n"), tp->num, - target_pid_to_str (inferior_ptid)); - execute_command (cmd, from_tty); - - /* Restore exact command used previously. */ - strcpy (cmd, saved_cmd); + 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 (!thread_alive (tp)) + { + warning (_("Thread %s has terminated."), print_thread_id (tp)); + continue; + } + + switch_to_thread (tp->ptid); + + 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); @@ -1894,30 +1985,42 @@ show_print_thread_events (struct ui_file *file, int from_tty, } static int -do_captured_thread_select (struct ui_out *uiout, void *tidstr) +do_captured_thread_select (struct ui_out *uiout, void *tidstr_v) { - int num; + const char *tidstr = tidstr_v; struct thread_info *tp; - num = value_as_long (parse_and_eval ((const char *) tidstr)); + if (ui_out_is_mi_like_p (uiout)) + { + int num = value_as_long (parse_and_eval (tidstr)); - tp = find_thread_id (num); - - if (!tp) - error (_("Thread ID %d not known."), num); + tp = find_thread_global_id (num); + if (tp == NULL) + error (_("Thread ID %d not known."), num); + } + else + { + tp = parse_thread_id (tidstr, NULL); + gdb_assert (tp != NULL); + } if (!thread_alive (tp)) - error (_("Thread ID %d has terminated."), num); + error (_("Thread ID %s has terminated."), tidstr); switch_to_thread (tp->ptid); annotate_thread_changed (); - ui_out_text (uiout, "[Switching to thread "); - ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp)); - ui_out_text (uiout, " ("); - ui_out_text (uiout, target_pid_to_str (inferior_ptid)); - ui_out_text (uiout, ")]"); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_int (uiout, "new-thread-id", inferior_thread ()->global_num); + else + { + ui_out_text (uiout, "[Switching to thread "); + ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp)); + ui_out_text (uiout, " ("); + ui_out_text (uiout, target_pid_to_str (inferior_ptid)); + ui_out_text (uiout, ")]"); + } /* Note that we can't reach this with an exited thread, due to the thread_alive check above. */ @@ -1971,8 +2074,9 @@ 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 per-inferior thread + number. Return a value of 0 if no thread is selected, or no + threads exist. */ static struct value * thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var, @@ -1981,7 +2085,7 @@ thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var, struct thread_info *tp = find_thread_ptid (inferior_ptid); return value_from_longest (builtin_type (gdbarch)->builtin_int, - (tp ? tp->num : 0)); + (tp ? tp->per_inf_num : 0)); } /* Commands with a prefix of `thread'. */ @@ -2004,8 +2108,8 @@ _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.")); +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\ diff --git a/gdb/tid-parse.c b/gdb/tid-parse.c new file mode 100644 index 0000000000..21b872d39d --- /dev/null +++ b/gdb/tid-parse.c @@ -0,0 +1,282 @@ +/* TID parsing for GDB, the GNU debugger. + + Copyright (C) 2015-2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" +#include "tid-parse.h" +#include "inferior.h" +#include "gdbthread.h" +#include + +/* See tid-parse.h. */ + +void ATTRIBUTE_NORETURN +invalid_thread_id_error (const char *string) +{ + error (_("Invalid thread ID: %s"), string); +} + +/* See tid-parse.h. */ + +struct thread_info * +parse_thread_id (const char *tidstr, const char **end) +{ + const char *number = tidstr; + const char *dot, *p1; + struct thread_info *tp; + struct inferior *inf; + int thr_num; + int explicit_inf_id = 0; + + dot = strchr (number, '.'); + + if (dot != NULL) + { + /* Parse number to the left of the dot. */ + int inf_num; + + p1 = number; + inf_num = get_number_trailer (&p1, '.'); + if (inf_num == 0) + invalid_thread_id_error (number); + + inf = find_inferior_id (inf_num); + if (inf == NULL) + error (_("No inferior number '%d'"), inf_num); + + explicit_inf_id = 1; + p1 = dot + 1; + } + else + { + inf = current_inferior (); + + p1 = number; + } + + thr_num = get_number_const (&p1); + if (thr_num == 0) + invalid_thread_id_error (number); + + ALL_THREADS (tp) + { + if (ptid_get_pid (tp->ptid) == inf->pid + && tp->per_inf_num == thr_num) + break; + } + + if (tp == NULL) + { + if (show_inferior_qualified_tids () || explicit_inf_id) + error (_("Unknown thread %d.%d."), inf->num, thr_num); + else + error (_("Unknown thread %d."), thr_num); + } + + if (end != NULL) + *end = p1; + + return tp; +} + +/* See tid-parse.h. */ + +void +tid_range_parser_init (struct tid_range_parser *parser, const char *tidlist, + int default_inferior) +{ + parser->state = TID_RANGE_STATE_INFERIOR; + parser->string = tidlist; + parser->inf_num = 0; + parser->qualified = 0; + parser->default_inferior = default_inferior; +} + +/* See tid-parse.h. */ + +int +tid_range_parser_finished (struct tid_range_parser *parser) +{ + switch (parser->state) + { + case TID_RANGE_STATE_INFERIOR: + return *parser->string == '\0'; + case TID_RANGE_STATE_THREAD_RANGE: + return parser->range_parser.finished; + } + + gdb_assert_not_reached (_("unhandled state")); +} + +/* See tid-parse.h. */ + +const char * +tid_range_parser_string (struct tid_range_parser *parser) +{ + switch (parser->state) + { + case TID_RANGE_STATE_INFERIOR: + return parser->string; + case TID_RANGE_STATE_THREAD_RANGE: + return parser->range_parser.string; + } + + gdb_assert_not_reached (_("unhandled state")); +} + +/* See tid-parse.h. */ + +void +tid_range_parser_skip (struct tid_range_parser *parser) +{ + gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE) + && parser->range_parser.in_range); + + tid_range_parser_init (parser, parser->range_parser.end_ptr, + parser->default_inferior); +} + +/* See tid-parse.h. */ + +int +tid_range_parser_qualified (struct tid_range_parser *parser) +{ + return parser->qualified; +} + +/* Helper for tid_range_parser_get_tid and + tid_range_parser_get_tid_range. Return the next range if THR_END + is non-NULL, return a single thread ID otherwise. */ + +static int +get_tid_or_range (struct tid_range_parser *parser, int *inf_num, + int *thr_start, int *thr_end) +{ + if (parser->state == TID_RANGE_STATE_INFERIOR) + { + const char *p; + const char *space; + + space = skip_to_space (parser->string); + + p = parser->string; + while (p < space && *p != '.') + p++; + if (p < space) + { + const char *dot = p; + + /* Parse number to the left of the dot. */ + p = parser->string; + parser->inf_num = get_number_trailer (&p, '.'); + if (parser->inf_num == 0) + invalid_thread_id_error (parser->string); + + parser->qualified = 1; + p = dot + 1; + + if (isspace (*p)) + invalid_thread_id_error (parser->string); + } + else + { + parser->inf_num = parser->default_inferior; + parser->qualified = 0; + p = parser->string; + } + + init_number_or_range (&parser->range_parser, p); + parser->state = TID_RANGE_STATE_THREAD_RANGE; + } + + *inf_num = parser->inf_num; + *thr_start = get_number_or_range (&parser->range_parser); + if (*thr_start == 0) + invalid_thread_id_error (parser->string); + + /* If we successfully parsed a thread number or finished parsing a + thread range, switch back to assuming the next TID is + inferior-qualified. */ + if (parser->range_parser.end_ptr == NULL + || parser->range_parser.string == parser->range_parser.end_ptr) + { + parser->state = TID_RANGE_STATE_INFERIOR; + parser->string = parser->range_parser.string; + + if (thr_end != NULL) + *thr_end = *thr_start; + } + + /* If we're midway through a range, and the caller wants the end + value, return it and skip to the end of the range. */ + if (thr_end != NULL && parser->state == TID_RANGE_STATE_THREAD_RANGE) + { + *thr_end = parser->range_parser.end_value; + tid_range_parser_skip (parser); + } + + return (*inf_num != 0 && *thr_start != 0); +} + +/* See tid-parse.h. */ + +int +tid_range_parser_get_tid_range (struct tid_range_parser *parser, int *inf_num, + int *thr_start, int *thr_end) +{ + gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL); + + return get_tid_or_range (parser, inf_num, thr_start, thr_end); +} + +/* See tid-parse.h. */ + +int +tid_range_parser_get_tid (struct tid_range_parser *parser, + int *inf_num, int *thr_num) +{ + gdb_assert (inf_num != NULL && thr_num != NULL); + + return get_tid_or_range (parser, inf_num, thr_num, NULL); +} + +/* See tid-parse.h. */ + +int +tid_is_in_list (const char *list, int default_inferior, + int inf_num, int thr_num) +{ + struct tid_range_parser parser; + + if (list == NULL || *list == '\0') + return 1; + + tid_range_parser_init (&parser, list, default_inferior); + while (!tid_range_parser_finished (&parser)) + { + int tmp_inf, tmp_thr_start, tmp_thr_end; + + if (!tid_range_parser_get_tid_range (&parser, &tmp_inf, + &tmp_thr_start, &tmp_thr_end)) + invalid_thread_id_error (parser.string); + if (tmp_inf == inf_num + && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end) + return 1; + } + return 0; +} diff --git a/gdb/tid-parse.h b/gdb/tid-parse.h new file mode 100644 index 0000000000..a690edfe31 --- /dev/null +++ b/gdb/tid-parse.h @@ -0,0 +1,170 @@ +/* TID parsing for GDB, the GNU debugger. + + Copyright (C) 2015-2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef TID_PARSE_H +#define TID_PARSE_H + +#include "cli/cli-utils.h" + +struct thread_info; + +/* Issue an invalid thread ID error, pointing at STRING, the invalid + ID. */ +extern void ATTRIBUTE_NORETURN invalid_thread_id_error (const char *string); + +/* Parse TIDSTR as a per-inferior thread ID, in either INF_NUM.THR_NUM + or THR_NUM form. In the latter case, the missing INF_NUM is filled + in from the current inferior. If ENDPTR is not NULL, + parse_thread_id stores the address of the first character after the + thread ID. Either a valid thread is returned, or an error is + thrown. */ +struct thread_info *parse_thread_id (const char *tidstr, const char **end); + +/* The possible states of the tid range parser's state machine. */ +enum tid_range_state +{ + /* Parsing the inferior number. */ + TID_RANGE_STATE_INFERIOR, + + /* Parsing the thread number or thread number range. */ + TID_RANGE_STATE_THREAD_RANGE, +}; + +/* An object of this type is passed to tid_range_parser_get_tid. It + must be initialized by calling tid_range_parser_init. This type is + defined here so that it can be stack-allocated, but all members + should be treated as opaque. */ +struct tid_range_parser +{ + /* What sub-component are we expecting. */ + enum tid_range_state state; + + /* The string being parsed. When parsing has finished, this points + past the last parsed token. */ + const char *string; + + /* The range parser state when we're parsing the thread number + sub-component. */ + struct get_number_or_range_state range_parser; + + /* Last inferior number returned. */ + int inf_num; + + /* True if the TID last parsed was explicitly inferior-qualified. + IOW, whether the spec specified an inferior number + explicitly. */ + int qualified; + + /* The inferior number to assume if the TID is not qualified. */ + int default_inferior; +}; + +/* Initialize a tid_range_parser for use with + tid_range_parser_get_tid. TIDLIST is the string to be parsed. + DEFAULT_INFERIOR is the inferior number to assume if a + non-qualified thread ID is found. */ +extern void tid_range_parser_init (struct tid_range_parser *parser, + const char *tidlist, + int default_inferior); + +/* Parse a thread ID or a thread range list. + + A range will be of the form + + .- + + and will represent all the threads of inferior INFERIOR_NUM with + number between THREAD_NUMBER1 and THREAD_NUMBER2, inclusive. + can also be omitted, as in + + - + + in which case GDB infers the inferior number from the default + passed to the tid_range_parser_init function. + + This function is designed to be called iteratively. While + processing a thread ID range list, at each call it will return (in + the INF_NUM and THR_NUM output parameters) the next thread ID in + the range (irrespective of whether the thread actually exists). + + At the beginning of parsing a thread range, the char pointer + PARSER->string will be advanced past 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 . + + This function advances through the input string for as long you + call it. Once the end of the input string is reached, a call to + tid_range_parser_finished returns false (see below). + + E.g., with list: "1.2 3.4-6": + + 1st call: *INF_NUM=1; *THR_NUM=2 (finished==0) + 2nd call: *INF_NUM=3; *THR_NUM=4 (finished==0) + 3rd call: *INF_NUM=3; *THR_NUM=5 (finished==0) + 4th call: *INF_NUM=3; *THR_NUM=6 (finished==1) + + Returns true if parsed a thread/range successfully, false + otherwise. */ +extern int tid_range_parser_get_tid (struct tid_range_parser *parser, + int *inf_num, int *thr_num); + +/* Like tid_range_parser_get_tid, but return a thread ID range per + call, rather then a single thread ID. + + If the next element in the list is a single thread ID, then + *THR_START and *THR_END are set to the same value. + + E.g.,. with list: "1.2 3.4-6" + + 1st call: *INF_NUM=1; *THR_START=2; *THR_END=2 (finished==0) + 2nd call: *INF_NUM=3; *THR_START=4; *THR_END=6 (finished==1) + + Returns true if parsed a thread/range successfully, false + otherwise. */ +extern int tid_range_parser_get_tid_range (struct tid_range_parser *parser, + int *inf_num, + int *thr_start, int *thr_end); + +/* Returns non-zero if parsing has completed. */ +extern int tid_range_parser_finished (struct tid_range_parser *parser); + +/* Return the string being parsed. When parsing has finished, this + points past the last parsed token. */ +const char *tid_range_parser_string (struct tid_range_parser *parser); + +/* When parsing a range, advance past the final token in the range. */ +extern void tid_range_parser_skip (struct tid_range_parser *parser); + +/* True if the TID last parsed was explicitly inferior-qualified. + IOW, whether the spec specified an inferior number explicitly. */ +extern int tid_range_parser_qualified (struct tid_range_parser *parser); + +/* Accept a string-form list of thread IDs such as is accepted by + tid_range_parser_get_tid. Return true if the INF_NUM.THR.NUM + thread is in the list. DEFAULT_INFERIOR is the inferior number to + assume if a non-qualified thread ID is found in the list. + + By definition, an empty list includes all threads. This is to be + interpreted as typing a command such as "info threads" with no + arguments. */ +extern int tid_is_in_list (const char *list, int default_inferior, + int inf_num, int thr_num); + +#endif /* TID_PARSE_H */ diff --git a/gdb/varobj.c b/gdb/varobj.c index d47bdc2acd..8116a5be08 100644 --- a/gdb/varobj.c +++ b/gdb/varobj.c @@ -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); diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c index dd8085f966..cddbf23c46 100644 --- a/gdb/windows-tdep.c +++ b/gdb/windows-tdep.c @@ -361,31 +361,12 @@ display_one_tib (ptid_t ptid) return 1; } -/* Display thread information block of a thread specified by ARGS. - If ARGS is empty, display thread information block of current_thread - if current_thread is non NULL. - Otherwise ARGS is parsed and converted to a integer that should - be the windows ThreadID (not the internal GDB thread ID). */ +/* Display thread information block of the current thread. */ static void display_tib (char * args, int from_tty) { - if (args) - { - struct thread_info *tp; - int gdb_id = value_as_long (parse_and_eval (args)); - - tp = find_thread_id (gdb_id); - - if (!tp) - error (_("Thread ID %d not known."), gdb_id); - - if (!target_thread_alive (tp->ptid)) - error (_("Thread ID %d has terminated."), gdb_id); - - display_one_tib (tp->ptid); - } - else if (!ptid_equal (inferior_ptid, null_ptid)) + if (!ptid_equal (inferior_ptid, null_ptid)) display_one_tib (inferior_ptid); }