| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 1 | """Class for printing reports on profiled python code.""" | 
 | 2 |  | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 3 | # Class for printing reports on profiled python code. rev 1.0  4/1/94 | 
 | 4 | # | 
 | 5 | # Based on prior profile module by Sjoerd Mullender... | 
 | 6 | #   which was hacked somewhat by: Guido van Rossum | 
 | 7 | # | 
| Guido van Rossum | dabcd00 | 1999-04-13 04:24:22 +0000 | [diff] [blame] | 8 | # see profile.doc and profile.py for more info. | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 9 |  | 
 | 10 | # Copyright 1994, by InfoSeek Corporation, all rights reserved. | 
 | 11 | # Written by James Roskind | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 12 | # | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 13 | # Permission to use, copy, modify, and distribute this Python software | 
 | 14 | # and its associated documentation for any purpose (subject to the | 
 | 15 | # restriction in the following sentence) without fee is hereby granted, | 
 | 16 | # provided that the above copyright notice appears in all copies, and | 
 | 17 | # that both that copyright notice and this permission notice appear in | 
 | 18 | # supporting documentation, and that the name of InfoSeek not be used in | 
 | 19 | # advertising or publicity pertaining to distribution of the software | 
 | 20 | # without specific, written prior permission.  This permission is | 
 | 21 | # explicitly restricted to the copying and modification of the software | 
 | 22 | # to remain in Python, compiled Python, or other languages (such as C) | 
 | 23 | # wherein the modified or derived code is exclusively imported into a | 
 | 24 | # Python module. | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 25 | # | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 26 | # INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS | 
 | 27 | # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | 
 | 28 | # FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY | 
 | 29 | # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | 
 | 30 | # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | 
 | 31 | # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | 
 | 32 | # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
 | 33 |  | 
 | 34 |  | 
 | 35 | import os | 
 | 36 | import time | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 37 | import marshal | 
| Guido van Rossum | 9694fca | 1997-10-22 21:00:49 +0000 | [diff] [blame] | 38 | import re | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 39 |  | 
| Skip Montanaro | c62c81e | 2001-02-12 02:00:42 +0000 | [diff] [blame] | 40 | __all__ = ["Stats"] | 
 | 41 |  | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 42 | class Stats: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 43 |     """This class is used for creating reports from data generated by the | 
 | 44 |     Profile class.  It is a "friend" of that class, and imports data either | 
 | 45 |     by direct access to members of Profile class, or by reading in a dictionary | 
 | 46 |     that was emitted (via marshal) from the Profile class. | 
| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 47 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 48 |     The big change from the previous Profiler (in terms of raw functionality) | 
 | 49 |     is that an "add()" method has been provided to combine Stats from | 
 | 50 |     several distinct profile runs.  Both the constructor and the add() | 
 | 51 |     method now take arbitrarily many file names as arguments. | 
| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 52 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 53 |     All the print methods now take an argument that indicates how many lines | 
 | 54 |     to print.  If the arg is a floating point number between 0 and 1.0, then | 
 | 55 |     it is taken as a decimal percentage of the available lines to be printed | 
 | 56 |     (e.g., .1 means print 10% of all available lines).  If it is an integer, | 
 | 57 |     it is taken to mean the number of lines of data that you wish to have | 
 | 58 |     printed. | 
| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 59 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 60 |     The sort_stats() method now processes some additional options (i.e., in | 
 | 61 |     addition to the old -1, 0, 1, or 2).  It takes an arbitrary number of quoted | 
 | 62 |     strings to select the sort order.  For example sort_stats('time', 'name') | 
 | 63 |     sorts on the major key of "internal function time", and on the minor | 
 | 64 |     key of 'the name of the function'.  Look at the two tables in sort_stats() | 
 | 65 |     and get_sort_arg_defs(self) for more examples. | 
| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 66 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 67 |     All methods now return "self",  so you can string together commands like: | 
 | 68 |         Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ | 
 | 69 |                             print_stats(5).print_callers(5) | 
 | 70 |     """ | 
 | 71 |  | 
 | 72 |     def __init__(self, *args): | 
 | 73 |         if not len(args): | 
 | 74 |             arg = None | 
 | 75 |         else: | 
 | 76 |             arg = args[0] | 
 | 77 |             args = args[1:] | 
 | 78 |         self.init(arg) | 
