test_gdb.py revision a8157183b89c08cf47c969185d40973ec21a81c5
1# Verify that gdb can pretty-print the various PyObject* types 2# 3# The code for testing gdb was adapted from similar work in Unladen Swallow's 4# Lib/test/test_jit_gdb.py 5 6import os 7import re 8import subprocess 9import sys 10import unittest 11 12from test.test_support import run_unittest, findfile 13 14try: 15 gdb_version, _ = subprocess.Popen(["gdb", "--version"], 16 stdout=subprocess.PIPE).communicate() 17except OSError: 18 # This is what "no gdb" looks like. There may, however, be other 19 # errors that manifest this way too. 20 raise unittest.SkipTest("Couldn't find gdb on the path") 21gdb_version_number = re.search(r"^GNU gdb [^\d]*(\d+)\.", gdb_version) 22if int(gdb_version_number.group(1)) < 7: 23 raise unittest.SkipTest("gdb versions before 7.0 didn't support python embedding" 24 " Saw:\n" + gdb_version) 25 26# Verify that "gdb" was built with the embedded python support enabled: 27cmd = "--eval-command=python import sys; print sys.version_info" 28p = subprocess.Popen(["gdb", "--batch", cmd], 29 stdout=subprocess.PIPE) 30gdbpy_version, _ = p.communicate() 31if gdbpy_version == '': 32 raise unittest.SkipTest("gdb not built with embedded python support") 33 34def gdb_has_frame_select(): 35 # Does this build of gdb have gdb.Frame.select ? 36 cmd = "--eval-command=python print(dir(gdb.Frame))" 37 p = subprocess.Popen(["gdb", "--batch", cmd], 38 stdout=subprocess.PIPE) 39 stdout, _ = p.communicate() 40 m = re.match(r'.*\[(.*)\].*', stdout) 41 if not m: 42 raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test") 43 gdb_frame_dir = m.group(1).split(', ') 44 return "'select'" in gdb_frame_dir 45 46HAS_PYUP_PYDOWN = gdb_has_frame_select() 47 48class DebuggerTests(unittest.TestCase): 49 50 """Test that the debugger can debug Python.""" 51 52 def run_gdb(self, *args): 53 """Runs gdb with the command line given by *args. 54 55 Returns its stdout, stderr 56 """ 57 out, err = subprocess.Popen( 58 args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 59 ).communicate() 60 return out, err 61 62 def get_stack_trace(self, source=None, script=None, 63 breakpoint='PyObject_Print', 64 cmds_after_breakpoint=None, 65 import_site=False): 66 ''' 67 Run 'python -c SOURCE' under gdb with a breakpoint. 68 69 Support injecting commands after the breakpoint is reached 70 71 Returns the stdout from gdb 72 73 cmds_after_breakpoint: if provided, a list of strings: gdb commands 74 ''' 75 # We use "set breakpoint pending yes" to avoid blocking with a: 76 # Function "foo" not defined. 77 # Make breakpoint pending on future shared library load? (y or [n]) 78 # error, which typically happens python is dynamically linked (the 79 # breakpoints of interest are to be found in the shared library) 80 # When this happens, we still get: 81 # Function "PyObject_Print" not defined. 82 # emitted to stderr each time, alas. 83 84 # Initially I had "--eval-command=continue" here, but removed it to 85 # avoid repeated print breakpoints when traversing hierarchical data 86 # structures 87 88 # Generate a list of commands in gdb's language: 89 commands = ['set breakpoint pending yes', 90 'break %s' % breakpoint, 91 'run'] 92 if cmds_after_breakpoint: 93 commands += cmds_after_breakpoint 94 else: 95 commands += ['backtrace'] 96 97 # print commands 98 99 # Use "commands" to generate the arguments with which to invoke "gdb": 100 args = ["gdb", "--batch"] 101 args += ['--eval-command=%s' % cmd for cmd in commands] 102 args += ["--args", 103 sys.executable] 104 105 if not import_site: 106 # -S suppresses the default 'import site' 107 args += ["-S"] 108 109 if source: 110 args += ["-c", source] 111 elif script: 112 args += [script] 113 114 # print args 115 # print ' '.join(args) 116 117 # Use "args" to invoke gdb, capturing stdout, stderr: 118 out, err = self.run_gdb(*args) 119 120 # Ignore some noise on stderr due to the pending breakpoint: 121 err = err.replace('Function "%s" not defined.\n' % breakpoint, '') 122 # Ignore some other noise on stderr (http://bugs.python.org/issue8600) 123 err = err.replace("warning: Unable to find libthread_db matching" 124 " inferior's thread library, thread debugging will" 125 " not be available.\n", 126 '') 127 128 # Ensure no unexpected error messages: 129 self.assertEquals(err, '') 130 131 return out 132 133 def get_gdb_repr(self, source, 134 cmds_after_breakpoint=None, 135 import_site=False): 136 # Given an input python source representation of data, 137 # run "python -c'print DATA'" under gdb with a breakpoint on 138 # PyObject_Print and scrape out gdb's representation of the "op" 139 # parameter, and verify that the gdb displays the same string 140 # 141 # For a nested structure, the first time we hit the breakpoint will 142 # give us the top-level structure 143 gdb_output = self.get_stack_trace(source, breakpoint='PyObject_Print', 144 cmds_after_breakpoint=cmds_after_breakpoint, 145 import_site=import_site) 146 # gdb can insert additional '\n' and space characters in various places 147 # in its output, depending on the width of the terminal it's connected 148 # to (using its "wrap_here" function) 149 m = re.match('.*#0\s+PyObject_Print\s+\(\s*op\=\s*(.*?),\s+fp=.*\).*', 150 gdb_output, re.DOTALL) 151 if not m: 152 self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) 153 return m.group(1), gdb_output 154 155 def assertEndsWith(self, actual, exp_end): 156 '''Ensure that the given "actual" string ends with "exp_end"''' 157 self.assert_(actual.endswith(exp_end), 158 msg='%r did not end with %r' % (actual, exp_end)) 159 160 def assertMultilineMatches(self, actual, pattern): 161 m = re.match(pattern, actual, re.DOTALL) 162 self.assert_(m, 163 msg='%r did not match %r' % (actual, pattern)) 164 165 def get_sample_script(self): 166 return findfile('gdb_sample.py') 167 168class PrettyPrintTests(DebuggerTests): 169 def test_getting_backtrace(self): 170 gdb_output = self.get_stack_trace('print 42') 171 self.assertTrue('PyObject_Print' in gdb_output) 172 173 def assertGdbRepr(self, val, cmds_after_breakpoint=None): 174 # Ensure that gdb's rendering of the value in a debugged process 175 # matches repr(value) in this process: 176 gdb_repr, gdb_output = self.get_gdb_repr('print ' + repr(val), 177 cmds_after_breakpoint) 178 self.assertEquals(gdb_repr, repr(val), gdb_output) 179 180 def test_int(self): 181 'Verify the pretty-printing of various "int" values' 182 self.assertGdbRepr(42) 183 self.assertGdbRepr(0) 184 self.assertGdbRepr(-7) 185 self.assertGdbRepr(sys.maxint) 186 self.assertGdbRepr(-sys.maxint) 187 188 def test_long(self): 189 'Verify the pretty-printing of various "long" values' 190 self.assertGdbRepr(0L) 191 self.assertGdbRepr(1000000000000L) 192 self.assertGdbRepr(-1L) 193 self.assertGdbRepr(-1000000000000000L) 194 195 def test_singletons(self): 196 'Verify the pretty-printing of True, False and None' 197 self.assertGdbRepr(True) 198 self.assertGdbRepr(False) 199 self.assertGdbRepr(None) 200 201 def test_dicts(self): 202 'Verify the pretty-printing of dictionaries' 203 self.assertGdbRepr({}) 204 self.assertGdbRepr({'foo': 'bar'}) 205 self.assertGdbRepr({'foo': 'bar', 'douglas':42}) 206 207 def test_lists(self): 208 'Verify the pretty-printing of lists' 209 self.assertGdbRepr([]) 210 self.assertGdbRepr(range(5)) 211 212 def test_strings(self): 213 'Verify the pretty-printing of strings' 214 self.assertGdbRepr('') 215 self.assertGdbRepr('And now for something hopefully the same') 216 self.assertGdbRepr('string with embedded NUL here \0 and then some more text') 217 self.assertGdbRepr('this is byte 255:\xff and byte 128:\x80') 218 219 def test_tuples(self): 220 'Verify the pretty-printing of tuples' 221 self.assertGdbRepr(tuple()) 222 self.assertGdbRepr((1,)) 223 self.assertGdbRepr(('foo', 'bar', 'baz')) 224 225 def test_unicode(self): 226 'Verify the pretty-printing of unicode values' 227 # Test the empty unicode string: 228 self.assertGdbRepr(u'') 229 230 self.assertGdbRepr(u'hello world') 231 232 # Test printing a single character: 233 # U+2620 SKULL AND CROSSBONES 234 self.assertGdbRepr(u'\u2620') 235 236 # Test printing a Japanese unicode string 237 # (I believe this reads "mojibake", using 3 characters from the CJK 238 # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE) 239 self.assertGdbRepr(u'\u6587\u5b57\u5316\u3051') 240 241 # Test a character outside the BMP: 242 # U+1D121 MUSICAL SYMBOL C CLEF 243 # This is: 244 # UTF-8: 0xF0 0x9D 0x84 0xA1 245 # UTF-16: 0xD834 0xDD21 246 try: 247 # This will only work on wide-unicode builds: 248 self.assertGdbRepr(unichr(0x1D121)) 249 except ValueError, e: 250 # We're probably on a narrow-unicode build; if we're seeing a 251 # different problem, then re-raise it: 252 if e.args != ('unichr() arg not in range(0x10000) (narrow Python build)',): 253 raise e 254 255 def test_sets(self): 256 'Verify the pretty-printing of sets' 257 self.assertGdbRepr(set()) 258 self.assertGdbRepr(set(['a', 'b'])) 259 self.assertGdbRepr(set([4, 5, 6])) 260 261 # Ensure that we handled sets containing the "dummy" key value, 262 # which happens on deletion: 263 gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b']) 264s.pop() 265print s''') 266 self.assertEquals(gdb_repr, "set(['b'])") 267 268 def test_frozensets(self): 269 'Verify the pretty-printing of frozensets' 270 self.assertGdbRepr(frozenset()) 271 self.assertGdbRepr(frozenset(['a', 'b'])) 272 self.assertGdbRepr(frozenset([4, 5, 6])) 273 274 def test_exceptions(self): 275 # Test a RuntimeError 276 gdb_repr, gdb_output = self.get_gdb_repr(''' 277try: 278 raise RuntimeError("I am an error") 279except RuntimeError, e: 280 print e 281''') 282 self.assertEquals(gdb_repr, 283 "exceptions.RuntimeError('I am an error',)") 284 285 286 # Test division by zero: 287 gdb_repr, gdb_output = self.get_gdb_repr(''' 288try: 289 a = 1 / 0 290except ZeroDivisionError, e: 291 print e 292''') 293 self.assertEquals(gdb_repr, 294 "exceptions.ZeroDivisionError('integer division or modulo by zero',)") 295 296 def test_classic_class(self): 297 'Verify the pretty-printing of classic class instances' 298 gdb_repr, gdb_output = self.get_gdb_repr(''' 299class Foo: 300 pass 301foo = Foo() 302foo.an_int = 42 303print foo''') 304 m = re.match(r'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr) 305 self.assertTrue(m, 306 msg='Unexpected classic-class rendering %r' % gdb_repr) 307 308 def test_modern_class(self): 309 'Verify the pretty-printing of new-style class instances' 310 gdb_repr, gdb_output = self.get_gdb_repr(''' 311class Foo(object): 312 pass 313foo = Foo() 314foo.an_int = 42 315print foo''') 316 m = re.match(r'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr) 317 self.assertTrue(m, 318 msg='Unexpected new-style class rendering %r' % gdb_repr) 319 320 def test_subclassing_list(self): 321 'Verify the pretty-printing of an instance of a list subclass' 322 gdb_repr, gdb_output = self.get_gdb_repr(''' 323class Foo(list): 324 pass 325foo = Foo() 326foo += [1, 2, 3] 327foo.an_int = 42 328print foo''') 329 m = re.match(r'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr) 330 self.assertTrue(m, 331 msg='Unexpected new-style class rendering %r' % gdb_repr) 332 333 def test_subclassing_tuple(self): 334 'Verify the pretty-printing of an instance of a tuple subclass' 335 # This should exercise the negative tp_dictoffset code in the 336 # new-style class support 337 gdb_repr, gdb_output = self.get_gdb_repr(''' 338class Foo(tuple): 339 pass 340foo = Foo((1, 2, 3)) 341foo.an_int = 42 342print foo''') 343 m = re.match(r'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr) 344 self.assertTrue(m, 345 msg='Unexpected new-style class rendering %r' % gdb_repr) 346 347 def assertSane(self, source, corruption, expvalue=None, exptype=None): 348 '''Run Python under gdb, corrupting variables in the inferior process 349 immediately before taking a backtrace. 350 351 Verify that the variable's representation is the expected failsafe 352 representation''' 353 if corruption: 354 cmds_after_breakpoint=[corruption, 'backtrace'] 355 else: 356 cmds_after_breakpoint=['backtrace'] 357 358 gdb_repr, gdb_output = \ 359 self.get_gdb_repr(source, 360 cmds_after_breakpoint=cmds_after_breakpoint) 361 362 if expvalue: 363 if gdb_repr == repr(expvalue): 364 # gdb managed to print the value in spite of the corruption; 365 # this is good (see http://bugs.python.org/issue8330) 366 return 367 368 if exptype: 369 pattern = '<' + exptype + ' at remote 0x[0-9a-f]+>' 370 else: 371 # Match anything for the type name; 0xDEADBEEF could point to 372 # something arbitrary (see http://bugs.python.org/issue8330) 373 pattern = '<.* at remote 0x[0-9a-f]+>' 374 375 m = re.match(pattern, gdb_repr) 376 if not m: 377 self.fail('Unexpected gdb representation: %r\n%s' % \ 378 (gdb_repr, gdb_output)) 379 380 def test_NULL_ptr(self): 381 'Ensure that a NULL PyObject* is handled gracefully' 382 gdb_repr, gdb_output = ( 383 self.get_gdb_repr('print 42', 384 cmds_after_breakpoint=['set variable op=0', 385 'backtrace']) 386 ) 387 388 self.assertEquals(gdb_repr, '0x0') 389 390 def test_NULL_ob_type(self): 391 'Ensure that a PyObject* with NULL ob_type is handled gracefully' 392 self.assertSane('print 42', 393 'set op->ob_type=0') 394 395 def test_corrupt_ob_type(self): 396 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully' 397 self.assertSane('print 42', 398 'set op->ob_type=0xDEADBEEF', 399 expvalue=42) 400 401 def test_corrupt_tp_flags(self): 402 'Ensure that a PyObject* with a type with corrupt tp_flags is handled' 403 self.assertSane('print 42', 404 'set op->ob_type->tp_flags=0x0', 405 expvalue=42) 406 407 def test_corrupt_tp_name(self): 408 'Ensure that a PyObject* with a type with corrupt tp_name is handled' 409 self.assertSane('print 42', 410 'set op->ob_type->tp_name=0xDEADBEEF', 411 expvalue=42) 412 413 def test_NULL_instance_dict(self): 414 'Ensure that a PyInstanceObject with with a NULL in_dict is handled' 415 self.assertSane(''' 416class Foo: 417 pass 418foo = Foo() 419foo.an_int = 42 420print foo''', 421 'set ((PyInstanceObject*)op)->in_dict = 0', 422 exptype='Foo') 423 424 def test_builtins_help(self): 425 'Ensure that the new-style class _Helper in site.py can be handled' 426 # (this was the issue causing tracebacks in 427 # http://bugs.python.org/issue8032#msg100537 ) 428 429 gdb_repr, gdb_output = self.get_gdb_repr('print __builtins__.help', import_site=True) 430 m = re.match(r'<_Helper at remote 0x[0-9a-f]+>', gdb_repr) 431 self.assertTrue(m, 432 msg='Unexpected rendering %r' % gdb_repr) 433 434 def test_selfreferential_list(self): 435 '''Ensure that a reference loop involving a list doesn't lead proxyval 436 into an infinite loop:''' 437 gdb_repr, gdb_output = \ 438 self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; print a") 439 440 self.assertEquals(gdb_repr, '[3, 4, 5, [...]]') 441 442 gdb_repr, gdb_output = \ 443 self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; print a") 444 445 self.assertEquals(gdb_repr, '[3, 4, 5, [[...]]]') 446 447 def test_selfreferential_dict(self): 448 '''Ensure that a reference loop involving a dict doesn't lead proxyval 449 into an infinite loop:''' 450 gdb_repr, gdb_output = \ 451 self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; print a") 452 453 self.assertEquals(gdb_repr, "{'foo': {'bar': {...}}}") 454 455 def test_selfreferential_old_style_instance(self): 456 gdb_repr, gdb_output = \ 457 self.get_gdb_repr(''' 458class Foo: 459 pass 460foo = Foo() 461foo.an_attr = foo 462print foo''') 463 self.assertTrue(re.match('<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>', 464 gdb_repr), 465 'Unexpected gdb representation: %r\n%s' % \ 466 (gdb_repr, gdb_output)) 467 468 def test_selfreferential_new_style_instance(self): 469 gdb_repr, gdb_output = \ 470 self.get_gdb_repr(''' 471class Foo(object): 472 pass 473foo = Foo() 474foo.an_attr = foo 475print foo''') 476 self.assertTrue(re.match('<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>', 477 gdb_repr), 478 'Unexpected gdb representation: %r\n%s' % \ 479 (gdb_repr, gdb_output)) 480 481 gdb_repr, gdb_output = \ 482 self.get_gdb_repr(''' 483class Foo(object): 484 pass 485a = Foo() 486b = Foo() 487a.an_attr = b 488b.an_attr = a 489print a''') 490 self.assertTrue(re.match('<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>\) at remote 0x[0-9a-f]+>', 491 gdb_repr), 492 'Unexpected gdb representation: %r\n%s' % \ 493 (gdb_repr, gdb_output)) 494 495 def test_truncation(self): 496 'Verify that very long output is truncated' 497 gdb_repr, gdb_output = self.get_gdb_repr('print range(1000)') 498 self.assertEquals(gdb_repr, 499 "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, " 500 "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, " 501 "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, " 502 "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, " 503 "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, " 504 "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, " 505 "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, " 506 "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, " 507 "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, " 508 "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, " 509 "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, " 510 "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, " 511 "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, " 512 "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, " 513 "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, " 514 "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, " 515 "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, " 516 "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, " 517 "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, " 518 "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, " 519 "224, 225, 226...(truncated)") 520 self.assertEquals(len(gdb_repr), 521 1024 + len('...(truncated)')) 522 523 def test_builtin_function(self): 524 gdb_repr, gdb_output = self.get_gdb_repr('print len') 525 self.assertEquals(gdb_repr, '<built-in function len>') 526 527 def test_builtin_method(self): 528 gdb_repr, gdb_output = self.get_gdb_repr('import sys; print sys.stdout.readlines') 529 self.assertTrue(re.match('<built-in method readlines of file object at remote 0x[0-9a-f]+>', 530 gdb_repr), 531 'Unexpected gdb representation: %r\n%s' % \ 532 (gdb_repr, gdb_output)) 533 534 def test_frames(self): 535 gdb_output = self.get_stack_trace(''' 536def foo(a, b, c): 537 pass 538 539foo(3, 4, 5) 540print foo.__code__''', 541 breakpoint='PyObject_Print', 542 cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)op)->co_zombieframe)'] 543 ) 544 self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*', 545 gdb_output, 546 re.DOTALL), 547 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) 548 549class PyListTests(DebuggerTests): 550 def assertListing(self, expected, actual): 551 self.assertEndsWith(actual, expected) 552 553 def test_basic_command(self): 554 'Verify that the "py-list" command works' 555 bt = self.get_stack_trace(script=self.get_sample_script(), 556 cmds_after_breakpoint=['py-list']) 557 558 self.assertListing(' 5 \n' 559 ' 6 def bar(a, b, c):\n' 560 ' 7 baz(a, b, c)\n' 561 ' 8 \n' 562 ' 9 def baz(*args):\n' 563 ' >10 print(42)\n' 564 ' 11 \n' 565 ' 12 foo(1, 2, 3)\n', 566 bt) 567 568 def test_one_abs_arg(self): 569 'Verify the "py-list" command with one absolute argument' 570 bt = self.get_stack_trace(script=self.get_sample_script(), 571 cmds_after_breakpoint=['py-list 9']) 572 573 self.assertListing(' 9 def baz(*args):\n' 574 ' >10 print(42)\n' 575 ' 11 \n' 576 ' 12 foo(1, 2, 3)\n', 577 bt) 578 579 def test_two_abs_args(self): 580 'Verify the "py-list" command with two absolute arguments' 581 bt = self.get_stack_trace(script=self.get_sample_script(), 582 cmds_after_breakpoint=['py-list 1,3']) 583 584 self.assertListing(' 1 # Sample script for use by test_gdb.py\n' 585 ' 2 \n' 586 ' 3 def foo(a, b, c):\n', 587 bt) 588 589class StackNavigationTests(DebuggerTests): 590 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") 591 def test_pyup_command(self): 592 'Verify that the "py-up" command works' 593 bt = self.get_stack_trace(script=self.get_sample_script(), 594 cmds_after_breakpoint=['py-up']) 595 self.assertMultilineMatches(bt, 596 r'''^.* 597#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) 598 baz\(a, b, c\) 599$''') 600 601 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") 602 def test_down_at_bottom(self): 603 'Verify handling of "py-down" at the bottom of the stack' 604 bt = self.get_stack_trace(script=self.get_sample_script(), 605 cmds_after_breakpoint=['py-down']) 606 self.assertEndsWith(bt, 607 'Unable to find a newer python frame\n') 608 609 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") 610 def test_up_at_top(self): 611 'Verify handling of "py-up" at the top of the stack' 612 bt = self.get_stack_trace(script=self.get_sample_script(), 613 cmds_after_breakpoint=['py-up'] * 4) 614 self.assertEndsWith(bt, 615 'Unable to find an older python frame\n') 616 617 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") 618 def test_up_then_down(self): 619 'Verify "py-up" followed by "py-down"' 620 bt = self.get_stack_trace(script=self.get_sample_script(), 621 cmds_after_breakpoint=['py-up', 'py-down']) 622 self.assertMultilineMatches(bt, 623 r'''^.* 624#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) 625 baz\(a, b, c\) 626#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\) 627 print\(42\) 628$''') 629 630class PyBtTests(DebuggerTests): 631 def test_basic_command(self): 632 'Verify that the "py-bt" command works' 633 bt = self.get_stack_trace(script=self.get_sample_script(), 634 cmds_after_breakpoint=['py-bt']) 635 self.assertMultilineMatches(bt, 636 r'''^.* 637#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) 638 baz\(a, b, c\) 639#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) 640 bar\(a, b, c\) 641#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\) 642foo\(1, 2, 3\) 643''') 644 645class PyPrintTests(DebuggerTests): 646 def test_basic_command(self): 647 'Verify that the "py-print" command works' 648 bt = self.get_stack_trace(script=self.get_sample_script(), 649 cmds_after_breakpoint=['py-print args']) 650 self.assertMultilineMatches(bt, 651 r".*\nlocal 'args' = \(1, 2, 3\)\n.*") 652 653 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") 654 def test_print_after_up(self): 655 bt = self.get_stack_trace(script=self.get_sample_script(), 656 cmds_after_breakpoint=['py-up', 'py-print c', 'py-print b', 'py-print a']) 657 self.assertMultilineMatches(bt, 658 r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*") 659 660 def test_printing_global(self): 661 bt = self.get_stack_trace(script=self.get_sample_script(), 662 cmds_after_breakpoint=['py-print __name__']) 663 self.assertMultilineMatches(bt, 664 r".*\nglobal '__name__' = '__main__'\n.*") 665 666 def test_printing_builtin(self): 667 bt = self.get_stack_trace(script=self.get_sample_script(), 668 cmds_after_breakpoint=['py-print len']) 669 self.assertMultilineMatches(bt, 670 r".*\nbuiltin 'len' = <built-in function len>\n.*") 671 672class PyLocalsTests(DebuggerTests): 673 def test_basic_command(self): 674 bt = self.get_stack_trace(script=self.get_sample_script(), 675 cmds_after_breakpoint=['py-locals']) 676 self.assertMultilineMatches(bt, 677 r".*\nargs = \(1, 2, 3\)\n.*") 678 679 @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") 680 def test_locals_after_up(self): 681 bt = self.get_stack_trace(script=self.get_sample_script(), 682 cmds_after_breakpoint=['py-up', 'py-locals']) 683 self.assertMultilineMatches(bt, 684 r".*\na = 1\nb = 2\nc = 3\n.*") 685 686def test_main(): 687 run_unittest(PrettyPrintTests, 688 PyListTests, 689 StackNavigationTests, 690 PyBtTests, 691 PyPrintTests, 692 PyLocalsTests 693 ) 694 695if __name__ == "__main__": 696 test_main() 697