Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
To: gdb-patches@sourceware.org
Cc: guinevere@redhat.com, eliz@gnu.org, pedro@palves.net
Subject: [PATCH v4 3/3] gdb: add '-stopped' and '-running' options to "info threads"
Date: Mon,  5 May 2025 18:19:31 +0200	[thread overview]
Message-ID: <a8badb171addff28a7b506303a01ac7ff03482f9.1746461232.git.tankut.baris.aktemur@intel.com> (raw)
In-Reply-To: <cover.1746461232.git.tankut.baris.aktemur@intel.com>

Add two options to "info threads": `-stopped` and `-running`.

The purpose of these options is to filter the output of the command.
The `-stopped` option means "print stopped threads only" and,
similarly, `-running` means "print the running threads only".  When
both options are provided by the user, the indication is that the user
wants the union.  That is, the output contains both stopped and
running threads.

Suppose we have an application with 5 threads, 2 of which have hit a
breakpoint.  The "info threads" command in the non-stop mode gives:

  (gdb) info threads
    Id   Target Id             Frame
  * 1    Thread 0x7ffff7d99740 (running)
    2    Thread 0x7ffff7d98700 something () at file.c:30
    3    Thread 0x7ffff7597700 (running)
    4    Thread 0x7ffff6d96700 something () at file.c:30
    5    Thread 0x7ffff6595700 (running)
  (gdb)

Using the "-stopped" flag, we get

  (gdb) info threads -stopped
    Id   Target Id             Frame
    2    Thread 0x7ffff7d98700 something () at file.c:30
    4    Thread 0x7ffff6d96700 something () at file.c:30
  (gdb)

Using the "-running" flag, we get

  (gdb) info threads -running
    Id   Target Id             Frame
  * 1    Thread 0x7ffff7d99740 (running)
    3    Thread 0x7ffff7597700 (running)
    5    Thread 0x7ffff6595700 (running)
  (gdb)

Using both flags prints all:

  (gdb) info threads -stopped -running
    Id   Target Id             Frame
  * 1    Thread 0x7ffff7d99740 (running)
    2    Thread 0x7ffff7d98700 something () at file.c:30
    3    Thread 0x7ffff7597700 (running)
    4    Thread 0x7ffff6d96700 something () at file.c:30
    5    Thread 0x7ffff6595700 (running)
  (gdb)

When combined with a thread ID, filtering applies to those threads that
are matched by the ID.

  (gdb) info threads 3
    Id   Target Id             Frame
    3    Thread 0x7ffff7597700 (running)
  (gdb) info threads -stopped 3
  No threads matched.
  (gdb)

Regression-tested on X86_64 Linux.

Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
---
 gdb/NEWS                                      |  11 +-
 gdb/doc/gdb.texinfo                           |  10 +-
 gdb/testsuite/gdb.base/options.exp            |  16 ++-
 .../gdb.threads/info-threads-options.c        |  77 ++++++++++
 .../gdb.threads/info-threads-options.exp      | 131 ++++++++++++++++++
 gdb/thread.c                                  |  41 ++++--
 6 files changed, 272 insertions(+), 14 deletions(-)
 create mode 100644 gdb/testsuite/gdb.threads/info-threads-options.c
 create mode 100644 gdb/testsuite/gdb.threads/info-threads-options.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 18a8b7475b4..70729b501cc 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -90,12 +90,17 @@ info sharedlibrary
   command are now for the full memory range allocated to the shared
   library.
 
-info threads [-gid] [ID]...
-  If no threads match the given ID(s), GDB now prints
+info threads [-gid] [-stopped] [-running] [ID]...
+  If no threads match the given ID(s) or filter options, GDB now prints
 
     No threads matched.
 
-  without printing the provided argument.
+  without printing the provided arguments.  The newly added '-stopped'
+  option makes GDB list the stopped threads only.  Similarly,
+  '-running' makes GDB list the running threads only.  If both options
+  are given together, GDB lists the union; that is, both stopped and
+  running threads are listed.  These new flags can be useful to get a
+  reduced list when there is a large number of threads.
 
 * GDB-internal Thread Local Storage (TLS) support
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b9fc160a171..989cc36fbee 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3807,7 +3807,7 @@ Thread 1 "main" received signal SIGINT, Interrupt.
 @table @code
 @anchor{info_threads}
 @kindex info threads
-@item info threads @r{[}-gid@r{]} @r{[}@var{thread-id-list}@r{]}
+@item info threads @r{[}-gid@r{]} @r{[}-stopped@r{]} @r{[}-running@r{]} @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
@@ -3857,6 +3857,14 @@ If you're debugging multiple inferiors, @value{GDBN} displays thread
 IDs using the qualified @var{inferior-num}.@var{thread-num} format.
 Otherwise, only @var{thread-num} is shown.
 
