aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp
blob: b5c0d84afbfaf48176ec64b1f1a0d84c02db091f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# Copyright 2022-2024 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 for conditional breakpoints where the breakpoint condition includes
# an inferior function call.
#
# The tests in this script are testing what happens when an event arrives in
# another thread while GDB is waiting for the inferior function call (in the
# breakpoint condition) to finish.
#
# The expectation is that GDB will queue events for other threads and wait
# for the inferior function call to complete, if the condition is true, then
# the conditional breakpoint should be reported first.  The other thread
# event should of course, not get lost, and should be reported as soon as
# the user tries to continue the inferior.
#
# If the conditional breakpoint ends up not being taken (the condition is
# false), then the other thread event should be reported immediately.
#
# This script tests what happens when the other thread event is (a) the
# other thread hitting a breakpoint, and (b) the other thread taking a
# signal (SIGSEGV in this case).

standard_testfile

if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \
	  {debug pthreads}] == -1 } {
    return
}

set cond_bp_line [gdb_get_line_number "First thread breakpoint"]
set other_bp_line [gdb_get_line_number "Other thread breakpoint"]
set final_bp_line [gdb_get_line_number "Final breakpoint here"]
set signal_line [gdb_get_line_number "Signal here"]

# Start GDB based on TARGET_ASYNC and TARGET_NON_STOP, and then runto main.
proc start_gdb_and_runto_main { target_async target_non_stop } {
    save_vars { ::GDBFLAGS } {
	append ::GDBFLAGS \
	    " -ex \"maint set target-non-stop $target_non_stop\""
	append ::GDBFLAGS \
	    " -ex \"maintenance set target-async ${target_async}\""

	clean_restart ${::binfile}
    }

    if { ![runto_main] } {
	return -1
    }

    return 0
}

# Run a test of GDB's conditional breakpoints, where the conditions include
# inferior function calls.  While the inferior function call is executing
# another thread will hit a breakpoint (when OTHER_THREAD_SIGNAL is false),
# or receive a signal (when OTHER_THREAD_SIGNAL is true).  GDB should report
# the conditional breakpoint first (if the condition is true), and then
# report the second thread event once the inferior is continued again.
#
# When STOP_AT_COND is true then the conditional breakpoint will have a
# condition that evaluates to true (and GDB will stop at the breakpoint),
# otherwise, the condition will evaluate to false (and GDB will not stop at
# the breakpoint).
proc run_condition_test { stop_at_cond other_thread_signal \
			      target_async target_non_stop } {
    if { [start_gdb_and_runto_main $target_async \
	      $target_non_stop] == -1 } {
	return
    }

    # Setup the conditional breakpoint.
    if { $stop_at_cond } {
	set cond_func "condition_true_func"
    } else {
	set cond_func "condition_false_func"
    }
    gdb_breakpoint \
	"${::srcfile}:${::cond_bp_line} if (${cond_func} ())"
    set cond_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
			"get number for conditional breakpoint"]

    if { $other_thread_signal } {
	# Arrange for the other thread to raise a signal while GDB is
	# evaluating the breakpoint condition.
	gdb_test_no_output "set raise_signal = 1"
    } else {
	# And a breakpoint that will be hit by another thread only once the
	# breakpoint condition starts to be evaluated.
	gdb_breakpoint "${::srcfile}:${::other_bp_line}"
	set other_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
			      "get number for other breakpoint"]
    }

    # A final breakpoint once the test has completed.
    gdb_breakpoint "${::srcfile}:${::final_bp_line}"
    set final_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
			  "get number for final breakpoint"]

    if { $stop_at_cond } {
	# Continue.  The first breakpoint we hit should be the conditional
	# breakpoint.  The other thread will have hit its breakpoint, but
	# that will have been deferred until the conditional breakpoint is
	# reported.
	gdb_test "continue" \
	    [multi_line \
		 "Continuing\\." \
		 ".*" \
		 "" \
		 "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${cond_bp_num}, worker_func \[^\r\n\]+:${::cond_bp_line}" \
		 "${::decimal}\\s+\[^\r\n\]+First thread breakpoint\[^\r\n\]+"] \
	    "hit the conditional breakpoint"
    }

    if { $other_thread_signal } {
	# Now continue again, the other thread will now report that it
	# received a signal.
	gdb_test "continue" \
	    [multi_line \
		 "Continuing\\." \
		 ".*" \
		 "Thread ${::decimal} \"\[^\"\r\n\]+\" received signal SIGSEGV, Segmentation fault\\." \
		 "\\\[Switching to Thread \[^\r\n\]+\\\]" \
		 "${::hex} in worker_func \[^\r\n\]+:${::signal_line}" \
		 "${::decimal}\\s+\[^\r\n\]+Signal here\[^\r\n\]+"] \
	    "received signal in other thread"
    } else {
	# Now continue again, the other thread will now report its
	# breakpoint.
	gdb_test "continue" \
	    [multi_line \
		 "Continuing\\." \
		 ".*" \
		 "" \
		 "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${other_bp_num}, worker_func \[^\r\n\]+:${::other_bp_line}" \
		 "${::decimal}\\s+\[^\r\n\]+Other thread breakpoint\[^\r\n\]+"] \
	    "hit the breakpoint in other thread"

	# Run to the stop marker.
	gdb_test "continue" \
	    [multi_line \
		 "Continuing\\." \
		 ".*" \
		 "" \
		 "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${final_bp_num}, stop_marker \[^\r\n\]+:${::final_bp_line}" \
		 "${::decimal}\\s+\[^\r\n\]+Final breakpoint here\[^\r\n\]+"] \
	    "hit the final breakpoint"
    }

    gdb_exit
}

foreach_with_prefix target_async { "on" "off" } {
    foreach_with_prefix target_non_stop { "on" "off" } {
	foreach_with_prefix other_thread_signal { true false } {
	    foreach_with_prefix stop_at_cond { true false } {
		run_condition_test $stop_at_cond $other_thread_signal \
		    $target_async $target_non_stop
	    }
	}
    }
}