| Guido van Rossum | 68468eb | 2003-02-27 20:14:51 +0000 | [diff] [blame] | 79 |         self.add(*args) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 80 |  | 
 | 81 |     def init(self, arg): | 
 | 82 |         self.all_callees = None  # calc only if needed | 
 | 83 |         self.files = [] | 
 | 84 |         self.fcn_list = None | 
 | 85 |         self.total_tt = 0 | 
 | 86 |         self.total_calls = 0 | 
 | 87 |         self.prim_calls = 0 | 
 | 88 |         self.max_name_len = 0 | 
 | 89 |         self.top_level = {} | 
 | 90 |         self.stats = {} | 
 | 91 |         self.sort_arg_dict = {} | 
 | 92 |         self.load_stats(arg) | 
 | 93 |         trouble = 1 | 
 | 94 |         try: | 
 | 95 |             self.get_top_level_stats() | 
 | 96 |             trouble = 0 | 
 | 97 |         finally: | 
 | 98 |             if trouble: | 
 | 99 |                 print "Invalid timing data", | 
 | 100 |                 if self.files: print self.files[-1], | 
 | 101 |                 print | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 102 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 103 |     def load_stats(self, arg): | 
 | 104 |         if not arg:  self.stats = {} | 
 | 105 |         elif type(arg) == type(""): | 
 | 106 |             f = open(arg, 'rb') | 
 | 107 |             self.stats = marshal.load(f) | 
 | 108 |             f.close() | 
 | 109 |             try: | 
 | 110 |                 file_stats = os.stat(arg) | 
| Raymond Hettinger | 32200ae | 2002-06-01 19:51:15 +0000 | [diff] [blame] | 111 |                 arg = time.ctime(file_stats.st_mtime) + "    " + arg | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 112 |             except:  # in case this is not unix | 
 | 113 |                 pass | 
 | 114 |             self.files = [ arg ] | 
 | 115 |         elif hasattr(arg, 'create_stats'): | 
 | 116 |             arg.create_stats() | 
 | 117 |             self.stats = arg.stats | 
 | 118 |             arg.stats = {} | 
 | 119 |         if not self.stats: | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 120 |             raise TypeError,  "Cannot create or construct a %r object from '%r''" % ( | 
 | 121 |                               self.__class__, arg) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 122 |         return | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 123 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 124 |     def get_top_level_stats(self): | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 125 |         for func, (cc, nc, tt, ct, callers) in self.stats.items(): | 
 | 126 |             self.total_calls += nc | 
 | 127 |             self.prim_calls  += cc | 
 | 128 |             self.total_tt    += tt | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 129 |             if callers.has_key(("jprofile", 0, "profiler")): | 
 | 130 |                 self.top_level[func] = None | 
 | 131 |             if len(func_std_string(func)) > self.max_name_len: | 
 | 132 |                 self.max_name_len = len(func_std_string(func)) | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 133 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 134 |     def add(self, *arg_list): | 
 | 135 |         if not arg_list: return self | 
| Guido van Rossum | 68468eb | 2003-02-27 20:14:51 +0000 | [diff] [blame] | 136 |         if len(arg_list) > 1: self.add(*arg_list[1:]) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 137 |         other = arg_list[0] | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 138 |         if type(self) != type(other) or self.__class__ != other.__class__: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 139 |             other = Stats(other) | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 140 |         self.files += other.files | 
 | 141 |         self.total_calls += other.total_calls | 
 | 142 |         self.prim_calls += other.prim_calls | 
 | 143 |         self.total_tt += other.total_tt | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 144 |         for func in other.top_level: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 145 |             self.top_level[func] = None | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 146 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 147 |         if self.max_name_len < other.max_name_len: | 
 | 148 |             self.max_name_len = other.max_name_len | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 149 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 150 |         self.fcn_list = None | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 151 |  | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 152 |         for func, stat in other.stats.iteritems(): | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 153 |             if func in self.stats: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 154 |                 old_func_stat = self.stats[func] | 
 | 155 |             else: | 
 | 156 |                 old_func_stat = (0, 0, 0, 0, {},) | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 157 |             self.stats[func] = add_func_stats(old_func_stat, stat) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 158 |         return self | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 159 |  | 
