mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-97670: Remove sys.getdxp() and analyze_dxp.py script (#97671)
Remove the sys.getdxp() function and the Tools/scripts/analyze_dxp.py script. DXP stands for "dynamic execution pairs". They were related to DYNAMIC_EXECUTION_PROFILE and DXPAIRS macros which have been removed in Python 3.11. Python can now be built with "./configure --enable-pystats" to gather statistics on Python opcodes.
This commit is contained in:
		
							parent
							
								
									6e53308829
								
							
						
					
					
						commit
						116fa62c6e
					
				
					 6 changed files with 7 additions and 201 deletions
				
			
		|  | @ -25,7 +25,7 @@ class TestSundryScripts(unittest.TestCase): | |||
|     # scripts that use windows-only modules | ||||
|     windows_only = ['win_add2path'] | ||||
|     # denylisted for other reasons | ||||
|     other = ['analyze_dxp', '2to3'] | ||||
|     other = ['2to3'] | ||||
| 
 | ||||
|     skiplist = denylist + allowlist + windows_only + other | ||||
| 
 | ||||
|  | @ -50,13 +50,6 @@ def test_sundry_windows(self): | |||
|         for name in self.windows_only: | ||||
|             import_tool(name) | ||||
| 
 | ||||
|     def test_analyze_dxp_import(self): | ||||
|         if hasattr(sys, 'getdxp'): | ||||
|             import_tool('analyze_dxp') | ||||
|         else: | ||||
|             with self.assertRaises(RuntimeError): | ||||
|                 import_tool('analyze_dxp') | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|  |  | |||
|  | @ -0,0 +1,6 @@ | |||
| Remove the :func:`sys.getdxp` function and the ``Tools/scripts/analyze_dxp.py`` | ||||
| script. DXP stands for "dynamic execution pairs". They were related to | ||||
| ``DYNAMIC_EXECUTION_PROFILE`` and ``DXPAIRS`` macros which have been removed in | ||||
| Python 3.11. Python can now be built with :option:`./configure --enable-pystats | ||||
| <--enable-pystats>` to gather statistics on Python opcodes.  Patch by Victor | ||||
| Stinner. | ||||
|  | @ -7207,61 +7207,6 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int oparg) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #ifdef Py_STATS | ||||
| 
 | ||||
| static PyObject * | ||||
| getarray(uint64_t a[256]) | ||||
| { | ||||
|     int i; | ||||
|     PyObject *l = PyList_New(256); | ||||
|     if (l == NULL) return NULL; | ||||
|     for (i = 0; i < 256; i++) { | ||||
|         PyObject *x = PyLong_FromUnsignedLongLong(a[i]); | ||||
|         if (x == NULL) { | ||||
|             Py_DECREF(l); | ||||
|             return NULL; | ||||
|         } | ||||
|         PyList_SET_ITEM(l, i, x); | ||||
|     } | ||||
|     for (i = 0; i < 256; i++) | ||||
|         a[i] = 0; | ||||
|     return l; | ||||
| } | ||||
| 
 | ||||
