gdb: new $_inferior_thread_count convenience variable

Add a new convenience variable $_inferior_thread_count that contains
the number of live (non-exited) threads in the current inferior.  This
can be used in command scripts, or breakpoint conditions, etc to
adjust the behaviour for multi-threaded inferiors.

This value is only stable in all-stop mode.  In non-stop mode, where
new threads can be started, and existing threads exit, at any time,
this convenience variable can give a different value each time it is
evaluated.
This commit is contained in:
Andrew Burgess
2022-11-02 13:48:42 +00:00
parent 91f63aa2e2
commit cbda14deaf
6 changed files with 66 additions and 2 deletions

View File

@ -78,6 +78,9 @@
re-enable styling using the new "set style tui-current-position" re-enable styling using the new "set style tui-current-position"
command. command.
* New convenience variable $_inferior_thread_count contains the number
of live threads in the current inferior.
* New commands * New commands
maintenance set ignore-prologue-end-flag on|off maintenance set ignore-prologue-end-flag on|off

View File

@ -3642,8 +3642,15 @@ The debugger convenience variables @samp{$_thread} and
@samp{$_gthread} contain, respectively, the per-inferior thread number @samp{$_gthread} contain, respectively, the per-inferior thread number
and the global thread number of the current thread. You may find this and the global thread number of the current thread. You may find this
useful in writing breakpoint conditional expressions, command scripts, useful in writing breakpoint conditional expressions, command scripts,
and so forth. @xref{Convenience Vars,, Convenience Variables}, for and so forth. The convenience variable @samp{$_inferior_thread_count}
general information on convenience variables. contains the number of live threads in the current inferior.
@xref{Convenience Vars,, Convenience Variables}, for general
information on convenience variables.
When running in non-stop mode (@pxref{Non-Stop Mode}), where new
threads can be created, and existing threads exit, at any time,
@samp{$_inferior_thread_count} could return a different value each
time it is evaluated.
If @value{GDBN} detects the program is multi-threaded, it augments the If @value{GDBN} detects the program is multi-threaded, it augments the
usual message about stopping at a breakpoint with the ID and name of usual message about stopping at a breakpoint with the ID and name of
@ -12655,6 +12662,9 @@ The thread number of the current thread. @xref{thread numbers}.
@item $_gthread @item $_gthread
The global number of the current thread. @xref{global thread numbers}. The global number of the current thread. @xref{global thread numbers}.
@item $_inferior_thread_count
The number of live threads in the current inferior. @xref{Threads}.
@item $_gdb_major @item $_gdb_major
@itemx $_gdb_minor @itemx $_gdb_minor
@vindex $_gdb_major@r{, convenience variable} @vindex $_gdb_major@r{, convenience variable}

View File

@ -584,6 +584,7 @@ set show_conv_list \
{$_thread = 0} \ {$_thread = 0} \
{$_gthread = 0} \ {$_gthread = 0} \
{$_inferior = 1} \ {$_inferior = 1} \
{$_inferior_thread_count = 0} \
{$_exception = <error: No frame selected>} \ {$_exception = <error: No frame selected>} \
{$_probe_argc = <error: No frame selected>} \ {$_probe_argc = <error: No frame selected>} \
{$_probe_arg0 = <error: No frame selected>} \ {$_probe_arg0 = <error: No frame selected>} \

View File

