17db96d56Sopenharmony_ci"""Class for printing reports on profiled python code.""" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci# Written by James Roskind 47db96d56Sopenharmony_ci# Based on prior profile module by Sjoerd Mullender... 57db96d56Sopenharmony_ci# which was hacked somewhat by: Guido van Rossum 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ci# Copyright Disney Enterprises, Inc. All Rights Reserved. 87db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement 97db96d56Sopenharmony_ci# 107db96d56Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 117db96d56Sopenharmony_ci# you may not use this file except in compliance with the License. 127db96d56Sopenharmony_ci# You may obtain a copy of the License at 137db96d56Sopenharmony_ci# 147db96d56Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 157db96d56Sopenharmony_ci# 167db96d56Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 177db96d56Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 187db96d56Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 197db96d56Sopenharmony_ci# either express or implied. See the License for the specific language 207db96d56Sopenharmony_ci# governing permissions and limitations under the License. 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ciimport sys 247db96d56Sopenharmony_ciimport os 257db96d56Sopenharmony_ciimport time 267db96d56Sopenharmony_ciimport marshal 277db96d56Sopenharmony_ciimport re 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_cifrom enum import StrEnum, _simple_enum 307db96d56Sopenharmony_cifrom functools import cmp_to_key 317db96d56Sopenharmony_cifrom dataclasses import dataclass 327db96d56Sopenharmony_cifrom typing import Dict 337db96d56Sopenharmony_ci 347db96d56Sopenharmony_ci__all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci@_simple_enum(StrEnum) 377db96d56Sopenharmony_ciclass SortKey: 387db96d56Sopenharmony_ci CALLS = 'calls', 'ncalls' 397db96d56Sopenharmony_ci CUMULATIVE = 'cumulative', 'cumtime' 407db96d56Sopenharmony_ci FILENAME = 'filename', 'module' 417db96d56Sopenharmony_ci LINE = 'line' 427db96d56Sopenharmony_ci NAME = 'name' 437db96d56Sopenharmony_ci NFL = 'nfl' 447db96d56Sopenharmony_ci PCALLS = 'pcalls' 457db96d56Sopenharmony_ci STDNAME = 'stdname' 467db96d56Sopenharmony_ci TIME = 'time', 'tottime' 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_ci def __new__(cls, *values): 497db96d56Sopenharmony_ci value = values[0] 507db96d56Sopenharmony_ci obj = str.__new__(cls, value) 517db96d56Sopenharmony_ci obj._value_ = value 527db96d56Sopenharmony_ci for other_value in values[1:]: 537db96d56Sopenharmony_ci cls._value2member_map_[other_value] = obj 547db96d56Sopenharmony_ci obj._all_values = values 557db96d56Sopenharmony_ci return obj 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ci@dataclass(unsafe_hash=True) 597db96d56Sopenharmony_ciclass FunctionProfile: 607db96d56Sopenharmony_ci ncalls: str 617db96d56Sopenharmony_ci tottime: float 627db96d56Sopenharmony_ci percall_tottime: float 637db96d56Sopenharmony_ci cumtime: float 647db96d56Sopenharmony_ci percall_cumtime: float 657db96d56Sopenharmony_ci file_name: str 667db96d56Sopenharmony_ci line_number: int 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci@dataclass(unsafe_hash=True) 697db96d56Sopenharmony_ciclass StatsProfile: 707db96d56Sopenharmony_ci '''Class for keeping track of an item in inventory.''' 717db96d56Sopenharmony_ci total_tt: float 727db96d56Sopenharmony_ci func_profiles: Dict[str, FunctionProfile] 737db96d56Sopenharmony_ci 747db96d56Sopenharmony_ciclass Stats: 757db96d56Sopenharmony_ci """This class is used for creating reports from data generated by the 767db96d56Sopenharmony_ci Profile class. It is a "friend" of that class, and imports data either 777db96d56Sopenharmony_ci by direct access to members of Profile class, or by reading in a dictionary 787db96d56Sopenharmony_ci that was emitted (via marshal) from the Profile class. 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci The big change from the previous Profiler (in terms of raw functionality) 817db96d56Sopenharmony_ci is that an "add()" method has been provided to combine Stats from 827db96d56Sopenharmony_ci several distinct profile runs. Both the constructor and the add() 837db96d56Sopenharmony_ci method now take arbitrarily many file names as arguments. 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci All the print methods now take an argument that indicates how many lines 867db96d56Sopenharmony_ci to print. If the arg is a floating point number between 0 and 1.0, then 877db96d56Sopenharmony_ci it is taken as a decimal percentage of the available lines to be printed 887db96d56Sopenharmony_ci (e.g., .1 means print 10% of all available lines). If it is an integer, 897db96d56Sopenharmony_ci it is taken to mean the number of lines of data that you wish to have 907db96d56Sopenharmony_ci printed. 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ci The sort_stats() method now processes some additional options (i.e., in 937db96d56Sopenharmony_ci addition to the old -1, 0, 1, or 2 that are respectively interpreted as 947db96d56Sopenharmony_ci 'stdname', 'calls', 'time', and 'cumulative'). It takes either an 957db96d56Sopenharmony_ci arbitrary number of quoted strings or SortKey enum to select the sort 967db96d56Sopenharmony_ci order. 977db96d56Sopenharmony_ci 987db96d56Sopenharmony_ci For example sort_stats('time', 'name') or sort_stats(SortKey.TIME, 997db96d56Sopenharmony_ci SortKey.NAME) sorts on the major key of 'internal function time', and on 1007db96d56Sopenharmony_ci the minor key of 'the name of the function'. Look at the two tables in 1017db96d56Sopenharmony_ci sort_stats() and get_sort_arg_defs(self) for more examples. 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci All methods return self, so you can string together commands like: 1047db96d56Sopenharmony_ci Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ 1057db96d56Sopenharmony_ci print_stats(5).print_callers(5) 1067db96d56Sopenharmony_ci """ 1077db96d56Sopenharmony_ci 1087db96d56Sopenharmony_ci def __init__(self, *args, stream=None): 1097db96d56Sopenharmony_ci self.stream = stream or sys.stdout 1107db96d56Sopenharmony_ci if not len(args): 1117db96d56Sopenharmony_ci arg = None 1127db96d56Sopenharmony_ci else: 1137db96d56Sopenharmony_ci arg = args[0] 1147db96d56Sopenharmony_ci args = args[1:] 1157db96d56Sopenharmony_ci self.init(arg) 1167db96d56Sopenharmony_ci self.add(*args) 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci def init(self, arg): 1197db96d56Sopenharmony_ci self.all_callees = None # calc only if needed 1207db96d56Sopenharmony_ci self.files = [] 1217db96d56Sopenharmony_ci self.fcn_list = None 1227db96d56Sopenharmony_ci self.total_tt = 0 1237db96d56Sopenharmony_ci self.total_calls = 0 1247db96d56Sopenharmony_ci self.prim_calls = 0 1257db96d56Sopenharmony_ci self.max_name_len = 0 1267db96d56Sopenharmony_ci self.top_level = set() 1277db96d56Sopenharmony_ci self.stats = {} 1287db96d56Sopenharmony_ci self.sort_arg_dict = {} 1297db96d56Sopenharmony_ci self.load_stats(arg) 1307db96d56Sopenharmony_ci try: 1317db96d56Sopenharmony_ci self.get_top_level_stats() 1327db96d56Sopenharmony_ci except Exception: 1337db96d56Sopenharmony_ci print("Invalid timing data %s" % 1347db96d56Sopenharmony_ci (self.files[-1] if self.files else ''), file=self.stream) 1357db96d56Sopenharmony_ci raise 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci def load_stats(self, arg): 1387db96d56Sopenharmony_ci if arg is None: 1397db96d56Sopenharmony_ci self.stats = {} 1407db96d56Sopenharmony_ci return 1417db96d56Sopenharmony_ci elif isinstance(arg, str): 1427db96d56Sopenharmony_ci with open(arg, 'rb') as f: 1437db96d56Sopenharmony_ci self.stats = marshal.load(f) 1447db96d56Sopenharmony_ci try: 1457db96d56Sopenharmony_ci file_stats = os.stat(arg) 1467db96d56Sopenharmony_ci arg = time.ctime(file_stats.st_mtime) + " " + arg 1477db96d56Sopenharmony_ci except: # in case this is not unix 1487db96d56Sopenharmony_ci pass 1497db96d56Sopenharmony_ci self.files = [arg] 1507db96d56Sopenharmony_ci elif hasattr(arg, 'create_stats'): 1517db96d56Sopenharmony_ci arg.create_stats() 1527db96d56Sopenharmony_ci self.stats = arg.stats 1537db96d56Sopenharmony_ci arg.stats = {} 1547db96d56Sopenharmony_ci if not self.stats: 1557db96d56Sopenharmony_ci raise TypeError("Cannot create or construct a %r object from %r" 1567db96d56Sopenharmony_ci % (self.__class__, arg)) 1577db96d56Sopenharmony_ci return 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_ci def get_top_level_stats(self): 1607db96d56Sopenharmony_ci for func, (cc, nc, tt, ct, callers) in self.stats.items(): 1617db96d56Sopenharmony_ci self.total_calls += nc 1627db96d56Sopenharmony_ci self.prim_calls += cc 1637db96d56Sopenharmony_ci self.total_tt += tt 1647db96d56Sopenharmony_ci if ("jprofile", 0, "profiler") in callers: 1657db96d56Sopenharmony_ci self.top_level.add(func) 1667db96d56Sopenharmony_ci if len(func_std_string(func)) > self.max_name_len: 1677db96d56Sopenharmony_ci self.max_name_len = len(func_std_string(func)) 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def add(self, *arg_list): 1707db96d56Sopenharmony_ci if not arg_list: 1717db96d56Sopenharmony_ci return self 1727db96d56Sopenharmony_ci for item in reversed(arg_list): 1737db96d56Sopenharmony_ci if type(self) != type(item): 1747db96d56Sopenharmony_ci item = Stats(item) 1757db96d56Sopenharmony_ci self.files += item.files 1767db96d56Sopenharmony_ci self.total_calls += item.total_calls 1777db96d56Sopenharmony_ci self.prim_calls += item.prim_calls 1787db96d56Sopenharmony_ci self.total_tt += item.total_tt 1797db96d56Sopenharmony_ci for func in item.top_level: 1807db96d56Sopenharmony_ci self.top_level.add(func) 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci if self.max_name_len < item.max_name_len: 1837db96d56Sopenharmony_ci self.max_name_len = item.max_name_len 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci self.fcn_list = None 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ci for func, stat in item.stats.items(): 1887db96d56Sopenharmony_ci if func in self.stats: 1897db96d56Sopenharmony_ci old_func_stat = self.stats[func] 1907db96d56Sopenharmony_ci else: 1917db96d56Sopenharmony_ci old_func_stat = (0, 0, 0, 0, {},) 1927db96d56Sopenharmony_ci self.stats[func] = add_func_stats(old_func_stat, stat) 1937db96d56Sopenharmony_ci return self 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci def dump_stats(self, filename): 1967db96d56Sopenharmony_ci """Write the profile data to a file we know how to load back.""" 1977db96d56Sopenharmony_ci with open(filename, 'wb') as f: 1987db96d56Sopenharmony_ci marshal.dump(self.stats, f) 1997db96d56Sopenharmony_ci 2007db96d56Sopenharmony_ci # list the tuple indices and directions for sorting, 2017db96d56Sopenharmony_ci # along with some printable description 2027db96d56Sopenharmony_ci sort_arg_dict_default = { 2037db96d56Sopenharmony_ci "calls" : (((1,-1), ), "call count"), 2047db96d56Sopenharmony_ci "ncalls" : (((1,-1), ), "call count"), 2057db96d56Sopenharmony_ci "cumtime" : (((3,-1), ), "cumulative time"), 2067db96d56Sopenharmony_ci "cumulative": (((3,-1), ), "cumulative time"), 2077db96d56Sopenharmony_ci "filename" : (((4, 1), ), "file name"), 2087db96d56Sopenharmony_ci "line" : (((5, 1), ), "line number"), 2097db96d56Sopenharmony_ci "module" : (((4, 1), ), "file name"), 2107db96d56Sopenharmony_ci "name" : (((6, 1), ), "function name"), 2117db96d56Sopenharmony_ci "nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"), 2127db96d56Sopenharmony_ci "pcalls" : (((0,-1), ), "primitive call count"), 2137db96d56Sopenharmony_ci "stdname" : (((7, 1), ), "standard name"), 2147db96d56Sopenharmony_ci "time" : (((2,-1), ), "internal time"), 2157db96d56Sopenharmony_ci "tottime" : (((2,-1), ), "internal time"), 2167db96d56Sopenharmony_ci } 2177db96d56Sopenharmony_ci 2187db96d56Sopenharmony_ci def get_sort_arg_defs(self): 2197db96d56Sopenharmony_ci """Expand all abbreviations that are unique.""" 2207db96d56Sopenharmony_ci if not self.sort_arg_dict: 2217db96d56Sopenharmony_ci self.sort_arg_dict = dict = {} 2227db96d56Sopenharmony_ci bad_list = {} 2237db96d56Sopenharmony_ci for word, tup in self.sort_arg_dict_default.items(): 2247db96d56Sopenharmony_ci fragment = word 2257db96d56Sopenharmony_ci while fragment: 2267db96d56Sopenharmony_ci if not fragment: 2277db96d56Sopenharmony_ci break 2287db96d56Sopenharmony_ci if fragment in dict: 2297db96d56Sopenharmony_ci bad_list[fragment] = 0 2307db96d56Sopenharmony_ci break 2317db96d56Sopenharmony_ci dict[fragment] = tup 2327db96d56Sopenharmony_ci fragment = fragment[:-1] 2337db96d56Sopenharmony_ci for word in bad_list: 2347db96d56Sopenharmony_ci del dict[word] 2357db96d56Sopenharmony_ci return self.sort_arg_dict 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_ci def sort_stats(self, *field): 2387db96d56Sopenharmony_ci if not field: 2397db96d56Sopenharmony_ci self.fcn_list = 0 2407db96d56Sopenharmony_ci return self 2417db96d56Sopenharmony_ci if len(field) == 1 and isinstance(field[0], int): 2427db96d56Sopenharmony_ci # Be compatible with old profiler 2437db96d56Sopenharmony_ci field = [ {-1: "stdname", 2447db96d56Sopenharmony_ci 0: "calls", 2457db96d56Sopenharmony_ci 1: "time", 2467db96d56Sopenharmony_ci 2: "cumulative"}[field[0]] ] 2477db96d56Sopenharmony_ci elif len(field) >= 2: 2487db96d56Sopenharmony_ci for arg in field[1:]: 2497db96d56Sopenharmony_ci if type(arg) != type(field[0]): 2507db96d56Sopenharmony_ci raise TypeError("Can't have mixed argument type") 2517db96d56Sopenharmony_ci 2527db96d56Sopenharmony_ci sort_arg_defs = self.get_sort_arg_defs() 2537db96d56Sopenharmony_ci 2547db96d56Sopenharmony_ci sort_tuple = () 2557db96d56Sopenharmony_ci self.sort_type = "" 2567db96d56Sopenharmony_ci connector = "" 2577db96d56Sopenharmony_ci for word in field: 2587db96d56Sopenharmony_ci if isinstance(word, SortKey): 2597db96d56Sopenharmony_ci word = word.value 2607db96d56Sopenharmony_ci sort_tuple = sort_tuple + sort_arg_defs[word][0] 2617db96d56Sopenharmony_ci self.sort_type += connector + sort_arg_defs[word][1] 2627db96d56Sopenharmony_ci connector = ", " 2637db96d56Sopenharmony_ci 2647db96d56Sopenharmony_ci stats_list = [] 2657db96d56Sopenharmony_ci for func, (cc, nc, tt, ct, callers) in self.stats.items(): 2667db96d56Sopenharmony_ci stats_list.append((cc, nc, tt, ct) + func + 2677db96d56Sopenharmony_ci (func_std_string(func), func)) 2687db96d56Sopenharmony_ci 2697db96d56Sopenharmony_ci stats_list.sort(key=cmp_to_key(TupleComp(sort_tuple).compare)) 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_ci self.fcn_list = fcn_list = [] 2727db96d56Sopenharmony_ci for tuple in stats_list: 2737db96d56Sopenharmony_ci fcn_list.append(tuple[-1]) 2747db96d56Sopenharmony_ci return self 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ci def reverse_order(self): 2777db96d56Sopenharmony_ci if self.fcn_list: 2787db96d56Sopenharmony_ci self.fcn_list.reverse() 2797db96d56Sopenharmony_ci return self 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci def strip_dirs(self): 2827db96d56Sopenharmony_ci oldstats = self.stats 2837db96d56Sopenharmony_ci self.stats = newstats = {} 2847db96d56Sopenharmony_ci max_name_len = 0 2857db96d56Sopenharmony_ci for func, (cc, nc, tt, ct, callers) in oldstats.items(): 2867db96d56Sopenharmony_ci newfunc = func_strip_path(func) 2877db96d56Sopenharmony_ci if len(func_std_string(newfunc)) > max_name_len: 2887db96d56Sopenharmony_ci max_name_len = len(func_std_string(newfunc)) 2897db96d56Sopenharmony_ci newcallers = {} 2907db96d56Sopenharmony_ci for func2, caller in callers.items(): 2917db96d56Sopenharmony_ci newcallers[func_strip_path(func2)] = caller 2927db96d56Sopenharmony_ci 2937db96d56Sopenharmony_ci if newfunc in newstats: 2947db96d56Sopenharmony_ci newstats[newfunc] = add_func_stats( 2957db96d56Sopenharmony_ci newstats[newfunc], 2967db96d56Sopenharmony_ci (cc, nc, tt, ct, newcallers)) 2977db96d56Sopenharmony_ci else: 2987db96d56Sopenharmony_ci newstats[newfunc] = (cc, nc, tt, ct, newcallers) 2997db96d56Sopenharmony_ci old_top = self.top_level 3007db96d56Sopenharmony_ci self.top_level = new_top = set() 3017db96d56Sopenharmony_ci for func in old_top: 3027db96d56Sopenharmony_ci new_top.add(func_strip_path(func)) 3037db96d56Sopenharmony_ci 3047db96d56Sopenharmony_ci self.max_name_len = max_name_len 3057db96d56Sopenharmony_ci 3067db96d56Sopenharmony_ci self.fcn_list = None 3077db96d56Sopenharmony_ci self.all_callees = None 3087db96d56Sopenharmony_ci return self 3097db96d56Sopenharmony_ci 3107db96d56Sopenharmony_ci def calc_callees(self): 3117db96d56Sopenharmony_ci if self.all_callees: 3127db96d56Sopenharmony_ci return 3137db96d56Sopenharmony_ci self.all_callees = all_callees = {} 3147db96d56Sopenharmony_ci for func, (cc, nc, tt, ct, callers) in self.stats.items(): 3157db96d56Sopenharmony_ci if not func in all_callees: 3167db96d56Sopenharmony_ci all_callees[func] = {} 3177db96d56Sopenharmony_ci for func2, caller in callers.items(): 3187db96d56Sopenharmony_ci if not func2 in all_callees: 3197db96d56Sopenharmony_ci all_callees[func2] = {} 3207db96d56Sopenharmony_ci all_callees[func2][func] = caller 3217db96d56Sopenharmony_ci return 3227db96d56Sopenharmony_ci 3237db96d56Sopenharmony_ci #****************************************************************** 3247db96d56Sopenharmony_ci # The following functions support actual printing of reports 3257db96d56Sopenharmony_ci #****************************************************************** 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci # Optional "amount" is either a line count, or a percentage of lines. 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_ci def eval_print_amount(self, sel, list, msg): 3307db96d56Sopenharmony_ci new_list = list 3317db96d56Sopenharmony_ci if isinstance(sel, str): 3327db96d56Sopenharmony_ci try: 3337db96d56Sopenharmony_ci rex = re.compile(sel) 3347db96d56Sopenharmony_ci except re.error: 3357db96d56Sopenharmony_ci msg += " <Invalid regular expression %r>\n" % sel 3367db96d56Sopenharmony_ci return new_list, msg 3377db96d56Sopenharmony_ci new_list = [] 3387db96d56Sopenharmony_ci for func in list: 3397db96d56Sopenharmony_ci if rex.search(func_std_string(func)): 3407db96d56Sopenharmony_ci new_list.append(func) 3417db96d56Sopenharmony_ci else: 3427db96d56Sopenharmony_ci count = len(list) 3437db96d56Sopenharmony_ci if isinstance(sel, float) and 0.0 <= sel < 1.0: 3447db96d56Sopenharmony_ci count = int(count * sel + .5) 3457db96d56Sopenharmony_ci new_list = list[:count] 3467db96d56Sopenharmony_ci elif isinstance(sel, int) and 0 <= sel < count: 3477db96d56Sopenharmony_ci count = sel 3487db96d56Sopenharmony_ci new_list = list[:count] 3497db96d56Sopenharmony_ci if len(list) != len(new_list): 3507db96d56Sopenharmony_ci msg += " List reduced from %r to %r due to restriction <%r>\n" % ( 3517db96d56Sopenharmony_ci len(list), len(new_list), sel) 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ci return new_list, msg 3547db96d56Sopenharmony_ci 3557db96d56Sopenharmony_ci def get_stats_profile(self): 3567db96d56Sopenharmony_ci """This method returns an instance of StatsProfile, which contains a mapping 3577db96d56Sopenharmony_ci of function names to instances of FunctionProfile. Each FunctionProfile 3587db96d56Sopenharmony_ci instance holds information related to the function's profile such as how 3597db96d56Sopenharmony_ci long the function took to run, how many times it was called, etc... 3607db96d56Sopenharmony_ci """ 3617db96d56Sopenharmony_ci func_list = self.fcn_list[:] if self.fcn_list else list(self.stats.keys()) 3627db96d56Sopenharmony_ci if not func_list: 3637db96d56Sopenharmony_ci return StatsProfile(0, {}) 3647db96d56Sopenharmony_ci 3657db96d56Sopenharmony_ci total_tt = float(f8(self.total_tt)) 3667db96d56Sopenharmony_ci func_profiles = {} 3677db96d56Sopenharmony_ci stats_profile = StatsProfile(total_tt, func_profiles) 3687db96d56Sopenharmony_ci 3697db96d56Sopenharmony_ci for func in func_list: 3707db96d56Sopenharmony_ci cc, nc, tt, ct, callers = self.stats[func] 3717db96d56Sopenharmony_ci file_name, line_number, func_name = func 3727db96d56Sopenharmony_ci ncalls = str(nc) if nc == cc else (str(nc) + '/' + str(cc)) 3737db96d56Sopenharmony_ci tottime = float(f8(tt)) 3747db96d56Sopenharmony_ci percall_tottime = -1 if nc == 0 else float(f8(tt/nc)) 3757db96d56Sopenharmony_ci cumtime = float(f8(ct)) 3767db96d56Sopenharmony_ci percall_cumtime = -1 if cc == 0 else float(f8(ct/cc)) 3777db96d56Sopenharmony_ci func_profile = FunctionProfile( 3787db96d56Sopenharmony_ci ncalls, 3797db96d56Sopenharmony_ci tottime, # time spent in this function alone 3807db96d56Sopenharmony_ci percall_tottime, 3817db96d56Sopenharmony_ci cumtime, # time spent in the function plus all functions that this function called, 3827db96d56Sopenharmony_ci percall_cumtime, 3837db96d56Sopenharmony_ci file_name, 3847db96d56Sopenharmony_ci line_number 3857db96d56Sopenharmony_ci ) 3867db96d56Sopenharmony_ci func_profiles[func_name] = func_profile 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ci return stats_profile 3897db96d56Sopenharmony_ci 3907db96d56Sopenharmony_ci def get_print_list(self, sel_list): 3917db96d56Sopenharmony_ci width = self.max_name_len 3927db96d56Sopenharmony_ci if self.fcn_list: 3937db96d56Sopenharmony_ci stat_list = self.fcn_list[:] 3947db96d56Sopenharmony_ci msg = " Ordered by: " + self.sort_type + '\n' 3957db96d56Sopenharmony_ci else: 3967db96d56Sopenharmony_ci stat_list = list(self.stats.keys()) 3977db96d56Sopenharmony_ci msg = " Random listing order was used\n" 3987db96d56Sopenharmony_ci 3997db96d56Sopenharmony_ci for selection in sel_list: 4007db96d56Sopenharmony_ci stat_list, msg = self.eval_print_amount(selection, stat_list, msg) 4017db96d56Sopenharmony_ci 4027db96d56Sopenharmony_ci count = len(stat_list) 4037db96d56Sopenharmony_ci 4047db96d56Sopenharmony_ci if not stat_list: 4057db96d56Sopenharmony_ci return 0, stat_list 4067db96d56Sopenharmony_ci print(msg, file=self.stream) 4077db96d56Sopenharmony_ci if count < len(self.stats): 4087db96d56Sopenharmony_ci width = 0 4097db96d56Sopenharmony_ci for func in stat_list: 4107db96d56Sopenharmony_ci if len(func_std_string(func)) > width: 4117db96d56Sopenharmony_ci width = len(func_std_string(func)) 4127db96d56Sopenharmony_ci return width+2, stat_list 4137db96d56Sopenharmony_ci 4147db96d56Sopenharmony_ci def print_stats(self, *amount): 4157db96d56Sopenharmony_ci for filename in self.files: 4167db96d56Sopenharmony_ci print(filename, file=self.stream) 4177db96d56Sopenharmony_ci if self.files: 4187db96d56Sopenharmony_ci print(file=self.stream) 4197db96d56Sopenharmony_ci indent = ' ' * 8 4207db96d56Sopenharmony_ci for func in self.top_level: 4217db96d56Sopenharmony_ci print(indent, func_get_function_name(func), file=self.stream) 4227db96d56Sopenharmony_ci 4237db96d56Sopenharmony_ci print(indent, self.total_calls, "function calls", end=' ', file=self.stream) 4247db96d56Sopenharmony_ci if self.total_calls != self.prim_calls: 4257db96d56Sopenharmony_ci print("(%d primitive calls)" % self.prim_calls, end=' ', file=self.stream) 4267db96d56Sopenharmony_ci print("in %.3f seconds" % self.total_tt, file=self.stream) 4277db96d56Sopenharmony_ci print(file=self.stream) 4287db96d56Sopenharmony_ci width, list = self.get_print_list(amount) 4297db96d56Sopenharmony_ci if list: 4307db96d56Sopenharmony_ci self.print_title() 4317db96d56Sopenharmony_ci for func in list: 4327db96d56Sopenharmony_ci self.print_line(func) 4337db96d56Sopenharmony_ci print(file=self.stream) 4347db96d56Sopenharmony_ci print(file=self.stream) 4357db96d56Sopenharmony_ci return self 4367db96d56Sopenharmony_ci 4377db96d56Sopenharmony_ci def print_callees(self, *amount): 4387db96d56Sopenharmony_ci width, list = self.get_print_list(amount) 4397db96d56Sopenharmony_ci if list: 4407db96d56Sopenharmony_ci self.calc_callees() 4417db96d56Sopenharmony_ci 4427db96d56Sopenharmony_ci self.print_call_heading(width, "called...") 4437db96d56Sopenharmony_ci for func in list: 4447db96d56Sopenharmony_ci if func in self.all_callees: 4457db96d56Sopenharmony_ci self.print_call_line(width, func, self.all_callees[func]) 4467db96d56Sopenharmony_ci else: 4477db96d56Sopenharmony_ci self.print_call_line(width, func, {}) 4487db96d56Sopenharmony_ci print(file=self.stream) 4497db96d56Sopenharmony_ci print(file=self.stream) 4507db96d56Sopenharmony_ci return self 4517db96d56Sopenharmony_ci 4527db96d56Sopenharmony_ci def print_callers(self, *amount): 4537db96d56Sopenharmony_ci width, list = self.get_print_list(amount) 4547db96d56Sopenharmony_ci if list: 4557db96d56Sopenharmony_ci self.print_call_heading(width, "was called by...") 4567db96d56Sopenharmony_ci for func in list: 4577db96d56Sopenharmony_ci cc, nc, tt, ct, callers = self.stats[func] 4587db96d56Sopenharmony_ci self.print_call_line(width, func, callers, "<-") 4597db96d56Sopenharmony_ci print(file=self.stream) 4607db96d56Sopenharmony_ci print(file=self.stream) 4617db96d56Sopenharmony_ci return self 4627db96d56Sopenharmony_ci 4637db96d56Sopenharmony_ci def print_call_heading(self, name_size, column_title): 4647db96d56Sopenharmony_ci print("Function ".ljust(name_size) + column_title, file=self.stream) 4657db96d56Sopenharmony_ci # print sub-header only if we have new-style callers 4667db96d56Sopenharmony_ci subheader = False 4677db96d56Sopenharmony_ci for cc, nc, tt, ct, callers in self.stats.values(): 4687db96d56Sopenharmony_ci if callers: 4697db96d56Sopenharmony_ci value = next(iter(callers.values())) 4707db96d56Sopenharmony_ci subheader = isinstance(value, tuple) 4717db96d56Sopenharmony_ci break 4727db96d56Sopenharmony_ci if subheader: 4737db96d56Sopenharmony_ci print(" "*name_size + " ncalls tottime cumtime", file=self.stream) 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ci def print_call_line(self, name_size, source, call_dict, arrow="->"): 4767db96d56Sopenharmony_ci print(func_std_string(source).ljust(name_size) + arrow, end=' ', file=self.stream) 4777db96d56Sopenharmony_ci if not call_dict: 4787db96d56Sopenharmony_ci print(file=self.stream) 4797db96d56Sopenharmony_ci return 4807db96d56Sopenharmony_ci clist = sorted(call_dict.keys()) 4817db96d56Sopenharmony_ci indent = "" 4827db96d56Sopenharmony_ci for func in clist: 4837db96d56Sopenharmony_ci name = func_std_string(func) 4847db96d56Sopenharmony_ci value = call_dict[func] 4857db96d56Sopenharmony_ci if isinstance(value, tuple): 4867db96d56Sopenharmony_ci nc, cc, tt, ct = value 4877db96d56Sopenharmony_ci if nc != cc: 4887db96d56Sopenharmony_ci substats = '%d/%d' % (nc, cc) 4897db96d56Sopenharmony_ci else: 4907db96d56Sopenharmony_ci substats = '%d' % (nc,) 4917db96d56Sopenharmony_ci substats = '%s %s %s %s' % (substats.rjust(7+2*len(indent)), 4927db96d56Sopenharmony_ci f8(tt), f8(ct), name) 4937db96d56Sopenharmony_ci left_width = name_size + 1 4947db96d56Sopenharmony_ci else: 4957db96d56Sopenharmony_ci substats = '%s(%r) %s' % (name, value, f8(self.stats[func][3])) 4967db96d56Sopenharmony_ci left_width = name_size + 3 4977db96d56Sopenharmony_ci print(indent*left_width + substats, file=self.stream) 4987db96d56Sopenharmony_ci indent = " " 4997db96d56Sopenharmony_ci 5007db96d56Sopenharmony_ci def print_title(self): 5017db96d56Sopenharmony_ci print(' ncalls tottime percall cumtime percall', end=' ', file=self.stream) 5027db96d56Sopenharmony_ci print('filename:lineno(function)', file=self.stream) 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ci def print_line(self, func): # hack: should print percentages 5057db96d56Sopenharmony_ci cc, nc, tt, ct, callers = self.stats[func] 5067db96d56Sopenharmony_ci c = str(nc) 5077db96d56Sopenharmony_ci if nc != cc: 5087db96d56Sopenharmony_ci c = c + '/' + str(cc) 5097db96d56Sopenharmony_ci print(c.rjust(9), end=' ', file=self.stream) 5107db96d56Sopenharmony_ci print(f8(tt), end=' ', file=self.stream) 5117db96d56Sopenharmony_ci if nc == 0: 5127db96d56Sopenharmony_ci print(' '*8, end=' ', file=self.stream) 5137db96d56Sopenharmony_ci else: 5147db96d56Sopenharmony_ci print(f8(tt/nc), end=' ', file=self.stream) 5157db96d56Sopenharmony_ci print(f8(ct), end=' ', file=self.stream) 5167db96d56Sopenharmony_ci if cc == 0: 5177db96d56Sopenharmony_ci print(' '*8, end=' ', file=self.stream) 5187db96d56Sopenharmony_ci else: 5197db96d56Sopenharmony_ci print(f8(ct/cc), end=' ', file=self.stream) 5207db96d56Sopenharmony_ci print(func_std_string(func), file=self.stream) 5217db96d56Sopenharmony_ci 5227db96d56Sopenharmony_ciclass TupleComp: 5237db96d56Sopenharmony_ci """This class provides a generic function for comparing any two tuples. 5247db96d56Sopenharmony_ci Each instance records a list of tuple-indices (from most significant 5257db96d56Sopenharmony_ci to least significant), and sort direction (ascending or descending) for 5267db96d56Sopenharmony_ci each tuple-index. The compare functions can then be used as the function 5277db96d56Sopenharmony_ci argument to the system sort() function when a list of tuples need to be 5287db96d56Sopenharmony_ci sorted in the instances order.""" 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci def __init__(self, comp_select_list): 5317db96d56Sopenharmony_ci self.comp_select_list = comp_select_list 5327db96d56Sopenharmony_ci 5337db96d56Sopenharmony_ci def compare (self, left, right): 5347db96d56Sopenharmony_ci for index, direction in self.comp_select_list: 5357db96d56Sopenharmony_ci l = left[index] 5367db96d56Sopenharmony_ci r = right[index] 5377db96d56Sopenharmony_ci if l < r: 5387db96d56Sopenharmony_ci return -direction 5397db96d56Sopenharmony_ci if l > r: 5407db96d56Sopenharmony_ci return direction 5417db96d56Sopenharmony_ci return 0 5427db96d56Sopenharmony_ci 5437db96d56Sopenharmony_ci 5447db96d56Sopenharmony_ci#************************************************************************** 5457db96d56Sopenharmony_ci# func_name is a triple (file:string, line:int, name:string) 5467db96d56Sopenharmony_ci 5477db96d56Sopenharmony_cidef func_strip_path(func_name): 5487db96d56Sopenharmony_ci filename, line, name = func_name 5497db96d56Sopenharmony_ci return os.path.basename(filename), line, name 5507db96d56Sopenharmony_ci 5517db96d56Sopenharmony_cidef func_get_function_name(func): 5527db96d56Sopenharmony_ci return func[2] 5537db96d56Sopenharmony_ci 5547db96d56Sopenharmony_cidef func_std_string(func_name): # match what old profile produced 5557db96d56Sopenharmony_ci if func_name[:2] == ('~', 0): 5567db96d56Sopenharmony_ci # special case for built-in functions 5577db96d56Sopenharmony_ci name = func_name[2] 5587db96d56Sopenharmony_ci if name.startswith('<') and name.endswith('>'): 5597db96d56Sopenharmony_ci return '{%s}' % name[1:-1] 5607db96d56Sopenharmony_ci else: 5617db96d56Sopenharmony_ci return name 5627db96d56Sopenharmony_ci else: 5637db96d56Sopenharmony_ci return "%s:%d(%s)" % func_name 5647db96d56Sopenharmony_ci 5657db96d56Sopenharmony_ci#************************************************************************** 5667db96d56Sopenharmony_ci# The following functions combine statistics for pairs functions. 5677db96d56Sopenharmony_ci# The bulk of the processing involves correctly handling "call" lists, 5687db96d56Sopenharmony_ci# such as callers and callees. 5697db96d56Sopenharmony_ci#************************************************************************** 5707db96d56Sopenharmony_ci 5717db96d56Sopenharmony_cidef add_func_stats(target, source): 5727db96d56Sopenharmony_ci """Add together all the stats for two profile entries.""" 5737db96d56Sopenharmony_ci cc, nc, tt, ct, callers = source 5747db96d56Sopenharmony_ci t_cc, t_nc, t_tt, t_ct, t_callers = target 5757db96d56Sopenharmony_ci return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct, 5767db96d56Sopenharmony_ci add_callers(t_callers, callers)) 5777db96d56Sopenharmony_ci 5787db96d56Sopenharmony_cidef add_callers(target, source): 5797db96d56Sopenharmony_ci """Combine two caller lists in a single list.""" 5807db96d56Sopenharmony_ci new_callers = {} 5817db96d56Sopenharmony_ci for func, caller in target.items(): 5827db96d56Sopenharmony_ci new_callers[func] = caller 5837db96d56Sopenharmony_ci for func, caller in source.items(): 5847db96d56Sopenharmony_ci if func in new_callers: 5857db96d56Sopenharmony_ci if isinstance(caller, tuple): 5867db96d56Sopenharmony_ci # format used by cProfile 5877db96d56Sopenharmony_ci new_callers[func] = tuple(i + j for i, j in zip(caller, new_callers[func])) 5887db96d56Sopenharmony_ci else: 5897db96d56Sopenharmony_ci # format used by profile 5907db96d56Sopenharmony_ci new_callers[func] += caller 5917db96d56Sopenharmony_ci else: 5927db96d56Sopenharmony_ci new_callers[func] = caller 5937db96d56Sopenharmony_ci return new_callers 5947db96d56Sopenharmony_ci 5957db96d56Sopenharmony_cidef count_calls(callers): 5967db96d56Sopenharmony_ci """Sum the caller statistics to get total number of calls received.""" 5977db96d56Sopenharmony_ci nc = 0 5987db96d56Sopenharmony_ci for calls in callers.values(): 5997db96d56Sopenharmony_ci nc += calls 6007db96d56Sopenharmony_ci return nc 6017db96d56Sopenharmony_ci 6027db96d56Sopenharmony_ci#************************************************************************** 6037db96d56Sopenharmony_ci# The following functions support printing of reports 6047db96d56Sopenharmony_ci#************************************************************************** 6057db96d56Sopenharmony_ci 6067db96d56Sopenharmony_cidef f8(x): 6077db96d56Sopenharmony_ci return "%8.3f" % x 6087db96d56Sopenharmony_ci 6097db96d56Sopenharmony_ci#************************************************************************** 6107db96d56Sopenharmony_ci# Statistics browser added by ESR, April 2001 6117db96d56Sopenharmony_ci#************************************************************************** 6127db96d56Sopenharmony_ci 6137db96d56Sopenharmony_ciif __name__ == '__main__': 6147db96d56Sopenharmony_ci import cmd 6157db96d56Sopenharmony_ci try: 6167db96d56Sopenharmony_ci import readline 6177db96d56Sopenharmony_ci except ImportError: 6187db96d56Sopenharmony_ci pass 6197db96d56Sopenharmony_ci 6207db96d56Sopenharmony_ci class ProfileBrowser(cmd.Cmd): 6217db96d56Sopenharmony_ci def __init__(self, profile=None): 6227db96d56Sopenharmony_ci cmd.Cmd.__init__(self) 6237db96d56Sopenharmony_ci self.prompt = "% " 6247db96d56Sopenharmony_ci self.stats = None 6257db96d56Sopenharmony_ci self.stream = sys.stdout 6267db96d56Sopenharmony_ci if profile is not None: 6277db96d56Sopenharmony_ci self.do_read(profile) 6287db96d56Sopenharmony_ci 6297db96d56Sopenharmony_ci def generic(self, fn, line): 6307db96d56Sopenharmony_ci args = line.split() 6317db96d56Sopenharmony_ci processed = [] 6327db96d56Sopenharmony_ci for term in args: 6337db96d56Sopenharmony_ci try: 6347db96d56Sopenharmony_ci processed.append(int(term)) 6357db96d56Sopenharmony_ci continue 6367db96d56Sopenharmony_ci except ValueError: 6377db96d56Sopenharmony_ci pass 6387db96d56Sopenharmony_ci try: 6397db96d56Sopenharmony_ci frac = float(term) 6407db96d56Sopenharmony_ci if frac > 1 or frac < 0: 6417db96d56Sopenharmony_ci print("Fraction argument must be in [0, 1]", file=self.stream) 6427db96d56Sopenharmony_ci continue 6437db96d56Sopenharmony_ci processed.append(frac) 6447db96d56Sopenharmony_ci continue 6457db96d56Sopenharmony_ci except ValueError: 6467db96d56Sopenharmony_ci pass 6477db96d56Sopenharmony_ci processed.append(term) 6487db96d56Sopenharmony_ci if self.stats: 6497db96d56Sopenharmony_ci getattr(self.stats, fn)(*processed) 6507db96d56Sopenharmony_ci else: 6517db96d56Sopenharmony_ci print("No statistics object is loaded.", file=self.stream) 6527db96d56Sopenharmony_ci return 0 6537db96d56Sopenharmony_ci def generic_help(self): 6547db96d56Sopenharmony_ci print("Arguments may be:", file=self.stream) 6557db96d56Sopenharmony_ci print("* An integer maximum number of entries to print.", file=self.stream) 6567db96d56Sopenharmony_ci print("* A decimal fractional number between 0 and 1, controlling", file=self.stream) 6577db96d56Sopenharmony_ci print(" what fraction of selected entries to print.", file=self.stream) 6587db96d56Sopenharmony_ci print("* A regular expression; only entries with function names", file=self.stream) 6597db96d56Sopenharmony_ci print(" that match it are printed.", file=self.stream) 6607db96d56Sopenharmony_ci 6617db96d56Sopenharmony_ci def do_add(self, line): 6627db96d56Sopenharmony_ci if self.stats: 6637db96d56Sopenharmony_ci try: 6647db96d56Sopenharmony_ci self.stats.add(line) 6657db96d56Sopenharmony_ci except OSError as e: 6667db96d56Sopenharmony_ci print("Failed to load statistics for %s: %s" % (line, e), file=self.stream) 6677db96d56Sopenharmony_ci else: 6687db96d56Sopenharmony_ci print("No statistics object is loaded.", file=self.stream) 6697db96d56Sopenharmony_ci return 0 6707db96d56Sopenharmony_ci def help_add(self): 6717db96d56Sopenharmony_ci print("Add profile info from given file to current statistics object.", file=self.stream) 6727db96d56Sopenharmony_ci 6737db96d56Sopenharmony_ci def do_callees(self, line): 6747db96d56Sopenharmony_ci return self.generic('print_callees', line) 6757db96d56Sopenharmony_ci def help_callees(self): 6767db96d56Sopenharmony_ci print("Print callees statistics from the current stat object.", file=self.stream) 6777db96d56Sopenharmony_ci self.generic_help() 6787db96d56Sopenharmony_ci 6797db96d56Sopenharmony_ci def do_callers(self, line): 6807db96d56Sopenharmony_ci return self.generic('print_callers', line) 6817db96d56Sopenharmony_ci def help_callers(self): 6827db96d56Sopenharmony_ci print("Print callers statistics from the current stat object.", file=self.stream) 6837db96d56Sopenharmony_ci self.generic_help() 6847db96d56Sopenharmony_ci 6857db96d56Sopenharmony_ci def do_EOF(self, line): 6867db96d56Sopenharmony_ci print("", file=self.stream) 6877db96d56Sopenharmony_ci return 1 6887db96d56Sopenharmony_ci def help_EOF(self): 6897db96d56Sopenharmony_ci print("Leave the profile browser.", file=self.stream) 6907db96d56Sopenharmony_ci 6917db96d56Sopenharmony_ci def do_quit(self, line): 6927db96d56Sopenharmony_ci return 1 6937db96d56Sopenharmony_ci def help_quit(self): 6947db96d56Sopenharmony_ci print("Leave the profile browser.", file=self.stream) 6957db96d56Sopenharmony_ci 6967db96d56Sopenharmony_ci def do_read(self, line): 6977db96d56Sopenharmony_ci if line: 6987db96d56Sopenharmony_ci try: 6997db96d56Sopenharmony_ci self.stats = Stats(line) 7007db96d56Sopenharmony_ci except OSError as err: 7017db96d56Sopenharmony_ci print(err.args[1], file=self.stream) 7027db96d56Sopenharmony_ci return 7037db96d56Sopenharmony_ci except Exception as err: 7047db96d56Sopenharmony_ci print(err.__class__.__name__ + ':', err, file=self.stream) 7057db96d56Sopenharmony_ci return 7067db96d56Sopenharmony_ci self.prompt = line + "% " 7077db96d56Sopenharmony_ci elif len(self.prompt) > 2: 7087db96d56Sopenharmony_ci line = self.prompt[:-2] 7097db96d56Sopenharmony_ci self.do_read(line) 7107db96d56Sopenharmony_ci else: 7117db96d56Sopenharmony_ci print("No statistics object is current -- cannot reload.", file=self.stream) 7127db96d56Sopenharmony_ci return 0 7137db96d56Sopenharmony_ci def help_read(self): 7147db96d56Sopenharmony_ci print("Read in profile data from a specified file.", file=self.stream) 7157db96d56Sopenharmony_ci print("Without argument, reload the current file.", file=self.stream) 7167db96d56Sopenharmony_ci 7177db96d56Sopenharmony_ci def do_reverse(self, line): 7187db96d56Sopenharmony_ci if self.stats: 7197db96d56Sopenharmony_ci self.stats.reverse_order() 7207db96d56Sopenharmony_ci else: 7217db96d56Sopenharmony_ci print("No statistics object is loaded.", file=self.stream) 7227db96d56Sopenharmony_ci return 0 7237db96d56Sopenharmony_ci def help_reverse(self): 7247db96d56Sopenharmony_ci print("Reverse the sort order of the profiling report.", file=self.stream) 7257db96d56Sopenharmony_ci 7267db96d56Sopenharmony_ci def do_sort(self, line): 7277db96d56Sopenharmony_ci if not self.stats: 7287db96d56Sopenharmony_ci print("No statistics object is loaded.", file=self.stream) 7297db96d56Sopenharmony_ci return 7307db96d56Sopenharmony_ci abbrevs = self.stats.get_sort_arg_defs() 7317db96d56Sopenharmony_ci if line and all((x in abbrevs) for x in line.split()): 7327db96d56Sopenharmony_ci self.stats.sort_stats(*line.split()) 7337db96d56Sopenharmony_ci else: 7347db96d56Sopenharmony_ci print("Valid sort keys (unique prefixes are accepted):", file=self.stream) 7357db96d56Sopenharmony_ci for (key, value) in Stats.sort_arg_dict_default.items(): 7367db96d56Sopenharmony_ci print("%s -- %s" % (key, value[1]), file=self.stream) 7377db96d56Sopenharmony_ci return 0 7387db96d56Sopenharmony_ci def help_sort(self): 7397db96d56Sopenharmony_ci print("Sort profile data according to specified keys.", file=self.stream) 7407db96d56Sopenharmony_ci print("(Typing `sort' without arguments lists valid keys.)", file=self.stream) 7417db96d56Sopenharmony_ci def complete_sort(self, text, *args): 7427db96d56Sopenharmony_ci return [a for a in Stats.sort_arg_dict_default if a.startswith(text)] 7437db96d56Sopenharmony_ci 7447db96d56Sopenharmony_ci def do_stats(self, line): 7457db96d56Sopenharmony_ci return self.generic('print_stats', line) 7467db96d56Sopenharmony_ci def help_stats(self): 7477db96d56Sopenharmony_ci print("Print statistics from the current stat object.", file=self.stream) 7487db96d56Sopenharmony_ci self.generic_help() 7497db96d56Sopenharmony_ci 7507db96d56Sopenharmony_ci def do_strip(self, line): 7517db96d56Sopenharmony_ci if self.stats: 7527db96d56Sopenharmony_ci self.stats.strip_dirs() 7537db96d56Sopenharmony_ci else: 7547db96d56Sopenharmony_ci print("No statistics object is loaded.", file=self.stream) 7557db96d56Sopenharmony_ci def help_strip(self): 7567db96d56Sopenharmony_ci print("Strip leading path information from filenames in the report.", file=self.stream) 7577db96d56Sopenharmony_ci 7587db96d56Sopenharmony_ci def help_help(self): 7597db96d56Sopenharmony_ci print("Show help for a given command.", file=self.stream) 7607db96d56Sopenharmony_ci 7617db96d56Sopenharmony_ci def postcmd(self, stop, line): 7627db96d56Sopenharmony_ci if stop: 7637db96d56Sopenharmony_ci return stop 7647db96d56Sopenharmony_ci return None 7657db96d56Sopenharmony_ci 7667db96d56Sopenharmony_ci if len(sys.argv) > 1: 7677db96d56Sopenharmony_ci initprofile = sys.argv[1] 7687db96d56Sopenharmony_ci else: 7697db96d56Sopenharmony_ci initprofile = None 7707db96d56Sopenharmony_ci try: 7717db96d56Sopenharmony_ci browser = ProfileBrowser(initprofile) 7727db96d56Sopenharmony_ci for profile in sys.argv[2:]: 7737db96d56Sopenharmony_ci browser.do_add(profile) 7747db96d56Sopenharmony_ci print("Welcome to the profile statistics browser.", file=browser.stream) 7757db96d56Sopenharmony_ci browser.cmdloop() 7767db96d56Sopenharmony_ci print("Goodbye.", file=browser.stream) 7777db96d56Sopenharmony_ci except KeyboardInterrupt: 7787db96d56Sopenharmony_ci pass 7797db96d56Sopenharmony_ci 7807db96d56Sopenharmony_ci# That's all, folks. 781