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