| """ | 
 | 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()) |