blob: e77459d38b33ecc900996ad9b753930c1531f57d [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Class for printing reports on profiled python code."""
2
Benjamin Peterson8d770692011-06-27 09:14:34 -05003# Written by James Roskind
Guido van Rossumadb31051994-06-23 11:42:52 +00004# Based on prior profile module by Sjoerd Mullender...
5# which was hacked somewhat by: Guido van Rossum
Guido van Rossumadb31051994-06-23 11:42:52 +00006
Benjamin Peterson8d770692011-06-27 09:14:34 -05007# Copyright Disney Enterprises, Inc. All Rights Reserved.
8# Licensed to PSF under a Contributor Agreement
Benjamin Peterson0f93d3d2011-06-27 09:18:46 -05009#
Benjamin Peterson8d770692011-06-27 09:14:34 -050010# Licensed under the Apache License, Version 2.0 (the "License");
11# you may not use this file except in compliance with the License.
12# You may obtain a copy of the License at
Benjamin Peterson0f93d3d2011-06-27 09:18:46 -050013#
Benjamin Peterson8d770692011-06-27 09:14:34 -050014# http://www.apache.org/licenses/LICENSE-2.0
Benjamin Peterson0f93d3d2011-06-27 09:18:46 -050015#
Benjamin Peterson8d770692011-06-27 09:14:34 -050016# Unless required by applicable law or agreed to in writing, software
17# distributed under the License is distributed on an "AS IS" BASIS,
18# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
19# either express or implied. See the License for the specific language
20# governing permissions and limitations under the License.
Guido van Rossumadb31051994-06-23 11:42:52 +000021
22
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000023import sys
Guido van Rossumadb31051994-06-23 11:42:52 +000024import os
25import time
Guido van Rossumadb31051994-06-23 11:42:52 +000026import marshal
Guido van Rossum9694fca1997-10-22 21:00:49 +000027import re
Daniel Olshansky01602ae2020-01-15 17:51:54 -050028
Ethan Furmana02cb472021-04-21 10:20:44 -070029from enum import StrEnum, _simple_enum
Raymond Hettingerc50846a2010-04-05 18:56:31 +000030from functools import cmp_to_key
Daniel Olshansky01602ae2020-01-15 17:51:54 -050031from dataclasses import dataclass
32from typing import Dict
Guido van Rossumadb31051994-06-23 11:42:52 +000033
Daniel Olshansky01602ae2020-01-15 17:51:54 -050034__all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"]
mwidjaja863b1e42018-01-25 20:49:56 -080035
Ethan Furmana02cb472021-04-21 10:20:44 -070036@_simple_enum(StrEnum)
37class SortKey:
mwidjaja863b1e42018-01-25 20:49:56 -080038 CALLS = 'calls', 'ncalls'
39 CUMULATIVE = 'cumulative', 'cumtime'
40 FILENAME = 'filename', 'module'
41 LINE = 'line'
42 NAME = 'name'
43 NFL = 'nfl'
44 PCALLS = 'pcalls'
45 STDNAME = 'stdname'
46 TIME = 'time', 'tottime'
47
48 def __new__(cls, *values):
Ethan Furmanae0d2a32020-09-19 11:12:57 -070049 value = values[0]
50 obj = str.__new__(cls, value)
51 obj._value_ = value
mwidjaja863b1e42018-01-25 20:49:56 -080052 for other_value in values[1:]:
53 cls._value2member_map_[other_value] = obj
54 obj._all_values = values
55 return obj
56
Skip Montanaroc62c81e2001-02-12 02:00:42 +000057
Daniel Olshansky01602ae2020-01-15 17:51:54 -050058@dataclass(unsafe_hash=True)
59class FunctionProfile:
60 ncalls: int
61 tottime: float
62 percall_tottime: float
63 cumtime: float
64 percall_cumtime: float
65 file_name: str
66 line_number: int
67
68@dataclass(unsafe_hash=True)
69class StatsProfile:
70 '''Class for keeping track of an item in inventory.'''
71 total_tt: float
72 func_profiles: Dict[str, FunctionProfile]
73
Guido van Rossumadb31051994-06-23 11:42:52 +000074class Stats:
Tim Peters2344fae2001-01-15 00:50:52 +000075 """This class is used for creating reports from data generated by the
76 Profile class. It is a "friend" of that class, and imports data either
77 by direct access to members of Profile class, or by reading in a dictionary
78 that was emitted (via marshal) from the Profile class.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000079
Tim Peters2344fae2001-01-15 00:50:52 +000080 The big change from the previous Profiler (in terms of raw functionality)
81 is that an "add()" method has been provided to combine Stats from
82 several distinct profile runs. Both the constructor and the add()
83 method now take arbitrarily many file names as arguments.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000084
Tim Peters2344fae2001-01-15 00:50:52 +000085 All the print methods now take an argument that indicates how many lines
86 to print. If the arg is a floating point number between 0 and 1.0, then
87 it is taken as a decimal percentage of the available lines to be printed
88 (e.g., .1 means print 10% of all available lines). If it is an integer,
89 it is taken to mean the number of lines of data that you wish to have
90 printed.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000091
Tim Peters2344fae2001-01-15 00:50:52 +000092 The sort_stats() method now processes some additional options (i.e., in
Matthias Bussonnier8fb1f6e2017-02-20 21:30:00 -080093 addition to the old -1, 0, 1, or 2 that are respectively interpreted as
mwidjaja863b1e42018-01-25 20:49:56 -080094 'stdname', 'calls', 'time', and 'cumulative'). It takes either an
95 arbitrary number of quoted strings or SortKey enum to select the sort
96 order.
Matthias Bussonnier8fb1f6e2017-02-20 21:30:00 -080097
mwidjaja863b1e42018-01-25 20:49:56 -080098 For example sort_stats('time', 'name') or sort_stats(SortKey.TIME,
99 SortKey.NAME) sorts on the major key of 'internal function time', and on
100 the minor key of 'the name of the function'. Look at the two tables in
101 sort_stats() and get_sort_arg_defs(self) for more examples.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000102
Georg Brandlb1a97af2010-08-02 12:06:18 +0000103 All methods return self, so you can string together commands like:
Tim Peters2344fae2001-01-15 00:50:52 +0000104 Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
105 print_stats(5).print_callers(5)
106 """
107
Georg Brandl7837a962009-09-02 20:33:30 +0000108 def __init__(self, *args, stream=None):
109 self.stream = stream or sys.stdout
Tim Peters2344fae2001-01-15 00:50:52 +0000110 if not len(args):
111 arg = None
112 else:
113 arg = args[0]
114 args = args[1:]
115 self.init(arg)
Guido van Rossum68468eb2003-02-27 20:14:51 +0000116 self.add(*args)
Tim Peters2344fae2001-01-15 00:50:52 +0000117
118 def init(self, arg):
119 self.all_callees = None # calc only if needed
120 self.files = []
121 self.fcn_list = None
122 self.total_tt = 0
123 self.total_calls = 0
124 self.prim_calls = 0
125 self.max_name_len = 0
Georg Brandleb7e5692010-10-22 06:29:21 +0000126 self.top_level = set()
Tim Peters2344fae2001-01-15 00:50:52 +0000127 self.stats = {}
128 self.sort_arg_dict = {}
129 self.load_stats(arg)
Tim Peters2344fae2001-01-15 00:50:52 +0000130 try:
131 self.get_top_level_stats()
Georg Brandl9a8439d2010-10-22 06:35:59 +0000132 except Exception:
133 print("Invalid timing data %s" %
134 (self.files[-1] if self.files else ''), file=self.stream)
135 raise
Guido van Rossumadb31051994-06-23 11:42:52 +0000136
Tim Peters2344fae2001-01-15 00:50:52 +0000137 def load_stats(self, arg):
Georg Brandl83938432010-10-22 06:28:01 +0000138 if arg is None:
139 self.stats = {}
140 return
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000141 elif isinstance(arg, str):
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100142 with open(arg, 'rb') as f:
143 self.stats = marshal.load(f)
Tim Peters2344fae2001-01-15 00:50:52 +0000144 try:
145 file_stats = os.stat(arg)
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000146 arg = time.ctime(file_stats.st_mtime) + " " + arg
Tim Peters2344fae2001-01-15 00:50:52 +0000147 except: # in case this is not unix
148 pass
Georg Brandl83938432010-10-22 06:28:01 +0000149 self.files = [arg]
Tim Peters2344fae2001-01-15 00:50:52 +0000150 elif hasattr(arg, 'create_stats'):
151 arg.create_stats()
152 self.stats = arg.stats
153 arg.stats = {}
154 if not self.stats:
Georg Brandl83938432010-10-22 06:28:01 +0000155 raise TypeError("Cannot create or construct a %r object from %r"
Collin Winterce36ad82007-08-30 01:19:48 +0000156 % (self.__class__, arg))
Tim Peters2344fae2001-01-15 00:50:52 +0000157 return
Guido van Rossumadb31051994-06-23 11:42:52 +0000158
Tim Peters2344fae2001-01-15 00:50:52 +0000159 def get_top_level_stats(self):
Tim Peters7d016852001-10-08 06:13:19 +0000160 for func, (cc, nc, tt, ct, callers) in self.stats.items():
161 self.total_calls += nc
162 self.prim_calls += cc
163 self.total_tt += tt
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000164 if ("jprofile", 0, "profiler") in callers:
Georg Brandleb7e5692010-10-22 06:29:21 +0000165 self.top_level.add(func)
Tim Peters2344fae2001-01-15 00:50:52 +0000166 if len(func_std_string(func)) > self.max_name_len:
167 self.max_name_len = len(func_std_string(func))
Guido van Rossumadb31051994-06-23 11:42:52 +0000168
Tim Peters2344fae2001-01-15 00:50:52 +0000169 def add(self, *arg_list):
Georg Brandl83938432010-10-22 06:28:01 +0000170 if not arg_list:
171 return self
172 for item in reversed(arg_list):
173 if type(self) != type(item):
174 item = Stats(item)
175 self.files += item.files
176 self.total_calls += item.total_calls
177 self.prim_calls += item.prim_calls
178 self.total_tt += item.total_tt
179 for func in item.top_level:
Georg Brandleb7e5692010-10-22 06:29:21 +0000180 self.top_level.add(func)
Guido van Rossumadb31051994-06-23 11:42:52 +0000181
Georg Brandl83938432010-10-22 06:28:01 +0000182 if self.max_name_len < item.max_name_len:
183 self.max_name_len = item.max_name_len
Guido van Rossumadb31051994-06-23 11:42:52 +0000184
Georg Brandl83938432010-10-22 06:28:01 +0000185 self.fcn_list = None
Guido van Rossumadb31051994-06-23 11:42:52 +0000186
Georg Brandl83938432010-10-22 06:28:01 +0000187 for func, stat in item.stats.items():
188 if func in self.stats:
189 old_func_stat = self.stats[func]
190 else:
191 old_func_stat = (0, 0, 0, 0, {},)
192 self.stats[func] = add_func_stats(old_func_stat, stat)
Tim Peters2344fae2001-01-15 00:50:52 +0000193 return self
Guido van Rossumadb31051994-06-23 11:42:52 +0000194
Fred Drake9c439102003-05-14 14:28:09 +0000195 def dump_stats(self, filename):
196 """Write the profile data to a file we know how to load back."""
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100197 with open(filename, 'wb') as f:
Fred Drake9c439102003-05-14 14:28:09 +0000198 marshal.dump(self.stats, f)
Fred Drake9c439102003-05-14 14:28:09 +0000199
Tim Peters2344fae2001-01-15 00:50:52 +0000200 # list the tuple indices and directions for sorting,
201 # along with some printable description
Tim Peters7d016852001-10-08 06:13:19 +0000202 sort_arg_dict_default = {
203 "calls" : (((1,-1), ), "call count"),
Andrew Svetlovc3e5b102012-10-07 19:18:39 +0300204 "ncalls" : (((1,-1), ), "call count"),
205 "cumtime" : (((3,-1), ), "cumulative time"),
Tim Peters7d016852001-10-08 06:13:19 +0000206 "cumulative": (((3,-1), ), "cumulative time"),
Andrew Svetlovc3e5b102012-10-07 19:18:39 +0300207 "filename" : (((4, 1), ), "file name"),
Tim Peters7d016852001-10-08 06:13:19 +0000208 "line" : (((5, 1), ), "line number"),
209 "module" : (((4, 1), ), "file name"),
210 "name" : (((6, 1), ), "function name"),
211 "nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"),
Andrew Svetlov2ef45842012-10-07 18:58:42 +0300212 "pcalls" : (((0,-1), ), "primitive call count"),
Tim Peters7d016852001-10-08 06:13:19 +0000213 "stdname" : (((7, 1), ), "standard name"),
214 "time" : (((2,-1), ), "internal time"),
Andrew Svetlovc3e5b102012-10-07 19:18:39 +0300215 "tottime" : (((2,-1), ), "internal time"),
Tim Peters2344fae2001-01-15 00:50:52 +0000216 }
Guido van Rossumadb31051994-06-23 11:42:52 +0000217
Tim Peters2344fae2001-01-15 00:50:52 +0000218 def get_sort_arg_defs(self):
219 """Expand all abbreviations that are unique."""
220 if not self.sort_arg_dict:
221 self.sort_arg_dict = dict = {}
Tim Peters2344fae2001-01-15 00:50:52 +0000222 bad_list = {}
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000223 for word, tup in self.sort_arg_dict_default.items():
Tim Peters2344fae2001-01-15 00:50:52 +0000224 fragment = word
225 while fragment:
226 if not fragment:
227 break
Raymond Hettinger54f02222002-06-01 14:18:47 +0000228 if fragment in dict:
Tim Peters2344fae2001-01-15 00:50:52 +0000229 bad_list[fragment] = 0
230 break
Raymond Hettingere0d49722002-06-02 18:55:56 +0000231 dict[fragment] = tup
Tim Peters2344fae2001-01-15 00:50:52 +0000232 fragment = fragment[:-1]
Raymond Hettingere0d49722002-06-02 18:55:56 +0000233 for word in bad_list:
Tim Peters2344fae2001-01-15 00:50:52 +0000234 del dict[word]
235 return self.sort_arg_dict
Guido van Rossumadb31051994-06-23 11:42:52 +0000236
Tim Peters2344fae2001-01-15 00:50:52 +0000237 def sort_stats(self, *field):
238 if not field:
239 self.fcn_list = 0
240 return self
Georg Brandlb1a97af2010-08-02 12:06:18 +0000241 if len(field) == 1 and isinstance(field[0], int):
Tim Peters2344fae2001-01-15 00:50:52 +0000242 # Be compatible with old profiler
Tim Peters7d016852001-10-08 06:13:19 +0000243 field = [ {-1: "stdname",
Georg Brandlb1a97af2010-08-02 12:06:18 +0000244 0: "calls",
245 1: "time",
246 2: "cumulative"}[field[0]] ]
mwidjaja863b1e42018-01-25 20:49:56 -0800247 elif len(field) >= 2:
248 for arg in field[1:]:
249 if type(arg) != type(field[0]):
250 raise TypeError("Can't have mixed argument type")
Tim Peters2344fae2001-01-15 00:50:52 +0000251
252 sort_arg_defs = self.get_sort_arg_defs()
mwidjaja863b1e42018-01-25 20:49:56 -0800253
Tim Peters2344fae2001-01-15 00:50:52 +0000254 sort_tuple = ()
255 self.sort_type = ""
256 connector = ""
257 for word in field:
mwidjaja863b1e42018-01-25 20:49:56 -0800258 if isinstance(word, SortKey):
259 word = word.value
Tim Peters2344fae2001-01-15 00:50:52 +0000260 sort_tuple = sort_tuple + sort_arg_defs[word][0]
Tim Peters7d016852001-10-08 06:13:19 +0000261 self.sort_type += connector + sort_arg_defs[word][1]
Tim Peters2344fae2001-01-15 00:50:52 +0000262 connector = ", "
263
264 stats_list = []
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000265 for func, (cc, nc, tt, ct, callers) in self.stats.items():
Tim Peters7d016852001-10-08 06:13:19 +0000266 stats_list.append((cc, nc, tt, ct) + func +
267 (func_std_string(func), func))
Tim Peters2344fae2001-01-15 00:50:52 +0000268
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000269 stats_list.sort(key=cmp_to_key(TupleComp(sort_tuple).compare))
Tim Peters2344fae2001-01-15 00:50:52 +0000270
271 self.fcn_list = fcn_list = []
272 for tuple in stats_list:
273 fcn_list.append(tuple[-1])
274 return self
275
Tim Peters2344fae2001-01-15 00:50:52 +0000276 def reverse_order(self):
Tim Peters7d016852001-10-08 06:13:19 +0000277 if self.fcn_list:
278 self.fcn_list.reverse()
Tim Peters2344fae2001-01-15 00:50:52 +0000279 return self
280
281 def strip_dirs(self):
282 oldstats = self.stats
283 self.stats = newstats = {}
284 max_name_len = 0
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000285 for func, (cc, nc, tt, ct, callers) in oldstats.items():
Tim Peters2344fae2001-01-15 00:50:52 +0000286 newfunc = func_strip_path(func)
287 if len(func_std_string(newfunc)) > max_name_len:
288 max_name_len = len(func_std_string(newfunc))
289 newcallers = {}
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000290 for func2, caller in callers.items():
Raymond Hettingere0d49722002-06-02 18:55:56 +0000291 newcallers[func_strip_path(func2)] = caller
Tim Peters2344fae2001-01-15 00:50:52 +0000292
Raymond Hettinger54f02222002-06-01 14:18:47 +0000293 if newfunc in newstats:
Tim Peters7d016852001-10-08 06:13:19 +0000294 newstats[newfunc] = add_func_stats(
295 newstats[newfunc],
296 (cc, nc, tt, ct, newcallers))
Tim Peters2344fae2001-01-15 00:50:52 +0000297 else:
298 newstats[newfunc] = (cc, nc, tt, ct, newcallers)
299 old_top = self.top_level
Georg Brandleb7e5692010-10-22 06:29:21 +0000300 self.top_level = new_top = set()
Raymond Hettingere0d49722002-06-02 18:55:56 +0000301 for func in old_top:
Georg Brandleb7e5692010-10-22 06:29:21 +0000302 new_top.add(func_strip_path(func))
Tim Peters2344fae2001-01-15 00:50:52 +0000303
304 self.max_name_len = max_name_len
305
306 self.fcn_list = None
307 self.all_callees = None
308 return self
Guido van Rossumadb31051994-06-23 11:42:52 +0000309
Tim Peters2344fae2001-01-15 00:50:52 +0000310 def calc_callees(self):
Georg Brandl9a8439d2010-10-22 06:35:59 +0000311 if self.all_callees:
312 return
Tim Peters2344fae2001-01-15 00:50:52 +0000313 self.all_callees = all_callees = {}
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000314 for func, (cc, nc, tt, ct, callers) in self.stats.items():
Raymond Hettinger54f02222002-06-01 14:18:47 +0000315 if not func in all_callees:
Tim Peters2344fae2001-01-15 00:50:52 +0000316 all_callees[func] = {}
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000317 for func2, caller in callers.items():
Raymond Hettinger54f02222002-06-01 14:18:47 +0000318 if not func2 in all_callees:
Tim Peters2344fae2001-01-15 00:50:52 +0000319 all_callees[func2] = {}
Raymond Hettingere0d49722002-06-02 18:55:56 +0000320 all_callees[func2][func] = caller
Tim Peters2344fae2001-01-15 00:50:52 +0000321 return
Guido van Rossumadb31051994-06-23 11:42:52 +0000322
Tim Peters2344fae2001-01-15 00:50:52 +0000323 #******************************************************************
324 # The following functions support actual printing of reports
325 #******************************************************************
Guido van Rossumadb31051994-06-23 11:42:52 +0000326
Tim Peters2344fae2001-01-15 00:50:52 +0000327 # Optional "amount" is either a line count, or a percentage of lines.
Guido van Rossumadb31051994-06-23 11:42:52 +0000328
Tim Peters2344fae2001-01-15 00:50:52 +0000329 def eval_print_amount(self, sel, list, msg):
330 new_list = list
Georg Brandlb1a97af2010-08-02 12:06:18 +0000331 if isinstance(sel, str):
332 try:
333 rex = re.compile(sel)
334 except re.error:
335 msg += " <Invalid regular expression %r>\n" % sel
336 return new_list, msg
Tim Peters2344fae2001-01-15 00:50:52 +0000337 new_list = []
338 for func in list:
Georg Brandlb1a97af2010-08-02 12:06:18 +0000339 if rex.search(func_std_string(func)):
Tim Peters2344fae2001-01-15 00:50:52 +0000340 new_list.append(func)
341 else:
342 count = len(list)
Georg Brandlb1a97af2010-08-02 12:06:18 +0000343 if isinstance(sel, float) and 0.0 <= sel < 1.0:
Tim Peters7d016852001-10-08 06:13:19 +0000344 count = int(count * sel + .5)
Tim Peters2344fae2001-01-15 00:50:52 +0000345 new_list = list[:count]
Georg Brandlb1a97af2010-08-02 12:06:18 +0000346 elif isinstance(sel, int) and 0 <= sel < count:
Tim Peters2344fae2001-01-15 00:50:52 +0000347 count = sel
348 new_list = list[:count]
349 if len(list) != len(new_list):
Georg Brandlb1a97af2010-08-02 12:06:18 +0000350 msg += " List reduced from %r to %r due to restriction <%r>\n" % (
351 len(list), len(new_list), sel)
Guido van Rossumadb31051994-06-23 11:42:52 +0000352
Tim Peters2344fae2001-01-15 00:50:52 +0000353 return new_list, msg
Guido van Rossumadb31051994-06-23 11:42:52 +0000354
Daniel Olshansky01602ae2020-01-15 17:51:54 -0500355 def get_stats_profile(self):
356 """This method returns an instance of StatsProfile, which contains a mapping
357 of function names to instances of FunctionProfile. Each FunctionProfile
358 instance holds information related to the function's profile such as how
359 long the function took to run, how many times it was called, etc...
360 """
361 func_list = self.fcn_list[:] if self.fcn_list else list(self.stats.keys())
362 if not func_list:
363 return StatsProfile(0, {})
364
365 total_tt = float(f8(self.total_tt))
366 func_profiles = {}
367 stats_profile = StatsProfile(total_tt, func_profiles)
368
369 for func in func_list:
370 cc, nc, tt, ct, callers = self.stats[func]
371 file_name, line_number, func_name = func
372 ncalls = str(nc) if nc == cc else (str(nc) + '/' + str(cc))
373 tottime = float(f8(tt))
374 percall_tottime = -1 if nc == 0 else float(f8(tt/nc))
375 cumtime = float(f8(ct))
376 percall_cumtime = -1 if cc == 0 else float(f8(ct/cc))
377 func_profile = FunctionProfile(
378 ncalls,
379 tottime, # time spent in this function alone
380 percall_tottime,
381 cumtime, # time spent in the function plus all functions that this function called,
382 percall_cumtime,
383 file_name,
384 line_number
385 )
386 func_profiles[func_name] = func_profile
387
388 return stats_profile
389
Tim Peters2344fae2001-01-15 00:50:52 +0000390 def get_print_list(self, sel_list):
391 width = self.max_name_len
392 if self.fcn_list:
Georg Brandlb1a97af2010-08-02 12:06:18 +0000393 stat_list = self.fcn_list[:]
Tim Peters2344fae2001-01-15 00:50:52 +0000394 msg = " Ordered by: " + self.sort_type + '\n'
395 else:
Georg Brandlb1a97af2010-08-02 12:06:18 +0000396 stat_list = list(self.stats.keys())
Tim Peters2344fae2001-01-15 00:50:52 +0000397 msg = " Random listing order was used\n"
398
399 for selection in sel_list:
Georg Brandlb1a97af2010-08-02 12:06:18 +0000400 stat_list, msg = self.eval_print_amount(selection, stat_list, msg)
Tim Peters2344fae2001-01-15 00:50:52 +0000401
Georg Brandlb1a97af2010-08-02 12:06:18 +0000402 count = len(stat_list)
Tim Peters2344fae2001-01-15 00:50:52 +0000403
Georg Brandlb1a97af2010-08-02 12:06:18 +0000404 if not stat_list:
405 return 0, stat_list
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000406 print(msg, file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000407 if count < len(self.stats):
408 width = 0
Georg Brandlb1a97af2010-08-02 12:06:18 +0000409 for func in stat_list:
Tim Peters2344fae2001-01-15 00:50:52 +0000410 if len(func_std_string(func)) > width:
411 width = len(func_std_string(func))
Georg Brandlb1a97af2010-08-02 12:06:18 +0000412 return width+2, stat_list
Tim Peters2344fae2001-01-15 00:50:52 +0000413
414 def print_stats(self, *amount):
415 for filename in self.files:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000416 print(filename, file=self.stream)
Georg Brandl9a8439d2010-10-22 06:35:59 +0000417 if self.files:
418 print(file=self.stream)
Tim Peters7d016852001-10-08 06:13:19 +0000419 indent = ' ' * 8
Raymond Hettingere0d49722002-06-02 18:55:56 +0000420 for func in self.top_level:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000421 print(indent, func_get_function_name(func), file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000422
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000423 print(indent, self.total_calls, "function calls", end=' ', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000424 if self.total_calls != self.prim_calls:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000425 print("(%d primitive calls)" % self.prim_calls, end=' ', file=self.stream)
Senthil Kumaran5e703cf2010-11-20 17:02:50 +0000426 print("in %.3f seconds" % self.total_tt, file=self.stream)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000427 print(file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000428 width, list = self.get_print_list(amount)
429 if list:
430 self.print_title()
431 for func in list:
432 self.print_line(func)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000433 print(file=self.stream)
434 print(file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000435 return self
Guido van Rossumadb31051994-06-23 11:42:52 +0000436
Tim Peters2344fae2001-01-15 00:50:52 +0000437 def print_callees(self, *amount):
438 width, list = self.get_print_list(amount)
439 if list:
440 self.calc_callees()
441
442 self.print_call_heading(width, "called...")
443 for func in list:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000444 if func in self.all_callees:
Tim Peters7d016852001-10-08 06:13:19 +0000445 self.print_call_line(width, func, self.all_callees[func])
Tim Peters2344fae2001-01-15 00:50:52 +0000446 else:
447 self.print_call_line(width, func, {})
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000448 print(file=self.stream)
449 print(file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000450 return self
451
452 def print_callers(self, *amount):
453 width, list = self.get_print_list(amount)
454 if list:
455 self.print_call_heading(width, "was called by...")
456 for func in list:
457 cc, nc, tt, ct, callers = self.stats[func]
Armin Rigoa871ef22006-02-08 12:53:56 +0000458 self.print_call_line(width, func, callers, "<-")
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000459 print(file=self.stream)
460 print(file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000461 return self
462
463 def print_call_heading(self, name_size, column_title):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000464 print("Function ".ljust(name_size) + column_title, file=self.stream)
Armin Rigoa871ef22006-02-08 12:53:56 +0000465 # print sub-header only if we have new-style callers
466 subheader = False
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000467 for cc, nc, tt, ct, callers in self.stats.values():
Armin Rigoa871ef22006-02-08 12:53:56 +0000468 if callers:
Georg Brandla18af4e2007-04-21 15:47:16 +0000469 value = next(iter(callers.values()))
Armin Rigoa871ef22006-02-08 12:53:56 +0000470 subheader = isinstance(value, tuple)
471 break
472 if subheader:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000473 print(" "*name_size + " ncalls tottime cumtime", file=self.stream)
Guido van Rossumadb31051994-06-23 11:42:52 +0000474
Armin Rigoa871ef22006-02-08 12:53:56 +0000475 def print_call_line(self, name_size, source, call_dict, arrow="->"):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000476 print(func_std_string(source).ljust(name_size) + arrow, end=' ', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000477 if not call_dict:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000478 print(file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000479 return
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000480 clist = sorted(call_dict.keys())
Tim Peters2344fae2001-01-15 00:50:52 +0000481 indent = ""
482 for func in clist:
483 name = func_std_string(func)
Armin Rigoa871ef22006-02-08 12:53:56 +0000484 value = call_dict[func]
485 if isinstance(value, tuple):
486 nc, cc, tt, ct = value
487 if nc != cc:
488 substats = '%d/%d' % (nc, cc)
489 else:
490 substats = '%d' % (nc,)
491 substats = '%s %s %s %s' % (substats.rjust(7+2*len(indent)),
492 f8(tt), f8(ct), name)
493 left_width = name_size + 1
494 else:
495 substats = '%s(%r) %s' % (name, value, f8(self.stats[func][3]))
496 left_width = name_size + 3
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000497 print(indent*left_width + substats, file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000498 indent = " "
499
Tim Peters2344fae2001-01-15 00:50:52 +0000500 def print_title(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000501 print(' ncalls tottime percall cumtime percall', end=' ', file=self.stream)
502 print('filename:lineno(function)', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000503
Georg Brandl9a8439d2010-10-22 06:35:59 +0000504 def print_line(self, func): # hack: should print percentages
Tim Peters2344fae2001-01-15 00:50:52 +0000505 cc, nc, tt, ct, callers = self.stats[func]
Tim Peters7d016852001-10-08 06:13:19 +0000506 c = str(nc)
Tim Peters2344fae2001-01-15 00:50:52 +0000507 if nc != cc:
Tim Peters7d016852001-10-08 06:13:19 +0000508 c = c + '/' + str(cc)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000509 print(c.rjust(9), end=' ', file=self.stream)
510 print(f8(tt), end=' ', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000511 if nc == 0:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000512 print(' '*8, end=' ', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000513 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000514 print(f8(tt/nc), end=' ', file=self.stream)
515 print(f8(ct), end=' ', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000516 if cc == 0:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000517 print(' '*8, end=' ', file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000518 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000519 print(f8(ct/cc), end=' ', file=self.stream)
520 print(func_std_string(func), file=self.stream)
Tim Peters2344fae2001-01-15 00:50:52 +0000521
Guido van Rossumadb31051994-06-23 11:42:52 +0000522class TupleComp:
Tim Peters2344fae2001-01-15 00:50:52 +0000523 """This class provides a generic function for comparing any two tuples.
524 Each instance records a list of tuple-indices (from most significant
525 to least significant), and sort direction (ascending or decending) for
526 each tuple-index. The compare functions can then be used as the function
527 argument to the system sort() function when a list of tuples need to be
528 sorted in the instances order."""
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000529
Tim Peters2344fae2001-01-15 00:50:52 +0000530 def __init__(self, comp_select_list):
531 self.comp_select_list = comp_select_list
Guido van Rossumadb31051994-06-23 11:42:52 +0000532
Tim Peters2344fae2001-01-15 00:50:52 +0000533 def compare (self, left, right):
534 for index, direction in self.comp_select_list:
535 l = left[index]
536 r = right[index]
537 if l < r:
538 return -direction
539 if l > r:
540 return direction
541 return 0
Guido van Rossumadb31051994-06-23 11:42:52 +0000542
Raymond Hettinger70b64fc2008-01-30 20:15:17 +0000543
Guido van Rossumadb31051994-06-23 11:42:52 +0000544#**************************************************************************
Tim Peters7d016852001-10-08 06:13:19 +0000545# func_name is a triple (file:string, line:int, name:string)
Guido van Rossumadb31051994-06-23 11:42:52 +0000546
547def func_strip_path(func_name):
Fred Drake9c439102003-05-14 14:28:09 +0000548 filename, line, name = func_name
549 return os.path.basename(filename), line, name
Guido van Rossumadb31051994-06-23 11:42:52 +0000550
551def func_get_function_name(func):
Tim Peters2344fae2001-01-15 00:50:52 +0000552 return func[2]
Guido van Rossumadb31051994-06-23 11:42:52 +0000553
554def func_std_string(func_name): # match what old profile produced
Armin Rigoa871ef22006-02-08 12:53:56 +0000555 if func_name[:2] == ('~', 0):
556 # special case for built-in functions
557 name = func_name[2]
558 if name.startswith('<') and name.endswith('>'):
559 return '{%s}' % name[1:-1]
560 else:
561 return name
562 else:
563 return "%s:%d(%s)" % func_name
Guido van Rossumadb31051994-06-23 11:42:52 +0000564
565#**************************************************************************
penguindustin96466302019-05-06 14:57:17 -0400566# The following functions combine statistics for pairs functions.
Guido van Rossumadb31051994-06-23 11:42:52 +0000567# The bulk of the processing involves correctly handling "call" lists,
Tim Peters2344fae2001-01-15 00:50:52 +0000568# such as callers and callees.
Guido van Rossumadb31051994-06-23 11:42:52 +0000569#**************************************************************************
570
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000571def add_func_stats(target, source):
Tim Peters2344fae2001-01-15 00:50:52 +0000572 """Add together all the stats for two profile entries."""
573 cc, nc, tt, ct, callers = source
574 t_cc, t_nc, t_tt, t_ct, t_callers = target
Tim Peters7d016852001-10-08 06:13:19 +0000575 return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct,
Tim Peters2344fae2001-01-15 00:50:52 +0000576 add_callers(t_callers, callers))
Guido van Rossumadb31051994-06-23 11:42:52 +0000577
Guido van Rossumadb31051994-06-23 11:42:52 +0000578def add_callers(target, source):
Tim Peters2344fae2001-01-15 00:50:52 +0000579 """Combine two caller lists in a single list."""
580 new_callers = {}
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000581 for func, caller in target.items():
Raymond Hettingere0d49722002-06-02 18:55:56 +0000582 new_callers[func] = caller
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000583 for func, caller in source.items():
Raymond Hettinger54f02222002-06-01 14:18:47 +0000584 if func in new_callers:
Georg Brandl2d3c4e72010-08-02 17:24:49 +0000585 if isinstance(caller, tuple):
586 # format used by cProfile
Serhiy Storchaka3f2e6f12018-02-26 16:50:11 +0200587 new_callers[func] = tuple(i + j for i, j in zip(caller, new_callers[func]))
Georg Brandl2d3c4e72010-08-02 17:24:49 +0000588 else:
589 # format used by profile
590 new_callers[func] += caller
Tim Peters2344fae2001-01-15 00:50:52 +0000591 else:
Raymond Hettingere0d49722002-06-02 18:55:56 +0000592 new_callers[func] = caller
Tim Peters2344fae2001-01-15 00:50:52 +0000593 return new_callers
Guido van Rossumadb31051994-06-23 11:42:52 +0000594
Guido van Rossumadb31051994-06-23 11:42:52 +0000595def count_calls(callers):
Tim Peters2344fae2001-01-15 00:50:52 +0000596 """Sum the caller statistics to get total number of calls received."""
597 nc = 0
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000598 for calls in callers.values():
Raymond Hettingere0d49722002-06-02 18:55:56 +0000599 nc += calls
Tim Peters2344fae2001-01-15 00:50:52 +0000600 return nc
Guido van Rossumadb31051994-06-23 11:42:52 +0000601
602#**************************************************************************
603# The following functions support printing of reports
604#**************************************************************************
605
606def f8(x):
Tim Peters7d016852001-10-08 06:13:19 +0000607 return "%8.3f" % x
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000608
609#**************************************************************************
610# Statistics browser added by ESR, April 2001
611#**************************************************************************
612
613if __name__ == '__main__':
614 import cmd
Eric S. Raymond9cb98572001-04-14 01:48:41 +0000615 try:
616 import readline
Brett Cannoncd171c82013-07-04 17:43:24 -0400617 except ImportError:
Eric S. Raymond9cb98572001-04-14 01:48:41 +0000618 pass
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000619
620 class ProfileBrowser(cmd.Cmd):
621 def __init__(self, profile=None):
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000622 cmd.Cmd.__init__(self)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000623 self.prompt = "% "
Georg Brandlb1a97af2010-08-02 12:06:18 +0000624 self.stats = None
625 self.stream = sys.stdout
Raymond Hettinger16e3c422002-06-01 16:07:16 +0000626 if profile is not None:
Georg Brandlb1a97af2010-08-02 12:06:18 +0000627 self.do_read(profile)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000628
629 def generic(self, fn, line):
630 args = line.split()
631 processed = []
632 for term in args:
633 try:
634 processed.append(int(term))
635 continue
636 except ValueError:
637 pass
638 try:
639 frac = float(term)
640 if frac > 1 or frac < 0:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000641 print("Fraction argument must be in [0, 1]", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000642 continue
643 processed.append(frac)
644 continue
645 except ValueError:
646 pass
647 processed.append(term)
648 if self.stats:
Guido van Rossum68468eb2003-02-27 20:14:51 +0000649 getattr(self.stats, fn)(*processed)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000650 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000651 print("No statistics object is loaded.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000652 return 0
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000653 def generic_help(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000654 print("Arguments may be:", file=self.stream)
655 print("* An integer maximum number of entries to print.", file=self.stream)
656 print("* A decimal fractional number between 0 and 1, controlling", file=self.stream)
657 print(" what fraction of selected entries to print.", file=self.stream)
658 print("* A regular expression; only entries with function names", file=self.stream)
659 print(" that match it are printed.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000660
661 def do_add(self, line):
Georg Brandl3e4f2ec2010-08-01 07:48:43 +0000662 if self.stats:
Stefan Krahe12a68b2016-08-02 22:30:24 +0200663 try:
664 self.stats.add(line)
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300665 except OSError as e:
Stefan Krahe12a68b2016-08-02 22:30:24 +0200666 print("Failed to load statistics for %s: %s" % (line, e), file=self.stream)
Georg Brandl3e4f2ec2010-08-01 07:48:43 +0000667 else:
668 print("No statistics object is loaded.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000669 return 0
670 def help_add(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000671 print("Add profile info from given file to current statistics object.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000672
673 def do_callees(self, line):
Eric S. Raymond3c1858a2001-04-14 15:16:05 +0000674 return self.generic('print_callees', line)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000675 def help_callees(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000676 print("Print callees statistics from the current stat object.", file=self.stream)
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000677 self.generic_help()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000678
679 def do_callers(self, line):
Eric S. Raymond3c1858a2001-04-14 15:16:05 +0000680 return self.generic('print_callers', line)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000681 def help_callers(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000682 print("Print callers statistics from the current stat object.", file=self.stream)
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000683 self.generic_help()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000684
685 def do_EOF(self, line):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000686 print("", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000687 return 1
688 def help_EOF(self):
Xtreak0d702272019-06-03 04:42:33 +0530689 print("Leave the profile browser.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000690
691 def do_quit(self, line):
692 return 1
693 def help_quit(self):
Xtreak0d702272019-06-03 04:42:33 +0530694 print("Leave the profile browser.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000695
696 def do_read(self, line):
697 if line:
698 try:
699 self.stats = Stats(line)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200700 except OSError as err:
Georg Brandl50da60c2008-01-06 21:38:54 +0000701 print(err.args[1], file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000702 return
Georg Brandlf02e7362010-08-01 07:57:47 +0000703 except Exception as err:
704 print(err.__class__.__name__ + ':', err, file=self.stream)
705 return
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000706 self.prompt = line + "% "
Martin v. Löwis22adac52001-06-07 05:49:05 +0000707 elif len(self.prompt) > 2:
Georg Brandlf02e7362010-08-01 07:57:47 +0000708 line = self.prompt[:-2]
709 self.do_read(line)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000710 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000711 print("No statistics object is current -- cannot reload.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000712 return 0
713 def help_read(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000714 print("Read in profile data from a specified file.", file=self.stream)
Georg Brandlf02e7362010-08-01 07:57:47 +0000715 print("Without argument, reload the current file.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000716
717 def do_reverse(self, line):
Georg Brandl3e4f2ec2010-08-01 07:48:43 +0000718 if self.stats:
719 self.stats.reverse_order()
720 else:
721 print("No statistics object is loaded.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000722 return 0
723 def help_reverse(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000724 print("Reverse the sort order of the profiling report.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000725
726 def do_sort(self, line):
Georg Brandl3e4f2ec2010-08-01 07:48:43 +0000727 if not self.stats:
728 print("No statistics object is loaded.", file=self.stream)
729 return
Raymond Hettingere0d49722002-06-02 18:55:56 +0000730 abbrevs = self.stats.get_sort_arg_defs()
Andrew M. Kuchling0a628232010-04-02 17:02:57 +0000731 if line and all((x in abbrevs) for x in line.split()):
Guido van Rossum68468eb2003-02-27 20:14:51 +0000732 self.stats.sort_stats(*line.split())
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000733 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000734 print("Valid sort keys (unique prefixes are accepted):", file=self.stream)
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000735 for (key, value) in Stats.sort_arg_dict_default.items():
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000736 print("%s -- %s" % (key, value[1]), file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000737 return 0
738 def help_sort(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000739 print("Sort profile data according to specified keys.", file=self.stream)
740 print("(Typing `sort' without arguments lists valid keys.)", file=self.stream)
Martin v. Löwis27c430e2001-07-30 10:21:13 +0000741 def complete_sort(self, text, *args):
Raymond Hettingere0d49722002-06-02 18:55:56 +0000742 return [a for a in Stats.sort_arg_dict_default if a.startswith(text)]
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000743
744 def do_stats(self, line):
745 return self.generic('print_stats', line)
746 def help_stats(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000747 print("Print statistics from the current stat object.", file=self.stream)
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000748 self.generic_help()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000749
750 def do_strip(self, line):
Georg Brandl3e4f2ec2010-08-01 07:48:43 +0000751 if self.stats:
752 self.stats.strip_dirs()
753 else:
754 print("No statistics object is loaded.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000755 def help_strip(self):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000756 print("Strip leading path information from filenames in the report.", file=self.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000757
Georg Brandlf02e7362010-08-01 07:57:47 +0000758 def help_help(self):
759 print("Show help for a given command.", file=self.stream)
760
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000761 def postcmd(self, stop, line):
762 if stop:
763 return stop
764 return None
765
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000766 if len(sys.argv) > 1:
767 initprofile = sys.argv[1]
768 else:
769 initprofile = None
770 try:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000771 browser = ProfileBrowser(initprofile)
Antoine Pitrou9d8c1862012-03-14 17:47:11 +0100772 for profile in sys.argv[2:]:
773 browser.do_add(profile)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000774 print("Welcome to the profile statistics browser.", file=browser.stream)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000775 browser.cmdloop()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000776 print("Goodbye.", file=browser.stream)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000777 except KeyboardInterrupt:
778 pass
779
780# That's all, folks.