| Fred Drake | 9c43910 | 2003-05-14 14:28:09 +0000 | [diff] [blame] | 160 |     def dump_stats(self, filename): | 
 | 161 |         """Write the profile data to a file we know how to load back.""" | 
 | 162 |         f = file(filename, 'wb') | 
 | 163 |         try: | 
 | 164 |             marshal.dump(self.stats, f) | 
 | 165 |         finally: | 
 | 166 |             f.close() | 
 | 167 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 168 |     # list the tuple indices and directions for sorting, | 
 | 169 |     # along with some printable description | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 170 |     sort_arg_dict_default = { | 
 | 171 |               "calls"     : (((1,-1),              ), "call count"), | 
 | 172 |               "cumulative": (((3,-1),              ), "cumulative time"), | 
 | 173 |               "file"      : (((4, 1),              ), "file name"), | 
 | 174 |               "line"      : (((5, 1),              ), "line number"), | 
 | 175 |               "module"    : (((4, 1),              ), "file name"), | 
 | 176 |               "name"      : (((6, 1),              ), "function name"), | 
 | 177 |               "nfl"       : (((6, 1),(4, 1),(5, 1),), "name/file/line"), | 
 | 178 |               "pcalls"    : (((0,-1),              ), "call count"), | 
 | 179 |               "stdname"   : (((7, 1),              ), "standard name"), | 
 | 180 |               "time"      : (((2,-1),              ), "internal time"), | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 181 |               } | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 182 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 183 |     def get_sort_arg_defs(self): | 
 | 184 |         """Expand all abbreviations that are unique.""" | 
 | 185 |         if not self.sort_arg_dict: | 
 | 186 |             self.sort_arg_dict = dict = {} | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 187 |             bad_list = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 188 |             for word, tup in self.sort_arg_dict_default.iteritems(): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 189 |                 fragment = word | 
 | 190 |                 while fragment: | 
 | 191 |                     if not fragment: | 
 | 192 |                         break | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 193 |                     if fragment in dict: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 194 |                         bad_list[fragment] = 0 | 
 | 195 |                         break | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 196 |                     dict[fragment] = tup | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 197 |                     fragment = fragment[:-1] | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 198 |             for word in bad_list: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 199 |                 del dict[word] | 
 | 200 |         return self.sort_arg_dict | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 201 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 202 |     def sort_stats(self, *field): | 
 | 203 |         if not field: | 
 | 204 |             self.fcn_list = 0 | 
 | 205 |             return self | 
 | 206 |         if len(field) == 1 and type(field[0]) == type(1): | 
 | 207 |             # Be compatible with old profiler | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 208 |             field = [ {-1: "stdname", | 
 | 209 |                       0:"calls", | 
 | 210 |                       1:"time", | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 211 |                       2: "cumulative" }  [ field[0] ] ] | 
 | 212 |  | 
 | 213 |         sort_arg_defs = self.get_sort_arg_defs() | 
 | 214 |         sort_tuple = () | 
 | 215 |         self.sort_type = "" | 
 | 216 |         connector = "" | 
 | 217 |         for word in field: | 
 | 218 |             sort_tuple = sort_tuple + sort_arg_defs[word][0] | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 219 |             self.sort_type += connector + sort_arg_defs[word][1] | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 220 |             connector = ", " | 
 | 221 |  | 
 | 222 |         stats_list = [] | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 223 |         for func, (cc, nc, tt, ct, callers) in self.stats.iteritems(): | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 224 |             stats_list.append((cc, nc, tt, ct) + func + | 
 | 225 |                               (func_std_string(func), func)) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 226 |  | 
 | 227 |         stats_list.sort(TupleComp(sort_tuple).compare) | 
 | 228 |  | 
 | 229 |         self.fcn_list = fcn_list = [] | 
 | 230 |         for tuple in stats_list: | 
 | 231 |             fcn_list.append(tuple[-1]) | 
 | 232 |         return self | 
 | 233 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 234 |     def reverse_order(self): | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 235 |         if self.fcn_list: | 
 | 236 |             self.fcn_list.reverse() | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 237 |         return self | 
 | 238 |  | 
 | 239 |     def strip_dirs(self): | 
 | 240 |         oldstats = self.stats | 
 | 241 |         self.stats = newstats = {} | 
 | 242 |         max_name_len = 0 | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 243 |         for func, (cc, nc, tt, ct, callers) in oldstats.iteritems(): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 244 |             newfunc = func_strip_path(func) | 
 | 245 |             if len(func_std_string(newfunc)) > max_name_len: | 
 | 246 |                 max_name_len = len(func_std_string(newfunc)) | 
 | 247 |             newcallers = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 248 |             for func2, caller in callers.iteritems(): | 
 | 249 |                 newcallers[func_strip_path(func2)] = caller | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 250 |  | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 251 |             if newfunc in newstats: | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 252 |                 newstats[newfunc] = add_func_stats( | 
 | 253 |                                         newstats[newfunc], | 
 | 254 |                                         (cc, nc, tt, ct, newcallers)) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 255 |             else: | 
 | 256 |                 newstats[newfunc] = (cc, nc, tt, ct, newcallers) | 
 | 257 |         old_top = self.top_level | 
 | 258 |         self.top_level = new_top = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 259 |         for func in old_top: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 260 |             new_top[func_strip_path(func)] = None | 
 | 261 |  | 
 | 262 |         self.max_name_len = max_name_len | 
 | 263 |  | 
 | 264 |         self.fcn_list = None | 
 | 265 |         self.all_callees = None | 
 | 266 |         return self | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 267 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 268 |     def calc_callees(self): | 
 | 269 |         if self.all_callees: return | 
 | 270 |         self.all_callees = all_callees = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 271 |         for func, (cc, nc, tt, ct, callers) in self.stats.iteritems(): | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 272 |             if not func in all_callees: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 273 |                 all_callees[func] = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 274 |             for func2, caller in callers.iteritems(): | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 275 |                 if not func2 in all_callees: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 276 |                     all_callees[func2] = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 277 |                 all_callees[func2][func]  = caller | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 278 |         return | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 279 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 280 |     #****************************************************************** | 
 | 281 |     # The following functions support actual printing of reports | 
 | 282 |     #****************************************************************** | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 283 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 284 |     # Optional "amount" is either a line count, or a percentage of lines. | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 285 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 286 |     def eval_print_amount(self, sel, list, msg): | 
 | 287 |         new_list = list | 
 | 288 |         if type(sel) == type(""): | 
 | 289 |             new_list = [] | 
 | 290 |             for func in list: | 
 | 291 |                 if re.search(sel, func_std_string(func)): | 
 | 292 |                     new_list.append(func) | 
 | 293 |         else: | 
 | 294 |             count = len(list) | 
 | 295 |             if type(sel) == type(1.0) and 0.0 <= sel < 1.0: | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 296 |                 count = int(count * sel + .5) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 297 |                 new_list = list[:count] | 
 | 298 |             elif type(sel) == type(1) and 0 <= sel < count: | 
 | 299 |                 count = sel | 
 | 300 |                 new_list = list[:count] | 
 | 301 |         if len(list) != len(new_list): | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 302 |             msg = msg + "   List reduced from %r to %r due to restriction <%r>\n" % ( | 
 | 303 |                          len(list), len(new_list), sel) | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 304 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 305 |         return new_list, msg | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 306 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 307 |     def get_print_list(self, sel_list): | 
 | 308 |         width = self.max_name_len | 
 | 309 |         if self.fcn_list: | 
 | 310 |             list = self.fcn_list[:] | 
 | 311 |             msg = "   Ordered by: " + self.sort_type + '\n' | 
 | 312 |         else: | 
 | 313 |             list = self.stats.keys() | 
 | 314 |             msg = "   Random listing order was used\n" | 
 | 315 |  | 
 | 316 |         for selection in sel_list: | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 317 |             list, msg = self.eval_print_amount(selection, list, msg) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 318 |  | 
 | 319 |         count = len(list) | 
 | 320 |  | 
 | 321 |         if not list: | 
 | 322 |             return 0, list | 
 | 323 |         print msg | 
 | 324 |         if count < len(self.stats): | 
 | 325 |             width = 0 | 
 | 326 |             for func in list: | 
 | 327 |                 if  len(func_std_string(func)) > width: | 
 | 328 |                     width = len(func_std_string(func)) | 
 | 329 |         return width+2, list | 
 | 330 |  | 
 | 331 |     def print_stats(self, *amount): | 
 | 332 |         for filename in self.files: | 
 | 333 |             print filename | 
 | 334 |         if self.files: print | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 335 |         indent = ' ' * 8 | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 336 |         for func in self.top_level: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 337 |             print indent, func_get_function_name(func) | 
 | 338 |  | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 339 |         print indent, self.total_calls, "function calls", | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 340 |         if self.total_calls != self.prim_calls: | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 341 |             print "(%d primitive calls)" % self.prim_calls, | 
 | 342 |         print "in %.3f CPU seconds" % self.total_tt | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 343 |         print | 
 | 344 |         width, list = self.get_print_list(amount) | 
 | 345 |         if list: | 
 | 346 |             self.print_title() | 
 | 347 |             for func in list: | 
 | 348 |                 self.print_line(func) | 
 | 349 |             print | 
 | 350 |             print | 
 | 351 |         return self | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 352 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 353 |     def print_callees(self, *amount): | 
 | 354 |         width, list = self.get_print_list(amount) | 
 | 355 |         if list: | 
 | 356 |             self.calc_callees() | 
 | 357 |  | 
 | 358 |             self.print_call_heading(width, "called...") | 
 | 359 |             for func in list: | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 360 |                 if func in self.all_callees: | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 361 |                     self.print_call_line(width, func, self.all_callees[func]) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 362 |                 else: | 
 | 363 |                     self.print_call_line(width, func, {}) | 
 | 364 |             print | 
 | 365 |             print | 
 | 366 |         return self | 
 | 367 |  | 
 | 368 |     def print_callers(self, *amount): | 
 | 369 |         width, list = self.get_print_list(amount) | 
 | 370 |         if list: | 
 | 371 |             self.print_call_heading(width, "was called by...") | 
 | 372 |             for func in list: | 
 | 373 |                 cc, nc, tt, ct, callers = self.stats[func] | 
 | 374 |                 self.print_call_line(width, func, callers) | 
 | 375 |             print | 
 | 376 |             print | 
 | 377 |         return self | 
 | 378 |  | 
 | 379 |     def print_call_heading(self, name_size, column_title): | 