+If you specify the @samp{-stopped} option, @value{GDBN} filters the
+output of the command to print the stopped threads only.  Similarly,
+if you specify the @samp{-running} option, @value{GDBN} filters the
+output to print the running threads only.  These options can be
+helpful to reduce the output list if there is a large number of
+threads.  If you specify both options, @value{GDBN} prints the union;
+both stopped and running threads are printed.
+
 If you specify the @samp{-gid} option, @value{GDBN} displays a column
 indicating each thread's global thread ID:
 
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 7822e4a0baa..a0947e2f6f4 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -508,12 +508,26 @@ proc_with_prefix test-thread-apply {} {
 proc_with_prefix test-info-threads {} {
     test_gdb_complete_multiple "info threads " "" "" {
 	"-gid"
+	"-running"
+	"-stopped"
 	"ID"
     }
 
+    test_gdb_complete_multiple "info threads " "-" "" {
+	"-gid"
+	"-running"
+	"-stopped"
+    }
+
     test_gdb_complete_unique \
-	"info threads -" \
+	"info threads -g" \
 	"info threads -gid"
+    test_gdb_complete_unique \
+	"info threads -r" \
+	"info threads -running"
+    test_gdb_complete_unique \
+	"info threads -s" \
+	"info threads -stopped"
 
     # "ID" isn't really something the user can type.
     test_gdb_complete_none "info threads I"
diff --git a/gdb/testsuite/gdb.threads/info-threads-options.c b/gdb/testsuite/gdb.threads/info-threads-options.c
new file mode 100644
index 00000000000..2c4cd854bb7
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/info-threads-options.c
@@ -0,0 +1,77 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022-2025 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#define NUM 4
+
+static pthread_barrier_t threads_started_barrier;
+
+static void
+stop_here ()
+{
+}
+
+static void
+spin ()
+{
+  while (1)
+    usleep (1);
+}
+
+static void *
+work (void *arg)
+{
+  int id = *(int *) arg;
+
+  pthread_barrier_wait (&threads_started_barrier);
+
+  if (id % 2 == 0)
+    stop_here ();
+  else
+    spin ();
+
+  pthread_exit (NULL);
+}
+
+int
+main ()
+{
+  /* Ensure we stop if GDB crashes and DejaGNU fails to kill us.  */
+  alarm (10);
+
+  pthread_t threads[NUM];
+  int ids[NUM];
+
+  pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1);
+
+  for (int i = 0; i < NUM; i++)
+    {
+      ids[i] = i;
+      pthread_create (&threads[i], NULL, work, &ids[i]);
+    }
+
+  /* Wait until all threads are seen running.  */
+  pthread_barrier_wait (&threads_started_barrier);
+
+  stop_here ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/info-threads-options.exp b/gdb/testsuite/gdb.threads/info-threads-options.exp
new file mode 100644
index 00000000000..38e4e67f08b
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/info-threads-options.exp
@@ -0,0 +1,131 @@
+# Copyright (C) 2022-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test the filter flags of the "info threads" command.
+
+standard_testfile
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+	 executable debug] != "" } {
+    return -1
+}
+
+save_vars { GDBFLAGS } {
+    append GDBFLAGS " -ex \"set non-stop on\""
+    clean_restart $binfile
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint "stop_here"
+gdb_test_multiple "continue -a&" "" {
+    -re "Continuing.\r\n$gdb_prompt " {
+	pass $gdb_test_name
+    }
+}
+
+set expected_hits 3
+set fill "\[^\r\n\]+"
+set num_hits 0
+gdb_test_multiple "" "hit the breakpoint" -lbl {
+    -re "\r\nThread ${fill} hit Breakpoint ${decimal}," {
+	incr num_hits
+	if {$num_hits < $expected_hits} {
+	    exp_continue
+	}
+    }
+}
+gdb_assert {$num_hits == $expected_hits} "expected threads hit the bp"
+
+# Count the number of running/stopped threads reported
+# by the "info threads" command.  We also capture thread ids
+# for additional tests.
+set running_tid "invalid"
+set stopped_tid "invalid"
+
+set eol "(?=\r\n)"
+
+foreach_with_prefix flag {"" "-running" "-stopped" "-running -stopped"} {
+    set num_running 0
+    set num_stopped 0
+    gdb_test_multiple "info threads $flag" "info threads $flag" -lbl {
+	-re "Id${fill}Target Id${fill}Frame${fill}${eol}" {
+	    exp_continue
+	}
+	-re "^\r\n. (${decimal})${fill}Thread ${fill}.running.${eol}" {
+	    incr num_running
+	    set running_tid $expect_out(1,string)
+	    exp_continue
+	}
+	-re "^\r\n. (${decimal})${fill}Thread ${fill}stop_here ${fill}${eol}" {
+	    incr num_stopped
+	    set stopped_tid $expect_out(1,string)
+	    exp_continue
+	}
+	-re "^\r\n$gdb_prompt $" {
+	    pass $gdb_test_name
+	}
+    }
+
+    if {$flag eq "-running"} {
+	gdb_assert {$num_running == 2} "num running"
+	gdb_assert {$num_stopped == 0} "num stopped"
+    } elseif {$flag  eq "-stopped"} {
+	gdb_assert {$num_running == 0} "num running"
+	gdb_assert {$num_stopped == 3} "num stopped"
+    } else {
+	gdb_assert {$num_running == 2} "num running"
+	gdb_assert {$num_stopped == 3} "num stopped"
+    }
+}
+
+verbose -log "running_tid=$running_tid, stopped_tid=$stopped_tid"
+
+# Test specifying thread ids.
+gdb_test "info threads -running $stopped_tid" \
+    "No threads matched\\." \
+    "info thread -running for a stopped thread"
+gdb_test "info threads -stopped $running_tid" \
+    "No threads matched\\." \
+    "info thread -stopped for a running thread"
+
+set ws "\[ \t\]+"
+foreach tid "\"$running_tid\" \"$running_tid $stopped_tid\"" {
+    gdb_test "info threads -running $tid" \
+	[multi_line \
+	     "${ws}Id${ws}Target Id${ws}Frame${ws}" \
+	     "${ws}${running_tid}${ws}Thread ${fill}.running."] \
+	"info thread -running with [llength $tid] thread ids"
+}
+
+foreach tid "\"$stopped_tid\" \"$stopped_tid $running_tid\"" {
+    gdb_test "info threads -stopped $tid" \
+	[multi_line \
+	     "${ws}Id${ws}Target Id${ws}Frame${ws}" \
+	     "${ws}${stopped_tid}${ws}Thread ${fill} stop_here ${fill}"] \
+	"info thread -stopped with [llength $tid] thread ids"
+}
+
+gdb_test_multiple "info threads -stopped -running $stopped_tid $running_tid" \
+    "filter flags and tids combined" {
+    -re -wrap ".*stop_here.*running.*" {
+	pass $gdb_test_name
+    }
+    -re -wrap ".*running.*stop_here.*" {
+	pass $gdb_test_name
+    }
+}
diff --git a/gdb/thread.c b/gdb/thread.c
index d84d326a8c3..0228027fb92 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1044,6 +1044,10 @@ struct info_threads_opts
 {
   /* For "-gid".  */
   bool show_global_ids = false;
+  /* For "-running".  */
+  bool show_running_threads = false;
+  /* For "-stopped".  */
+  bool show_stopped_threads = false;
 };
 
 static const gdb::option::option_def info_threads_option_defs[] = {
@@ -1053,7 +1057,16 @@ static const gdb::option::option_def info_threads_option_defs[] = {
     [] (info_threads_opts *opts) { return &opts->show_global_ids; },
     N_("Show global thread IDs."),
   },
-
+  gdb::option::flag_option_def<info_threads_opts> {
+    "running",
+    [] (info_threads_opts *opts) { return &opts->show_running_threads; },
+    N_("Show running threads only."),
+  },
+  gdb::option::flag_option_def<info_threads_opts> {
+    "stopped",
+    [] (info_threads_opts *opts) { return &opts->show_stopped_threads; },
+    N_("Show stopped threads only."),
+  },
 };
 
 /* Helper for print_thread_info.  Returns true if THR should be
@@ -1095,7 +1108,17 @@ should_print_thread (const char *requested_threads,
   if (thr->state == THREAD_EXITED)
     return false;
 
-  return true;
+  bool is_stopped = (thr->state == THREAD_STOPPED);
+  if (opts.show_stopped_threads && is_stopped)
+    return true;
+
+  bool is_running = (thr->state == THREAD_RUNNING);
+  if (opts.show_running_threads && is_running)
+    return true;
+
+  /* If the user did not pass a filter flag, show the thread.  */
+  return (!opts.show_stopped_threads
+	  && !opts.show_running_threads);
 }
 
 /* Return the string to display in "info threads"'s "Target Id"
@@ -1254,13 +1277,15 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
       list_emitter.emplace (uiout, "threads");
     else
       {
-	int n_threads = 0;
+	int n_matching_threads = 0;
 	/* The width of the "Target Id" column.  Grown below to
 	   accommodate the largest entry.  */
 	size_t target_id_col_width = 17;
 
 	for (thread_info *tp : all_threads ())
 	  {
+	    any_thread = true;
+
 	    /* In case REQUESTED_THREADS contains $_thread.  */
 	    if (current_thread != nullptr)
 	      switch_to_thread (current_thread);
@@ -1277,12 +1302,12 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
 	      = std::max (target_id_col_width,
 			  thread_target_id_str (tp).size ());
 
-	    ++n_threads;
+	    ++n_matching_threads;
 	  }
 
