mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-44590: Lazily allocate frame objects (GH-27077)
* Convert "specials" array to InterpreterFrame struct, adding f_lasti, f_state and other non-debug FrameObject fields to it. * Refactor, calls pushing the call to the interpreter upward toward _PyEval_Vector. * Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation. * Move ownership of InterpreterFrame in generator from frame object to generator object. * Do not create frame objects for Python calls. * Do not create frame objects for generators.
This commit is contained in:
		
							parent
							
								
									0363a4014d
								
							
						
					
					
						commit
						ae0a2b7562
					
				
					 27 changed files with 1037 additions and 619 deletions
				
			
		|  | @ -854,28 +854,102 @@ class PyNoneStructPtr(PyObjectPtr): | |||
|     def proxyval(self, visited): | ||||
|         return None | ||||
| 
 | ||||
| FRAME_SPECIALS_GLOBAL_OFFSET = 0 | ||||
| FRAME_SPECIALS_BUILTINS_OFFSET = 1 | ||||
| FRAME_SPECIALS_CODE_OFFSET = 3 | ||||
| FRAME_SPECIALS_SIZE = 4 | ||||
| 
 | ||||
| class PyFrameObjectPtr(PyObjectPtr): | ||||
|     _typename = 'PyFrameObject' | ||||
| 
 | ||||
|     def __init__(self, gdbval, cast_to=None): | ||||
|         PyObjectPtr.__init__(self, gdbval, cast_to) | ||||
| 
 | ||||
|         if not self.is_optimized_out(): | ||||
|             self._frame = PyFramePtr(self.field('f_frame')) | ||||
| 
 | ||||
|     def iter_locals(self): | ||||
|         ''' | ||||
|         Yield a sequence of (name,value) pairs of PyObjectPtr instances, for | ||||
|         the local variables of this frame | ||||
|         ''' | ||||
|         if self.is_optimized_out(): | ||||
|             return | ||||
|         return self._frame.iter_locals() | ||||
| 
 | ||||
|     def iter_globals(self): | ||||
|         ''' | ||||
|         Yield a sequence of (name,value) pairs of PyObjectPtr instances, for | ||||
|         the global variables of this frame | ||||
|         ''' | ||||
|         if self.is_optimized_out(): | ||||
|             return () | ||||
|         return self._frame.iter_globals() | ||||
| 
 | ||||
|     def iter_builtins(self): | ||||
|         ''' | ||||
|         Yield a sequence of (name,value) pairs of PyObjectPtr instances, for | ||||
|         the builtin variables | ||||
|         ''' | ||||
|         if self.is_optimized_out(): | ||||
|             return () | ||||
|         return self._frame.iter_builtins() | ||||
| 
 | ||||
|     def get_var_by_name(self, name): | ||||
| 
 | ||||
|         if self.is_optimized_out(): | ||||
|             return None, None | ||||
|         return self._frame.get_var_by_name(name) | ||||
| 
 | ||||
|     def filename(self): | ||||
|         '''Get the path of the current Python source file, as a string''' | ||||
|         if self.is_optimized_out(): | ||||
|             return FRAME_INFO_OPTIMIZED_OUT | ||||
|         return self._frame.filename() | ||||
| 
 | ||||
|     def current_line_num(self): | ||||
|         '''Get current line number as an integer (1-based) | ||||
| 
 | ||||
|         Translated from PyFrame_GetLineNumber and PyCode_Addr2Line | ||||
| 
 | ||||
|         See Objects/lnotab_notes.txt | ||||
|         ''' | ||||
|         if self.is_optimized_out(): | ||||
|             return None | ||||
|         return self._frame.current_line_num() | ||||
| 
 | ||||
|     def current_line(self): | ||||
|         '''Get the text of the current source line as a string, with a trailing | ||||
|         newline character''' | ||||
|         if self.is_optimized_out(): | ||||
|             return FRAME_INFO_OPTIMIZED_OUT | ||||
|         return self._frame.current_line() | ||||
| 
 | ||||
