bpo-45256: Remove the usage of the C stack in Python to Python calls (GH-28488)

Ths commit inlines calls to Python functions in the eval loop and steals all the arguments in the call from the caller for
performance.
This commit is contained in:
Pablo Galindo Salgado 2021-10-09 16:51:30 +01:00 committed by GitHub
parent ec04db74e2
commit b4903afd4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 224 additions and 64 deletions

View file

@ -45,6 +45,7 @@ The module also extends gdb with some python-specific commands.
# compatible (2.6+ and 3.0+). See #19308.
from __future__ import print_function
import gdb
import os
import locale
@ -991,6 +992,11 @@ class PyFramePtr:
def _f_lasti(self):
return self._f_special("f_lasti", int_from_int)
def depth(self):
return self._f_special("depth", int_from_int)
def previous(self):
return self._f_special("previous", PyFramePtr)
def iter_globals(self):
'''
@ -1797,16 +1803,20 @@ class Frame(object):
def print_summary(self):
if self.is_evalframe():
pyop = self.get_pyop()
if pyop:
line = pyop.get_truncated_repr(MAX_OUTPUT_LEN)
write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line))
if not pyop.is_optimized_out():
line = pyop.current_line()
if line is not None:
sys.stdout.write(' %s\n' % line.strip())
else:
sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index())
interp_frame = self.get_pyop()
while True:
if interp_frame:
line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN)
write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(), line))
if not interp_frame.is_optimized_out():
line = interp_frame.current_line()
if line is not None:
sys.stdout.write(' %s\n' % line.strip())
if interp_frame.depth() == 0:
break
else:
sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index())
interp_frame = interp_frame.previous()
else:
info = self.is_other_python_frame()
if info:
@ -1816,15 +1826,19 @@ class Frame(object):
def print_traceback(self):
if self.is_evalframe():
pyop = self.get_pyop()
if pyop:
pyop.print_traceback()
if not pyop.is_optimized_out():
line = pyop.current_line()
if line is not None:
sys.stdout.write(' %s\n' % line.strip())
else:
sys.stdout.write(' (unable to read python frame information)\n')
interp_frame = self.get_pyop()
while True:
if interp_frame:
interp_frame.print_traceback()
if not interp_frame.is_optimized_out():
line = interp_frame.current_line()
if line is not None:
sys.stdout.write(' %s\n' % line.strip())
if interp_frame.depth() == 0:
break
else:
sys.stdout.write(' (unable to read python frame information)\n')
interp_frame = interp_frame.previous()
else:
info = self.is_other_python_frame()
if info:
@ -1914,11 +1928,15 @@ PyList()
def move_in_stack(move_up):
'''Move up or down the stack (for the py-up/py-down command)'''
# Important:
# The amount of frames that are printed out depends on how many frames are inlined
# in the same evaluation loop. As this command links directly the C stack with the
# Python stack, the results are sensitive to the number of inlined frames and this
# is likely to change between versions and optimizations.
frame = Frame.get_selected_python_frame()
if not frame:
print('Unable to locate python frame')
return
while frame:
if move_up:
iter_frame = frame.older()
@ -1940,9 +1958,10 @@ def move_in_stack(move_up):
print('Unable to find an older python frame')
else:
print('Unable to find a newer python frame')
class PyUp(gdb.Command):
'Select and print the python stack frame that called this one (if any)'
'Select and print all python stack frame in the same eval loop starting from the one that called this one (if any)'
def __init__(self):
gdb.Command.__init__ (self,
"py-up",
@ -1954,7 +1973,7 @@ class PyUp(gdb.Command):
move_in_stack(move_up=True)
class PyDown(gdb.Command):
'Select and print the python stack frame called by this one (if any)'
'Select and print all python stack frame in the same eval loop starting from the one called this one (if any)'
def __init__(self):
gdb.Command.__init__ (self,
"py-down",
@ -2067,13 +2086,20 @@ class PyLocals(gdb.Command):
return
pyop_frame = frame.get_pyop()
if not pyop_frame:
print(UNABLE_READ_INFO_PYTHON_FRAME)
return
while True:
if not pyop_frame:
print(UNABLE_READ_INFO_PYTHON_FRAME)
for pyop_name, pyop_value in pyop_frame.iter_locals():
print('%s = %s'
% (pyop_name.proxyval(set()),
pyop_value.get_truncated_repr(MAX_OUTPUT_LEN)))
sys.stdout.write('Locals for %s\n' % (pyop_frame.co_name.proxyval(set())))
for pyop_name, pyop_value in pyop_frame.iter_locals():
print('%s = %s'
% (pyop_name.proxyval(set()),
pyop_value.get_truncated_repr(MAX_OUTPUT_LEN)))
if pyop_frame.depth() == 0:
break
pyop_frame = pyop_frame.previous()
PyLocals()