| Eric S. Raymond | 373c55e | 2001-02-09 08:25:29 +0000 | [diff] [blame] | 380 |         print "Function ".ljust(name_size) + column_title | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 381 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 382 |     def print_call_line(self, name_size, source, call_dict): | 
| Eric S. Raymond | 373c55e | 2001-02-09 08:25:29 +0000 | [diff] [blame] | 383 |         print func_std_string(source).ljust(name_size), | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 384 |         if not call_dict: | 
 | 385 |             print "--" | 
 | 386 |             return | 
 | 387 |         clist = call_dict.keys() | 
 | 388 |         clist.sort() | 
 | 389 |         name_size = name_size + 1 | 
 | 390 |         indent = "" | 
 | 391 |         for func in clist: | 
 | 392 |             name = func_std_string(func) | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 393 |             print indent*name_size + name + '(%r)' % (call_dict[func],), \ | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 394 |                       f8(self.stats[func][3]) | 
 | 395 |             indent = " " | 
 | 396 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 397 |     def print_title(self): | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 398 |         print '   ncalls  tottime  percall  cumtime  percall', \ | 
 | 399 |               'filename:lineno(function)' | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 400 |  | 
 | 401 |     def print_line(self, func):  # hack : should print percentages | 
 | 402 |         cc, nc, tt, ct, callers = self.stats[func] | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 403 |         c = str(nc) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 404 |         if nc != cc: | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 405 |             c = c + '/' + str(cc) | 
