aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/break-idempotent.exp
blob: 2b55130e3e80595b189e1e1e14afbb04f1fa631c (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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# Copyright 2014-2021 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 that the Zx breakpoint/watchpoint packets are idempotent.

# GDBserver used to not treat Zx breakpoints other than Z0 as
# idempotent, although it must, to avoid problems with
# retransmissions.  Even without spurious transport problems, if the
# target supports target conditions or commands, GDB re-inserts Zx
# breakpoints even if they are already inserted, to update the
# target-side condition/commands.  E.g., simply when a duplicate
# breakpoint is created, or when a shared library load causes a
# re-set, which creates duplicate locations while breakpoints are
# inserted, or when the condition is really changed while breakpoints
# are inserted.  To make the test not depend on shared library support
# or on details of the breakpoint re-set implementation, or on GDB
# optimizing out re-sends if the condition hasn't actually changed, we
# force always-inserted on, and really change the breakpoint's
# condition.  For good measure, test with both always-inserted "on"
# and "off" modes.

# The test is written in black-box style, and doesn't actually use
# anything target remote specific, so let it run on all targets.

standard_testfile

# Force a breakpoint re-set in GDB.  Currently this is done by
# reloading symbols with the "file" command.

proc force_breakpoint_re_set {} {
    global binfile gdb_prompt

    set test "file \$binfile"
    gdb_test_multiple "file $binfile" $test {
	-re "Are you sure you want to change the file. .*y or n. $" {
	    send_gdb "y\n" optional
	    exp_continue
	}
	-re "Load new symbol table from \".*\".*y or n. $" {
	    send_gdb "y\n" optional
	    exp_continue
	}
	-re "Reading symbols from.*$gdb_prompt $" {
	    pass $test
	}
    }
}

# Set a break/hbreak/watch/rwatch/awatch.

proc set_breakpoint { break_command } {
    global gdb_prompt srcfile

    if { $break_command == "break" } {
	gdb_test "$break_command foo" "Breakpoint.*at.* file .*$srcfile, line.*"
    } elseif { $break_command == "hbreak" } {
	set test "$break_command foo"
	gdb_test_multiple $test $test {
	    -re "No hardware breakpoint support in the target.*$gdb_prompt $" {
		unsupported $test
	    }
	    -re "Hardware breakpoints used exceeds limit.*$gdb_prompt $" {
		unsupported $test
	    }
	    -re "Cannot insert hardware breakpoint.*$gdb_prompt $" {
		unsupported $test
	    }
	    -re "Hardware assisted breakpoint.*at.* file .*$srcfile, line.*$gdb_prompt $" {
		pass $test
	    }
	}
    } elseif { [string first "watch" $break_command] != -1 } {
	set test "$break_command global"
	gdb_test_multiple $test $test {
	    -re "Target does not support this type of hardware watchpoint\\.\r\n$gdb_prompt $" {
		unsupported $test
	    }
	    -re "Could not insert hardware watchpoint.*$gdb_prompt $" {
		unsupported $test
	    }
	    -re "atchpoint \[0-9\]+: global\r\n$gdb_prompt $" {
		pass $test
	    }
	}
    } else {
	error "unhandled command: $break_command"
    }
}

# Run the test proper.  ALWAYS_INSERT determines whether
# always-inserted mode is on/off, and BREAK_COMMAND is the
# break/watch/etc. command being tested.
#
proc test_break { always_inserted break_command } {
    set cmd [lindex [split "$break_command"] 0]

    with_test_prefix "$cmd" {
	delete_breakpoints

	if ![runto_main] then {
	    fail "can't run to main"
	    return
	}

	gdb_test_no_output "set breakpoint always-inserted $always_inserted"

	# Set breakpoints/watchpoints twice.  With always-inserted on,
	# GDB reinserts the exact same Z breakpoint twice...  Do this
	# to make sure the stub pays attention to idempotency even
	# when the condition doesn't change.  If GDB end up optimizing
	# out exact duplicate packets, we should come up with a way to
	# keep testing this case.
	foreach iter { "once" "twice" } {
	    with_test_prefix $iter {
		set_breakpoint $break_command
	    }
	}

	# Force a breakpoint re-set.  In always-inserted mode, this
	# makes GDB re-send Z packets too...
	force_breakpoint_re_set

	# Now really change the condition, which forces a reinsert by
	# design.
	gdb_test "condition \$bpnum cond_global == 0" ".*"

	# Now delete breakpoints, and let the program execute the
	# address where the breakpoint used to be set.  If the target
	# doesn't treat insertions an idempotent way, we'll get a
	# spurious SIGTRAP.
	delete_breakpoints
	gdb_test "b bar" "Breakpoint .* at .*"
	gdb_test "continue" "Breakpoint .*, bar .*"
    }
}

# The testcase uses the "file" command to force breakpoint re-set in
# GDB.  Test both with and without PIE, as GDB used to mishandle
# breakpoint re-set when reloading PIEs.
foreach_with_prefix pie { "nopie" "pie" } {

    set opts {debug}
    lappend opts $pie

    set binfile [standard_output_file $testfile-$pie]

    if {[prepare_for_testing "failed to prepare" $binfile $srcfile $opts]} {
	continue
    }

    if [is_remote host] {
	set arg [remote_download host $binfile]
	if { $arg == "" } {
	    untested "download failed"
	    continue
	}
    }

    foreach_with_prefix always_inserted { "off" "on" } {
	test_break $always_inserted "break"

	if {![skip_hw_breakpoint_tests]} {
	    test_break $always_inserted "hbreak"
	}

	if {![skip_hw_watchpoint_tests]} {
	    test_break $always_inserted "watch"
	}

	if {![skip_hw_watchpoint_access_tests]
	    && ![skip_hw_watchpoint_multi_tests]} {
	    test_break $always_inserted "rwatch"
	    test_break $always_inserted "awatch"
	}
    }
}