1 # Common functions and variables for testing the Python pretty printers.
3 # Copyright (C) 2016-2021 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 # <https://www.gnu.org/licenses/>.
20 """These tests require PExpect 4.0 or newer.
23 PASS, FAIL, UNSUPPORTED (int): Test exit codes, as per evaluate-test.sh.
28 from test_printers_exceptions
import *
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)
44 print('PExpect 4.0 or newer must be installed to test the pretty printers.')
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.')
53 if not pexpect
.which(gdb_bin
):
54 print('gdb 7.8 or newer must be installed to test the pretty printers.')
58 TIMEOUTFACTOR
= os
.environ
.get('TIMEOUTFACTOR')
61 timeout
= int(TIMEOUTFACTOR
)
63 # Otherwise GDB is run in interactive mode and readline may send escape
64 # sequences confusing output for pexpect.
65 os
.environ
["TERM"]="dumb"
68 # Check the gdb version.
69 version_cmd
= '{0} --version'.format(gdb_invocation
, timeout
=timeout
)
70 gdb_version_out
= pexpect
.run(version_cmd
, encoding
=encoding
)
72 # The gdb version string is "GNU gdb <PKGVERSION><version>", where
73 # PKGVERSION can be any text. We assume that there'll always be a space
74 # between PKGVERSION and the version number for the sake of the regexp.
75 version_match
= re
.search(r
'GNU gdb .* ([1-9][0-9]*)\.([0-9]+)',
79 print('The gdb version string (gdb -v) is incorrectly formatted.')
82 gdb_version
= (int(version_match
.group(1)), int(version_match
.group(2)))
84 if gdb_version
< gdb_min_version
:
85 print('gdb 7.8 or newer must be installed to test the pretty printers.')
88 # Check if gdb supports Python.
89 gdb_python_cmd
= '{0} -ex "python import os" -batch'.format(gdb_invocation
,
91 gdb_python_error
= pexpect
.run(gdb_python_cmd
, encoding
=encoding
)
94 print('gdb must have python support to test the pretty printers.')
95 print('gdb output: {!r}'.format(gdb_python_error
))
98 # If everything's ok, spawn the gdb process we'll use for testing.
99 gdb
= pexpect
.spawn(gdb_invocation
, echo
=False, timeout
=timeout
,
101 gdb_prompt
= u
'\(gdb\)'
102 gdb
.expect(gdb_prompt
)
104 except pexpect
.ExceptionPexpect
as exception
:
105 print('Error: {0}'.format(exception
))
108 def test(command
, pattern
=None):
109 """Sends 'command' to gdb and expects the given 'pattern'.
111 If 'pattern' is None, simply consumes everything up to and including
115 command (string): The command we'll send to gdb.
116 pattern (raw string): A pattern the gdb output should match.
119 string: The string that matched 'pattern', or an empty string if
125 gdb
.sendline(command
)
128 # PExpect does a non-greedy match for '+' and '*'. Since it can't look
129 # ahead on the gdb output stream, if 'pattern' ends with a '+' or a '*'
130 # we may end up matching only part of the required output.
131 # To avoid this, we'll consume 'pattern' and anything that follows it
132 # up to and including the gdb prompt, then extract 'pattern' later.
133 index
= gdb
.expect([u
'{0}.+{1}'.format(pattern
, gdb_prompt
),
137 # gdb.after now contains the whole match. Extract the text that
139 match
= re
.match(pattern
, gdb
.after
, re
.DOTALL
).group()
141 # We got a timeout exception. Print information on what caused it
143 error
= ('Response does not match the expected pattern.\n'
145 'Expected pattern: {1}\n'
146 'Response: {2}'.format(command
, pattern
, gdb
.before
))
148 raise pexpect
.TIMEOUT(error
)
150 # Consume just the the gdb prompt.
151 gdb
.expect(gdb_prompt
)
155 def init_test(test_bin
, printer_files
, printer_names
):
156 """Loads the test binary file and the required pretty printers to gdb.
159 test_bin (string): The name of the test binary file.
160 pretty_printers (list of strings): A list with the names of the pretty
164 # Load all the pretty printer files. We're assuming these are safe.
165 for printer_file
in printer_files
:
166 test('source {0}'.format(printer_file
))
168 # Disable all the pretty printers.
169 test('disable pretty-printer', r
'0 of [0-9]+ printers enabled')
171 # Enable only the required printers.
172 for printer
in printer_names
:
173 test('enable pretty-printer {0}'.format(printer
),
174 r
'[1-9][0-9]* of [1-9]+ printers enabled')
176 # Finally, load the test binary.
177 test('file {0}'.format(test_bin
))
179 # Disable lock elision.
180 test('set environment GLIBC_TUNABLES glibc.elision.enable=0')
183 """Executes a gdb 'start' command, which takes us to main."""
185 test('start', r
'main')
187 def get_line_number(file_name
, string
):
188 """Returns the number of the line in which 'string' appears within a file.
191 file_name (string): The name of the file we'll search through.
192 string (string): The string we'll look for.
195 int: The number of the line in which 'string' appears, starting from 1.
199 with
open(file_name
) as src_file
:
200 for i
, line
in enumerate(src_file
):
206 raise NoLineError(file_name
, string
)
210 def break_at(file_name
, string
, temporary
=True, thread
=None):
211 """Places a breakpoint on the first line in 'file_name' containing 'string'.
213 'string' is usually a comment like "Stop here". Notice this may fail unless
214 the comment is placed inline next to actual code, e.g.:
223 some_func(); /* Stop here */
228 If 'thread' isn't None, the breakpoint will be set for all the threads.
229 Otherwise, it'll be set only for 'thread'.
232 file_name (string): The name of the file we'll place the breakpoint in.
233 string (string): A string we'll look for inside the file.
234 We'll place a breakpoint on the line which contains it.
235 temporary (bool): Whether the breakpoint should be automatically deleted
237 thread (int): The number of the thread we'll place the breakpoint for,
238 as seen by gdb. If specified, it should be greater than zero.
244 thread_str
= 'thread {0}'.format(thread
)
248 break_type
= 'Temporary breakpoint'
251 break_type
= 'Breakpoint'
253 line_number
= str(get_line_number(file_name
, string
))
255 test('{0} {1}:{2} {3}'.format(command
, file_name
, line_number
, thread_str
),
256 r
'{0} [0-9]+ at 0x[a-f0-9]+: file {1}, line {2}\.'.format(break_type
,
260 def continue_cmd(thread
=None):
261 """Executes a gdb 'continue' command.
263 If 'thread' isn't None, the command will be applied to all the threads.
264 Otherwise, it'll be applied only to 'thread'.
267 thread (int): The number of the thread we'll apply the command to,
268 as seen by gdb. If specified, it should be greater than zero.
274 command
= 'thread apply {0} continue'.format(thread
)
278 def next_cmd(count
=1, thread
=None):
279 """Executes a gdb 'next' command.
281 If 'thread' isn't None, the command will be applied to all the threads.
282 Otherwise, it'll be applied only to 'thread'.
285 count (int): The 'count' argument of the 'next' command.
286 thread (int): The number of the thread we'll apply the command to,
287 as seen by gdb. If specified, it should be greater than zero.
293 command
= 'thread apply {0} next'
295 test('{0} {1}'.format(command
, count
))
297 def select_thread(thread
):
298 """Selects the thread indicated by 'thread'.
301 thread (int): The number of the thread we'll switch to, as seen by gdb.
302 This should be greater than zero.
306 test('thread {0}'.format(thread
))
308 def get_current_thread_lwpid():
309 """Gets the current thread's Lightweight Process ID.
312 string: The current thread's LWP ID.
315 # It's easier to get the LWP ID through the Python API than the gdb CLI.
316 command
= 'python print(gdb.selected_thread().ptid[1])'
318 return test(command
, r
'[0-9]+')
320 def set_scheduler_locking(mode
):
321 """Executes the gdb 'set scheduler-locking' command.
324 mode (bool): Whether the scheduler locking mode should be 'on'.
331 test('set scheduler-locking {0}'.format(modes
[mode
]))
333 def test_printer(var
, to_string
, children
=None, is_ptr
=True):
334 """ Tests the output of a pretty printer.
336 For a variable called 'var', this tests whether its associated printer
337 outputs the expected 'to_string' and children (if any).
340 var (string): The name of the variable we'll print.
341 to_string (raw string): The expected output of the printer's 'to_string'
343 children (map {raw string->raw string}): A map with the expected output
344 of the printer's children' method.
345 is_ptr (bool): Whether 'var' is a pointer, and thus should be
350 var
= '*{0}'.format(var
)
352 test('print {0}'.format(var
), to_string
)
355 for name
, value
in children
.items():
356 # Children are shown as 'name = value'.
357 test('print {0}'.format(var
), r
'{0} = {1}'.format(name
, value
))
359 def check_debug_symbol(symbol
):
360 """ Tests whether a given debugging symbol exists.
362 If the symbol doesn't exist, raises a DebugError.
365 symbol (string): The symbol we're going to check for.
369 test('ptype {0}'.format(symbol
), r
'type = {0}'.format(symbol
))
371 except pexpect
.TIMEOUT
:
372 # The symbol doesn't exist.
373 raise DebugError(symbol
)