summaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp
blob: 8dc9af05c8efc57e0e7a56ebaf8cf6a8ad365cc6 (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
189
190
191
192
193
194
# Copyright (C) 2021-2022 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 we can access memory while all the threads of the inferior
# are running, and even if:
#
# - the leader thread exits
# - the selected thread exits
#
# This test constantly spawns short lived threads to make sure that on
# systems with debug APIs that require passing down a specific thread
# to work with (e.g., GNU/Linux ptrace and /proc filesystem), GDB
# copes with accessing memory just while the thread it is accessing
# memory through exits.
#
# The test spawns two processes and alternates memory accesses between
# them to force flushing per-process caches.  When the testcase was
# originally written, the Linux backend would access inferior memory
# via /proc/PID/mem, and kept one such file open, as a cache.
# Alternating inferiors would force re-opening such file for a
# different process, which would fail if GDB tried to open the file
# for a thread that exited.  The test thus ensured those reopen/fail
# code paths were exercised.  Nowadays, GDB keeps one /proc/PID/mem
# file open per inferior.

standard_testfile

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

# The test proper.  NON_STOP indicates whether we're testing in
# non-stop, or all-stop mode.

proc test { non_stop } {
    global binfile
    global gdb_prompt
    global GDBFLAGS

    save_vars { GDBFLAGS } {
      append GDBFLAGS " -ex \"set non-stop $non_stop\""
      clean_restart ${binfile}
    }

    if ![runto_main] {
	return -1
    }

    # If debugging with target remote, check whether the all-stop variant
    # of the RSP is being used.  If so, we can't run the background tests.
    if {!$non_stop
	&& [target_info exists gdb_protocol]
	&& ([target_info gdb_protocol] == "remote"
	    || [target_info gdb_protocol] == "extended-remote")} {

	gdb_test_multiple "maint show target-non-stop" "" {
	    -wrap -re "(is|currently) on.*" {
	    }
	    -wrap -re "(is|currently) off.*" {
		unsupported "can't issue commands while target is running"
		return 0
	    }
	}
    }

    delete_breakpoints

    # Start the second inferior.
    with_test_prefix "second inferior" {
	# With stub targets that do reload on run, if we let the new
	# inferior share inferior 1's connection, runto_main would
	# fail because GDB is already connected to something, like
	# e.g. with --target_board=native-gdbserver:
	#
	#  (gdb) kill
	#  ...
	#  (gdb) target remote localhost:2348
	#  Already connected to a remote target.  Disconnect? (y or n)
	#
	# Instead, start the inferior with no connection, and let
	# gdb_load/runto_main spawn a new remote connection/gdbserver.
	#
	# OTOH, with extended-remote, we must let the new inferior
	# reuse the current connection, so that runto_main below can
	# issue the "run" command, and have the inferior run on the
	# remote target.  If we forced no connection, then "run" would
	# either fail if "set auto-connect-native-target" is on, like
	# the native-extended-gdbserver board enforces, or it would
	# run the inferior on the native target, which isn't what is
	# being tested.
	#
	# Since it's reload_on_run targets that need special care, we
	# default to reusing the connection on most targets.
	if [target_info exists gdb,do_reload_on_run] {
	    gdb_test "add-inferior -no-connection" "New inferior 2.*"
	} else {
	    gdb_test "add-inferior" "New inferior 2.*"
	}
	gdb_test "inferior 2" "Switching to inferior 2 .*"

	gdb_load $binfile

	if ![runto_main] {
	    return -1
	}
    }

    delete_breakpoints

    # These put too much noise in the logs.
    gdb_test_no_output "set print thread-events off"

    # Continue all threads of both processes.
    gdb_test_no_output "set schedule-multiple on"
    if {$non_stop == "off"} {
	set cmd "continue &"
    } else {
	set cmd "continue -a &"
    }
    gdb_test_multiple $cmd "continuing" {
	-re "Continuing\.\r\n$gdb_prompt " {
	    pass $gdb_test_name
	}
    }

    # Like gdb_test, but:
    # - don't issue a pass on success.
    # - on failure, clear the ok variable in the calling context, and
    #   break it.
    proc my_gdb_test {cmd pattern message} {
	upvar inf inf
	upvar iter iter
	if {[gdb_test_multiple $cmd "access mem ($message, inf=$inf, iter=$iter)" {
	    -wrap -re $pattern {
	    }
	}] != 0} {
	    uplevel 1 {set ok 0}
	    return -code break
	}
    }

    # Hammer away for 5 seconds, alternating between inferiors.
    set ::done 0
    after 5000 { set ::done 1 }

    set inf 1
    set ok 1
    set iter 0
    while {!$::done && $ok} {
	incr iter
	verbose -log "xxxxx: iteration $iter"
	gdb_test "info threads" ".*" ""

	if {$inf == 1} {
	    set inf 2
	} else {
	    set inf 1
	}

	my_gdb_test "inferior $inf" ".*" "inferior $inf"

	my_gdb_test "print global_var = 555" " = 555" \
	    "write to global_var"
	my_gdb_test "print global_var" " = 555" \
	    "print global_var after writing"
	my_gdb_test "print global_var = 333" " = 333" \
	    "write to global_var again"
	my_gdb_test "print global_var" " = 333" \
	    "print global_var after writing again"
    }

   if {$ok} {
       pass "access mem"
   }
}

foreach non_stop { "off" "on" } {
    set stop_mode [expr ($non_stop=="off")?"all-stop":"non-stop"]
    with_test_prefix "$stop_mode" {
	test $non_stop
    }
}