import gdb
from .server import request
+from .startup import in_gdb_thread
+@in_gdb_thread
def _thread_name(thr):
if thr.name is not None:
return thr.name
if thr.details is not None:
return thr.details
- return None
+ # Always return a name, as the protocol doesn't allow for nameless
+ # threads. Use the local thread number here... it doesn't matter
+ # without multi-inferior but in that case it might make more
+ # sense.
+ return f"Thread #{thr.num}"
-@request("threads")
+@request("threads", expect_stopped=False)
def threads(**args):
result = []
for thr in gdb.selected_inferior().threads():
- one_result = {
- "id": thr.global_num,
- }
- name = _thread_name(thr)
- if name is not None:
- one_result["name"] = name
- result.append(one_result)
+ result.append(
+ {
+ "id": thr.global_num,
+ "name": _thread_name(thr),
+ }
+ )
return {
"threads": result,
}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-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 2
+
+static pthread_barrier_t threads_started_barrier;
+
+static void *
+thread_function (void *arg)
+{
+ pthread_barrier_wait (&threads_started_barrier);
+
+ while (1)
+ sleep (1);
+
+ pthread_exit (NULL);
+}
+
+static void
+all_started (void)
+{
+}
+
+int
+main ()
+{
+ pthread_t threads[NUM];
+ long i;
+
+ pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1);
+
+ for (i = 1; i <= NUM; i++)
+ {
+ int res;
+
+ res = pthread_create (&threads[i - 1], NULL, thread_function, NULL);
+ }
+
+ pthread_barrier_wait (&threads_started_barrier);
+
+ all_started ();
+
+ printf ("sleeping\n");
+ fflush (stdout);
+ sleep (180);
+
+ exit (EXIT_SUCCESS);
+}
--- /dev/null
+# Copyright 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 DAP "threads" request.
+
+require allow_shlib_tests allow_dap_tests
+
+load_lib dap-support.exp
+
+standard_testfile
+
+set libname $testfile-solib
+set srcfile_lib $srcdir/$subdir/$libname.c
+set binfile_lib [standard_output_file $libname.so]
+
+if {[build_executable "failed to prepare" $testfile $srcfile \
+ {debug pthreads}] == -1} {
+ return
+}
+
+if {[dap_initialize] == ""} {
+ return
+}
+
+set launch_id [dap_launch $testfile]
+
+set obj [dap_check_request_and_response "set breakpoint on all_started function" \
+ setFunctionBreakpoints \
+ {o breakpoints [a [o name [s all_started]]]}]
+set fn_bpno [dap_get_breakpoint_number $obj]
+
+dap_check_request_and_response "configurationDone" configurationDone
+
+dap_check_response "launch response" launch $launch_id
+
+lassign [dap_wait_for_event_and_check "stopped at function breakpoint" \
+ stopped \
+ "body reason" breakpoint \
+ "body hitBreakpointIds" $fn_bpno] \
+ ignore \
+ all_events
+
+# Verify that we saw the correct number of thread events.
+set count 0
+foreach event $all_events {
+ if {[dict get $event type] == "event"
+ && [dict get $event event] == "thread"
+ && [dict get $event body reason] == "started"} {
+ incr count
+ }
+}
+gdb_assert {$count == 3} "correct number of thread events"
+
+dap_check_request_and_response "continue" continue \
+ {o threadId [i 1]}
+
+# Make sure that the inferior has really re-started -- note that there
+# is no "continue" event, because the "continue" request suppresses
+# those.
+dap_wait_for_event_and_check "output from inferior" output \
+ {body output} "sleeping\\n"
+
+lassign [dap_check_request_and_response "threads request" threads] \
+ response ignore
+
+gdb_assert {[llength [dict get $response body threads]] == 3} \
+ "correct number of threads"
+
+dap_shutdown true