| Eric S. Raymond | 373c55e | 2001-02-09 08:25:29 +0000 | [diff] [blame] | 406 |         print c.rjust(9), | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 407 |         print f8(tt), | 
 | 408 |         if nc == 0: | 
 | 409 |             print ' '*8, | 
 | 410 |         else: | 
 | 411 |             print f8(tt/nc), | 
 | 412 |         print f8(ct), | 
 | 413 |         if cc == 0: | 
 | 414 |             print ' '*8, | 
 | 415 |         else: | 
 | 416 |             print f8(ct/cc), | 
 | 417 |         print func_std_string(func) | 
 | 418 |  | 
| Tim Peters | 281084f | 2001-10-08 06:28:18 +0000 | [diff] [blame] | 419 |     def ignore(self): | 
 | 420 |         # Deprecated since 1.5.1 -- see the docs. | 
 | 421 |         pass # has no return value, so use at end of line :-) | 
 | 422 |  | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 423 | class TupleComp: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 424 |     """This class provides a generic function for comparing any two tuples. | 
 | 425 |     Each instance records a list of tuple-indices (from most significant | 
 | 426 |     to least significant), and sort direction (ascending or decending) for | 
 | 427 |     each tuple-index.  The compare functions can then be used as the function | 
 | 428 |     argument to the system sort() function when a list of tuples need to be | 
 | 429 |     sorted in the instances order.""" | 
| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 430 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 431 |     def __init__(self, comp_select_list): | 
 | 432 |         self.comp_select_list = comp_select_list | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 433 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 434 |     def compare (self, left, right): | 
 | 435 |         for index, direction in self.comp_select_list: | 
 | 436 |             l = left[index] | 
 | 437 |             r = right[index] | 
 | 438 |             if l < r: | 
 | 439 |                 return -direction | 
 | 440 |             if l > r: | 
 | 441 |                 return direction | 
 | 442 |         return 0 | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 443 |  | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 444 | #************************************************************************** | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 445 | # func_name is a triple (file:string, line:int, name:string) | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 446 |  | 
 | 447 | def func_strip_path(func_name): | 
| Fred Drake | 9c43910 | 2003-05-14 14:28:09 +0000 | [diff] [blame] | 448 |     filename, line, name = func_name | 
 | 449 |     return os.path.basename(filename), line, name | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 450 |  | 
 | 451 | def func_get_function_name(func): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 452 |     return func[2] | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 453 |  | 
 | 454 | def func_std_string(func_name): # match what old profile produced | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 455 |     return "%s:%d(%s)" % func_name | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 456 |  | 
 | 457 | #************************************************************************** | 
 | 458 | # The following functions combine statists for pairs functions. | 
 | 459 | # The bulk of the processing involves correctly handling "call" lists, | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 460 | # such as callers and callees. | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 461 | #************************************************************************** | 
 | 462 |  | 
| Guido van Rossum | 54f22ed | 2000-02-04 15:10:34 +0000 | [diff] [blame] | 463 | def add_func_stats(target, source): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 464 |     """Add together all the stats for two profile entries.""" | 
 | 465 |     cc, nc, tt, ct, callers = source | 
 | 466 |     t_cc, t_nc, t_tt, t_ct, t_callers = target | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 467 |     return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct, | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 468 |               add_callers(t_callers, callers)) | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 469 |  | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 470 | def add_callers(target, source): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 471 |     """Combine two caller lists in a single list.""" | 
 | 472 |     new_callers = {} | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 473 |     for func, caller in target.iteritems(): | 
 | 474 |         new_callers[func] = caller | 
 | 475 |     for func, caller in source.iteritems(): | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 476 |         if func in new_callers: | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 477 |             new_callers[func] = caller + new_callers[func] | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 478 |         else: | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 479 |             new_callers[func] = caller | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 480 |     return new_callers | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 481 |  | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 482 | def count_calls(callers): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 483 |     """Sum the caller statistics to get total number of calls received.""" | 
 | 484 |     nc = 0 | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 485 |     for calls in callers.itervalues(): | 
 | 486 |         nc += calls | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 487 |     return nc | 
| Guido van Rossum | adb3105 | 1994-06-23 11:42:52 +0000 | [diff] [blame] | 488 |  | 
 | 489 | #************************************************************************** | 
 | 490 | # The following functions support printing of reports | 
 | 491 | #************************************************************************** | 
 | 492 |  | 
 | 493 | def f8(x): | 
