From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id UOi0HI7lGGjNCRYAWB0awg (envelope-from ) for ; Mon, 05 May 2025 12:21:34 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=BoGueIJ0; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 703111E10E; Mon, 5 May 2025 12:21:34 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-10.1 required=5.0 tests=ARC_SIGNED,ARC_VALID, BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED, RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 972AE1E089 for ; Mon, 5 May 2025 12:21:32 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 271F93858435 for ; Mon, 5 May 2025 16:21:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 271F93858435 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=BoGueIJ0 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.19]) by sourceware.org (Postfix) with ESMTPS id E0F7D3858C60 for ; Mon, 5 May 2025 16:20:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E0F7D3858C60 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E0F7D3858C60 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.19 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1746462015; cv=none; b=nw98au/1Z4q17dgbFzvx6HQ3oQ3bhnRVdfgHBr06i3qJmL7nQfY9UDT9oX00qzSONwA5vtHf7XjuLwZKW6ICBhAemDmV2O9mXfqvN9VvEuMeUZIa0D6xLlLCSrMjw3G2Aa3RnmP+wiv2JWMoPwe/h90tnu9nWEF8QMSd09OtzQI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1746462015; c=relaxed/simple; bh=rXYpaInkR6sBSaILB4vjHw23vMtrSCwgIfpFAjU2qTI=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=VJYzOqFZUtNExy/JjcILqBVrpNhb6kU5iaaMd03CdkWLQWyiGc+hWyS6jeylHUPgenpsrwrjzAW1C+757h53QuGML0OR6TMY0N0TcoitDv/7B8mNyz7qDk1sx2x9At5IMgT38osnTSlxHWcV/MKpldUi5wCnzdx2Z/gBF8BnNck= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E0F7D3858C60 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1746462015; x=1777998015; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=rXYpaInkR6sBSaILB4vjHw23vMtrSCwgIfpFAjU2qTI=; b=BoGueIJ0AxPMLRfz86Rd5K+rA9ovBOoWB1DuHyQCKuJjfdEVs9x8p8LN a4ad2BCieso9691VmR6zF+rL0MQUKHyJ7jZobHiXVMufrJw07YfDlFxYK sv5rFy+0+TQP0G2SZAH6Z13zSDXBcol0cNubt/MEiQh2MYYQ6C8gX3qRk OZSIDJt7nu7S9XXGOGiuT+3iNnAAmDIDlwdWwoDQ9WYl6XhIpZYnFzc8Q FnX1dD7RX3j5zR/Tl9r9L+rtGlo9CgKShc9rqlG/Yz5Udeg7z1ZyhxrtO Uk2jEovW37q99/3SJl0YHibn1MXN/CKbFZ9+EsruSmlebgEndFRC53jr/ Q==; X-CSE-ConnectionGUID: BVGepAdCSpmQ3LGZaPUl/Q== X-CSE-MsgGUID: nwaYlZ2bSr6gT4mLjpYFEg== X-IronPort-AV: E=McAfee;i="6700,10204,11424"; a="47961828" X-IronPort-AV: E=Sophos;i="6.15,264,1739865600"; d="scan'208";a="47961828" Received: from fmviesa001.fm.intel.com ([10.60.135.141]) by orvoesa111.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 May 2025 09:20:14 -0700 X-CSE-ConnectionGUID: CbyV4YJaTNys7gyNLYIq1Q== X-CSE-MsgGUID: 7ciwUoIUQF+NRXvRv+sc8g== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,264,1739865600"; d="scan'208";a="166352103" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 May 2025 09:20:12 -0700 From: Tankut Baris Aktemur 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 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org 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 --- 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 . */ + +#include +#include +#include +#include + +#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 . + +# 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 { + "running", + [] (info_threads_opts *opts) { return &opts->show_running_threads; }, + N_("Show running threads only."), + }, + gdb::option::flag_option_def { + "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