]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - 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
1 # Copyright 2023-2024 Free Software Foundation, Inc.
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
37 standard_testfile
38
39 require allow_python_tests
40
41 if {[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
50 proc 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
132 foreach_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 }