17db96d56Sopenharmony_ci"""
27db96d56Sopenharmony_ciSome helper functions to analyze the output of sys.getdxp() (which is
37db96d56Sopenharmony_cionly available if Python was built with -DDYNAMIC_EXECUTION_PROFILE).
47db96d56Sopenharmony_ciThese will tell you which opcodes have been executed most frequently
57db96d56Sopenharmony_ciin the current process, and, if Python was also built with -DDXPAIRS,
67db96d56Sopenharmony_ciwill tell you which instruction _pairs_ were executed most frequently,
77db96d56Sopenharmony_ciwhich may help in choosing new instructions.
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ciIf Python was built without -DDYNAMIC_EXECUTION_PROFILE, importing
107db96d56Sopenharmony_cithis module will raise a RuntimeError.
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ciIf you're running a script you want to profile, a simple way to get
137db96d56Sopenharmony_cithe common pairs is:
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ci$ PYTHONPATH=$PYTHONPATH:<python_srcdir>/Tools/scripts \
167db96d56Sopenharmony_ci./python -i -O the_script.py --args
177db96d56Sopenharmony_ci...
187db96d56Sopenharmony_ci> from analyze_dxp import *
197db96d56Sopenharmony_ci> s = render_common_pairs()
207db96d56Sopenharmony_ci> open('/tmp/some_file', 'w').write(s)
217db96d56Sopenharmony_ci"""
227db96d56Sopenharmony_ci
237db96d56Sopenharmony_ciimport copy
247db96d56Sopenharmony_ciimport opcode
257db96d56Sopenharmony_ciimport operator
267db96d56Sopenharmony_ciimport sys
277db96d56Sopenharmony_ciimport threading
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciif not hasattr(sys, "getdxp"):
307db96d56Sopenharmony_ci    raise RuntimeError("Can't import analyze_dxp: Python built without"
317db96d56Sopenharmony_ci                       " -DDYNAMIC_EXECUTION_PROFILE.")
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci_profile_lock = threading.RLock()
357db96d56Sopenharmony_ci_cumulative_profile = sys.getdxp()
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci# If Python was built with -DDXPAIRS, sys.getdxp() returns a list of
387db96d56Sopenharmony_ci# lists of ints.  Otherwise it returns just a list of ints.
397db96d56Sopenharmony_cidef has_pairs(profile):
407db96d56Sopenharmony_ci    """Returns True if the Python that produced the argument profile
417db96d56Sopenharmony_ci    was built with -DDXPAIRS."""
427db96d56Sopenharmony_ci
437db96d56Sopenharmony_ci    return len(profile) > 0 and isinstance(profile[0], list)
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_cidef reset_profile():
477db96d56Sopenharmony_ci    """Forgets any execution profile that has been gathered so far."""
487db96d56Sopenharmony_ci    with _profile_lock:
497db96d56Sopenharmony_ci        sys.getdxp()  # Resets the internal profile
507db96d56Sopenharmony_ci        global _cumulative_profile
517db96d56Sopenharmony_ci        _cumulative_profile = sys.getdxp()  # 0s out our copy.
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_cidef merge_profile():
557db96d56Sopenharmony_ci    """Reads sys.getdxp() and merges it into this module's cached copy.
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ci    We need this because sys.getdxp() 0s itself every time it's called."""
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci    with _profile_lock:
607db96d56Sopenharmony_ci        new_profile = sys.getdxp()
617db96d56Sopenharmony_ci        if has_pairs(new_profile):
627db96d56Sopenharmony_ci            for first_inst in range(len(_cumulative_profile)):
637db96d56Sopenharmony_ci                for second_inst in range(len(_cumulative_profile[first_inst])):
647db96d56Sopenharmony_ci                    _cumulative_profile[first_inst][second_inst] += (
657db96d56Sopenharmony_ci                        new_profile[first_inst][second_inst])
667db96d56Sopenharmony_ci        else:
677db96d56Sopenharmony_ci            for inst in range(len(_cumulative_profile)):
687db96d56Sopenharmony_ci                _cumulative_profile[inst] += new_profile[inst]
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_cidef snapshot_profile():
727db96d56Sopenharmony_ci    """Returns the cumulative execution profile until this call."""
737db96d56Sopenharmony_ci    with _profile_lock:
747db96d56Sopenharmony_ci        merge_profile()
757db96d56Sopenharmony_ci        return copy.deepcopy(_cumulative_profile)
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ci
787db96d56Sopenharmony_cidef common_instructions(profile):
797db96d56Sopenharmony_ci    """Returns the most common opcodes in order of descending frequency.
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    The result is a list of tuples of the form
827db96d56Sopenharmony_ci      (opcode, opname, # of occurrences)
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci    """
857db96d56Sopenharmony_ci    if has_pairs(profile) and profile:
867db96d56Sopenharmony_ci        inst_list = profile[-1]
877db96d56Sopenharmony_ci    else:
887db96d56Sopenharmony_ci        inst_list = profile
897db96d56Sopenharmony_ci    result = [(op, opcode.opname[op], count)
907db96d56Sopenharmony_ci              for op, count in enumerate(inst_list)
917db96d56Sopenharmony_ci              if count > 0]
927db96d56Sopenharmony_ci    result.sort(key=operator.itemgetter(2), reverse=True)
937db96d56Sopenharmony_ci    return result
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci
967db96d56Sopenharmony_cidef common_pairs(profile):
977db96d56Sopenharmony_ci    """Returns the most common opcode pairs in order of descending frequency.
987db96d56Sopenharmony_ci
997db96d56Sopenharmony_ci    The result is a list of tuples of the form
1007db96d56Sopenharmony_ci      ((1st opcode, 2nd opcode),
1017db96d56Sopenharmony_ci       (1st opname, 2nd opname),
1027db96d56Sopenharmony_ci       # of occurrences of the pair)
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_ci    """
1057db96d56Sopenharmony_ci    if not has_pairs(profile):
1067db96d56Sopenharmony_ci        return []
1077db96d56Sopenharmony_ci    result = [((op1, op2), (opcode.opname[op1], opcode.opname[op2]), count)
1087db96d56Sopenharmony_ci              # Drop the row of single-op profiles with [:-1]
1097db96d56Sopenharmony_ci              for op1, op1profile in enumerate(profile[:-1])
1107db96d56Sopenharmony_ci              for op2, count in enumerate(op1profile)
1117db96d56Sopenharmony_ci              if count > 0]
1127db96d56Sopenharmony_ci    result.sort(key=operator.itemgetter(2), reverse=True)
1137db96d56Sopenharmony_ci    return result
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_cidef render_common_pairs(profile=None):
1177db96d56Sopenharmony_ci    """Renders the most common opcode pairs to a string in order of
1187db96d56Sopenharmony_ci    descending frequency.
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_ci    The result is a series of lines of the form:
1217db96d56Sopenharmony_ci      # of occurrences: ('1st opname', '2nd opname')
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci    """
1247db96d56Sopenharmony_ci    if profile is None:
1257db96d56Sopenharmony_ci        profile = snapshot_profile()
1267db96d56Sopenharmony_ci    def seq():
1277db96d56Sopenharmony_ci        for _, ops, count in common_pairs(profile):
1287db96d56Sopenharmony_ci            yield "%s: %s\n" % (count, ops)
1297db96d56Sopenharmony_ci    return ''.join(seq())
130