| Tim Peters | 7d01685 | 2001-10-08 06:13:19 +0000 | [diff] [blame] | 494 |     return "%8.3f" % x | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 495 |  | 
 | 496 | #************************************************************************** | 
 | 497 | # Statistics browser added by ESR, April 2001 | 
 | 498 | #************************************************************************** | 
 | 499 |  | 
 | 500 | if __name__ == '__main__': | 
 | 501 |     import cmd | 
| Eric S. Raymond | 9cb9857 | 2001-04-14 01:48:41 +0000 | [diff] [blame] | 502 |     try: | 
 | 503 |         import readline | 
| Fred Drake | e818761 | 2001-05-11 19:21:41 +0000 | [diff] [blame] | 504 |     except ImportError: | 
| Eric S. Raymond | 9cb9857 | 2001-04-14 01:48:41 +0000 | [diff] [blame] | 505 |         pass | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 506 |  | 
 | 507 |     class ProfileBrowser(cmd.Cmd): | 
 | 508 |         def __init__(self, profile=None): | 
| Martin v. Löwis | 66b6e19 | 2001-07-28 14:44:03 +0000 | [diff] [blame] | 509 |             cmd.Cmd.__init__(self) | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 510 |             self.prompt = "% " | 
| Raymond Hettinger | 16e3c42 | 2002-06-01 16:07:16 +0000 | [diff] [blame] | 511 |             if profile is not None: | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 512 |                 self.stats = Stats(profile) | 
 | 513 |             else: | 
 | 514 |                 self.stats = None | 
 | 515 |  | 
 | 516 |         def generic(self, fn, line): | 
 | 517 |             args = line.split() | 
 | 518 |             processed = [] | 
 | 519 |             for term in args: | 
 | 520 |                 try: | 
 | 521 |                     processed.append(int(term)) | 
 | 522 |                     continue | 
 | 523 |                 except ValueError: | 
 | 524 |                     pass | 
 | 525 |                 try: | 
 | 526 |                     frac = float(term) | 
 | 527 |                     if frac > 1 or frac < 0: | 
 | 528 |                         print "Fraction argument mus be in [0, 1]" | 
 | 529 |                         continue | 
 | 530 |                     processed.append(frac) | 
 | 531 |                     continue | 
 | 532 |                 except ValueError: | 
 | 533 |                     pass | 
 | 534 |                 processed.append(term) | 
 | 535 |             if self.stats: | 
| Guido van Rossum | 68468eb | 2003-02-27 20:14:51 +0000 | [diff] [blame] | 536 |                 getattr(self.stats, fn)(*processed) | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 537 |             else: | 
 | 538 |                 print "No statistics object is loaded." | 
 | 539 |             return 0 | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 540 |         def generic_help(self): | 
 | 541 |             print "Arguments may be:" | 
 | 542 |             print "* An integer maximum number of entries to print." | 
 | 543 |             print "* A decimal fractional number between 0 and 1, controlling" | 
 | 544 |             print "  what fraction of selected entries to print." | 
 | 545 |             print "* A regular expression; only entries with function names" | 
 | 546 |             print "  that match it are printed." | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 547 |  | 
 | 548 |         def do_add(self, line): | 
 | 549 |             self.stats.add(line) | 
 | 550 |             return 0 | 
 | 551 |         def help_add(self): | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 552 |             print "Add profile info from given file to current statistics object." | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 553 |  | 
 | 554 |         def do_callees(self, line): | 
| Eric S. Raymond | 3c1858a | 2001-04-14 15:16:05 +0000 | [diff] [blame] | 555 |             return self.generic('print_callees', line) | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 556 |         def help_callees(self): | 
 | 557 |             print "Print callees statistics from the current stat object." | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 558 |             self.generic_help() | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 559 |  | 
 | 560 |         def do_callers(self, line): | 
| Eric S. Raymond | 3c1858a | 2001-04-14 15:16:05 +0000 | [diff] [blame] | 561 |             return self.generic('print_callers', line) | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 562 |         def help_callers(self): | 
 | 563 |             print "Print callers statistics from the current stat object." | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 564 |             self.generic_help() | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 565 |  | 
 | 566 |         def do_EOF(self, line): | 
 | 567 |             print "" | 
 | 568 |             return 1 | 
 | 569 |         def help_EOF(self): | 
 | 570 |             print "Leave the profile brower." | 
 | 571 |  | 
 | 572 |         def do_quit(self, line): | 
 | 573 |             return 1 | 
 | 574 |         def help_quit(self): | 
 | 575 |             print "Leave the profile brower." | 
 | 576 |  | 
 | 577 |         def do_read(self, line): | 
 | 578 |             if line: | 
 | 579 |                 try: | 
 | 580 |                     self.stats = Stats(line) | 
 | 581 |                 except IOError, args: | 
 | 582 |                     print args[1] | 
 | 583 |                     return | 
 | 584 |                 self.prompt = line + "% " | 