| PyObject * | ||||
| _Py_GetDXProfile(PyObject *self, PyObject *args) | ||||
| { | ||||
|     int i; | ||||
|     PyObject *l = PyList_New(257); | ||||
|     if (l == NULL) return NULL; | ||||
|     for (i = 0; i < 256; i++) { | ||||
|         PyObject *x = getarray(_py_stats_struct.opcode_stats[i].pair_count); | ||||
|         if (x == NULL) { | ||||
|             Py_DECREF(l); | ||||
|             return NULL; | ||||
|         } | ||||
|         PyList_SET_ITEM(l, i, x); | ||||
|     } | ||||
|     PyObject *counts = PyList_New(256); | ||||
|     if (counts == NULL) { | ||||
|         Py_DECREF(l); | ||||
|         return NULL; | ||||
|     } | ||||
|     for (i = 0; i < 256; i++) { | ||||
|         PyObject *x = PyLong_FromUnsignedLongLong( | ||||
|             _py_stats_struct.opcode_stats[i].execution_count); | ||||
|         if (x == NULL) { | ||||
|             Py_DECREF(counts); | ||||
|             Py_DECREF(l); | ||||
|             return NULL; | ||||
|         } | ||||
|         PyList_SET_ITEM(counts, i, x); | ||||
|     } | ||||
|     PyList_SET_ITEM(l, 256, counts); | ||||
|     return l; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| Py_ssize_t | ||||
| _PyEval_RequestCodeExtraIndex(freefunc free) | ||||
|  |  | |||
|  | @ -2014,11 +2014,6 @@ sys__debugmallocstats_impl(PyObject *module) | |||
| extern PyObject *_Py_GetObjects(PyObject *, PyObject *); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef Py_STATS | ||||
| /* Defined in ceval.c because it uses static globals in that file */ | ||||
| extern PyObject *_Py_GetDXProfile(PyObject *,  PyObject *); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | @ -2217,9 +2212,6 @@ static PyMethodDef sys_methods[] = { | |||
|     SYS_GETDEFAULTENCODING_METHODDEF | ||||
|     SYS_GETDLOPENFLAGS_METHODDEF | ||||
|     SYS_GETALLOCATEDBLOCKS_METHODDEF | ||||
| #ifdef Py_STATS | ||||
|     {"getdxp", _Py_GetDXProfile, METH_VARARGS}, | ||||
| #endif | ||||
|     SYS_GETFILESYSTEMENCODING_METHODDEF | ||||
|     SYS_GETFILESYSTEMENCODEERRORS_METHODDEF | ||||
|     SYS__GETQUICKENEDCOUNT_METHODDEF | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ useful while building, extending or managing Python. | |||
| 
 | ||||
| 2to3                      Main script for running the 2to3 conversion tool | ||||
| abitype.py                Converts a C file to use the PEP 384 type definition API | ||||
| analyze_dxp.py            Analyzes the result of sys.getdxp() | ||||
| combinerefs.py            A helper for analyzing PYTHONDUMPREFS output | ||||
| diff.py                   Print file diffs in context, unified, or ndiff formats | ||||
| eptags.py                 Create Emacs TAGS file for Python modules | ||||
|  |  | |||
|  | @ -1,129 +0,0 @@ | |||
| """ | ||||
| Some helper functions to analyze the output of sys.getdxp() (which is | ||||
| only available if Python was built with -DDYNAMIC_EXECUTION_PROFILE). | ||||
| These will tell you which opcodes have been executed most frequently | ||||
| in the current process, and, if Python was also built with -DDXPAIRS, | ||||
| will tell you which instruction _pairs_ were executed most frequently, | ||||
| which may help in choosing new instructions. | ||||
| 
 | ||||
| If Python was built without -DDYNAMIC_EXECUTION_PROFILE, importing | ||||
| this module will raise a RuntimeError. | ||||
| 
 | ||||
| If you're running a script you want to profile, a simple way to get | ||||
| the common pairs is: | ||||
| 
 | ||||
| $ PYTHONPATH=$PYTHONPATH:<python_srcdir>/Tools/scripts \ | ||||
| ./python -i -O the_script.py --args | ||||
| ... | ||||
| > from analyze_dxp import * | ||||
| > s = render_common_pairs() | ||||
| > open('/tmp/some_file', 'w').write(s) | ||||
| """ | ||||
| 
 | ||||
| import copy | ||||
| import opcode | ||||
| import operator | ||||
| import sys | ||||
| import threading | ||||
| 
 | ||||
| if not hasattr(sys, "getdxp"): | ||||
|     raise RuntimeError("Can't import analyze_dxp: Python built without" | ||||
|                        " -DDYNAMIC_EXECUTION_PROFILE.") | ||||
| 
 | ||||
| 
 | ||||
| _profile_lock = threading.RLock() | ||||
| _cumulative_profile = sys.getdxp() | ||||
| 
 | ||||
| # If Python was built with -DDXPAIRS, sys.getdxp() returns a list of | ||||
| # lists of ints.  Otherwise it returns just a list of ints. | ||||
| def has_pairs(profile): | ||||
|     """Returns True if the Python that produced the argument profile | ||||
|     was built with -DDXPAIRS.""" | ||||
| 
 | ||||
|     return len(profile) > 0 and isinstance(profile[0], list) | ||||
| 
 | ||||
| 
 | ||||
| def reset_profile(): | ||||
|     """Forgets any execution profile that has been gathered so far.""" | ||||
|     with _profile_lock: | ||||
|         sys.getdxp()  # Resets the internal profile | ||||
|         global _cumulative_profile | ||||
|         _cumulative_profile = sys.getdxp()  # 0s out our copy. | ||||
| 
 | ||||
| 
 | ||||
| def merge_profile(): | ||||
|     """Reads sys.getdxp() and merges it into this module's cached copy. | ||||
| 
 | ||||
|     We need this because sys.getdxp() 0s itself every time it's called.""" | ||||
| 
 | ||||
|     with _profile_lock: | ||||
|         new_profile = sys.getdxp() | ||||
|         if has_pairs(new_profile): | ||||
|             for first_inst in range(len(_cumulative_profile)): | ||||
|                 for second_inst in range(len(_cumulative_profile[first_inst])): | ||||
|                     _cumulative_profile[first_inst][second_inst] += ( | ||||
|                         new_profile[first_inst][second_inst]) | ||||
|         else: | ||||
|             for inst in range(len(_cumulative_profile)): | ||||
|                 _cumulative_profile[inst] += new_profile[inst] | ||||
| 
 | ||||
| 
 | ||||
| def snapshot_profile(): | ||||
|     """Returns the cumulative execution profile until this call.""" | ||||
|     with _profile_lock: | ||||
|         merge_profile() | ||||
|         return copy.deepcopy(_cumulative_profile) | ||||
| 
 | ||||
| 
 | ||||
| def common_instructions(profile): | ||||
|     """Returns the most common opcodes in order of descending frequency. | ||||
| 
 | ||||
|     The result is a list of tuples of the form | ||||
|       (opcode, opname, # of occurrences) | ||||
| 
 | ||||
|     """ | ||||
|     if has_pairs(profile) and profile: | ||||
|         inst_list = profile[-1] | ||||
|     else: | ||||
|         inst_list = profile | ||||
|     result = [(op, opcode.opname[op], count) | ||||
|               for op, count in enumerate(inst_list) | ||||
|               if count > 0] | ||||
|     result.sort(key=operator.itemgetter(2), reverse=True) | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| def common_pairs(profile): | ||||
|     """Returns the most common opcode pairs in order of descending frequency. | ||||
| 
 | ||||
|     The result is a list of tuples of the form | ||||
|       ((1st opcode, 2nd opcode), | ||||
|        (1st opname, 2nd opname), | ||||
|        # of occurrences of the pair) | ||||
| 
 | ||||
|     """ | ||||
|     if not has_pairs(profile): | ||||
|         return [] | ||||
|     result = [((op1, op2), (opcode.opname[op1], opcode.opname[op2]), count) | ||||
|               # Drop the row of single-op profiles with [:-1] | ||||
|               for op1, op1profile in enumerate(profile[:-1]) | ||||
|               for op2, count in enumerate(op1profile) | ||||
|               if count > 0] | ||||
|     result.sort(key=operator.itemgetter(2), reverse=True) | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| def render_common_pairs(profile=None): | ||||
|     """Renders the most common opcode pairs to a string in order of | ||||
|     descending frequency. | ||||
| 
 | ||||
|     The result is a series of lines of the form: | ||||
|       # of occurrences: ('1st opname', '2nd opname') | ||||
| 
 | ||||
|     """ | ||||
|     if profile is None: | ||||
|         profile = snapshot_profile() | ||||
|     def seq(): | ||||
|         for _, ops, count in common_pairs(profile): | ||||
|             yield "%s: %s\n" % (count, ops) | ||||
|     return ''.join(seq()) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner