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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
# Copyright 2002, 2003, 2004, 2005, 2007, 2008 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/>.
# This only works with native configurations
if {![isnative]} {
return
}
load_lib mi-support.exp
set MIFLAGS "-i=mi"
gdb_exit
if {[mi_gdb_start]} {
continue
}
proc myreverse { list } {
if { [llength $list] <= 1 } {
return $list
}
set tail [lrange $list 1 [llength $list]]
set rtail [myreverse $tail]
lappend rtail [lindex $list 0]
return $rtail
}
proc check_thread_states { xstates test } {
global expect_out
set states [myreverse $xstates]
set pattern "\\^done,threads=\\\["
foreach s $states {
set pattern "${pattern}(.*)state=\"$s\""
}
set pattern "$pattern\\\}\\\].*"
verbose -log "expecting: $pattern"
mi_gdb_test "-thread-info" $pattern $test
}
#
# Start here
#
set testfile "non-stop"
set srcfile "$testfile.c"
set binfile "$objdir/$subdir/mi-$testfile"
set options [list debug incdir=$objdir]
if {[gdb_compile_pthreads "$srcdir/$subdir/$srcfile" $binfile executable $options] != "" } {
return -1
}
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_load $binfile
set supported 0
send_gdb "-gdb-show non-stop\n"
gdb_expect {
-re ".*\\^done,value=\"off\",supported=\"(\[^\"\]+)\"\r\n$mi_gdb_prompt$" {
if { $expect_out(1,string) == "1" } {
set supported 1
}
}
-re ".$mi_gdb_prompt$" {
}
}
mi_gdb_test "-gdb-set non-stop 1" ".*"
mi_gdb_test "-gdb-set target-async 1" ".*"
detect_async
mi_gdb_test "200-break-insert -t main" ".*"
# Note: presently, we skip this test on non-native targets,
# so 'run' is OK. As soon as we start to run this on remote
# target, the logic from mi_run_cmd will have to be refactored.
send_gdb "-exec-run\n"
gdb_expect {
-re ".*\\^running.*$mi_gdb_prompt$" {
}
-re "\\^error,msg=\"The target does not support running in non-stop mode.\"" {
verbose -log "Non-stop mode not supported, skipping all tests"
return
}
-re ".*\r\n$mi_gdb_prompt$" {
perror "Cannot start target (unknown output after running)"
return -1
}
timeout {
perror "Cannot start target (timeout)"
return -1
}
}
mi_expect_stop "breakpoint-hit" main ".*" ".*" "\[0-9\]+" { "" "disp=\"del\"" } "run to main"
mi_create_breakpoint break_at_me 2 keep break_at_me .* .* .* "breakpoint at marker"
mi_send_resuming_command "exec-continue" "resume 1"
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w0,i0 stop"
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w1,i0 stop"
check_thread_states {"running" "stopped" "stopped"} "thread state, stop 1"
mi_gdb_test "-thread-select 2" "\\^done.*" "select thread 2"
mi_create_varobj I_W0 "i" "create varobj in first thread"
mi_gdb_test "-thread-select 3" "\\^done.*" "select thread 3"
mi_create_varobj I_W1 "i" "create varobj in second thread"
mi_send_resuming_command "exec-continue --thread 2" "resume 1"
check_thread_states {"running" "running" "stopped"} "thread state, resume 1"
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w0,i1 stop"
check_thread_states {"running" "stopped" "stopped"} "thread state, stop 2"
mi_send_resuming_command "exec-continue --thread 3" "resume 2"
check_thread_states {"running" "stopped" "running"} "thread state, resume 2"
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w1,i1 stop"
check_thread_states {"running" "stopped" "stopped"} "thread state, stop 3"
mi_varobj_update * {I_W1 I_W0} "update varobj, 1"
mi_check_varobj_value I_W0 1 "check varobj, w0, 1"
mi_check_varobj_value I_W1 1 "check varobj, w1, 1"
send_gdb "-exec-interrupt --thread 1\n"
gdb_expect {
-re "\\^done\r\n$mi_gdb_prompt\\*stopped\[^\r\n\]+\r\n$" {
pass "interrupted"
}
timeout {
fail "interrupted (timeout)"
}
}
# The interrupt command sends SIGINT to the target, and therefore the
# thread might not be stopped immediately when we return from the target.
# So, wait a bit
sleep 1
check_thread_states {"stopped" "stopped" "stopped"} "thread state, stop 4"
send_gdb "-exec-continue --all\n"
gdb_expect {
-re ".*\\*running,thread-id=\"3\"\r\n\\*running,thread-id=\"2\"\r\n\\*running,thread-id=\"1\"\r\n$mi_gdb_prompt" {
pass "resume all"
}
timeout {
fail "resume all (timeout)"
}
}
check_thread_states {"running" "running" "running"} "thread state, resume all"
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w0,i2 stop"
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w1,i2 stop"
# At this point, thread 1 (main) is running, and worker threads are stopped.
# Check that we can modify breakpoint condition, even when operating on a
# running thread.
mi_gdb_test "-break-condition --thread 1 2 id==1" "\\^done" "set condition, 1"
mi_send_resuming_command "exec-continue --thread 2" "resume 2"
mi_send_resuming_command "exec-continue --thread 3" "resume 3"
sleep 2
mi_expect_stop "breakpoint-hit" "break_at_me" ".*" "non-stop.c" ".*" {"" "disp=\"keep\""} "w0,i3 stop"
check_thread_states {"running" "running" "stopped"} "thread state, stop on cond breakpoint"
# Check that when we update all varobj, we don't get no error about varobj
# bound to running thread.
mi_varobj_update * {I_W1} "update varobj, 2"
mi_check_varobj_value I_W1 3 "check varobj, w1, 1"
# Check that stack commands are allowed on a stopped thread, even if some other threads
# are running, and produce something sane. Also check we check error on running thread.
mi_gdb_test "-stack-list-frames --thread 2" "\\^error,msg=\".*\"" "stacktrace of running thread"
mi_gdb_test "-stack-list-frames --thread 3" \
"\\^done,stack=\\\[frame={level=\"0\",addr=\".*\",func=\"break_at_me\".*" \
"stacktrace of stopped thread"
# verify that after thread exit, the thread is reported as exited in -thread-info, and
# we can still interact with other threads.
mi_gdb_test "-thread-select 2" "\\^done.*" "select first worker thread"
# Since thread 2 is running, we need to set variable via another thread.
mi_gdb_test "-gdb-set --thread 3 variable exit_first_thread=1" ".*\\^done" "ask the second thread to exit"
gdb_expect {
-re ".*=thread-exited,id=\"2\"\r\n$" {
pass "wait for thread exit"
}
timeout {
fail "wait for thread exit (timeout)"
}
}
# See that we can still poke other threads.
mi_gdb_test "-stack-list-frames --thread 3" \
"\\^done,stack=\\\[frame={level=\"0\",addr=\".*\",func=\"break_at_me\".*" \
"stacktrace of stopped thread"
mi_gdb_exit
|