mirror of
https://github.com/python/cpython.git
synced 2025-09-30 04:15:43 +00:00
[3.6] bpo-30345: Update test_gdb.py and python-gdb.py from master (#1549)
* python-gdb.py supports method-wrapper
bpo-29367: python-gdb.py now supports also method-wrapper (wrapperobject)
objects.
(cherry picked from commit 611083331d
)
* Update and enhance python-gdb.py
bpo-29259: Detect PyCFunction is the current frame, not only in the
older frame.
This commit is contained in:
parent
3dc7c52a9f
commit
d05f7fdf6c
3 changed files with 78 additions and 14 deletions
|
@ -3,13 +3,14 @@
|
||||||
# The code for testing gdb was adapted from similar work in Unladen Swallow's
|
# The code for testing gdb was adapted from similar work in Unladen Swallow's
|
||||||
# Lib/test/test_jit_gdb.py
|
# Lib/test/test_jit_gdb.py
|
||||||
|
|
||||||
|
import locale
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
import textwrap
|
||||||
import unittest
|
import unittest
|
||||||
import locale
|
|
||||||
|
|
||||||
# Is this Python configured to support threads?
|
# Is this Python configured to support threads?
|
||||||
try:
|
try:
|
||||||
|
@ -845,7 +846,24 @@ id(42)
|
||||||
breakpoint='time_gmtime',
|
breakpoint='time_gmtime',
|
||||||
cmds_after_breakpoint=['py-bt-full'],
|
cmds_after_breakpoint=['py-bt-full'],
|
||||||
)
|
)
|
||||||
self.assertIn('#0 <built-in method gmtime', gdb_output)
|
self.assertIn('#1 <built-in method gmtime', gdb_output)
|
||||||
|
|
||||||
|
@unittest.skipIf(python_is_optimized(),
|
||||||
|
"Python was compiled with optimizations")
|
||||||
|
def test_wrapper_call(self):
|
||||||
|
cmd = textwrap.dedent('''
|
||||||
|
class MyList(list):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__() # wrapper_call()
|
||||||
|
|
||||||
|
id("first break point")
|
||||||
|
l = MyList()
|
||||||
|
''')
|
||||||
|
# Verify with "py-bt":
|
||||||
|
gdb_output = self.get_stack_trace(cmd,
|
||||||
|
cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
|
||||||
|
self.assertRegex(gdb_output,
|
||||||
|
r"<method-wrapper u?'__init__' of MyList object at ")
|
||||||
|
|
||||||
|
|
||||||
class PyPrintTests(DebuggerTests):
|
class PyPrintTests(DebuggerTests):
|
||||||
|
|
|
@ -151,6 +151,12 @@ Documentation
|
||||||
|
|
||||||
- bpo-26985: Add missing info of code object in inspect documentation.
|
- bpo-26985: Add missing info of code object in inspect documentation.
|
||||||
|
|
||||||
|
Tools/Demos
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- Issue #29367: python-gdb.py now supports also ``method-wrapper``
|
||||||
|
(``wrapperobject``) objects.
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -362,6 +362,7 @@ class PyObjectPtr(object):
|
||||||
'set' : PySetObjectPtr,
|
'set' : PySetObjectPtr,
|
||||||
'frozenset' : PySetObjectPtr,
|
'frozenset' : PySetObjectPtr,
|
||||||
'builtin_function_or_method' : PyCFunctionObjectPtr,
|
'builtin_function_or_method' : PyCFunctionObjectPtr,
|
||||||
|
'method-wrapper': wrapperobject,
|
||||||
}
|
}
|
||||||
if tp_name in name_map:
|
if tp_name in name_map:
|
||||||
return name_map[tp_name]
|
return name_map[tp_name]
|
||||||
|
@ -1330,6 +1331,39 @@ class PyUnicodeObjectPtr(PyObjectPtr):
|
||||||
out.write(quote)
|
out.write(quote)
|
||||||
|
|
||||||
|
|
||||||
|
class wrapperobject(PyObjectPtr):
|
||||||
|
_typename = 'wrapperobject'
|
||||||
|
|
||||||
|
def safe_name(self):
|
||||||
|
try:
|
||||||
|
name = self.field('descr')['d_base']['name'].string()
|
||||||
|
return repr(name)
|
||||||
|
except (NullPyObjectPtr, RuntimeError):
|
||||||
|
return '<unknown name>'
|
||||||
|
|
||||||
|
def safe_tp_name(self):
|
||||||
|
try:
|
||||||
|
return self.field('self')['ob_type']['tp_name'].string()
|
||||||
|
except (NullPyObjectPtr, RuntimeError):
|
||||||
|
return '<unknown tp_name>'
|
||||||
|
|
||||||
|
def safe_self_addresss(self):
|
||||||
|
try:
|
||||||
|
address = long(self.field('self'))
|
||||||
|
return '%#x' % address
|
||||||
|
except (NullPyObjectPtr, RuntimeError):
|
||||||
|
return '<failed to get self address>'
|
||||||
|
|
||||||
|
def proxyval(self, visited):
|
||||||
|
name = self.safe_name()
|
||||||
|
tp_name = self.safe_tp_name()
|
||||||
|
self_address = self.safe_self_addresss()
|
||||||
|
return ("<method-wrapper %s of %s object at %s>"
|
||||||
|
% (name, tp_name, self_address))
|
||||||
|
|
||||||
|
def write_repr(self, out, visited):
|
||||||
|
proxy = self.proxyval(visited)
|
||||||
|
out.write(proxy)
|
||||||
|
|
||||||
|
|
||||||
def int_from_int(gdbval):
|
def int_from_int(gdbval):
|
||||||
|
@ -1364,11 +1398,13 @@ class PyObjectPtrPrinter:
|
||||||
|
|
||||||
def pretty_printer_lookup(gdbval):
|
def pretty_printer_lookup(gdbval):
|
||||||
type = gdbval.type.unqualified()
|
type = gdbval.type.unqualified()
|
||||||
if type.code == gdb.TYPE_CODE_PTR:
|
if type.code != gdb.TYPE_CODE_PTR:
|
||||||
type = type.target().unqualified()
|
return None
|
||||||
t = str(type)
|
|
||||||
if t in ("PyObject", "PyFrameObject", "PyUnicodeObject"):
|
type = type.target().unqualified()
|
||||||
return PyObjectPtrPrinter(gdbval)
|
t = str(type)
|
||||||
|
if t in ("PyObject", "PyFrameObject", "PyUnicodeObject", "wrapperobject"):
|
||||||
|
return PyObjectPtrPrinter(gdbval)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
During development, I've been manually invoking the code in this way:
|
During development, I've been manually invoking the code in this way:
|
||||||
|
@ -1497,11 +1533,8 @@ class Frame(object):
|
||||||
return 'Garbage-collecting'
|
return 'Garbage-collecting'
|
||||||
|
|
||||||
# Detect invocations of PyCFunction instances:
|
# Detect invocations of PyCFunction instances:
|
||||||
older = self.older()
|
frame = self._gdbframe
|
||||||
if not older:
|
caller = frame.name()
|
||||||
return False
|
|
||||||
|
|
||||||
caller = older._gdbframe.name()
|
|
||||||
if not caller:
|
if not caller:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -1513,18 +1546,25 @@ class Frame(object):
|
||||||
# "self" is the (PyObject*) of the 'self'
|
# "self" is the (PyObject*) of the 'self'
|
||||||
try:
|
try:
|
||||||
# Use the prettyprinter for the func:
|
# Use the prettyprinter for the func:
|
||||||
func = older._gdbframe.read_var('func')
|
func = frame.read_var('func')
|
||||||
return str(func)
|
return str(func)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return 'PyCFunction invocation (unable to read "func")'
|
return 'PyCFunction invocation (unable to read "func")'
|
||||||
|
|
||||||
elif caller == '_PyCFunction_FastCallDict':
|
elif caller == '_PyCFunction_FastCallDict':
|
||||||
try:
|
try:
|
||||||
func = older._gdbframe.read_var('func_obj')
|
func = frame.read_var('func_obj')
|
||||||
return str(func)
|
return str(func)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return 'PyCFunction invocation (unable to read "func_obj")'
|
return 'PyCFunction invocation (unable to read "func_obj")'
|
||||||
|
|
||||||
|
if caller == 'wrapper_call':
|
||||||
|
try:
|
||||||
|
func = frame.read_var('wp')
|
||||||
|
return str(func)
|
||||||
|
except RuntimeError:
|
||||||
|
return '<wrapper_call invocation>'
|
||||||
|
|
||||||
# This frame isn't worth reporting:
|
# This frame isn't worth reporting:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue