ยปCore Development>Code coverage>Lib/test/test_gdb.py

Python code coverage for Lib/test/test_gdb.py

#countcontent
1n/a# Verify that gdb can pretty-print the various PyObject* types
2n/a#
3n/a# The code for testing gdb was adapted from similar work in Unladen Swallow's
4n/a# Lib/test/test_jit_gdb.py
5n/a
6n/aimport locale
7n/aimport os
8n/aimport re
9n/aimport subprocess
10n/aimport sys
11n/aimport sysconfig
12n/aimport textwrap
13n/aimport unittest
14n/a
15n/a# Is this Python configured to support threads?
16n/atry:
17n/a import _thread
18n/aexcept ImportError:
19n/a _thread = None
20n/a
21n/afrom test import support
22n/afrom test.support import run_unittest, findfile, python_is_optimized
23n/a
24n/adef get_gdb_version():
25n/a try:
26n/a proc = subprocess.Popen(["gdb", "-nx", "--version"],
27n/a stdout=subprocess.PIPE,
28n/a stderr=subprocess.PIPE,
29n/a universal_newlines=True)
30n/a with proc:
31n/a version = proc.communicate()[0]
32n/a except OSError:
33n/a # This is what "no gdb" looks like. There may, however, be other
34n/a # errors that manifest this way too.
35n/a raise unittest.SkipTest("Couldn't find gdb on the path")
36n/a
37n/a # Regex to parse:
38n/a # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7
39n/a # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9
40n/a # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1
41n/a # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5
42n/a match = re.search(r"^GNU gdb.*?\b(\d+)\.(\d+)", version)
43n/a if match is None:
44n/a raise Exception("unable to parse GDB version: %r" % version)
45n/a return (version, int(match.group(1)), int(match.group(2)))
46n/a
47n/agdb_version, gdb_major_version, gdb_minor_version = get_gdb_version()
48n/aif gdb_major_version < 7:
49n/a raise unittest.SkipTest("gdb versions before 7.0 didn't support python "
50n/a "embedding. Saw %s.%s:\n%s"
51n/a % (gdb_major_version, gdb_minor_version,
52n/a gdb_version))
53n/a
54n/aif not sysconfig.is_python_build():
55n/a raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
56n/a
57n/a# Location of custom hooks file in a repository checkout.
58n/acheckout_hook_path = os.path.join(os.path.dirname(sys.executable),
59n/a 'python-gdb.py')
60n/a
61n/aPYTHONHASHSEED = '123'
62n/a
63n/adef run_gdb(*args, **env_vars):
64n/a """Runs gdb in --batch mode with the additional arguments given by *args.
65n/a
66n/a Returns its (stdout, stderr) decoded from utf-8 using the replace handler.
67n/a """
68n/a if env_vars:
69n/a env = os.environ.copy()
70n/a env.update(env_vars)
71n/a else:
72n/a env = None
73n/a # -nx: Do not execute commands from any .gdbinit initialization files
74n/a # (issue #22188)
75n/a base_cmd = ('gdb', '--batch', '-nx')
76n/a if (gdb_major_version, gdb_minor_version) >= (7, 4):
77n/a base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
78n/a proc = subprocess.Popen(base_cmd + args,
79n/a # Redirect stdin to prevent GDB from messing with
80n/a # the terminal settings
81n/a stdin=subprocess.PIPE,
82n/a stdout=subprocess.PIPE,
83n/a stderr=subprocess.PIPE,
84n/a env=env)
85n/a with proc:
86n/a out, err = proc.communicate()
87n/a return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
88n/a
89n/a# Verify that "gdb" was built with the embedded python support enabled:
90n/agdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)")
91n/aif not gdbpy_version:
92n/a raise unittest.SkipTest("gdb not built with embedded python support")
93n/a
94n/a# Verify that "gdb" can load our custom hooks, as OS security settings may
95n/a# disallow this without a customized .gdbinit.
96n/a_, gdbpy_errors = run_gdb('--args', sys.executable)
97n/aif "auto-loading has been declined" in gdbpy_errors:
98n/a msg = "gdb security settings prevent use of custom hooks: "
99n/a raise unittest.SkipTest(msg + gdbpy_errors.rstrip())
100n/a
101n/adef gdb_has_frame_select():
102n/a # Does this build of gdb have gdb.Frame.select ?
103n/a stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))")
104n/a m = re.match(r'.*\[(.*)\].*', stdout)
105n/a if not m:
106n/a raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test")
107n/a gdb_frame_dir = m.group(1).split(', ')
108n/a return "'select'" in gdb_frame_dir
109n/a
110n/aHAS_PYUP_PYDOWN = gdb_has_frame_select()
111n/a
112n/aBREAKPOINT_FN='builtin_id'
113n/a
114n/a@unittest.skipIf(support.PGO, "not useful for PGO")
115n/aclass DebuggerTests(unittest.TestCase):
116n/a
117n/a """Test that the debugger can debug Python."""
118n/a
119n/a def get_stack_trace(self, source=None, script=None,
120n/a breakpoint=BREAKPOINT_FN,
121n/a cmds_after_breakpoint=None,
122n/a import_site=False):
123n/a '''
124n/a Run 'python -c SOURCE' under gdb with a breakpoint.
125n/a
126n/a Support injecting commands after the breakpoint is reached
127n/a
128n/a Returns the stdout from gdb
129n/a
130n/a cmds_after_breakpoint: if provided, a list of strings: gdb commands
131n/a '''
132n/a # We use "set breakpoint pending yes" to avoid blocking with a:
133n/a # Function "foo" not defined.
134n/a # Make breakpoint pending on future shared library load? (y or [n])
135n/a # error, which typically happens python is dynamically linked (the
136n/a # breakpoints of interest are to be found in the shared library)
137n/a # When this happens, we still get:
138n/a # Function "textiowrapper_write" not defined.
139n/a # emitted to stderr each time, alas.
140n/a
141n/a # Initially I had "--eval-command=continue" here, but removed it to
142n/a # avoid repeated print breakpoints when traversing hierarchical data
143n/a # structures
144n/a
145n/a # Generate a list of commands in gdb's language:
146n/a commands = ['set breakpoint pending yes',
147n/a 'break %s' % breakpoint,
148n/a
149n/a # The tests assume that the first frame of printed
150n/a # backtrace will not contain program counter,
151n/a # that is however not guaranteed by gdb
152n/a # therefore we need to use 'set print address off' to
153n/a # make sure the counter is not there. For example:
154n/a # #0 in PyObject_Print ...
155n/a # is assumed, but sometimes this can be e.g.
156n/a # #0 0x00003fffb7dd1798 in PyObject_Print ...
157n/a 'set print address off',
158n/a
159n/a 'run']
160n/a
161n/a # GDB as of 7.4 onwards can distinguish between the
162n/a # value of a variable at entry vs current value:
163n/a # http://sourceware.org/gdb/onlinedocs/gdb/Variables.html
164n/a # which leads to the selftests failing with errors like this:
165n/a # AssertionError: 'v@entry=()' != '()'
166n/a # Disable this:
167n/a if (gdb_major_version, gdb_minor_version) >= (7, 4):
168n/a commands += ['set print entry-values no']
169n/a
170n/a if cmds_after_breakpoint:
171n/a commands += cmds_after_breakpoint
172n/a else:
173n/a commands += ['backtrace']
174n/a
175n/a # print commands
176n/a
177n/a # Use "commands" to generate the arguments with which to invoke "gdb":
178n/a args = ['--eval-command=%s' % cmd for cmd in commands]
179n/a args += ["--args",
180n/a sys.executable]
181n/a args.extend(subprocess._args_from_interpreter_flags())
182n/a
183n/a if not import_site:
184n/a # -S suppresses the default 'import site'
185n/a args += ["-S"]
186n/a
187n/a if source:
188n/a args += ["-c", source]
189n/a elif script:
190n/a args += [script]
191n/a
192n/a # print args
193n/a # print (' '.join(args))
194n/a
195n/a # Use "args" to invoke gdb, capturing stdout, stderr:
196n/a out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED)
197n/a
198n/a errlines = err.splitlines()
199n/a unexpected_errlines = []
200n/a
201n/a # Ignore some benign messages on stderr.
202n/a ignore_patterns = (
203n/a 'Function "%s" not defined.' % breakpoint,
204n/a 'Do you need "set solib-search-path" or '
205n/a '"set sysroot"?',
206n/a # BFD: /usr/lib/debug/(...): unable to initialize decompress
207n/a # status for section .debug_aranges
208n/a 'BFD: ',
209n/a # ignore all warnings
210n/a 'warning: ',
211n/a )
212n/a for line in errlines:
213n/a if not line:
214n/a continue
215n/a if not line.startswith(ignore_patterns):
216n/a unexpected_errlines.append(line)
217n/a
218n/a # Ensure no unexpected error messages:
219n/a self.assertEqual(unexpected_errlines, [])
220n/a return out
221n/a
222n/a def get_gdb_repr(self, source,
223n/a cmds_after_breakpoint=None,
224n/a import_site=False):
225n/a # Given an input python source representation of data,
226n/a # run "python -c'id(DATA)'" under gdb with a breakpoint on
227n/a # builtin_id and scrape out gdb's representation of the "op"
228n/a # parameter, and verify that the gdb displays the same string
229n/a #
230n/a # Verify that the gdb displays the expected string
231n/a #
232n/a # For a nested structure, the first time we hit the breakpoint will
233n/a # give us the top-level structure
234n/a
235n/a # NOTE: avoid decoding too much of the traceback as some
236n/a # undecodable characters may lurk there in optimized mode
237n/a # (issue #19743).
238n/a cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"]
239n/a gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN,
240n/a cmds_after_breakpoint=cmds_after_breakpoint,
241n/a import_site=import_site)
242n/a # gdb can insert additional '\n' and space characters in various places
243n/a # in its output, depending on the width of the terminal it's connected
244n/a # to (using its "wrap_here" function)
245n/a m = re.match(r'.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*Python/bltinmodule.c.*',
246n/a gdb_output, re.DOTALL)
247n/a if not m:
248n/a self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output))
249n/a return m.group(1), gdb_output
250n/a
251n/a def assertEndsWith(self, actual, exp_end):
252n/a '''Ensure that the given "actual" string ends with "exp_end"'''
253n/a self.assertTrue(actual.endswith(exp_end),
254n/a msg='%r did not end with %r' % (actual, exp_end))
255n/a
256n/a def assertMultilineMatches(self, actual, pattern):
257n/a m = re.match(pattern, actual, re.DOTALL)
258n/a if not m:
259n/a self.fail(msg='%r did not match %r' % (actual, pattern))
260n/a
261n/a def get_sample_script(self):
262n/a return findfile('gdb_sample.py')
263n/a
264n/aclass PrettyPrintTests(DebuggerTests):
265n/a def test_getting_backtrace(self):
266n/a gdb_output = self.get_stack_trace('id(42)')
267n/a self.assertTrue(BREAKPOINT_FN in gdb_output)
268n/a
269n/a def assertGdbRepr(self, val, exp_repr=None):
270n/a # Ensure that gdb's rendering of the value in a debugged process
271n/a # matches repr(value) in this process:
272n/a gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')')
273n/a if not exp_repr:
274n/a exp_repr = repr(val)
275n/a self.assertEqual(gdb_repr, exp_repr,
276n/a ('%r did not equal expected %r; full output was:\n%s'
277n/a % (gdb_repr, exp_repr, gdb_output)))
278n/a
279n/a def test_int(self):
280n/a 'Verify the pretty-printing of various int values'
281n/a self.assertGdbRepr(42)
282n/a self.assertGdbRepr(0)
283n/a self.assertGdbRepr(-7)
284n/a self.assertGdbRepr(1000000000000)
285n/a self.assertGdbRepr(-1000000000000000)
286n/a
287n/a def test_singletons(self):
288n/a 'Verify the pretty-printing of True, False and None'
289n/a self.assertGdbRepr(True)
290n/a self.assertGdbRepr(False)
291n/a self.assertGdbRepr(None)
292n/a
293n/a def test_dicts(self):
294n/a 'Verify the pretty-printing of dictionaries'
295n/a self.assertGdbRepr({})
296n/a self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}")
297n/a # Python preserves insertion order since 3.6
298n/a self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}")
299n/a
300n/a def test_lists(self):
301n/a 'Verify the pretty-printing of lists'
302n/a self.assertGdbRepr([])
303n/a self.assertGdbRepr(list(range(5)))
304n/a
305n/a def test_bytes(self):
306n/a 'Verify the pretty-printing of bytes'
307n/a self.assertGdbRepr(b'')
308n/a self.assertGdbRepr(b'And now for something hopefully the same')
309n/a self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text')
310n/a self.assertGdbRepr(b'this is a tab:\t'
311n/a b' this is a slash-N:\n'
312n/a b' this is a slash-R:\r'
313n/a )
314n/a
315n/a self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80')
316n/a
317n/a self.assertGdbRepr(bytes([b for b in range(255)]))
318n/a
319n/a def test_strings(self):
320n/a 'Verify the pretty-printing of unicode strings'
321n/a encoding = locale.getpreferredencoding()
322n/a def check_repr(text):
323n/a try:
324n/a text.encode(encoding)
325n/a printable = True
326n/a except UnicodeEncodeError:
327n/a self.assertGdbRepr(text, ascii(text))
328n/a else:
329n/a self.assertGdbRepr(text)
330n/a
331n/a self.assertGdbRepr('')
332n/a self.assertGdbRepr('And now for something hopefully the same')
333n/a self.assertGdbRepr('string with embedded NUL here \0 and then some more text')
334n/a
335n/a # Test printing a single character:
336n/a # U+2620 SKULL AND CROSSBONES
337n/a check_repr('\u2620')
338n/a
339n/a # Test printing a Japanese unicode string
340n/a # (I believe this reads "mojibake", using 3 characters from the CJK
341n/a # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE)
342n/a check_repr('\u6587\u5b57\u5316\u3051')
343n/a
344n/a # Test a character outside the BMP:
345n/a # U+1D121 MUSICAL SYMBOL C CLEF
346n/a # This is:
347n/a # UTF-8: 0xF0 0x9D 0x84 0xA1
348n/a # UTF-16: 0xD834 0xDD21
349n/a check_repr(chr(0x1D121))
350n/a
351n/a def test_tuples(self):
352n/a 'Verify the pretty-printing of tuples'
353n/a self.assertGdbRepr(tuple(), '()')
354n/a self.assertGdbRepr((1,), '(1,)')
355n/a self.assertGdbRepr(('foo', 'bar', 'baz'))
356n/a
357n/a def test_sets(self):
358n/a 'Verify the pretty-printing of sets'
359n/a if (gdb_major_version, gdb_minor_version) < (7, 3):
360n/a self.skipTest("pretty-printing of sets needs gdb 7.3 or later")
361n/a self.assertGdbRepr(set(), "set()")
362n/a self.assertGdbRepr(set(['a']), "{'a'}")
363n/a # PYTHONHASHSEED is need to get the exact frozenset item order
364n/a if not sys.flags.ignore_environment:
365n/a self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}")
366n/a self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}")
367n/a
368n/a # Ensure that we handle sets containing the "dummy" key value,
369n/a # which happens on deletion:
370n/a gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b'])
371n/as.remove('a')
372n/aid(s)''')
373n/a self.assertEqual(gdb_repr, "{'b'}")
374n/a
375n/a def test_frozensets(self):
376n/a 'Verify the pretty-printing of frozensets'
377n/a if (gdb_major_version, gdb_minor_version) < (7, 3):
378n/a self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later")
379n/a self.assertGdbRepr(frozenset(), "frozenset()")
380n/a self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})")
381n/a # PYTHONHASHSEED is need to get the exact frozenset item order
382n/a if not sys.flags.ignore_environment:
383n/a self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})")
384n/a self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})")
385n/a
386n/a def test_exceptions(self):
387n/a # Test a RuntimeError
388n/a gdb_repr, gdb_output = self.get_gdb_repr('''
389n/atry:
390n/a raise RuntimeError("I am an error")
391n/aexcept RuntimeError as e:
392n/a id(e)
393n/a''')
394n/a self.assertEqual(gdb_repr,
395n/a "RuntimeError('I am an error',)")
396n/a
397n/a
398n/a # Test division by zero:
399n/a gdb_repr, gdb_output = self.get_gdb_repr('''
400n/atry:
401n/a a = 1 / 0
402n/aexcept ZeroDivisionError as e:
403n/a id(e)
404n/a''')
405n/a self.assertEqual(gdb_repr,
406n/a "ZeroDivisionError('division by zero',)")
407n/a
408n/a def test_modern_class(self):
409n/a 'Verify the pretty-printing of new-style class instances'
410n/a gdb_repr, gdb_output = self.get_gdb_repr('''
411n/aclass Foo:
412n/a pass
413n/afoo = Foo()
414n/afoo.an_int = 42
415n/aid(foo)''')
416n/a m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
417n/a self.assertTrue(m,
418n/a msg='Unexpected new-style class rendering %r' % gdb_repr)
419n/a
420n/a def test_subclassing_list(self):
421n/a 'Verify the pretty-printing of an instance of a list subclass'
422n/a gdb_repr, gdb_output = self.get_gdb_repr('''
423n/aclass Foo(list):
424n/a pass
425n/afoo = Foo()
426n/afoo += [1, 2, 3]
427n/afoo.an_int = 42
428n/aid(foo)''')
429n/a m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
430n/a
431n/a self.assertTrue(m,
432n/a msg='Unexpected new-style class rendering %r' % gdb_repr)
433n/a
434n/a def test_subclassing_tuple(self):
435n/a 'Verify the pretty-printing of an instance of a tuple subclass'
436n/a # This should exercise the negative tp_dictoffset code in the
437n/a # new-style class support
438n/a gdb_repr, gdb_output = self.get_gdb_repr('''
439n/aclass Foo(tuple):
440n/a pass
441n/afoo = Foo((1, 2, 3))
442n/afoo.an_int = 42
443n/aid(foo)''')
444n/a m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
445n/a
446n/a self.assertTrue(m,
447n/a msg='Unexpected new-style class rendering %r' % gdb_repr)
448n/a
449n/a def assertSane(self, source, corruption, exprepr=None):
450n/a '''Run Python under gdb, corrupting variables in the inferior process
451n/a immediately before taking a backtrace.
452n/a
453n/a Verify that the variable's representation is the expected failsafe
454n/a representation'''
455n/a if corruption:
456n/a cmds_after_breakpoint=[corruption, 'backtrace']
457n/a else:
458n/a cmds_after_breakpoint=['backtrace']
459n/a
460n/a gdb_repr, gdb_output = \
461n/a self.get_gdb_repr(source,
462n/a cmds_after_breakpoint=cmds_after_breakpoint)
463n/a if exprepr:
464n/a if gdb_repr == exprepr:
465n/a # gdb managed to print the value in spite of the corruption;
466n/a # this is good (see http://bugs.python.org/issue8330)
467n/a return
468n/a
469n/a # Match anything for the type name; 0xDEADBEEF could point to
470n/a # something arbitrary (see http://bugs.python.org/issue8330)
471n/a pattern = '<.* at remote 0x-?[0-9a-f]+>'
472n/a
473n/a m = re.match(pattern, gdb_repr)
474n/a if not m:
475n/a self.fail('Unexpected gdb representation: %r\n%s' % \
476n/a (gdb_repr, gdb_output))
477n/a
478n/a def test_NULL_ptr(self):
479n/a 'Ensure that a NULL PyObject* is handled gracefully'
480n/a gdb_repr, gdb_output = (
481n/a self.get_gdb_repr('id(42)',
482n/a cmds_after_breakpoint=['set variable v=0',
483n/a 'backtrace'])
484n/a )
485n/a
486n/a self.assertEqual(gdb_repr, '0x0')
487n/a
488n/a def test_NULL_ob_type(self):
489n/a 'Ensure that a PyObject* with NULL ob_type is handled gracefully'
490n/a self.assertSane('id(42)',
491n/a 'set v->ob_type=0')
492n/a
493n/a def test_corrupt_ob_type(self):
494n/a 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully'
495n/a self.assertSane('id(42)',
496n/a 'set v->ob_type=0xDEADBEEF',
497n/a exprepr='42')
498n/a
499n/a def test_corrupt_tp_flags(self):
500n/a 'Ensure that a PyObject* with a type with corrupt tp_flags is handled'
501n/a self.assertSane('id(42)',
502n/a 'set v->ob_type->tp_flags=0x0',
503n/a exprepr='42')
504n/a
505n/a def test_corrupt_tp_name(self):
506n/a 'Ensure that a PyObject* with a type with corrupt tp_name is handled'
507n/a self.assertSane('id(42)',
508n/a 'set v->ob_type->tp_name=0xDEADBEEF',
509n/a exprepr='42')
510n/a
511n/a def test_builtins_help(self):
512n/a 'Ensure that the new-style class _Helper in site.py can be handled'
513n/a
514n/a if sys.flags.no_site:
515n/a self.skipTest("need site module, but -S option was used")
516n/a
517n/a # (this was the issue causing tracebacks in
518n/a # http://bugs.python.org/issue8032#msg100537 )
519n/a gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True)
520n/a
521n/a m = re.match(r'<_Helper at remote 0x-?[0-9a-f]+>', gdb_repr)
522n/a self.assertTrue(m,
523n/a msg='Unexpected rendering %r' % gdb_repr)
524n/a
525n/a def test_selfreferential_list(self):
526n/a '''Ensure that a reference loop involving a list doesn't lead proxyval
527n/a into an infinite loop:'''
528n/a gdb_repr, gdb_output = \
529n/a self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)")
530n/a self.assertEqual(gdb_repr, '[3, 4, 5, [...]]')
531n/a
532n/a gdb_repr, gdb_output = \
533n/a self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)")
534n/a self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]')
535n/a
536n/a def test_selfreferential_dict(self):
537n/a '''Ensure that a reference loop involving a dict doesn't lead proxyval
538n/a into an infinite loop:'''
539n/a gdb_repr, gdb_output = \
540n/a self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)")
541n/a
542n/a self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}")
543n/a
544n/a def test_selfreferential_old_style_instance(self):
545n/a gdb_repr, gdb_output = \
546n/a self.get_gdb_repr('''
547n/aclass Foo:
548n/a pass
549n/afoo = Foo()
550n/afoo.an_attr = foo
551n/aid(foo)''')
552n/a self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
553n/a gdb_repr),
554n/a 'Unexpected gdb representation: %r\n%s' % \
555n/a (gdb_repr, gdb_output))
556n/a
557n/a def test_selfreferential_new_style_instance(self):
558n/a gdb_repr, gdb_output = \
559n/a self.get_gdb_repr('''
560n/aclass Foo(object):
561n/a pass
562n/afoo = Foo()
563n/afoo.an_attr = foo
564n/aid(foo)''')
565n/a self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
566n/a gdb_repr),
567n/a 'Unexpected gdb representation: %r\n%s' % \
568n/a (gdb_repr, gdb_output))
569n/a
570n/a gdb_repr, gdb_output = \
571n/a self.get_gdb_repr('''
572n/aclass Foo(object):
573n/a pass
574n/aa = Foo()
575n/ab = Foo()
576n/aa.an_attr = b
577n/ab.an_attr = a
578n/aid(a)''')
579n/a self.assertTrue(re.match(r'<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>',
580n/a gdb_repr),
581n/a 'Unexpected gdb representation: %r\n%s' % \
582n/a (gdb_repr, gdb_output))
583n/a
584n/a def test_truncation(self):
585n/a 'Verify that very long output is truncated'
586n/a gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))')
587n/a self.assertEqual(gdb_repr,
588n/a "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "
589n/a "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, "
590n/a "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, "
591n/a "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, "
592n/a "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, "
593n/a "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, "
594n/a "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, "
595n/a "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, "
596n/a "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, "
597n/a "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, "
598n/a "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, "
599n/a "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, "
600n/a "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, "
601n/a "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, "
602n/a "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, "
603n/a "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, "
604n/a "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, "
605n/a "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, "
606n/a "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, "
607n/a "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, "
608n/a "224, 225, 226...(truncated)")
609n/a self.assertEqual(len(gdb_repr),
610n/a 1024 + len('...(truncated)'))
611n/a
612n/a def test_builtin_method(self):
613n/a gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)')
614n/a self.assertTrue(re.match(r'<built-in method readlines of _io.TextIOWrapper object at remote 0x-?[0-9a-f]+>',
615n/a gdb_repr),
616n/a 'Unexpected gdb representation: %r\n%s' % \
617n/a (gdb_repr, gdb_output))
618n/a
619n/a def test_frames(self):
620n/a gdb_output = self.get_stack_trace('''
621n/adef foo(a, b, c):
622n/a pass
623n/a
624n/afoo(3, 4, 5)
625n/aid(foo.__code__)''',
626n/a breakpoint='builtin_id',
627n/a cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)']
628n/a )
629n/a self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*',
630n/a gdb_output,
631n/a re.DOTALL),
632n/a 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))
633n/a
634n/a@unittest.skipIf(python_is_optimized(),
635n/a "Python was compiled with optimizations")
636n/aclass PyListTests(DebuggerTests):
637n/a def assertListing(self, expected, actual):
638n/a self.assertEndsWith(actual, expected)
639n/a
640n/a def test_basic_command(self):
641n/a 'Verify that the "py-list" command works'
642n/a bt = self.get_stack_trace(script=self.get_sample_script(),
643n/a cmds_after_breakpoint=['py-list'])
644n/a
645n/a self.assertListing(' 5 \n'
646n/a ' 6 def bar(a, b, c):\n'
647n/a ' 7 baz(a, b, c)\n'
648n/a ' 8 \n'
649n/a ' 9 def baz(*args):\n'
650n/a ' >10 id(42)\n'
651n/a ' 11 \n'
652n/a ' 12 foo(1, 2, 3)\n',
653n/a bt)
654n/a
655n/a def test_one_abs_arg(self):
656n/a 'Verify the "py-list" command with one absolute argument'
657n/a bt = self.get_stack_trace(script=self.get_sample_script(),
658n/a cmds_after_breakpoint=['py-list 9'])
659n/a
660n/a self.assertListing(' 9 def baz(*args):\n'
661n/a ' >10 id(42)\n'
662n/a ' 11 \n'
663n/a ' 12 foo(1, 2, 3)\n',
664n/a bt)
665n/a
666n/a def test_two_abs_args(self):
667n/a 'Verify the "py-list" command with two absolute arguments'
668n/a bt = self.get_stack_trace(script=self.get_sample_script(),
669n/a cmds_after_breakpoint=['py-list 1,3'])
670n/a
671n/a self.assertListing(' 1 # Sample script for use by test_gdb.py\n'
672n/a ' 2 \n'
673n/a ' 3 def foo(a, b, c):\n',
674n/a bt)
675n/a
676n/aclass StackNavigationTests(DebuggerTests):
677n/a @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
678n/a @unittest.skipIf(python_is_optimized(),
679n/a "Python was compiled with optimizations")
680n/a def test_pyup_command(self):
681n/a 'Verify that the "py-up" command works'
682n/a bt = self.get_stack_trace(script=self.get_sample_script(),
683n/a cmds_after_breakpoint=['py-up', 'py-up'])
684n/a self.assertMultilineMatches(bt,
685n/a r'''^.*
686n/a#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
687n/a baz\(a, b, c\)
688n/a$''')
689n/a
690n/a @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
691n/a def test_down_at_bottom(self):
692n/a 'Verify handling of "py-down" at the bottom of the stack'
693n/a bt = self.get_stack_trace(script=self.get_sample_script(),
694n/a cmds_after_breakpoint=['py-down'])
695n/a self.assertEndsWith(bt,
696n/a 'Unable to find a newer python frame\n')
697n/a
698n/a @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
699n/a def test_up_at_top(self):
700n/a 'Verify handling of "py-up" at the top of the stack'
701n/a bt = self.get_stack_trace(script=self.get_sample_script(),
702n/a cmds_after_breakpoint=['py-up'] * 5)
703n/a self.assertEndsWith(bt,
704n/a 'Unable to find an older python frame\n')
705n/a
706n/a @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
707n/a @unittest.skipIf(python_is_optimized(),
708n/a "Python was compiled with optimizations")
709n/a def test_up_then_down(self):
710n/a 'Verify "py-up" followed by "py-down"'
711n/a bt = self.get_stack_trace(script=self.get_sample_script(),
712n/a cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
713n/a self.assertMultilineMatches(bt,
714n/a r'''^.*
715n/a#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
716n/a baz\(a, b, c\)
717n/a#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
718n/a id\(42\)
719n/a$''')
720n/a
721n/aclass PyBtTests(DebuggerTests):
722n/a @unittest.skipIf(python_is_optimized(),
723n/a "Python was compiled with optimizations")
724n/a def test_bt(self):
725n/a 'Verify that the "py-bt" command works'
726n/a bt = self.get_stack_trace(script=self.get_sample_script(),
727n/a cmds_after_breakpoint=['py-bt'])
728n/a self.assertMultilineMatches(bt,
729n/a r'''^.*
730n/aTraceback \(most recent call first\):
731n/a <built-in method id of module object .*>
732n/a File ".*gdb_sample.py", line 10, in baz
733n/a id\(42\)
734n/a File ".*gdb_sample.py", line 7, in bar
735n/a baz\(a, b, c\)
736n/a File ".*gdb_sample.py", line 4, in foo
737n/a bar\(a, b, c\)
738n/a File ".*gdb_sample.py", line 12, in <module>
739n/a foo\(1, 2, 3\)
740n/a''')
741n/a
742n/a @unittest.skipIf(python_is_optimized(),
743n/a "Python was compiled with optimizations")
744n/a def test_bt_full(self):
745n/a 'Verify that the "py-bt-full" command works'
746n/a bt = self.get_stack_trace(script=self.get_sample_script(),
747n/a cmds_after_breakpoint=['py-bt-full'])
748n/a self.assertMultilineMatches(bt,
749n/a r'''^.*
750n/a#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
751n/a baz\(a, b, c\)
752n/a#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
753n/a bar\(a, b, c\)
754n/a#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
755n/a foo\(1, 2, 3\)
756n/a''')
757n/a
758n/a @unittest.skipUnless(_thread,
759n/a "Python was compiled without thread support")
760n/a def test_threads(self):
761n/a 'Verify that "py-bt" indicates threads that are waiting for the GIL'
762n/a cmd = '''
763n/afrom threading import Thread
764n/a
765n/aclass TestThread(Thread):
766n/a # These threads would run forever, but we'll interrupt things with the
767n/a # debugger
768n/a def run(self):
769n/a i = 0
770n/a while 1:
771n/a i += 1
772n/a
773n/at = {}
774n/afor i in range(4):
775n/a t[i] = TestThread()
776n/a t[i].start()
777n/a
778n/a# Trigger a breakpoint on the main thread
779n/aid(42)
780n/a
781n/a'''
782n/a # Verify with "py-bt":
783n/a gdb_output = self.get_stack_trace(cmd,
784n/a cmds_after_breakpoint=['thread apply all py-bt'])
785n/a self.assertIn('Waiting for the GIL', gdb_output)
786n/a
787n/a # Verify with "py-bt-full":
788n/a gdb_output = self.get_stack_trace(cmd,
789n/a cmds_after_breakpoint=['thread apply all py-bt-full'])
790n/a self.assertIn('Waiting for the GIL', gdb_output)
791n/a
792n/a @unittest.skipIf(python_is_optimized(),
793n/a "Python was compiled with optimizations")
794n/a # Some older versions of gdb will fail with
795n/a # "Cannot find new threads: generic error"
796n/a # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
797n/a @unittest.skipUnless(_thread,
798n/a "Python was compiled without thread support")
799n/a def test_gc(self):
800n/a 'Verify that "py-bt" indicates if a thread is garbage-collecting'
801n/a cmd = ('from gc import collect\n'
802n/a 'id(42)\n'
803n/a 'def foo():\n'
804n/a ' collect()\n'
805n/a 'def bar():\n'
806n/a ' foo()\n'
807n/a 'bar()\n')
808n/a # Verify with "py-bt":
809n/a gdb_output = self.get_stack_trace(cmd,
810n/a cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'],
811n/a )
812n/a self.assertIn('Garbage-collecting', gdb_output)
813n/a
814n/a # Verify with "py-bt-full":
815n/a gdb_output = self.get_stack_trace(cmd,
816n/a cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'],
817n/a )
818n/a self.assertIn('Garbage-collecting', gdb_output)
819n/a
820n/a @unittest.skipIf(python_is_optimized(),
821n/a "Python was compiled with optimizations")
822n/a # Some older versions of gdb will fail with
823n/a # "Cannot find new threads: generic error"
824n/a # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
825n/a @unittest.skipUnless(_thread,
826n/a "Python was compiled without thread support")
827n/a def test_pycfunction(self):
828n/a 'Verify that "py-bt" displays invocations of PyCFunction instances'
829n/a # Tested function must not be defined with METH_NOARGS or METH_O,
830n/a # otherwise call_function() doesn't call PyCFunction_Call()
831n/a cmd = ('from time import gmtime\n'
832n/a 'def foo():\n'
833n/a ' gmtime(1)\n'
834n/a 'def bar():\n'
835n/a ' foo()\n'
836n/a 'bar()\n')
837n/a # Verify with "py-bt":
838n/a gdb_output = self.get_stack_trace(cmd,
839n/a breakpoint='time_gmtime',
840n/a cmds_after_breakpoint=['bt', 'py-bt'],
841n/a )
842n/a self.assertIn('<built-in method gmtime', gdb_output)
843n/a
844n/a # Verify with "py-bt-full":
845n/a gdb_output = self.get_stack_trace(cmd,
846n/a breakpoint='time_gmtime',
847n/a cmds_after_breakpoint=['py-bt-full'],
848n/a )
849n/a self.assertIn('#2 <built-in method gmtime', gdb_output)
850n/a
851n/a @unittest.skipIf(python_is_optimized(),
852n/a "Python was compiled with optimizations")
853n/a def test_wrapper_call(self):
854n/a cmd = textwrap.dedent('''
855n/a class MyList(list):
856n/a def __init__(self):
857n/a super().__init__() # wrapper_call()
858n/a
859n/a id("first break point")
860n/a l = MyList()
861n/a ''')
862n/a # Verify with "py-bt":
863n/a gdb_output = self.get_stack_trace(cmd,
864n/a cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
865n/a self.assertRegex(gdb_output,
866n/a r"<method-wrapper u?'__init__' of MyList object at ")
867n/a
868n/a
869n/aclass PyPrintTests(DebuggerTests):
870n/a @unittest.skipIf(python_is_optimized(),
871n/a "Python was compiled with optimizations")
872n/a def test_basic_command(self):
873n/a 'Verify that the "py-print" command works'
874n/a bt = self.get_stack_trace(script=self.get_sample_script(),
875n/a cmds_after_breakpoint=['py-up', 'py-print args'])
876n/a self.assertMultilineMatches(bt,
877n/a r".*\nlocal 'args' = \(1, 2, 3\)\n.*")
878n/a
879n/a @unittest.skipIf(python_is_optimized(),
880n/a "Python was compiled with optimizations")
881n/a @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
882n/a def test_print_after_up(self):
883n/a bt = self.get_stack_trace(script=self.get_sample_script(),
884n/a cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a'])
885n/a self.assertMultilineMatches(bt,
886n/a r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")
887n/a
888n/a @unittest.skipIf(python_is_optimized(),
889n/a "Python was compiled with optimizations")
890n/a def test_printing_global(self):
891n/a bt = self.get_stack_trace(script=self.get_sample_script(),
892n/a cmds_after_breakpoint=['py-up', 'py-print __name__'])
893n/a self.assertMultilineMatches(bt,
894n/a r".*\nglobal '__name__' = '__main__'\n.*")
895n/a
896n/a @unittest.skipIf(python_is_optimized(),
897n/a "Python was compiled with optimizations")
898n/a def test_printing_builtin(self):
899n/a bt = self.get_stack_trace(script=self.get_sample_script(),
900n/a cmds_after_breakpoint=['py-up', 'py-print len'])
901n/a self.assertMultilineMatches(bt,
902n/a r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
903n/a
904n/aclass PyLocalsTests(DebuggerTests):
905n/a @unittest.skipIf(python_is_optimized(),
906n/a "Python was compiled with optimizations")
907n/a def test_basic_command(self):
908n/a bt = self.get_stack_trace(script=self.get_sample_script(),
909n/a cmds_after_breakpoint=['py-up', 'py-locals'])
910n/a self.assertMultilineMatches(bt,
911n/a r".*\nargs = \(1, 2, 3\)\n.*")
912n/a
913n/a @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
914n/a @unittest.skipIf(python_is_optimized(),
915n/a "Python was compiled with optimizations")
916n/a def test_locals_after_up(self):
917n/a bt = self.get_stack_trace(script=self.get_sample_script(),
918n/a cmds_after_breakpoint=['py-up', 'py-up', 'py-locals'])
919n/a self.assertMultilineMatches(bt,
920n/a r".*\na = 1\nb = 2\nc = 3\n.*")
921n/a
922n/adef test_main():
923n/a if support.verbose:
924n/a print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version))
925n/a for line in gdb_version.splitlines():
926n/a print(" " * 4 + line)
927n/a run_unittest(PrettyPrintTests,
928n/a PyListTests,
929n/a StackNavigationTests,
930n/a PyBtTests,
931n/a PyPrintTests,
932n/a PyLocalsTests
933n/a )
934n/a
935n/aif __name__ == "__main__":
936n/a test_main()