@ -123,6 +123,8 @@ with_test_prefix "single inferior" {
info_threads "" "1" info_threads "" "1"
gdb_test "thread" "Current thread is 1 .*" gdb_test "thread" "Current thread is 1 .*"
gdb_test "print \$_inferior_thread_count" " = 1"
} }
# "info threads" while there are multiple inferiors should show # "info threads" while there are multiple inferiors should show
@ -140,8 +142,14 @@ with_test_prefix "two inferiors" {
gdb_test "inferior 2" "Switching to inferior 2 .*" "switch to inferior 2" gdb_test "inferior 2" "Switching to inferior 2 .*" "switch to inferior 2"
gdb_test "file ${binfile}" ".*" "load file in inferior 2" gdb_test "file ${binfile}" ".*" "load file in inferior 2"
gdb_test "print \$_inferior_thread_count" " = 0" \
"no threads before we start the second inferior"
runto_main runto_main
gdb_test "print \$_inferior_thread_count" " = 1" \
"no other threads started yet"
# Now that we've added another inferior, thread IDs now show the # Now that we've added another inferior, thread IDs now show the
# inferior number. # inferior number.
info_threads "" "1.1 2.1" \ info_threads "" "1.1 2.1" \
@ -153,8 +161,14 @@ with_test_prefix "two inferiors" {
gdb_breakpoint "thread_function1" gdb_breakpoint "thread_function1"
gdb_continue_to_breakpoint "once" gdb_continue_to_breakpoint "once"
gdb_test "print \$_inferior_thread_count" " = 2" \
"second thread started in inferior 2"
gdb_test "inferior 1" "Switching to inferior 1 .*" gdb_test "inferior 1" "Switching to inferior 1 .*"
gdb_test "print \$_inferior_thread_count" " = 1" \
"still only one thread in inferior 1"
gdb_continue_to_breakpoint "twice" gdb_continue_to_breakpoint "twice"
gdb_test "print \$_inferior_thread_count" " = 2" \
"second thread started in inferior 1"
info_threads "" "1.1 1.2 2.1 2.2" \ info_threads "" "1.1 1.2 2.1 2.2" \
"info threads again" "info threads again"
@ -187,9 +201,15 @@ with_test_prefix "two inferiors" {
gdb_test "inferior 2" "Switching to inferior 2 .*" gdb_test "inferior 2" "Switching to inferior 2 .*"
gdb_continue_to_breakpoint "once" gdb_continue_to_breakpoint "once"
gdb_test "print \$_inferior_thread_count" " = 3" \
"third thread started in inferior 2"
gdb_test "inferior 1" "Switching to inferior 1 .*" gdb_test "inferior 1" "Switching to inferior 1 .*"
gdb_test "print \$_inferior_thread_count" " = 2" \
"still only two threads in inferior 1"
gdb_continue_to_breakpoint "twice" gdb_continue_to_breakpoint "twice"
gdb_test "print \$_inferior_thread_count" " = 3" \
"third thread started in inferior 1"
} }
thr_apply_info_thr "1" \ thr_apply_info_thr "1" \

View File

@ -2123,6 +2123,22 @@ global_thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
return thread_num_make_value_helper (gdbarch, 1); return thread_num_make_value_helper (gdbarch, 1);
} }
/* Return a new value for the number of non-exited threads in the current
inferior. If there are no threads in the current inferior return a
value of 0. */
static struct value *
inferior_thread_count_make_value (struct gdbarch *gdbarch,
struct internalvar *var, void *ignore)
{
int int_val = 0;
if (inferior_ptid != null_ptid)
int_val = current_inferior ()->non_exited_threads ().size ();
return value_from_longest (builtin_type (gdbarch)->builtin_int, int_val);
}
/* Commands with a prefix of `thread'. */ /* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL; struct cmd_list_element *thread_cmd_list = NULL;
@ -2142,6 +2158,14 @@ static const struct internalvar_funcs gthread_funcs =
NULL, NULL,
}; };
/* Implementation of `_inferior_thread_count` convenience variable. */
static const struct internalvar_funcs inferior_thread_count_funcs =
{
inferior_thread_count_make_value,
NULL,
};
void _initialize_thread (); void _initialize_thread ();
void void
_initialize_thread () _initialize_thread ()
@ -2258,4 +2282,6 @@ When on messages about thread creation and deletion are printed."),
create_internalvar_type_lazy ("_thread", &thread_funcs, NULL); create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
create_internalvar_type_lazy ("_gthread", &gthread_funcs, NULL); create_internalvar_type_lazy ("_gthread", &gthread_funcs, NULL);
create_internalvar_type_lazy ("_inferior_thread_count",
&inferior_thread_count_funcs, NULL);
} }

View File

@ -53,6 +53,10 @@ struct iterator_range
IteratorType end () const IteratorType end () const
{ return m_end; } { return m_end; }
/* The number of items in this iterator_range. */
std::size_t size () const
{ return std::distance (m_begin, m_end); }
private: private:
IteratorType m_begin, m_end; IteratorType m_begin, m_end;
}; };