|     def write_repr(self, out, visited): | ||||
|         if self.is_optimized_out(): | ||||
|             out.write(FRAME_INFO_OPTIMIZED_OUT) | ||||
|             return | ||||
|         return self._frame.write_repr(out, visited) | ||||
| 
 | ||||
|     def print_traceback(self): | ||||
|         if self.is_optimized_out(): | ||||
|             sys.stdout.write('  %s\n' % FRAME_INFO_OPTIMIZED_OUT) | ||||
|             return | ||||
|         return self._frame.print_traceback() | ||||
| 
 | ||||
| class PyFramePtr: | ||||
| 
 | ||||
|     def __init__(self, gdbval): | ||||
|         self._gdbval = gdbval | ||||
| 
 | ||||
|         if not self.is_optimized_out(): | ||||
|             self.co = self._f_code() | ||||
|             self.co_name = self.co.pyop_field('co_name') | ||||
|             self.co_filename = self.co.pyop_field('co_filename') | ||||
| 
 | ||||
|             self.f_lineno = int_from_int(self.field('f_lineno')) | ||||
|             self.f_lasti = int_from_int(self.field('f_lasti')) | ||||
|             self.f_lasti = self._f_lasti() | ||||
|             self.co_nlocals = int_from_int(self.co.field('co_nlocals')) | ||||
|             pnames = self.co.field('co_localsplusnames') | ||||
|             self.co_localsplusnames = PyTupleObjectPtr.from_pyobject_ptr(pnames) | ||||
| 
 | ||||
|     def is_optimized_out(self): | ||||
|         return self._gdbval.is_optimized_out | ||||
| 
 | ||||
