1 # Copyright 2023-2024 Free Software Foundation, Inc.
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.
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.
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/>.
16 # Check that the =breakpoint-deleted notification for a thread-specific
17 # breakpoint is sent as soon as the related thread exits, and not when the
18 # inferior next stops.
20 # This test is based on gdb.threads/thread-bp-deleted.exp.
22 load_lib mi-support.exp
25 # We need to do things a little differently when using the remote protocol.
27 [expr [target_info exists gdb_protocol] \
28 && ([string equal [target_info gdb_protocol] "remote"] \
29 || [string equal [target_info gdb_protocol] "extended-remote"])]
33 if { [build_executable "failed to prepare" $testfile $srcfile \
38 foreach_mi_ui_mode mode {
41 if {$mode eq "separate"} {
42 set start_ops "separate-mi-tty"
47 # Restart, but enable non-stop mode, we need it for background
49 save_vars { GDBFLAGS } {
50 append GDBFLAGS " -ex \"maint set target-non-stop on\""
51 append GDBFLAGS " -ex \"set mi-async on\""
52 mi_clean_restart $binfile $start_ops
57 if {![mi_detect_async]} {
58 unsupported "async-mode is required"
64 # Place a breakpoint on 'breakpt' and run to this breakpoint.
65 mi_create_breakpoint "breakpt" "place breakpoint on breakpt"
66 set breakpt_num [mi_get_valueof "/d" "\$bpnum" "INVALID" \
67 "get number for breakpt breakpoint"]
68 mi_execute_to "exec-continue" "breakpoint-hit" "breakpt" "" \
69 ".*" ".*" {"" "disp=\"keep\""} \
70 "continue to breakpoint in breakpt"
72 # Now drain all the pending output from the CLI if we are using a separate
74 if {$mode eq "separate"} {
75 with_spawn_id $gdb_main_spawn_id {
76 gdb_test_multiple "" "drain CLI output upto breakpoint" {
77 -re "Thread 1 \[^\r\n\]+ hit Breakpoint $decimal,\
79 \[^\r\n\]+\r\n$decimal\\s+\[^\r\n\]+\r\n" {
86 # This is just for convenience, this refers to the second thread the
90 # Create a thread-specific breakpoint.
91 mi_create_breakpoint "-p $worker_thread main" \
92 "place thread breakpoint on main" \
93 -thread "$worker_thread"
94 set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID" \
95 "get number for thread-specific breakpoint"]
97 set loc1 [mi_make_breakpoint -number "$breakpt_num"]
98 set loc2 [mi_make_breakpoint -number "$bpnum" -thread "$worker_thread"]
99 set table_2_locs [mi_make_breakpoint_table [list $loc1 $loc2]]
100 set table_1_locs [mi_make_breakpoint_table [list $loc1]]
102 mi_gdb_test "-break-info" \
103 "\\^done,$table_2_locs" \
104 "-break-info, expecting two locations"
106 # Resume the inferior, at this point the inferior will spin while
107 # we interact with it.
108 mi_send_resuming_command "exec-continue" "continue"
110 # Look for the thread-exited notification and the breakpoint-deleted
111 # notification. When using a single UI we see both the MI and CLI
112 # messages. When using a separate MI UI we only see the MI messages.
113 set saw_cli_thread_exited false
114 set saw_mi_thread_exited false
115 set saw_cli_bp_deleted false
116 set saw_mi_bp_deleted false
118 # When running with a remote target, the thread-exited event doesn't
119 # appear to be pushed from the target to GDB; instead GDB has to fetch the
120 # thread list from the target and spot that a thread exited.
122 # In order to achieve this, when running with a remote target we run the
123 # '-thread-info 99' command. There isn't a thread 99, but GDB doesn't
124 # know that until it fetches the thread list. By fetching the thread list
125 # GDB will spot that the thread we are interested in has exited.
127 set cmd "-thread-info 99"
135 gdb_test_multiple $cmd "collect thread exited output" \
136 -prompt "$::mi_gdb_prompt$" {
138 -re "^~\"\\\[Thread \[^\r\n\]+ exited\\\]\\\\n\"\r\n" {
139 set saw_cli_thread_exited true
143 -re "^~\"Thread-specific breakpoint $bpnum deleted -\
144 thread $worker_thread no longer in the thread list\\.\\\\n\"\r\n" {
145 set saw_cli_bp_deleted true
149 -re "^=thread-exited,id=\"$worker_thread\",group-id=\"i1\"\r\n" {
150 set saw_mi_thread_exited true
152 # The order of the MI notifications depends on the order in which
153 # the observers where registered within GDB. If we have not seen
154 # the other MI notification yet then keep looking.
156 # Additionally, for remote targets, we're going to wait for the
157 # output of the '-thread-info 99' command before we check the
159 if {!$saw_mi_bp_deleted || $is_remote} {
163 # We get here with a native target; check we saw all the output
165 if {$mode eq "separate"} {
166 gdb_assert { $saw_mi_thread_exited && $saw_mi_bp_deleted \
167 && !$saw_cli_thread_exited \
168 && !$saw_cli_bp_deleted } \
171 gdb_assert { $saw_mi_thread_exited && $saw_mi_bp_deleted \
172 && $saw_cli_thread_exited \
173 && $saw_cli_bp_deleted } \
178 -re "^=breakpoint-deleted,id=\"3\"\r\n" {
179 set saw_mi_bp_deleted true
181 # The order of the MI notifications depends on the order in which
182 # the observers where registered within GDB. If we have not seen
183 # the other MI notification yet then keep looking.
185 # Additionally, for remote targets, we're going to wait for the
186 # output of the '-thread-info 99' command before we check the
188 if {!$saw_mi_thread_exited || $is_remote} {
192 # We get here with a native target; check we saw all the output
194 if {$mode eq "separate"} {
195 gdb_assert { $saw_mi_thread_exited && $saw_mi_bp_deleted \
196 && !$saw_cli_thread_exited \
197 && !$saw_cli_bp_deleted } \
200 gdb_assert { $saw_mi_thread_exited && $saw_mi_bp_deleted \
201 && $saw_cli_thread_exited \
202 && $saw_cli_bp_deleted } \
207 -re "^-thread-info 99\r\n" {
209 fail "$gdb_test_name (unexpected output)"
211 # This is the command being echoed back, ignore it.
215 -re "^\\^done,threads=\\\[\\\]\r\n$::mi_gdb_prompt$" {
217 # This is the result of the '-thread-info 99' trick, which is only
218 # used in remote mode. If we see this in native mode then
219 # something has gone wrong.
221 fail "$gdb_test_name (unexpected output)"
224 # If we've not seen any of the expected output yet then maybe the
225 # remote thread just hasn't exited yet. Wait a short while and
227 if { !$saw_mi_thread_exited && !$saw_mi_bp_deleted \
228 && !$saw_cli_thread_exited && !$saw_cli_bp_deleted \
229 && $attempt_count > 0 } {
231 incr attempt_count -1
236 # The output has arrived! Check how we did. There are other bugs
237 # that come into play here which change what output we'll see.
238 gdb_assert { $saw_mi_thread_exited && $saw_mi_bp_deleted \
239 && $saw_cli_thread_exited \
240 && $saw_cli_bp_deleted } $gdb_test_name
244 # When the MI is running on a separate UI the CLI message will be seen
245 # over there, but only if we are not running remote. When we are running
246 # remote then the thread-exited event will only be triggered as a result
247 # of user triggering a refresh of the thread list (hence the '-thread-info
248 # 99' trick above). By typing a command we change the current UI to the
249 # terminal we are typing at, as a result these CLI style message will
250 # actually appear on the MI when using a remote target.
251 if {$mode eq "separate" && !$is_remote} {
252 with_spawn_id $gdb_main_spawn_id {
253 set saw_thread_exited false
254 gdb_test_multiple "" "collect cli thread exited output" {
255 -re "\\\[Thread \[^\r\n\]+ exited\\\]\r\n" {
256 set saw_thread_exited true
260 -re "^Thread-specific breakpoint $bpnum deleted -\
261 thread $worker_thread no longer in the thread list\\.\r\n" {
262 gdb_assert { $saw_thread_exited } \
269 mi_gdb_test "-break-info" \
270 "\\^done,$table_1_locs" \
271 "-break-info, expecting one location"
273 # Set 'do_spin' to zero, this allows the inferior to progress again; we
274 # should then hit the breakpoint in 'breakpt' again.
275 mi_gdb_test "set var do_spin = 0" \
277 ".*=memory-changed,thread-group=\"i${decimal}\".addr=\"${hex}\",len=\"${hex}\"" \
279 "set do_spin variable in inferior, inferior should now finish"
280 mi_expect_stop "breakpoint-hit" "breakpt" ".*" ".*" "$::decimal" \
281 {"" "disp=\"keep\""} "stop in breakpt at the end of the test"