Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(249889)

Delta Between Two Patch Sets: Lib/test/test_gdb.py

Issue 16510: Using appropriate checks in tests
Left Patch Set: Created 6 years, 9 months ago
Right Patch Set: Created 5 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « Lib/test/test_gc.py ('k') | Lib/test/test_grammar.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # Verify that gdb can pretty-print the various PyObject* types 1 # Verify that gdb can pretty-print the various PyObject* types
2 # 2 #
3 # The code for testing gdb was adapted from similar work in Unladen Swallow's 3 # The code for testing gdb was adapted from similar work in Unladen Swallow's
4 # Lib/test/test_jit_gdb.py 4 # Lib/test/test_jit_gdb.py
5 5
6 import os 6 import os
7 import re 7 import re
8 import pprint
8 import subprocess 9 import subprocess
9 import sys 10 import sys
10 import sysconfig 11 import sysconfig
11 import unittest 12 import unittest
12 import locale 13 import locale
13 14
14 # Is this Python configured to support threads? 15 # Is this Python configured to support threads?
15 try: 16 try:
16 import _thread 17 import _thread
17 except ImportError: 18 except ImportError:
18 _thread = None 19 _thread = None
19 20
21 from test import support
20 from test.support import run_unittest, findfile, python_is_optimized 22 from test.support import run_unittest, findfile, python_is_optimized
21 23
22 try: 24 try:
23 gdb_version, _ = subprocess.Popen(["gdb", "--version"], 25 gdb_version, _ = subprocess.Popen(["gdb", "--version"],
24 stdout=subprocess.PIPE).communicate() 26 stdout=subprocess.PIPE).communicate()
25 except OSError: 27 except OSError:
26 # This is what "no gdb" looks like. There may, however, be other 28 # This is what "no gdb" looks like. There may, however, be other
27 # errors that manifest this way too. 29 # errors that manifest this way too.
28 raise unittest.SkipTest("Couldn't find gdb on the path") 30 raise unittest.SkipTest("Couldn't find gdb on the path")
29 gdb_version_number = re.search(b"^GNU gdb [^\d]*(\d+)\.(\d)", gdb_version) 31 gdb_version_number = re.search(b"^GNU gdb [^\d]*(\d+)\.(\d)", gdb_version)
30 gdb_major_version = int(gdb_version_number.group(1)) 32 gdb_major_version = int(gdb_version_number.group(1))
31 gdb_minor_version = int(gdb_version_number.group(2)) 33 gdb_minor_version = int(gdb_version_number.group(2))
32 if gdb_major_version < 7: 34 if gdb_major_version < 7:
33 raise unittest.SkipTest("gdb versions before 7.0 didn't support python embed ding" 35 raise unittest.SkipTest("gdb versions before 7.0 didn't support python embed ding"
34 " Saw:\n" + gdb_version.decode('ascii', 'replace')) 36 " Saw:\n" + gdb_version.decode('ascii', 'replace'))
35 37
36 if not sysconfig.is_python_build(): 38 if not sysconfig.is_python_build():
37 raise unittest.SkipTest("test_gdb only works on source builds at the moment. ") 39 raise unittest.SkipTest("test_gdb only works on source builds at the moment. ")
38 40
39 # Location of custom hooks file in a repository checkout. 41 # Location of custom hooks file in a repository checkout.
40 checkout_hook_path = os.path.join(os.path.dirname(sys.executable), 42 checkout_hook_path = os.path.join(os.path.dirname(sys.executable),
41 'python-gdb.py') 43 'python-gdb.py')
44
45 PYTHONHASHSEED = '123'
42 46
43 def run_gdb(*args, **env_vars): 47 def run_gdb(*args, **env_vars):
44 """Runs gdb in --batch mode with the additional arguments given by *args. 48 """Runs gdb in --batch mode with the additional arguments given by *args.
45 49
46 Returns its (stdout, stderr) decoded from utf-8 using the replace handler. 50 Returns its (stdout, stderr) decoded from utf-8 using the replace handler.
47 """ 51 """
48 if env_vars: 52 if env_vars:
49 env = os.environ.copy() 53 env = os.environ.copy()
50 env.update(env_vars) 54 env.update(env_vars)
51 else: 55 else:
52 env = None 56 env = None
53 base_cmd = ('gdb', '--batch') 57 base_cmd = ('gdb', '--batch')
54 if (gdb_major_version, gdb_minor_version) >= (7, 4): 58 if (gdb_major_version, gdb_minor_version) >= (7, 4):
55 base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path) 59 base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
56 out, err = subprocess.Popen(base_cmd + args, 60 out, err = subprocess.Popen(base_cmd + args,
57 stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, 61 stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env,
58 ).communicate() 62 ).communicate()
59 return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace') 63 return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
60 64
61 # Verify that "gdb" was built with the embedded python support enabled: 65 # Verify that "gdb" was built with the embedded python support enabled:
62 gdbpy_version, _ = run_gdb("--eval-command=python import sys; print sys.version_ info") 66 gdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_ info)")
63 if not gdbpy_version: 67 if not gdbpy_version:
64 raise unittest.SkipTest("gdb not built with embedded python support") 68 raise unittest.SkipTest("gdb not built with embedded python support")
65 69
66 # Verify that "gdb" can load our custom hooks. In theory this should never fail. 70 # Verify that "gdb" can load our custom hooks, as OS security settings may
71 # disallow this without a customised .gdbinit.
67 cmd = ['--args', sys.executable] 72 cmd = ['--args', sys.executable]
68 _, gdbpy_errors = run_gdb('--args', sys.executable) 73 _, gdbpy_errors = run_gdb('--args', sys.executable)
69 if "auto-loading has been declined" in gdbpy_errors: 74 if "auto-loading has been declined" in gdbpy_errors:
70 msg = "gdb security settings prevent use of custom hooks: " 75 msg = "gdb security settings prevent use of custom hooks: "
71 raise unittest.SkipTest(msg + gdbpy_errors.rstrip()) 76 raise unittest.SkipTest(msg + gdbpy_errors.rstrip())
72 77
73 def gdb_has_frame_select(): 78 def gdb_has_frame_select():
74 # Does this build of gdb have gdb.Frame.select ? 79 # Does this build of gdb have gdb.Frame.select ?
75 stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))") 80 stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))")
76 m = re.match(r'.*\[(.*)\].*', stdout) 81 m = re.match(r'.*\[(.*)\].*', stdout)
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 if not import_site: 138 if not import_site:
134 # -S suppresses the default 'import site' 139 # -S suppresses the default 'import site'
135 args += ["-S"] 140 args += ["-S"]
136 141
137 if source: 142 if source:
138 args += ["-c", source] 143 args += ["-c", source]
139 elif script: 144 elif script:
140 args += [script] 145 args += [script]
141 146
142 # print args 147 # print args
143 # print ' '.join(args) 148 # print (' '.join(args))
144 149
145 # Use "args" to invoke gdb, capturing stdout, stderr: 150 # Use "args" to invoke gdb, capturing stdout, stderr:
146 out, err = run_gdb(*args, PYTHONHASHSEED='0') 151 out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED)
147 152
148 # Ignore some noise on stderr due to the pending breakpoint: 153 errlines = err.splitlines()
149 err = err.replace('Function "%s" not defined.\n' % breakpoint, '') 154 unexpected_errlines = []
150 # Ignore some other noise on stderr (http://bugs.python.org/issue8600) 155
151 err = err.replace("warning: Unable to find libthread_db matching" 156 # Ignore some benign messages on stderr.
152 " inferior's thread library, thread debugging will" 157 ignore_patterns = (
153 " not be available.\n", 158 'Function "%s" not defined.' % breakpoint,
154 '') 159 "warning: no loadable sections found in added symbol-file"
155 err = err.replace("warning: Cannot initialize thread debugging" 160 " system-supplied DSO",
156 " library: Debugger service failed\n", 161 "warning: Unable to find libthread_db matching"
157 '') 162 " inferior's thread library, thread debugging will"
158 err = err.replace('warning: Could not load shared library symbols for ' 163 " not be available.",
159 'linux-vdso.so.1.\n' 164 "warning: Cannot initialize thread debugging"
160 'Do you need "set solib-search-path" or ' 165 " library: Debugger service failed",
161 '"set sysroot"?\n', 166 'warning: Could not load shared library symbols for '
162 '') 167 'linux-vdso.so',
163 err = err.replace('warning: Could not load shared library symbols for ' 168 'warning: Could not load shared library symbols for '
164 'linux-gate.so.1.\n' 169 'linux-gate.so',
165 'Do you need "set solib-search-path" or ' 170 'Do you need "set solib-search-path" or '
166 '"set sysroot"?\n', 171 '"set sysroot"?',
167 '') 172 'warning: Source file is more recent than executable.',
173 # Issue #19753: missing symbols on System Z
174 'Missing separate debuginfo for ',
175 'Try: zypper install -C ',
176 )
177 for line in errlines:
178 if not line.startswith(ignore_patterns):
179 unexpected_errlines.append(line)
168 180
169 # Ensure no unexpected error messages: 181 # Ensure no unexpected error messages:
170 self.assertEqual(err, '') 182 self.assertEqual(unexpected_errlines, [])
171 return out 183 return out
172 184
173 def get_gdb_repr(self, source, 185 def get_gdb_repr(self, source,
174 cmds_after_breakpoint=None, 186 cmds_after_breakpoint=None,
175 import_site=False): 187 import_site=False):
176 # Given an input python source representation of data, 188 # Given an input python source representation of data,
177 # run "python -c'id(DATA)'" under gdb with a breakpoint on 189 # run "python -c'id(DATA)'" under gdb with a breakpoint on
178 # builtin_id and scrape out gdb's representation of the "op" 190 # builtin_id and scrape out gdb's representation of the "op"
179 # parameter, and verify that the gdb displays the same string 191 # parameter, and verify that the gdb displays the same string
180 # 192 #
181 # Verify that the gdb displays the expected string 193 # Verify that the gdb displays the expected string
182 # 194 #
183 # For a nested structure, the first time we hit the breakpoint will 195 # For a nested structure, the first time we hit the breakpoint will
184 # give us the top-level structure 196 # give us the top-level structure
197
198 # NOTE: avoid decoding too much of the traceback as some
199 # undecodable characters may lurk there in optimized mode
200 # (issue #19743).
201 cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"]
185 gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, 202 gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN,
186 cmds_after_breakpoint=cmds_after_break point, 203 cmds_after_breakpoint=cmds_after_break point,
187 import_site=import_site) 204 import_site=import_site)
188 # gdb can insert additional '\n' and space characters in various places 205 # gdb can insert additional '\n' and space characters in various places
189 # in its output, depending on the width of the terminal it's connected 206 # in its output, depending on the width of the terminal it's connected
190 # to (using its "wrap_here" function) 207 # to (using its "wrap_here" function)
191 m = re.match('.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*P ython/bltinmodule.c.*', 208 m = re.match('.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*P ython/bltinmodule.c.*',
192 gdb_output, re.DOTALL) 209 gdb_output, re.DOTALL)
193 if not m: 210 if not m:
194 self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output) ) 211 self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output) )
(...skipping 10 matching lines...) Expand all
205 self.fail(msg='%r did not match %r' % (actual, pattern)) 222 self.fail(msg='%r did not match %r' % (actual, pattern))
206 223
207 def get_sample_script(self): 224 def get_sample_script(self):
208 return findfile('gdb_sample.py') 225 return findfile('gdb_sample.py')
209 226
210 class PrettyPrintTests(DebuggerTests): 227 class PrettyPrintTests(DebuggerTests):
211 def test_getting_backtrace(self): 228 def test_getting_backtrace(self):
212 gdb_output = self.get_stack_trace('id(42)') 229 gdb_output = self.get_stack_trace('id(42)')
213 self.assertIn(BREAKPOINT_FN, gdb_output) 230 self.assertIn(BREAKPOINT_FN, gdb_output)
214 231
215 def assertGdbRepr(self, val, exp_repr=None, cmds_after_breakpoint=None): 232 def assertGdbRepr(self, val, exp_repr=None):
216 # Ensure that gdb's rendering of the value in a debugged process 233 # Ensure that gdb's rendering of the value in a debugged process
217 # matches repr(value) in this process: 234 # matches repr(value) in this process:
218 gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')', 235 gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')')
219 cmds_after_breakpoint)
220 if not exp_repr: 236 if not exp_repr:
221 exp_repr = repr(val) 237 exp_repr = repr(val)
222 self.assertEqual(gdb_repr, exp_repr, 238 self.assertEqual(gdb_repr, exp_repr,
223 ('%r did not equal expected %r; full output was:\n%s' 239 ('%r did not equal expected %r; full output was:\n%s'
224 % (gdb_repr, exp_repr, gdb_output))) 240 % (gdb_repr, exp_repr, gdb_output)))
225 241
226 def test_int(self): 242 def test_int(self):
227 'Verify the pretty-printing of various "int"/long values' 243 'Verify the pretty-printing of various int values'
228 self.assertGdbRepr(42) 244 self.assertGdbRepr(42)
229 self.assertGdbRepr(0) 245 self.assertGdbRepr(0)
230 self.assertGdbRepr(-7) 246 self.assertGdbRepr(-7)
231 self.assertGdbRepr(1000000000000) 247 self.assertGdbRepr(1000000000000)
232 self.assertGdbRepr(-1000000000000000) 248 self.assertGdbRepr(-1000000000000000)
233 249
234 def test_singletons(self): 250 def test_singletons(self):
235 'Verify the pretty-printing of True, False and None' 251 'Verify the pretty-printing of True, False and None'
236 self.assertGdbRepr(True) 252 self.assertGdbRepr(True)
237 self.assertGdbRepr(False) 253 self.assertGdbRepr(False)
238 self.assertGdbRepr(None) 254 self.assertGdbRepr(None)
239 255
240 def test_dicts(self): 256 def test_dicts(self):
241 'Verify the pretty-printing of dictionaries' 257 'Verify the pretty-printing of dictionaries'
242 self.assertGdbRepr({}) 258 self.assertGdbRepr({})
243 self.assertGdbRepr({'foo': 'bar'}) 259 self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}")
244 self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, 260 self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'douglas': 42, 'foo' : 'bar'}")
245 "{'foo': 'bar', 'douglas': 42}")
246 261
247 def test_lists(self): 262 def test_lists(self):
248 'Verify the pretty-printing of lists' 263 'Verify the pretty-printing of lists'
249 self.assertGdbRepr([]) 264 self.assertGdbRepr([])
250 self.assertGdbRepr(list(range(5))) 265 self.assertGdbRepr(list(range(5)))
251 266
252 def test_bytes(self): 267 def test_bytes(self):
253 'Verify the pretty-printing of bytes' 268 'Verify the pretty-printing of bytes'
254 self.assertGdbRepr(b'') 269 self.assertGdbRepr(b'')
255 self.assertGdbRepr(b'And now for something hopefully the same') 270 self.assertGdbRepr(b'And now for something hopefully the same')
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 305
291 # Test a character outside the BMP: 306 # Test a character outside the BMP:
292 # U+1D121 MUSICAL SYMBOL C CLEF 307 # U+1D121 MUSICAL SYMBOL C CLEF
293 # This is: 308 # This is:
294 # UTF-8: 0xF0 0x9D 0x84 0xA1 309 # UTF-8: 0xF0 0x9D 0x84 0xA1
295 # UTF-16: 0xD834 0xDD21 310 # UTF-16: 0xD834 0xDD21
296 check_repr(chr(0x1D121)) 311 check_repr(chr(0x1D121))
297 312
298 def test_tuples(self): 313 def test_tuples(self):
299 'Verify the pretty-printing of tuples' 314 'Verify the pretty-printing of tuples'
300 self.assertGdbRepr(tuple()) 315 self.assertGdbRepr(tuple(), '()')
301 self.assertGdbRepr((1,), '(1,)') 316 self.assertGdbRepr((1,), '(1,)')
302 self.assertGdbRepr(('foo', 'bar', 'baz')) 317 self.assertGdbRepr(('foo', 'bar', 'baz'))
303 318
304 def test_sets(self): 319 def test_sets(self):
305 'Verify the pretty-printing of sets' 320 'Verify the pretty-printing of sets'
306 self.assertGdbRepr(set()) 321 if (gdb_major_version, gdb_minor_version) < (7, 3):
322 self.skipTest("pretty-printing of sets needs gdb 7.3 or later")
323 self.assertGdbRepr(set(), 'set()')
307 self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}") 324 self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}")
308 self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}") 325 self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}")
309 326
310 # Ensure that we handle sets containing the "dummy" key value, 327 # Ensure that we handle sets containing the "dummy" key value,
311 # which happens on deletion: 328 # which happens on deletion:
312 gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b']) 329 gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b'])
313 s.pop() 330 s.remove('a')
314 id(s)''') 331 id(s)''')
315 self.assertEqual(gdb_repr, "{'b'}") 332 self.assertEqual(gdb_repr, "{'b'}")
316 333
317 def test_frozensets(self): 334 def test_frozensets(self):
318 'Verify the pretty-printing of frozensets' 335 'Verify the pretty-printing of frozensets'
319 self.assertGdbRepr(frozenset()) 336 if (gdb_major_version, gdb_minor_version) < (7, 3):
337 self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later" )
338 self.assertGdbRepr(frozenset(), 'frozenset()')
320 self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})") 339 self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})")
321 self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})") 340 self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})")
322 341
323 def test_exceptions(self): 342 def test_exceptions(self):
324 # Test a RuntimeError 343 # Test a RuntimeError
325 gdb_repr, gdb_output = self.get_gdb_repr(''' 344 gdb_repr, gdb_output = self.get_gdb_repr('''
326 try: 345 try:
327 raise RuntimeError("I am an error") 346 raise RuntimeError("I am an error")
328 except RuntimeError as e: 347 except RuntimeError as e:
329 id(e) 348 id(e)
(...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after
826 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands" ) 845 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands" )
827 @unittest.skipIf(python_is_optimized(), 846 @unittest.skipIf(python_is_optimized(),
828 "Python was compiled with optimizations") 847 "Python was compiled with optimizations")
829 def test_locals_after_up(self): 848 def test_locals_after_up(self):
830 bt = self.get_stack_trace(script=self.get_sample_script(), 849 bt = self.get_stack_trace(script=self.get_sample_script(),
831 cmds_after_breakpoint=['py-up', 'py-locals']) 850 cmds_after_breakpoint=['py-up', 'py-locals'])
832 self.assertMultilineMatches(bt, 851 self.assertMultilineMatches(bt,
833 r".*\na = 1\nb = 2\nc = 3\n.*") 852 r".*\na = 1\nb = 2\nc = 3\n.*")
834 853
835 def test_main(): 854 def test_main():
855 if support.verbose:
856 print("GDB version:")
857 for line in os.fsdecode(gdb_version).splitlines():
858 print(" " * 4 + line)
836 run_unittest(PrettyPrintTests, 859 run_unittest(PrettyPrintTests,
837 PyListTests, 860 PyListTests,
838 StackNavigationTests, 861 StackNavigationTests,
839 PyBtTests, 862 PyBtTests,
840 PyPrintTests, 863 PyPrintTests,
841 PyLocalsTests 864 PyLocalsTests
842 ) 865 )
843 866
844 if __name__ == "__main__": 867 if __name__ == "__main__":
845 test_main() 868 test_main()
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+