|     def iter_locals(self): | ||||
|         ''' | ||||
|         Yield a sequence of (name,value) pairs of PyObjectPtr instances, for | ||||
|  | @ -884,26 +958,34 @@ def iter_locals(self): | |||
|         if self.is_optimized_out(): | ||||
|             return | ||||
| 
 | ||||
|         f_localsplus = self.field('f_localsptr') | ||||
|         obj_ptr_ptr = gdb.lookup_type("PyObject").pointer().pointer() | ||||
|         base = self._gdbval.cast(obj_ptr_ptr) | ||||
|         localsplus = base - self._f_nlocalsplus() | ||||
|         for i in safe_range(self.co_nlocals): | ||||
|             pyop_value = PyObjectPtr.from_pyobject_ptr(f_localsplus[i]) | ||||
|             pyop_value = PyObjectPtr.from_pyobject_ptr(localsplus[i]) | ||||
|             if pyop_value.is_null(): | ||||
|                 continue | ||||
|             pyop_name = PyObjectPtr.from_pyobject_ptr(self.co_localsplusnames[i]) | ||||
|             yield (pyop_name, pyop_value) | ||||
| 
 | ||||
|     def _f_specials(self, index, cls=PyObjectPtr): | ||||
|         f_valuestack = self.field('f_valuestack') | ||||
|         return cls.from_pyobject_ptr(f_valuestack[index - FRAME_SPECIALS_SIZE]) | ||||
|     def _f_special(self, name, convert=PyObjectPtr.from_pyobject_ptr): | ||||
|         return convert(self._gdbval[name]) | ||||
| 
 | ||||
|     def _f_globals(self): | ||||
|         return self._f_specials(FRAME_SPECIALS_GLOBAL_OFFSET) | ||||
|         return self._f_special("f_globals") | ||||
| 
 | ||||
|     def _f_builtins(self): | ||||
|         return self._f_specials(FRAME_SPECIALS_BUILTINS_OFFSET) | ||||
|         return self._f_special("f_builtins") | ||||
| 
 | ||||
|     def _f_code(self): | ||||
|         return self._f_specials(FRAME_SPECIALS_CODE_OFFSET, PyCodeObjectPtr) | ||||
|         return self._f_special("f_code", PyCodeObjectPtr.from_pyobject_ptr) | ||||
| 
 | ||||
|     def _f_nlocalsplus(self): | ||||
|         return self._f_special("nlocalsplus", int_from_int) | ||||
| 
 | ||||
|     def _f_lasti(self): | ||||
|         return self._f_special("f_lasti", int_from_int) | ||||
| 
 | ||||
| 
 | ||||
|     def iter_globals(self): | ||||
|         ''' | ||||
|  | @ -960,11 +1042,6 @@ def current_line_num(self): | |||
|         ''' | ||||
|         if self.is_optimized_out(): | ||||
|             return None | ||||
|         f_trace = self.field('f_trace') | ||||
|         if long(f_trace) != 0: | ||||
|             # we have a non-NULL f_trace: | ||||
|             return self.f_lineno | ||||
| 
 | ||||
|         try: | ||||
|             return self.co.addr2line(self.f_lasti*2) | ||||
|         except Exception: | ||||
|  | @ -1021,6 +1098,9 @@ def write_repr(self, out, visited): | |||
| 
 | ||||
|         out.write(')') | ||||
| 
 | ||||
|     def as_address(self): | ||||
|         return long(self._gdbval) | ||||
| 
 | ||||
|     def print_traceback(self): | ||||
|         if self.is_optimized_out(): | ||||
|             sys.stdout.write('  %s\n' % FRAME_INFO_OPTIMIZED_OUT) | ||||
|  | @ -1033,6 +1113,21 @@ def print_traceback(self): | |||
|                      lineno, | ||||
|                      self.co_name.proxyval(visited))) | ||||
| 
 | ||||
|     def get_truncated_repr(self, maxlen): | ||||
|         ''' | ||||
|         Get a repr-like string for the data, but truncate it at "maxlen" bytes | ||||
|         (ending the object graph traversal as soon as you do) | ||||
|         ''' | ||||
|         out = TruncatedStringIO(maxlen) | ||||
|         try: | ||||
|             self.write_repr(out, set()) | ||||
|         except StringTruncated: | ||||
|             # Truncation occurred: | ||||
|             return out.getvalue() + '...(truncated)' | ||||
| 
 | ||||
|         # No truncation occurred: | ||||
|         return out.getvalue() | ||||
| 
 | ||||
| class PySetObjectPtr(PyObjectPtr): | ||||
|     _typename = 'PySetObject' | ||||
| 
 | ||||
|  | @ -1638,18 +1733,18 @@ def is_gc_collect(self): | |||
| 
 | ||||
|     def get_pyop(self): | ||||
|         try: | ||||
|             f = self._gdbframe.read_var('f') | ||||
|             frame = PyFrameObjectPtr.from_pyobject_ptr(f) | ||||
|             frame = self._gdbframe.read_var('frame') | ||||
|             frame = PyFramePtr(frame) | ||||
|             if not frame.is_optimized_out(): | ||||
|                 return frame | ||||
|             # gdb is unable to get the "f" argument of PyEval_EvalFrameEx() | ||||
|             # because it was "optimized out". Try to get "f" from the frame | ||||
|             # of the caller, PyEval_EvalCodeEx(). | ||||
|             # gdb is unable to get the "frame" argument of PyEval_EvalFrameEx() | ||||
|             # because it was "optimized out". Try to get "frame" from the frame | ||||
|             # of the caller, _PyEval_Vector(). | ||||
|             orig_frame = frame | ||||
|             caller = self._gdbframe.older() | ||||
|             if caller: | ||||
|                 f = caller.read_var('f') | ||||
|                 frame = PyFrameObjectPtr.from_pyobject_ptr(f) | ||||
|                 frame = caller.read_var('frame') | ||||
|                 frame = PyFramePtr(frame) | ||||
|                 if not frame.is_optimized_out(): | ||||
|                     return frame | ||||
|             return orig_frame | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Shannon
						Mark Shannon