]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/testsuite/gdb.threads/main-thread-exit-during-detach.exp
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / testsuite / gdb.threads / main-thread-exit-during-detach.exp
CommitLineData
1d506c26 1# Copyright 2023-2024 Free Software Foundation, Inc.
fd492bf1
AB
2# This program is free software; you can redistribute it and/or modify
3# it under the terms of the GNU General Public License as published by
4# the Free Software Foundation; either version 3 of the License, or
5# (at your option) any later version.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.
14
15# Check for a race condition where in non-stop mode, the user might
16# have a thread other than the main (original) thread selected and use
17# the 'detach' command.
18#
19# As GDB tries to detach it is possible that the main thread might
20# exit, the main thread is still running due to non-stop mode.
21#
22# GDB used to assume that the main thread would always exist when
23# processing the detach, clearly this isn't the case, and this
24# assumption would lead to assertion failures and segfaults.
25#
26# Triggering the precise timing is pretty hard, we need the main
27# thread to exit after the user has entered the 'detach' command, but
28# before GDB enters the detach implementation and stops all threads,
29# the window of opportunity for this bug is actually tiny.
30#
31# However, we can trigger this bug 100% from Python, as GDB's
32# event-loop only kicks in once we return from a Python function.
33# Thus, if we have a single Python function that causes the main
34# thread to exit, and then calls detach GDB will not have a chance to
35# handle the main thread exiting before entering the detach code.
36
37standard_testfile
38
39require allow_python_tests
40
41if {[build_executable "failed to prepare" $testfile $srcfile \
42 {debug pthreads}] == -1} {
43 return -1
44}
45
46# Run the test. When SPAWN_INFERIOR is true the inferior is started
47# as a separate process which GDB then attaches too. When
48# SPAWN_INFERIOR is false the inferior is started directly within GDB.
49
50proc run_test { spawn_inferior } {
51 save_vars { ::GDBFLAGS } {
52 append ::GDBFLAGS " -ex \"set non-stop on\""
53 clean_restart $::binfile
54 }
55
56 # Setup the inferior. When complete the main thread (#1) will
57 # still be running (due to non-stop mode), while the worker thread
58 # (#2) will be stopped.
59 #
60 # There are two setup modes, when SPAWN_INFERIOR is true we span a
61 # separate process and attach to it, after the attach both threads
62 # are stopped, so it is necessary to resume thread #1.
63 #
64 # When SPAWN_INFERIOR is false we just start the inferior within
65 # GDB, in this case we place a breakpoint that will be hit by
66 # thread #2. When the breakpoint is hit thread #1 will remain
67 # running.
68 if {$spawn_inferior} {
69 set test_spawn_id [spawn_wait_for_attach $::binfile]
70 set testpid [spawn_id_get_pid $test_spawn_id]
71
72 set escapedbinfile [string_to_regexp $::binfile]
73 gdb_test -no-prompt-anchor "attach $testpid" \
74 "Attaching to program.*`?$escapedbinfile'?, process $testpid.*" \
75 "attach to the inferior"
76
77 # Attaching to a multi-threaded application in non-stop mode
78 # can result in thread stops being reported after the prompt
79 # is displayed.
80 #
81 # Send a simple command now just to resync the command prompt.
82 gdb_test "p 1 + 2" " = 3"
83
84 # Set thread 1 (the current thread) running again.
85 gdb_test "continue&"
86 } else {
87 if {![runto_main]} {
88 return -1
89 }
90
91 gdb_breakpoint "breakpt"
92 gdb_continue_to_breakpoint "run to breakpoint"
93 }
94
95 # Switch to thread 2.
96 gdb_test "thread 2" \
97 [multi_line \
98 "Switching to thread 2\[^\r\n\]*" \
99 "#0\\s+.*"]
100
101 # Create a Python function that sets a variable in the inferior and
102 # then detaches. Setting the variable in the inferior will allow the
103 # main thread to exit, we even sleep for a short while in order to
104 # give the inferior a chance to exit.
105 #
106 # However, we don't want GDB to notice the exit before we call detach,
107 # which is why we perform both these actions from a Python function.
108 gdb_test_multiline "Create worker function" \
109 "python" "" \
110 "import time" "" \
111 "def set_and_detach():" "" \
112 " gdb.execute(\"set variable dont_exit_just_yet=0\")" "" \
113 " time.sleep(1)" "" \
114 " gdb.execute(\"detach\")" "" \
115 "end" ""
116
117 # The Python function performs two actions, the first causes the
118 # main thread to exit, while the second detaches from the inferior.
119 #
120 # In both cases the stop arrives while GDB is processing the
121 # detach, however, for remote targets GDB doesn't report the stop,
122 # while for local targets GDB does report the stop.
123 if {![gdb_is_target_remote]} {
124 set stop_re "\\\[Thread.*exited\\\]\r\n"
125 } else {
126 set stop_re ""
127 }
128 gdb_test "python set_and_detach()" \
129 "${stop_re}\\\[Inferior.*detached\\\]"
130}
131
132foreach_with_prefix spawn_inferior { true false } {
133 if {$spawn_inferior && ![can_spawn_for_attach]} {
134 # If spawning (and attaching too) a separate inferior is not
135 # supported for the current board, then skip this test.
136 continue
137 }
138
139 run_test $spawn_inferior
140}