Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.6] bpo-30345: Update test_gdb.py and python-gdb.py from master #1549

Merged
merged 2 commits into from May 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 20 additions & 2 deletions Lib/test/test_gdb.py
Expand Up @@ -3,13 +3,14 @@
# The code for testing gdb was adapted from similar work in Unladen Swallow's
# Lib/test/test_jit_gdb.py

import locale
import os
import re
import subprocess
import sys
import sysconfig
import textwrap
import unittest
import locale

# Is this Python configured to support threads?
try:
Expand Down Expand Up @@ -845,7 +846,24 @@ def test_pycfunction(self):
breakpoint='time_gmtime',
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):
Expand Down
6 changes: 6 additions & 0 deletions Misc/NEWS
Expand Up @@ -145,6 +145,12 @@ 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
-----

Expand Down
64 changes: 52 additions & 12 deletions Tools/gdb/libpython.py
Expand Up @@ -362,6 +362,7 @@ def subclass_from_type(cls, t):
'set' : PySetObjectPtr,
'frozenset' : PySetObjectPtr,
'builtin_function_or_method' : PyCFunctionObjectPtr,
'method-wrapper': wrapperobject,
}
if tp_name in name_map:
return name_map[tp_name]
Expand Down Expand Up @@ -1330,6 +1331,39 @@ def write_repr(self, out, visited):
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):
Expand Down Expand Up @@ -1364,11 +1398,13 @@ def to_string (self):

def pretty_printer_lookup(gdbval):
type = gdbval.type.unqualified()
if type.code == gdb.TYPE_CODE_PTR:
type = type.target().unqualified()
t = str(type)
if t in ("PyObject", "PyFrameObject", "PyUnicodeObject"):
return PyObjectPtrPrinter(gdbval)
if type.code != gdb.TYPE_CODE_PTR:
return None

type = type.target().unqualified()
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:
Expand Down Expand Up @@ -1497,11 +1533,8 @@ def is_other_python_frame(self):
return 'Garbage-collecting'

# Detect invocations of PyCFunction instances:
older = self.older()
if not older:
return False

caller = older._gdbframe.name()
frame = self._gdbframe
caller = frame.name()
if not caller:
return False

Expand All @@ -1513,18 +1546,25 @@ def is_other_python_frame(self):
# "self" is the (PyObject*) of the 'self'
try:
# Use the prettyprinter for the func:
func = older._gdbframe.read_var('func')
func = frame.read_var('func')
return str(func)
except RuntimeError:
return 'PyCFunction invocation (unable to read "func")'

elif caller == '_PyCFunction_FastCallDict':
try:
func = older._gdbframe.read_var('func_obj')
func = frame.read_var('func_obj')
return str(func)
except RuntimeError:
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:
return False

Expand Down