-	if (n_threads == 0)
+	if (n_matching_threads == 0)
 	  {
-	    if (requested_threads == NULL || *requested_threads == '\0')
+	    if (!any_thread)
 	      uiout->message (_("No threads.\n"));
 	    else
 	      uiout->message (_("No threads matched.\n"));
@@ -1290,7 +1315,7 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
 	  }
 
 	table_emitter.emplace (uiout, opts.show_global_ids ? 5 : 4,
-			       n_threads, "threads");
+			       n_matching_threads, "threads");
 
 	uiout->table_header (1, ui_left, "current", "");
 	uiout->table_header (4, ui_left, "id-in-tg", "Id");
@@ -1305,8 +1330,6 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
     for (inferior *inf : all_inferiors ())
       for (thread_info *tp : inf->threads ())
 	{
-	  any_thread = true;
-
 	  if (tp == current_thread && tp->state == THREAD_EXITED)
 	    current_exited = true;
 
-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


  parent reply	other threads:[~2025-05-05 16:21 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-05  9:19 [PATCH 0/3] Option to show stopped threads only Tankut Baris Aktemur via Gdb-patches
2023-04-05  9:20 ` [PATCH 1/3] gdb: pass info_threads_opts to print_thread_info_1 Tankut Baris Aktemur via Gdb-patches
2023-04-05  9:20 ` [PATCH 2/3] gdb, doc: add the missing '-gid' option to 'info threads' Tankut Baris Aktemur via Gdb-patches
2023-04-05  9:56   ` Eli Zaretskii via Gdb-patches
2023-04-05 10:12     ` Aktemur, Tankut Baris via Gdb-patches
2023-04-05  9:20 ` [PATCH 3/3] gdb: add a '-stopped' option to "info threads" Tankut Baris Aktemur via Gdb-patches
2023-04-05 10:00   ` Eli Zaretskii via Gdb-patches
2023-04-05 10:19     ` Aktemur, Tankut Baris via Gdb-patches
2023-04-05 10:50       ` Eli Zaretskii via Gdb-patches
2023-04-05 11:31         ` Aktemur, Tankut Baris via Gdb-patches
2023-04-05 11:56           ` Eli Zaretskii via Gdb-patches
2025-04-24 14:50           ` Pedro Alves
2025-03-18 18:04 ` [PATCH v2 0/2] Option to show stopped threads only Tankut Baris Aktemur
2025-03-18 18:05   ` [PATCH v2 1/2] gdb: pass info_threads_opts to print_thread_info_1 Tankut Baris Aktemur
2025-03-18 18:05   ` [PATCH v2 2/2] gdb: add a '-stopped' option to "info threads" Tankut Baris Aktemur
2025-03-28 16:38   ` [PATCH v2 0/2] Option to show stopped threads only Guinevere Larsen
2025-04-04 13:39     ` Aktemur, Tankut Baris
2025-04-04 13:36 ` [PATCH v3 " Tankut Baris Aktemur
2025-04-04 13:36   ` [PATCH v3 1/2] gdb: pass info_threads_opts to print_thread_info_1 Tankut Baris Aktemur
2025-04-24 18:09     ` Pedro Alves
2025-04-04 13:36   ` [PATCH v3 2/2] gdb: add a '-stopped' option to "info threads" Tankut Baris Aktemur
2025-04-24 19:23     ` Pedro Alves
2025-05-05 16:17       ` Aktemur, Tankut Baris
2025-04-23  8:00   ` [PATCH v3 0/2] Option to show stopped threads only Aktemur, Tankut Baris
2025-04-24 17:53   ` Pedro Alves
2025-05-05 16:19   ` [PATCH v4 0/3] Option to show stopped/running " Tankut Baris Aktemur
2025-05-05 16:19     ` [PATCH v4 1/3] gdb: pass info_threads_opts to print_thread_info_1 Tankut Baris Aktemur
2025-05-05 16:19     ` [PATCH v4 2/3] gdb: update "info threads" output when no threads match the arguments Tankut Baris Aktemur
2025-05-05 17:19       ` Eli Zaretskii
2025-05-05 16:19     ` Tankut Baris Aktemur [this message]
2025-05-05 17:21       ` [PATCH v4 3/3] gdb: add '-stopped' and '-running' options to "info threads" Eli Zaretskii
2025-05-09 20:54     ` [PATCH v4 0/3] Option to show stopped/running threads only Pedro Alves

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a8badb171addff28a7b506303a01ac7ff03482f9.1746461232.git.tankut.baris.aktemur@intel.com \
    --to=tankut.baris.aktemur@intel.com \
    --cc=eliz@gnu.org \
    --cc=gdb-patches@sourceware.org \
    --cc=guinevere@redhat.com \
    --cc=pedro@palves.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox