summaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.mi/mi-pthreads.exp
blob: 8fc5061c803e5c2376244511900a12e3d82a7f39 (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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# Copyright 2002, 2003 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

# Please email any bugs, comments, and/or additions to this file to:
# bug-gdb@prep.ai.mit.edu

# This file tests that GDB's console can be accessed via the MI.
# Specifically, we are testing the "interpreter-exec" command and that
# the commands that are executed via this command are properly executed.
# Console commands executed via MI should use MI output wrappers, MI event
# handlers, etc.

# 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 get_mi_thread_list {name} {
  global expect_out

  # MI will return a list of thread ids:
  #
  # -thread-list-ids
  # ^done,thread-ids=[thread-id="1",thread-id="2",...],number-of-threads="N"
  # (gdb)
  mi_gdb_test "-thread-list-ids" \
    {\^done,thread-ids=\{(thread-id="[0-9]+"(,)*)+\},number-of-threads="[0-9]+"} \
    "-thread_list_ids ($name)"

  set thread_list {}
  if {![regexp {thread-ids=\{(thread-id="[0-9]+"(,)?)*\}} $expect_out(buffer) threads]} {
    fail "finding threads in MI output ($name)"
  } else {
    pass "finding threads in MI output ($name)"

    # Make list of console threads
    set start [expr {[string first \{ $threads] + 1}]
    set end   [expr {[string first \} $threads] - 1}]
    set threads [string range $threads $start $end]
    foreach thread [split $threads ,] {
      if {[scan $thread {thread-id="%d"} num]} {
	lappend thread_list $num
      }
    }
  }

  return $thread_list
}

# Check that MI and the console know of the same threads.
# Appends NAME to all test names.
proc check_mi_and_console_threads {name} {
  global expect_out

  mi_gdb_test "-thread-list-ids" \
    {\^done,thread-ids=\{(thread-id="[0-9]+"(,)*)+\},number-of-threads="[0-9]+"} \
    "-thread-list-ids ($name)"
  set mi_output $expect_out(buffer)

  # GDB will return a list of thread ids and some more info:
  #
  # (gdb) 
  # -interpreter-exec console "info threads"
  # ~"  4 Thread 2051 (LWP 7734)  0x401166b1 in __libc_nanosleep () at __libc_nanosleep:-1"
  # ~"  3 Thread 1026 (LWP 7733)   () at __libc_nanosleep:-1"
  # ~"  2 Thread 2049 (LWP 7732)  0x401411f8 in __poll (fds=0x804bb24, nfds=1, timeout=2000) at ../sysdeps/unix/sysv/linux/poll.c:63"
  # ~"* 1 Thread 1024 (LWP 7731)  main (argc=1, argv=0xbfffdd94) at ../../../src/gdb/testsuite/gdb.mi/pthreads.c:160"
  mi_gdb_test {-interpreter-exec console "info threads"} \
    {.*(~".*"[\r\n]*)+.*} \
    "info threads ($name)"
  set console_output $expect_out(buffer)

  # Make a list of all known threads to console (gdb's thread IDs)
  set console_thread_list {}
  foreach line [split $console_output \n] {
    if {[string index $line 0] == "~"} {
      # This is a line from the console; trim off "~", " ", "*", and "\""
      set line [string trim $line ~\ \"\*]
      if {[scan $line "%d" id] == 1} {
	lappend console_thread_list $id
      }
    }
  }

  # Now find the result string from MI
  set mi_result ""
  foreach line [split $mi_output \n] {
    if {[string range $line 0 4] == "^done"} {
      set mi_result $line
    }
  }
  if {$mi_result == ""} {
    fail "finding MI result string ($name)"
  } else {
    pass "finding MI result string ($name)"
  }

  # Finally, extract the thread ids and compare them to the console
  set num_mi_threads_str ""
  if {![regexp {number-of-threads="[0-9]+"} $mi_result num_mi_threads_str]} {
    fail "finding number of threads in MI output ($name)"
  } else {
    pass "finding number of threads in MI output ($name)"

    # Extract the number of threads from the MI result
    if {![scan $num_mi_threads_str {number-of-threads="%d"} num_mi_threads]} {
      fail "got number of threads from MI ($name)"
    } else {
      pass "got number of threads from MI ($name)"

      # Check if MI and console have same number of threads
      if {$num_mi_threads != [llength $console_thread_list]} {
	fail "console and MI have same number of threads ($name)"
      } else {
	pass "console and MI have same number of threads ($name)"

	# Get MI thread list
	set mi_thread_list [get_mi_thread_list $name]

	# Check if MI and console have the same threads
	set fails 0
	foreach ct [lsort $console_thread_list] mt [lsort $mi_thread_list] {
	  if {$ct != $mt} {
	    incr fails
	  }
	}
	if {$fails > 0} {
	  fail "MI and console have same threads ($name)"

	  # Send a list of failures to the log
	  send_log "Console has thread ids: $console_thread_list\n"
	  send_log "MI has thread ids: $mi_thread_list\n"
	} else {
	  pass "MI and console have same threads ($name)"
	}
      }
    }
  }
}

# This procedure checks for the bug gdb/669, where the console
# command "info threads" and the MI command "-thread-list-ids"
# return different threads in the system.
proc check_for_gdb669_bug {} {
  mi_run_to_main
  check_mi_and_console_threads "at main"

  for {set i 0} {$i < 4} {incr i} {
    mi_next "next, try $i"
    check_mi_and_console_threads "try $i"
  }
}

# This procedure tests the various thread commands in MI.
proc check_mi_thread_command_set {} {

  mi_runto done_making_threads

  set thread_list [get_mi_thread_list "in check_mi_thread_command_set"]
  
  mi_gdb_test "-thread-select" \
    {\^error,msg="mi_cmd_thread_select: USAGE: threadnum."} \
    "check_mi_thread_command_set: -thread-select"

  mi_gdb_test "-thread-select 123456789" \
    {\^error,msg="Thread ID 123456789 not known\."} \
    "check_mi_thread_command_set: -thread-select 123456789"

  foreach thread $thread_list {
    # line and file are optional.
    # many of the threads are blocked in libc calls,
    # and many people have libc's with no symbols.
    mi_gdb_test "-thread-select $thread" \
      "=context-changed,thread=\"$thread\"\r\n\\^done,new-thread-id=\"$thread\",frame={.*},line=\"(-)?\[0-9\]+\",file=\".*\"" \
      "check_mi_thread_command_set: -thread-select $thread"
  }
}

# This procedure checks that the console and MI don't get out
# of sync with each other.
proc check_console_thread_commands {} {

  # Assumed that we're at done_making_threads
  set thread_list [get_mi_thread_list "in check_console_thread_commands"]
  foreach thread $thread_list {
    mi_gdb_test "-interpreter-exec console \"thread $thread\"" \
      "(\\~\".*\"\r\n)*=context-changed,thread=\"$thread\"\r\n\\^done" \
      "-interpreter-exec console \"thread $thread\""
  }
}

#
# Start here
#
set testfile "pthreads"
set srcfile "$testfile.c"
set binfile "$objdir/$subdir/$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

check_mi_thread_command_set
check_console_thread_commands
check_for_gdb669_bug

mi_gdb_exit