mbstowcs: Remove outdated comment
[glibc.git] / scripts / test_printers_common.py
blobcf4de5ae23aa47da1881282ad81c1752cd3e550c
1 # Common functions and variables for testing the Python pretty printers.
3 # Copyright (C) 2016-2018 Free Software Foundation, Inc.
4 # This file is part of the GNU C Library.
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.
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.
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/>.
20 """These tests require PExpect 4.0 or newer.
22 Exported constants:
23 PASS, FAIL, UNSUPPORTED (int): Test exit codes, as per evaluate-test.sh.
24 """
26 import os
27 import re
28 from test_printers_exceptions import *
30 PASS = 0
31 FAIL = 1
32 UNSUPPORTED = 77
34 gdb_bin = 'gdb'
35 gdb_options = '-q -nx'
36 gdb_invocation = '{0} {1}'.format(gdb_bin, gdb_options)
37 pexpect_min_version = 4
38 gdb_min_version = (7, 8)
39 encoding = 'utf-8'
41 try:
42 import pexpect
43 except ImportError:
44 print('PExpect 4.0 or newer must be installed to test the pretty printers.')
45 exit(UNSUPPORTED)
47 pexpect_version = pexpect.__version__.split('.')[0]
49 if int(pexpect_version) < pexpect_min_version:
50 print('PExpect 4.0 or newer must be installed to test the pretty printers.')
51 exit(UNSUPPORTED)
53 if not pexpect.which(gdb_bin):
54 print('gdb 7.8 or newer must be installed to test the pretty printers.')
55 exit(UNSUPPORTED)
57 timeout = 5
58 TIMEOUTFACTOR = os.environ.get('TIMEOUTFACTOR')
60 if TIMEOUTFACTOR:
61 timeout = int(TIMEOUTFACTOR)
63 try:
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)
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)
73 if not version_match:
74 print('The gdb version string (gdb -v) is incorrectly formatted.')
75 exit(UNSUPPORTED)
77 gdb_version = (int(version_match.group(1)), int(version_match.group(2)))
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)
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)
88 if gdb_python_error:
89 print('gdb must have python support to test the pretty printers.')
90 print('gdb output: {!r}'.format(gdb_python_error))
91 exit(UNSUPPORTED)
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)
99 except pexpect.ExceptionPexpect as exception:
100 print('Error: {0}'.format(exception))
101 exit(FAIL)
103 def test(command, pattern=None):
104 """Sends 'command' to gdb and expects the given 'pattern'.
106 If 'pattern' is None, simply consumes everything up to and including
107 the gdb prompt.
109 Args:
110 command (string): The command we'll send to gdb.
111 pattern (raw string): A pattern the gdb output should match.
113 Returns:
114 string: The string that matched 'pattern', or an empty string if
115 'pattern' was None.
118 match = ''
120 gdb.sendline(command)
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])
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))
143 raise pexpect.TIMEOUT(error)
144 else:
145 # Consume just the the gdb prompt.
146 gdb.expect(gdb_prompt)
148 return match
150 def init_test(test_bin, printer_files, printer_names):
151 """Loads the test binary file and the required pretty printers to gdb.
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.
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))
163 # Disable all the pretty printers.
164 test('disable pretty-printer', r'0 of [0-9]+ printers enabled')
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')
171 # Finally, load the test binary.
172 test('file {0}'.format(test_bin))
174 # Disable lock elision.
175 test('set environment GLIBC_TUNABLES glibc.elision.enable=0')
177 def go_to_main():
178 """Executes a gdb 'start' command, which takes us to main."""
180 test('start', r'main')
182 def get_line_number(file_name, string):
183 """Returns the number of the line in which 'string' appears within a file.
185 Args:
186 file_name (string): The name of the file we'll search through.
187 string (string): The string we'll look for.
189 Returns:
190 int: The number of the line in which 'string' appears, starting from 1.
192 number = -1
194 with open(file_name) as src_file:
195 for i, line in enumerate(src_file):
196 if string in line:
197 number = i + 1
198 break
200 if number == -1:
201 raise NoLineError(file_name, string)
203 return number
205 def break_at(file_name, string, temporary=True, thread=None):
206 """Places a breakpoint on the first line in 'file_name' containing 'string'.
208 'string' is usually a comment like "Stop here". Notice this may fail unless
209 the comment is placed inline next to actual code, e.g.:
212 /* Stop here */
215 may fail, while:
218 some_func(); /* Stop here */
221 will succeed.
223 If 'thread' isn't None, the breakpoint will be set for all the threads.
224 Otherwise, it'll be set only for 'thread'.
226 Args:
227 file_name (string): The name of the file we'll place the breakpoint in.
228 string (string): A string we'll look for inside the file.
229 We'll place a breakpoint on the line which contains it.
230 temporary (bool): Whether the breakpoint should be automatically deleted
231 after we reach it.
232 thread (int): The number of the thread we'll place the breakpoint for,
233 as seen by gdb. If specified, it should be greater than zero.
236 if not thread:
237 thread_str = ''
238 else:
239 thread_str = 'thread {0}'.format(thread)
241 if temporary:
242 command = 'tbreak'
243 break_type = 'Temporary breakpoint'
244 else:
245 command = 'break'
246 break_type = 'Breakpoint'
248 line_number = str(get_line_number(file_name, string))
250 test('{0} {1}:{2} {3}'.format(command, file_name, line_number, thread_str),
251 r'{0} [0-9]+ at 0x[a-f0-9]+: file {1}, line {2}\.'.format(break_type,
252 file_name,
253 line_number))
255 def continue_cmd(thread=None):
256 """Executes a gdb 'continue' command.
258 If 'thread' isn't None, the command will be applied to all the threads.
259 Otherwise, it'll be applied only to 'thread'.
261 Args:
262 thread (int): The number of the thread we'll apply the command to,
263 as seen by gdb. If specified, it should be greater than zero.
266 if not thread:
267 command = 'continue'
268 else:
269 command = 'thread apply {0} continue'.format(thread)
271 test(command)
273 def next_cmd(count=1, thread=None):
274 """Executes a gdb 'next' command.
276 If 'thread' isn't None, the command will be applied to all the threads.
277 Otherwise, it'll be applied only to 'thread'.
279 Args:
280 count (int): The 'count' argument of the 'next' command.
281 thread (int): The number of the thread we'll apply the command to,
282 as seen by gdb. If specified, it should be greater than zero.
285 if not thread:
286 command = 'next'
287 else:
288 command = 'thread apply {0} next'
290 test('{0} {1}'.format(command, count))
292 def select_thread(thread):
293 """Selects the thread indicated by 'thread'.
295 Args:
296 thread (int): The number of the thread we'll switch to, as seen by gdb.
297 This should be greater than zero.
300 if thread > 0:
301 test('thread {0}'.format(thread))
303 def get_current_thread_lwpid():
304 """Gets the current thread's Lightweight Process ID.
306 Returns:
307 string: The current thread's LWP ID.
310 # It's easier to get the LWP ID through the Python API than the gdb CLI.
311 command = 'python print(gdb.selected_thread().ptid[1])'
313 return test(command, r'[0-9]+')
315 def set_scheduler_locking(mode):
316 """Executes the gdb 'set scheduler-locking' command.
318 Args:
319 mode (bool): Whether the scheduler locking mode should be 'on'.
321 modes = {
322 True: 'on',
323 False: 'off'
326 test('set scheduler-locking {0}'.format(modes[mode]))
328 def test_printer(var, to_string, children=None, is_ptr=True):
329 """ Tests the output of a pretty printer.
331 For a variable called 'var', this tests whether its associated printer
332 outputs the expected 'to_string' and children (if any).
334 Args:
335 var (string): The name of the variable we'll print.
336 to_string (raw string): The expected output of the printer's 'to_string'
337 method.
338 children (map {raw string->raw string}): A map with the expected output
339 of the printer's children' method.
340 is_ptr (bool): Whether 'var' is a pointer, and thus should be
341 dereferenced.
344 if is_ptr:
345 var = '*{0}'.format(var)
347 test('print {0}'.format(var), to_string)
349 if children:
350 for name, value in children.items():
351 # Children are shown as 'name = value'.
352 test('print {0}'.format(var), r'{0} = {1}'.format(name, value))
354 def check_debug_symbol(symbol):
355 """ Tests whether a given debugging symbol exists.
357 If the symbol doesn't exist, raises a DebugError.
359 Args:
360 symbol (string): The symbol we're going to check for.
363 try:
364 test('ptype {0}'.format(symbol), r'type = {0}'.format(symbol))
366 except pexpect.TIMEOUT:
367 # The symbol doesn't exist.
368 raise DebugError(symbol)