| 12
 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
 
 | # Copyright 2008-2016 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 attaching to a program that is constantly spawning short-lived
# threads.  The stresses the edge cases of attaching to threads that
# have just been created or are in process of dying.  In addition, the
# test attaches, debugs, detaches, reattaches in a loop a few times,
# to stress the behavior of the debug API around detach (some systems
# end up leaving stale state behind that confuse the following
# attach).
# Return true if the running version of DejaGnu is known to not be
# able to run this test.
proc bad_dejagnu {} {
    global frame_version
    verbose -log "DejaGnu version: $frame_version"
    verbose -log "Expect version: [exp_version]"
    verbose -log "Tcl version: [info tclversion]"
    set dj_ver [split $frame_version .]
    set dj_ver_major [lindex $dj_ver 0]
    set dj_ver_minor [lindex $dj_ver 1]
    # DejaGnu versions prior to 1.6 manage to kill the wrong process
    # due to PID-reuse races.  Since this test spawns many threads, it
    # widens the race window a whole lot, enough that the inferior is
    # often killed, and thus the test randomly fails.  See:
    # http://lists.gnu.org/archive/html/dejagnu/2015-07/msg00005.html
    # The fix added a close_wait_program procedure.  If that procedure
    # is defined, and DejaGnu is older than 1.6, assume that means the
    # fix was backported.
    if {$dj_ver_major == 1
	&& ($dj_ver_minor < 6 && [info procs close_wait_program] == "")} {
	return 1
    }
    return 0
}
if {[bad_dejagnu]} {
    unsupported "broken DejaGnu"
    return 0
}
if {![can_spawn_for_attach]} {
    return 0
}
standard_testfile
# The test proper.  See description above.
proc test {} {
    global binfile
    global gdb_prompt
    global decimal
    clean_restart ${binfile}
    set test_spawn_id [spawn_wait_for_attach $binfile]
    set testpid [spawn_id_get_pid $test_spawn_id]
    set attempts 10
    for {set attempt 1} { $attempt <= $attempts } { incr attempt } {
	with_test_prefix "iter $attempt" {
	    set attached 0
	    set eperm 0
	    set test "attach"
	    gdb_test_multiple "attach $testpid" $test {
		-re "new threads in iteration" {
		    # Seen when "set debug libthread_db" is on.
		    exp_continue
		}
		-re "warning: Cannot attach to lwp $decimal: Operation not permitted" {
		    # On Linux, PTRACE_ATTACH sometimes fails with
		    # EPERM, even though /proc/PID/status indicates
		    # the thread is running.
		    set eperm 1
		    exp_continue
		}
		-re "debugger service failed.*$gdb_prompt $" {
		    fail $test
		}
		-re "$gdb_prompt $" {
		    if {$eperm} {
			xfail "$test (EPERM)"
		    } else {
			pass $test
		    }
		}
		-re "Attaching to program.*process $testpid.*$gdb_prompt $" {
		    pass $test
		}
	    }
	    # Sleep a bit and try updating the thread list.  We should
	    # know about all threads already at this point.  If we see
	    # "New Thread" or similar being output, then "attach" is
	    # failing to actually attach to all threads in the process,
	    # which would be a bug.
	    sleep 1
	    set test "no new threads"
	    gdb_test_multiple "info threads" $test {
		-re "New .*$gdb_prompt $" {
		    fail $test
		}
		-re "$gdb_prompt $" {
		    pass $test
		}
	    }
	    # Force breakpoints always inserted, so that threads we might
	    # have failed to attach to hit them even when threads we do
	    # know about are stopped.
	    gdb_test_no_output "set breakpoint always-inserted on"
	    # Run to a breakpoint a few times.  A few threads should spawn
	    # and die meanwhile.  This checks that thread creation/death
	    # events carry on correctly after attaching.  Also, be
	    # detaching from the program and reattaching, we check that
	    # the program doesn't die due to gdb leaving a pending
	    # breakpoint hit on a new thread unprocessed.
	    gdb_test "break break_fn" "Breakpoint.*" "break break_fn"
	    # Wait a bit, to give time for most threads to hit the
	    # breakpoint, including threads we might have failed to
	    # attach.
	    sleep 2
	    set bps 3
	    for {set bp 1} { $bp <= $bps } { incr bp } {
		gdb_test "continue" "Breakpoint.*" "break at break_fn: $bp"
	    }
	    if {$attempt < $attempts} {
		# Kick the time out timer for another round.
		gdb_test "print again = 1" " = 1" "reset timer in the inferior"
		# Show the time we had left in the logs, in case
		# something goes wrong.
		gdb_test "print seconds_left" " = .*"
		gdb_test "detach" "Detaching from.*"
	    } else {
		gdb_test "kill" "" "kill process" "Kill the program being debugged.*y or n. $" "y"
	    }
	    gdb_test_no_output "set breakpoint always-inserted off"
	    delete_breakpoints
	}
    }
    kill_wait_spawned_process $test_spawn_id
}
# The test program exits after a while, in case GDB crashes.  Make it
# wait at least as long as we may wait before declaring a time out
# failure.
set options { "additional_flags=-DTIMEOUT=$timeout" debug pthreads }
if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options] == -1} {
    return -1
}
test
 |