aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/multi-forks.exp
blob: bdc7ce59bbec257438e79c0d45f17614d22f4e97 (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
#   Copyright 2005-2019 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/>.

# Until "set follow-fork-mode" and "catch fork" are implemented on
# other targets...
#
if { ![istarget "*-*-linux*"] } then {
    continue
}


standard_testfile .c

if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
     untested "failed to compile"
     return -1
}

# Start with a fresh gdb

clean_restart ${binfile}

global gdb_prompt

# This is a test of gdb's ability to follow the parent, child or both
# parent and child of multiple Unix fork() system calls.

set exit_bp_loc [gdb_get_line_number "Set exit breakpoint here."]

# Insert a breakpoint at the location provided by the exit_bp_loc global
# and resume the execution until hitting that breakpoint.  We also make
# sure to consume all the expected output from all processes as well,
# to make sure it doesn't cause trouble during a subsequent test.

proc continue_to_exit_bp_loc {} {
    global exit_bp_loc decimal gdb_prompt
    global inferior_spawn_id gdb_spawn_id

    gdb_breakpoint $exit_bp_loc

    send_gdb "continue\n"

    # The output from the child processes can be interleaved arbitrarily
    # with the output from GDB and the parent process.  If we don't
    # consume it all now, it can confuse later interactions.
    set seen_done 0
    set seen_break 0
    set seen_prompt 0
    set seen_timeout 0
    while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
	# We don't know what order the interesting things will arrive in.
	# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
	# ... -re z ensures that expect always chooses the match that
	# occurs leftmost in the input, and not the pattern appearing
	# first in the script that occurs anywhere in the input, so that
	# we don't skip anything.
	gdb_expect {
	    -i "$inferior_spawn_id $gdb_spawn_id"
	    -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
		if {[info exists expect_out(1,string)]} {
		    incr seen_done
		} elseif {[info exists expect_out(2,string)]} {
		    set seen_break 1
		} elseif {[info exists expect_out(3,string)]} {
		    set seen_prompt 1
		}
		array unset expect_out
	    }
	    timeout { set seen_timeout 1 }
	}
    }

    if { $seen_timeout } {
	fail "run to exit 2 (timeout)"
    } elseif { ! $seen_prompt } {
	fail "run to exit 2 (no prompt)"
    } elseif { ! $seen_break } {
	fail "run to exit 2 (no breakpoint hit)"
    } elseif { $seen_done != 16 } {
	fail "run to exit 2 (missing done messages)"
    } else {
	pass "run to exit 2"
    }
}

# The inferior program builds a tree of processes by executing a loop
# four times, calling fork at each iteration.  Thus, at each
# iteration, the total number of processes doubles; after four
# iterations, we have 16 processes.  Each process saves the results
# from its 'fork' calls, so we can tell which leaf a given process is
# by looking at which forks returned zero and which returned a pid: a
# zero means to take the child's branch; a pid means to take the
# parent's branch.

# First set gdb to follow the child.
# The result should be that each of the 4 forks returns zero.

clean_restart ${binfile}
runto_main
gdb_test_no_output "set follow-fork child"
continue_to_exit_bp_loc

gdb_test "print pids" "\\$.* = \\{0, 0, 0, 0\\}.*" "follow child, print pids"

# Now set gdb to follow the parent.
# Result should be that none of the 4 forks returns zero.

clean_restart ${binfile}
runto_main
gdb_test_no_output "set follow-fork parent" ""
continue_to_exit_bp_loc

gdb_test "print pids\[0\]==0 || pids\[1\]==0 || pids\[2\]==0 || pids\[3\]==0" \
    " = 0" "follow parent, print pids"

#
# Now test with detach-on-fork off.
#

# Start with a fresh gdb

clean_restart ${binfile}

runto_main
gdb_breakpoint $exit_bp_loc

gdb_test "help set detach-on-fork" "whether gdb will detach the child.*" \
    "help set detach"

gdb_test "show detach-on-fork" "on." "show detach default on"

gdb_test_no_output "set detach off" "set detach off"

#
# We will now run every fork up to the exit bp, 
# eventually winding up with 16 inferiors.
#

for {set i 1} {$i <= 15} {incr i} {
  gdb_test "continue" "Breakpoint .* main .*exit.*" "run to exit $i"
  gdb_test "info inferior" " 2 .* 3 .* 4 .* 5 .*" "info inferior $i"
  gdb_test "inferior $i + 1" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
      "inferior $i"
}

gdb_test "continue" "Breakpoint .* main .*exit.*" "run to exit 16"
gdb_test "info inferior" " 2 .* 3 .* 4 .* 5 .*" "info inferior 16"
gdb_test "inferior 2" " main .*" "restart final"

#
# Now we should examine all the pids.
#

# 
# Test detach inferior
# 

# [assumes we're at #1]
gdb_test "detach inferior 2" "Detaching .*" "detach 2"
gdb_test "detach inferior 3" "Detaching .*" "detach 3"
gdb_test "detach inferior 4" "Detaching .*" "detach 4"
gdb_test "detach inferior 5" "Detaching .*" "detach 5"

# 
# Test kill inferior
#

for {set i 6} { $i <= 16} {incr i} {
    gdb_test_no_output "kill inferior $i" "kill $i"
    gdb_test "info inferior $i" "<null>.*" "did kill $i"
}

return 0