]> git.ipfire.org Git - thirdparty/glibc.git/blame - scripts/test_printers_common.py
Correct build-many-glibcs.py arm-linux-gnueabihf configurations.
[thirdparty/glibc.git] / scripts / test_printers_common.py
CommitLineData
23b5cae1
MG
1# Common functions and variables for testing the Python pretty printers.
2#
bfff8b1b 3# Copyright (C) 2016-2017 Free Software Foundation, Inc.
23b5cae1
MG
4# This file is part of the GNU C Library.
5#
6# The GNU C Library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# The GNU C Library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with the GNU C Library; if not, see
18# <http://www.gnu.org/licenses/>.
19
20"""These tests require PExpect 4.0 or newer.
21
22Exported constants:
23 PASS, FAIL, UNSUPPORTED (int): Test exit codes, as per evaluate-test.sh.
24"""
25
26import os
27import re
28from test_printers_exceptions import *
29
30PASS = 0
31FAIL = 1
32UNSUPPORTED = 77
33
34gdb_bin = 'gdb'
35gdb_options = '-q -nx'
36gdb_invocation = '{0} {1}'.format(gdb_bin, gdb_options)
37pexpect_min_version = 4
38gdb_min_version = (7, 8)
39encoding = 'utf-8'
40
41try:
42 import pexpect
43except ImportError:
44 print('PExpect 4.0 or newer must be installed to test the pretty printers.')
45 exit(UNSUPPORTED)
46
47pexpect_version = pexpect.__version__.split('.')[0]
48
49if int(pexpect_version) < pexpect_min_version:
50 print('PExpect 4.0 or newer must be installed to test the pretty printers.')
51 exit(UNSUPPORTED)
52
53if not pexpect.which(gdb_bin):
54 print('gdb 7.8 or newer must be installed to test the pretty printers.')
55 exit(UNSUPPORTED)
56
57timeout = 5
58TIMEOUTFACTOR = os.environ.get('TIMEOUTFACTOR')
59
60if TIMEOUTFACTOR:
61 timeout = int(TIMEOUTFACTOR)
62
63try:
64 # Check the gdb version.
65 version_cmd = '{0} --version'.format(gdb_invocation, timeout=timeout)
66 gdb_version_out = pexpect.run(version_cmd, encoding=encoding)
67
68 # The gdb version string is "GNU gdb <PKGVERSION><version>", where
69 # PKGVERSION can be any text. We assume that there'll always be a space
70 # between PKGVERSION and the version number for the sake of the regexp.
71 version_match = re.search(r'GNU gdb .* ([1-9]+)\.([0-9]+)', gdb_version_out)
72
73 if not version_match:
74 print('The gdb version string (gdb -v) is incorrectly formatted.')
75 exit(UNSUPPORTED)
76
77 gdb_version = (int(version_match.group(1)), int(version_match.group(2)))
78
79 if gdb_version < gdb_min_version:
80 print('gdb 7.8 or newer must be installed to test the pretty printers.')
81 exit(UNSUPPORTED)
82
83 # Check if gdb supports Python.
84 gdb_python_cmd = '{0} -ex "python import os" -batch'.format(gdb_invocation,
85 timeout=timeout)
86 gdb_python_error = pexpect.run(gdb_python_cmd, encoding=encoding)
87
88 if gdb_python_error:
89 print('gdb must have python support to test the pretty printers.')
b064bba5 90 print('gdb output: {!r}'.format(gdb_python_error))
23b5cae1
MG
91 exit(UNSUPPORTED)
92
93 # If everything's ok, spawn the gdb process we'll use for testing.
94 gdb = pexpect.spawn(gdb_invocation, echo=False, timeout=timeout,
95 encoding=encoding)
96 gdb_prompt = u'\(gdb\)'
97 gdb.expect(gdb_prompt)
98
99except pexpect.ExceptionPexpect as exception:
100 print('Error: {0}'.format(exception))
101 exit(FAIL)
102
103def test(command, pattern=None):
104 """Sends 'command' to gdb and expects the given 'pattern'.
105
106 If 'pattern' is None, simply consumes everything up to and including
107 the gdb prompt.
108
109 Args:
110 command (string): The command we'll send to gdb.
111 pattern (raw string): A pattern the gdb output should match.
112
113 Returns:
114 string: The string that matched 'pattern', or an empty string if
115 'pattern' was None.
116 """
117
118 match = ''
119
120 gdb.sendline(command)
121
122 if pattern:
123 # PExpect does a non-greedy match for '+' and '*'. Since it can't look
124 # ahead on the gdb output stream, if 'pattern' ends with a '+' or a '*'
125 # we may end up matching only part of the required output.
126 # To avoid this, we'll consume 'pattern' and anything that follows it
127 # up to and including the gdb prompt, then extract 'pattern' later.
128 index = gdb.expect([u'{0}.+{1}'.format(pattern, gdb_prompt),
129 pexpect.TIMEOUT])
130
131 if index == 0:
132 # gdb.after now contains the whole match. Extract the text that
133 # matches 'pattern'.
134 match = re.match(pattern, gdb.after, re.DOTALL).group()
135 elif index == 1:
136 # We got a timeout exception. Print information on what caused it
137 # and bail out.
138 error = ('Response does not match the expected pattern.\n'
139 'Command: {0}\n'
140 'Expected pattern: {1}\n'
141 'Response: {2}'.format(command, pattern, gdb.before))
142
143 raise pexpect.TIMEOUT(error)
144 else:
145 # Consume just the the gdb prompt.
146 gdb.expect(gdb_prompt)
147
148 return match
149
150def init_test(test_bin, printer_files, printer_names):
151 """Loads the test binary file and the required pretty printers to gdb.
152
153 Args:
154 test_bin (string): The name of the test binary file.
155 pretty_printers (list of strings): A list with the names of the pretty
156 printer files.
157 """
158
159 # Load all the pretty printer files. We're assuming these are safe.
160 for printer_file in printer_files:
161 test('source {0}'.format(printer_file))
162
163 # Disable all the pretty printers.
164 test('disable pretty-printer', r'0 of [0-9]+ printers enabled')
165
166 # Enable only the required printers.
167 for printer in printer_names:
168 test('enable pretty-printer {0}'.format(printer),
169 r'[1-9][0-9]* of [1-9]+ printers enabled')
170
171 # Finally, load the test binary.
172 test('file {0}'.format(test_bin))
173
174def go_to_main():
175 """Executes a gdb 'start' command, which takes us to main."""
176
177 test('start', r'main')
178
179def get_line_number(file_name, string):
180 """Returns the number of the line in which 'string' appears within a file.
181
182 Args:
183 file_name (string): The name of the file we'll search through.
184 string (string): The string we'll look for.
185
186 Returns:
187 int: The number of the line in which 'string' appears, starting from 1.
188 """
189 number = -1
190
191 with open(file_name) as src_file:
192 for i, line in enumerate(src_file):
193 if string in line:
194 number = i + 1
195 break
196
197 if number == -1:
198 raise NoLineError(file_name, string)
199
200 return number
201
202def break_at(file_name, string, temporary=True, thread=None):
203 """Places a breakpoint on the first line in 'file_name' containing 'string'.
204
205 'string' is usually a comment like "Stop here". Notice this may fail unless
206 the comment is placed inline next to actual code, e.g.:
207
208 ...
209 /* Stop here */
210 ...
211
212 may fail, while:
213
214 ...
215 some_func(); /* Stop here */
216 ...
217
218 will succeed.
219
220 If 'thread' isn't None, the breakpoint will be set for all the threads.
221 Otherwise, it'll be set only for 'thread'.
222
223 Args:
224 file_name (string): The name of the file we'll place the breakpoint in.
225 string (string): A string we'll look for inside the file.
226 We'll place a breakpoint on the line which contains it.
227 temporary (bool): Whether the breakpoint should be automatically deleted
228 after we reach it.
229 thread (int): The number of the thread we'll place the breakpoint for,
230 as seen by gdb. If specified, it should be greater than zero.
231 """
232
233 if not thread:
234 thread_str = ''
235 else:
236 thread_str = 'thread {0}'.format(thread)
237
238 if temporary:
239 command = 'tbreak'
240 break_type = 'Temporary breakpoint'
241 else:
242 command = 'break'
243 break_type = 'Breakpoint'
244
245 line_number = str(get_line_number(file_name, string))
246
247 test('{0} {1}:{2} {3}'.format(command, file_name, line_number, thread_str),
248 r'{0} [0-9]+ at 0x[a-f0-9]+: file {1}, line {2}\.'.format(break_type,
249 file_name,
250 line_number))
251
252def continue_cmd(thread=None):
253 """Executes a gdb 'continue' command.
254
255 If 'thread' isn't None, the command will be applied to all the threads.
256 Otherwise, it'll be applied only to 'thread'.
257
258 Args:
259 thread (int): The number of the thread we'll apply the command to,
260 as seen by gdb. If specified, it should be greater than zero.
261 """
262
263 if not thread:
264 command = 'continue'
265 else:
266 command = 'thread apply {0} continue'.format(thread)
267
268 test(command)
269
270def next_cmd(count=1, thread=None):
271 """Executes a gdb 'next' command.
272
273 If 'thread' isn't None, the command will be applied to all the threads.
274 Otherwise, it'll be applied only to 'thread'.
275
276 Args:
277 count (int): The 'count' argument of the 'next' command.
278 thread (int): The number of the thread we'll apply the command to,
279 as seen by gdb. If specified, it should be greater than zero.
280 """
281
282 if not thread:
283 command = 'next'
284 else:
285 command = 'thread apply {0} next'
286
287 test('{0} {1}'.format(command, count))
288
289def select_thread(thread):
290 """Selects the thread indicated by 'thread'.
291
292 Args:
293 thread (int): The number of the thread we'll switch to, as seen by gdb.
294 This should be greater than zero.
295 """
296
297 if thread > 0:
298 test('thread {0}'.format(thread))
299
300def get_current_thread_lwpid():
301 """Gets the current thread's Lightweight Process ID.
302
303 Returns:
304 string: The current thread's LWP ID.
305 """
306
307 # It's easier to get the LWP ID through the Python API than the gdb CLI.
308 command = 'python print(gdb.selected_thread().ptid[1])'
309
310 return test(command, r'[0-9]+')
311
312def set_scheduler_locking(mode):
313 """Executes the gdb 'set scheduler-locking' command.
314
315 Args:
316 mode (bool): Whether the scheduler locking mode should be 'on'.
317 """
318 modes = {
319 True: 'on',
320 False: 'off'
321 }
322
323 test('set scheduler-locking {0}'.format(modes[mode]))
324
325def test_printer(var, to_string, children=None, is_ptr=True):
326 """ Tests the output of a pretty printer.
327
328 For a variable called 'var', this tests whether its associated printer
329 outputs the expected 'to_string' and children (if any).
330
331 Args:
332 var (string): The name of the variable we'll print.
333 to_string (raw string): The expected output of the printer's 'to_string'
334 method.
335 children (map {raw string->raw string}): A map with the expected output
336 of the printer's children' method.
337 is_ptr (bool): Whether 'var' is a pointer, and thus should be
338 dereferenced.
339 """
340
341 if is_ptr:
342 var = '*{0}'.format(var)
343
344 test('print {0}'.format(var), to_string)
345
346 if children:
347 for name, value in children.items():
348 # Children are shown as 'name = value'.
349 test('print {0}'.format(var), r'{0} = {1}'.format(name, value))
350
351def check_debug_symbol(symbol):
352 """ Tests whether a given debugging symbol exists.
353
354 If the symbol doesn't exist, raises a DebugError.
355
356 Args:
357 symbol (string): The symbol we're going to check for.
358 """
359
360 try:
361 test('ptype {0}'.format(symbol), r'type = {0}'.format(symbol))
362
363 except pexpect.TIMEOUT:
364 # The symbol doesn't exist.
365 raise DebugError(symbol)