]>
Commit | Line | Data |
---|---|---|
1d506c26 | 1 | # Copyright 2023-2024 Free Software Foundation, Inc. |
75b2eb97 AB |
2 | |
3 | # This program is free software; you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation; either version 3 of the License, or | |
6 | # (at your option) any later version. | |
7 | # | |
8 | # This program is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License | |
14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | ||
16 | # Check that the $_inferior_thread_count convenience variable will | |
17 | # correctly update its value without passing through a normal stop | |
18 | # event. | |
19 | # | |
20 | # When GDB is using a remote (or extended-remote) target, GDB only | |
21 | # learns about changes to the thread list be explicitly querying the | |
22 | # target. This is done as part of a normal stop, but there are some | |
23 | # situations where the thread list can change, but GDB doesn't pass | |
24 | # through a normal stop. | |
25 | # | |
26 | # For example, when the target is running asynchronously in non-stop | |
27 | # mode; in this case GDB can query the thread list without the target | |
28 | # ever stopping. | |
29 | # | |
30 | # Or after an inferior function call. | |
31 | # | |
32 | # The solution is to ensure that $_inferior_thread_count explicitly | |
33 | # queries the target to update the thread list. This test checks that | |
34 | # this is done. | |
35 | ||
36 | standard_testfile | |
37 | ||
38 | if {[build_executable "failed to prepare" $testfile $srcfile \ | |
39 | {debug pthreads}] == -1} { | |
40 | return -1 | |
41 | } | |
42 | ||
43 | # Start GDB. Ensure we are in non-stop mode as we need to read from | |
44 | # the inferior while it is running. | |
45 | save_vars {GDBFLAGS} { | |
46 | append GDBFLAGS " -ex \"set non-stop on\"" | |
47 | clean_restart $binfile | |
48 | } | |
49 | ||
50 | if ![runto_main] { | |
51 | return -1 | |
52 | } | |
53 | ||
54 | gdb_breakpoint breakpt | |
55 | gdb_continue_to_breakpoint "first breakpt call" | |
56 | ||
57 | # Check we can see a single thread to begin with. | |
58 | gdb_test "p \$_inferior_thread_count" \ | |
59 | "^\\\$$::decimal = 1" \ | |
60 | "only one thread in \$_inferior_thread_count" | |
61 | ||
62 | # We don't want thread events, it makes it harder to match GDB's | |
63 | # output. | |
64 | gdb_test_no_output "set print thread-events off" | |
65 | ||
66 | # Continue the program in the background. | |
67 | set test "continue&" | |
68 | gdb_test_multiple "continue&" $test { | |
69 | -re "Continuing\\.\r\n$gdb_prompt " { | |
70 | pass $test | |
71 | } | |
72 | } | |
73 | ||
74 | # Read the 'stage' flag from the inferior. This is initially 0, but | |
75 | # will be set to 1 once the extra thread has been created, and then 2 | |
76 | # once the extra thread has exited. | |
77 | # | |
78 | # We catch the case where we can't read from the inferior while the | |
79 | # inferior is running, this happens if the target hasn't entered | |
80 | # non-stop mode like we asked. In this case we interrupt the inferior | |
81 | # and bail. | |
82 | # | |
83 | # Otherwise, if we can read from the inferior we try at most 10 times | |
84 | # to read the flag (with a 1 second delay after each read). If the | |
85 | # flag isn't set after this then we bail. The inferior is either very | |
86 | # slow, or the thread hasn't started for some reason. | |
87 | proc wait_for_stage { num } { | |
88 | set failure_count 0 | |
89 | set cmd "print /d stage" | |
90 | set stage_flag 0 | |
91 | gdb_test_multiple "$cmd" "wait for 'stage' flag to be $num" { | |
92 | -re -wrap "^Cannot execute this command while the target is running\\.\r\nUse the \"interrupt\" command to stop the target\r\nand then try again\\." { | |
93 | fail "$gdb_test_name (can't read asynchronously)" | |
94 | gdb_test_no_output "interrupt" | |
95 | ||
96 | gdb_test_multiple "" "wait for thread to stop" { | |
97 | -re "Thread .* received signal .*" { | |
98 | pass $gdb_test_name | |
99 | gdb_test "p 1 + 2" " = 3" | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | -re -wrap "^\\$\[0-9\]* = (\[-\]*\[0-9\]*).*" { | |
105 | set stage_flag $expect_out(1,string) | |
106 | if {$stage_flag != $num} { | |
107 | set stage_flag 0 | |
108 | incr failure_count | |
109 | if { $failure_count < 10 } { | |
110 | sleep 1 | |
111 | send_gdb "$cmd\n" | |
112 | exp_continue | |
113 | } | |
114 | fail $gdb_test_name | |
115 | } else { | |
116 | pass $gdb_test_name | |
117 | } | |
118 | } | |
119 | } | |
120 | ||
121 | return $stage_flag | |
122 | } | |
123 | ||
124 | # Wait until we can see that the extra thread has been created. | |
125 | if {![wait_for_stage 1]} { | |
126 | unresolved "failed to see thread start" | |
127 | return -1 | |
128 | } | |
129 | ||
130 | ||
131 | if {[target_info exists gdb_protocol] | |
132 | && ([target_info gdb_protocol] == "remote" | |
133 | || [target_info gdb_protocol] == "extended-remote")} { | |
134 | set new_thread_re "\\\[New Thread \[^\r\n\]+\\\]\r\n" | |
135 | set exit_thread_re "\\\[Thread \[^\r\n\]+ exited\\\]\r\n" | |
136 | } else { | |
137 | set new_thread_re "" | |
138 | set exit_thread_re "" | |
139 | } | |
140 | ||
141 | # This is the test we actually care about. Check that the | |
142 | # $_inferior_thread_count convenience variable shows the correct | |
143 | # thread count; the new thread should be visible. | |
144 | gdb_test "with print thread-events on -- p \$_inferior_thread_count" \ | |
145 | "^${new_thread_re}\\\$$::decimal = 2" \ | |
146 | "second thread visible in \$_inferior_thread_count" | |
147 | ||
148 | # Set a variable in the inferior, this will cause the second thread to | |
149 | # exit. | |
150 | gdb_test_no_output "set variable spin = 0" \ | |
151 | "set 'spin' flag to allow worker thread to exit" | |
152 | ||
153 | # Wait until the extra thread has exited. | |
154 | if {![wait_for_stage 2]} { | |
155 | unresolved "failed to see thread start" | |
156 | return -1 | |
157 | } | |
158 | ||
159 | # Check that the second thread has gone away. | |
160 | gdb_test "with print thread-events on -- p \$_inferior_thread_count" \ | |
161 | "^${exit_thread_re}\\\$$::decimal = 1" \ | |
162 | "back to one thread visible in \$_inferior_thread_count" | |
163 | ||
164 | # Set a variable in the inferior, this will cause the second thread to | |
165 | # exit. | |
166 | gdb_test_no_output "set variable spin = 0" \ | |
167 | "set 'spin' flag to allow main thread to exit" | |
168 | ||
169 | # When the second thread exits, the main thread joins with it, and | |
170 | # then proceeds to hit the breakpt function again. | |
171 | gdb_test_multiple "" "wait for main thread to stop" { | |
172 | -re "Thread 1 \[^\r\n\]+ hit Breakpoint $decimal, breakpt \\(\\)\[^\r\n\]+\r\n\[^\r\n\]+\r\n" { | |
173 | pass $gdb_test_name | |
174 | } | |
175 | } |