| Martin v. Löwis | 22adac5 | 2001-06-07 05:49:05 +0000 | [diff] [blame] | 585 |             elif len(self.prompt) > 2: | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 586 |                 line = self.prompt[-2:] | 
 | 587 |             else: | 
 | 588 |                 print "No statistics object is current -- cannot reload." | 
 | 589 |             return 0 | 
 | 590 |         def help_read(self): | 
 | 591 |             print "Read in profile data from a specified file." | 
 | 592 |  | 
 | 593 |         def do_reverse(self, line): | 
 | 594 |             self.stats.reverse_order() | 
 | 595 |             return 0 | 
 | 596 |         def help_reverse(self): | 
 | 597 |             print "Reverse the sort order of the profiling report." | 
 | 598 |  | 
 | 599 |         def do_sort(self, line): | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 600 |             abbrevs = self.stats.get_sort_arg_defs() | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 601 |             if line and not filter(lambda x,a=abbrevs: x not in a,line.split()): | 
| Guido van Rossum | 68468eb | 2003-02-27 20:14:51 +0000 | [diff] [blame] | 602 |                 self.stats.sort_stats(*line.split()) | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 603 |             else: | 
 | 604 |                 print "Valid sort keys (unique prefixes are accepted):" | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 605 |                 for (key, value) in Stats.sort_arg_dict_default.iteritems(): | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 606 |                     print "%s -- %s" % (key, value[1]) | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 607 |             return 0 | 
 | 608 |         def help_sort(self): | 
 | 609 |             print "Sort profile data according to specified keys." | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 610 |             print "(Typing `sort' without arguments lists valid keys.)" | 
| Martin v. Löwis | 27c430e | 2001-07-30 10:21:13 +0000 | [diff] [blame] | 611 |         def complete_sort(self, text, *args): | 
| Raymond Hettinger | e0d4972 | 2002-06-02 18:55:56 +0000 | [diff] [blame] | 612 |             return [a for a in Stats.sort_arg_dict_default if a.startswith(text)] | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 613 |  | 
 | 614 |         def do_stats(self, line): | 
 | 615 |             return self.generic('print_stats', line) | 
 | 616 |         def help_stats(self): | 
 | 617 |             print "Print statistics from the current stat object." | 
| Eric S. Raymond | 53b809d | 2001-04-26 07:32:38 +0000 | [diff] [blame] | 618 |             self.generic_help() | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 619 |  | 
 | 620 |         def do_strip(self, line): | 
| Eric S. Raymond | 3c1858a | 2001-04-14 15:16:05 +0000 | [diff] [blame] | 621 |             self.stats.strip_dirs() | 
| Eric S. Raymond | 4f3980d | 2001-04-13 00:23:01 +0000 | [diff] [blame] | 622 |             return 0 | 
 | 623 |         def help_strip(self): | 
 | 624 |             print "Strip leading path information from filenames in the report." | 
 | 625 |  | 
 | 626 |         def postcmd(self, stop, line): | 
 | 627 |             if stop: | 
 | 628 |                 return stop | 
 | 629 |             return None | 
 | 630 |  | 
 | 631 |     import sys | 
 | 632 |     print "Welcome to the profile statistics browser." | 
 | 633 |     if len(sys.argv) > 1: | 
 | 634 |         initprofile = sys.argv[1] | 
 | 635 |     else: | 
 | 636 |         initprofile = None | 
 | 637 |     try: | 
 | 638 |         ProfileBrowser(initprofile).cmdloop() | 
 | 639 |         print "Goodbye." | 
 | 640 |     except KeyboardInterrupt: | 
 | 641 |         pass | 
 | 642 |  | 
 | 643 | # That's all, folks. |