2 Some helper functions to analyze the output of sys.getdxp() (which is
3 only available if Python was built with -DDYNAMIC_EXECUTION_PROFILE).
4 These will tell you which opcodes have been executed most frequently
5 in the current process, and, if Python was also built with -DDXPAIRS,
6 will tell you which instruction _pairs_ were executed most frequently,
7 which may help in choosing new instructions.
9 If Python was built without -DDYNAMIC_EXECUTION_PROFILE, importing
10 this module will raise a RuntimeError.
12 If you're running a script you want to profile, a simple way to get
15 $ PYTHONPATH=$PYTHONPATH:<python_srcdir>/Tools/scripts \
16 ./python -i -O the_script.py --args
18 > from analyze_dxp import *
19 > s = render_common_pairs()
20 > open('/tmp/some_file', 'w').write(s)
29 if not hasattr(sys
, "getdxp"):
30 raise RuntimeError("Can't import analyze_dxp: Python built without"
31 " -DDYNAMIC_EXECUTION_PROFILE.")
34 _profile_lock
= threading
.RLock()
35 _cumulative_profile
= sys
.getdxp()
37 # If Python was built with -DDXPAIRS, sys.getdxp() returns a list of
38 # lists of ints. Otherwise it returns just a list of ints.
39 def has_pairs(profile
):
40 """Returns True if the Python that produced the argument profile
41 was built with -DDXPAIRS."""
43 return len(profile
) > 0 and isinstance(profile
[0], list)
47 """Forgets any execution profile that has been gathered so far."""
49 sys
.getdxp() # Resets the internal profile
50 global _cumulative_profile
51 _cumulative_profile
= sys
.getdxp() # 0s out our copy.
55 """Reads sys.getdxp() and merges it into this module's cached copy.
57 We need this because sys.getdxp() 0s itself every time it's called."""
60 new_profile
= sys
.getdxp()
61 if has_pairs(new_profile
):
62 for first_inst
in range(len(_cumulative_profile
)):
63 for second_inst
in range(len(_cumulative_profile
[first_inst
])):
64 _cumulative_profile
[first_inst
][second_inst
] += (
65 new_profile
[first_inst
][second_inst
])
67 for inst
in range(len(_cumulative_profile
)):
68 _cumulative_profile
[inst
] += new_profile
[inst
]
71 def snapshot_profile():
72 """Returns the cumulative execution profile until this call."""
75 return copy
.deepcopy(_cumulative_profile
)
78 def common_instructions(profile
):
79 """Returns the most common opcodes in order of descending frequency.
81 The result is a list of tuples of the form
82 (opcode, opname, # of occurrences)
85 if has_pairs(profile
) and profile
:
86 inst_list
= profile
[-1]
89 result
= [(op
, opcode
.opname
[op
], count
)
90 for op
, count
in enumerate(inst_list
)
92 result
.sort(key
=operator
.itemgetter(2), reverse
=True)
96 def common_pairs(profile
):
97 """Returns the most common opcode pairs in order of descending frequency.
99 The result is a list of tuples of the form
100 ((1st opcode, 2nd opcode),
101 (1st opname, 2nd opname),
102 # of occurrences of the pair)
105 if not has_pairs(profile
):
107 result
= [((op1
, op2
), (opcode
.opname
[op1
], opcode
.opname
[op2
]), count
)
108 # Drop the row of single-op profiles with [:-1]
109 for op1
, op1profile
in enumerate(profile
[:-1])
110 for op2
, count
in enumerate(op1profile
)
112 result
.sort(key
=operator
.itemgetter(2), reverse
=True)
116 def render_common_pairs(profile
=None):
117 """Renders the most common opcode pairs to a string in order of
118 descending frequency.
120 The result is a series of lines of the form:
121 # of occurrences: ('1st opname', '2nd opname')
125 profile
= snapshot_profile()
127 for _
, ops
, count
in common_pairs(profile
):
128 yield "%s: %s\n" % (count
, ops
)
129 return ''.join(seq())