blob: 51faaf6a0d98678f3f2b4344e238f2e2175950e0 [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Class for printing reports on profiled python code."""
2
Guido van Rossumadb31051994-06-23 11:42:52 +00003# 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 Rossumdabcd001999-04-13 04:24:22 +00008# see profile.doc and profile.py for more info.
Guido van Rossumadb31051994-06-23 11:42:52 +00009
10# Copyright 1994, by InfoSeek Corporation, all rights reserved.
11# Written by James Roskind
Tim Peters2344fae2001-01-15 00:50:52 +000012#
Guido van Rossumadb31051994-06-23 11:42:52 +000013# 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 Peters2344fae2001-01-15 00:50:52 +000025#
Guido van Rossumadb31051994-06-23 11:42:52 +000026# 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
35import os
36import time
Guido van Rossumadb31051994-06-23 11:42:52 +000037import marshal
Guido van Rossum9694fca1997-10-22 21:00:49 +000038import re
Guido van Rossumadb31051994-06-23 11:42:52 +000039
Guido van Rossumadb31051994-06-23 11:42:52 +000040import fpformat
41
Skip Montanaroc62c81e2001-02-12 02:00:42 +000042__all__ = ["Stats"]
43
Guido van Rossumadb31051994-06-23 11:42:52 +000044class Stats:
Tim Peters2344fae2001-01-15 00:50:52 +000045 """This class is used for creating reports from data generated by the
46 Profile class. It is a "friend" of that class, and imports data either
47 by direct access to members of Profile class, or by reading in a dictionary
48 that was emitted (via marshal) from the Profile class.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000049
Tim Peters2344fae2001-01-15 00:50:52 +000050 The big change from the previous Profiler (in terms of raw functionality)
51 is that an "add()" method has been provided to combine Stats from
52 several distinct profile runs. Both the constructor and the add()
53 method now take arbitrarily many file names as arguments.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000054
Tim Peters2344fae2001-01-15 00:50:52 +000055 All the print methods now take an argument that indicates how many lines
56 to print. If the arg is a floating point number between 0 and 1.0, then
57 it is taken as a decimal percentage of the available lines to be printed
58 (e.g., .1 means print 10% of all available lines). If it is an integer,
59 it is taken to mean the number of lines of data that you wish to have
60 printed.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000061
Tim Peters2344fae2001-01-15 00:50:52 +000062 The sort_stats() method now processes some additional options (i.e., in
63 addition to the old -1, 0, 1, or 2). It takes an arbitrary number of quoted
64 strings to select the sort order. For example sort_stats('time', 'name')
65 sorts on the major key of "internal function time", and on the minor
66 key of 'the name of the function'. Look at the two tables in sort_stats()
67 and get_sort_arg_defs(self) for more examples.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000068
Tim Peters2344fae2001-01-15 00:50:52 +000069 All methods now return "self", so you can string together commands like:
70 Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
71 print_stats(5).print_callers(5)
72 """
73
74 def __init__(self, *args):
75 if not len(args):
76 arg = None
77 else:
78 arg = args[0]
79 args = args[1:]
80 self.init(arg)
81 apply(self.add, args).ignore()
82
83 def init(self, arg):
84 self.all_callees = None # calc only if needed
85 self.files = []
86 self.fcn_list = None
87 self.total_tt = 0
88 self.total_calls = 0
89 self.prim_calls = 0
90 self.max_name_len = 0
91 self.top_level = {}
92 self.stats = {}
93 self.sort_arg_dict = {}
94 self.load_stats(arg)
95 trouble = 1
96 try:
97 self.get_top_level_stats()
98 trouble = 0
99 finally:
100 if trouble:
101 print "Invalid timing data",
102 if self.files: print self.files[-1],
103 print
Guido van Rossumadb31051994-06-23 11:42:52 +0000104
105
Tim Peters2344fae2001-01-15 00:50:52 +0000106 def load_stats(self, arg):
107 if not arg: self.stats = {}
108 elif type(arg) == type(""):
109 f = open(arg, 'rb')
110 self.stats = marshal.load(f)
111 f.close()
112 try:
113 file_stats = os.stat(arg)
114 arg = time.ctime(file_stats[8]) + " " + arg
115 except: # in case this is not unix
116 pass
117 self.files = [ arg ]
118 elif hasattr(arg, 'create_stats'):
119 arg.create_stats()
120 self.stats = arg.stats
121 arg.stats = {}
122 if not self.stats:
123 raise TypeError, "Cannot create or construct a " \
124 + `self.__class__` \
125 + " object from '" + `arg` + "'"
126 return
Guido van Rossumadb31051994-06-23 11:42:52 +0000127
Tim Peters2344fae2001-01-15 00:50:52 +0000128 def get_top_level_stats(self):
129 for func in self.stats.keys():
130 cc, nc, tt, ct, callers = self.stats[func]
131 self.total_calls = self.total_calls + nc
132 self.prim_calls = self.prim_calls + cc
133 self.total_tt = self.total_tt + tt
134 if callers.has_key(("jprofile", 0, "profiler")):
135 self.top_level[func] = None
136 if len(func_std_string(func)) > self.max_name_len:
137 self.max_name_len = len(func_std_string(func))
Guido van Rossumadb31051994-06-23 11:42:52 +0000138
Tim Peters2344fae2001-01-15 00:50:52 +0000139 def add(self, *arg_list):
140 if not arg_list: return self
141 if len(arg_list) > 1: apply(self.add, arg_list[1:])
142 other = arg_list[0]
143 if type(self) != type(other) or \
144 self.__class__ != other.__class__:
145 other = Stats(other)
146 self.files = self.files + other.files
147 self.total_calls = self.total_calls + other.total_calls
148 self.prim_calls = self.prim_calls + other.prim_calls
149 self.total_tt = self.total_tt + other.total_tt
150 for func in other.top_level.keys():
151 self.top_level[func] = None
Guido van Rossumadb31051994-06-23 11:42:52 +0000152
Tim Peters2344fae2001-01-15 00:50:52 +0000153 if self.max_name_len < other.max_name_len:
154 self.max_name_len = other.max_name_len
Guido van Rossumadb31051994-06-23 11:42:52 +0000155
Tim Peters2344fae2001-01-15 00:50:52 +0000156 self.fcn_list = None
Guido van Rossumadb31051994-06-23 11:42:52 +0000157
Tim Peters2344fae2001-01-15 00:50:52 +0000158 for func in other.stats.keys():
159 if self.stats.has_key(func):
160 old_func_stat = self.stats[func]
161 else:
162 old_func_stat = (0, 0, 0, 0, {},)
163 self.stats[func] = add_func_stats(old_func_stat, \
164 other.stats[func])
165 return self
Guido van Rossumadb31051994-06-23 11:42:52 +0000166
167
168
Tim Peters2344fae2001-01-15 00:50:52 +0000169 # list the tuple indices and directions for sorting,
170 # along with some printable description
171 sort_arg_dict_default = {\
172 "calls" : (((1,-1), ), "call count"),\
173 "cumulative": (((3,-1), ), "cumulative time"),\
174 "file" : (((4, 1), ), "file name"),\
175 "line" : (((5, 1), ), "line number"),\
176 "module" : (((4, 1), ), "file name"),\
177 "name" : (((6, 1), ), "function name"),\
178 "nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"), \
179 "pcalls" : (((0,-1), ), "call count"),\
180 "stdname" : (((7, 1), ), "standard name"),\
181 "time" : (((2,-1), ), "internal time"),\
182 }
Guido van Rossumadb31051994-06-23 11:42:52 +0000183
Tim Peters2344fae2001-01-15 00:50:52 +0000184 def get_sort_arg_defs(self):
185 """Expand all abbreviations that are unique."""
186 if not self.sort_arg_dict:
187 self.sort_arg_dict = dict = {}
Tim Peters2344fae2001-01-15 00:50:52 +0000188 bad_list = {}
189 for word in self.sort_arg_dict_default.keys():
190 fragment = word
191 while fragment:
192 if not fragment:
193 break
194 if dict.has_key(fragment):
195 bad_list[fragment] = 0
196 break
197 dict[fragment] = self. \
198 sort_arg_dict_default[word]
199 fragment = fragment[:-1]
200 for word in bad_list.keys():
201 del dict[word]
202 return self.sort_arg_dict
Guido van Rossumadb31051994-06-23 11:42:52 +0000203
Guido van Rossumadb31051994-06-23 11:42:52 +0000204
Tim Peters2344fae2001-01-15 00:50:52 +0000205 def sort_stats(self, *field):
206 if not field:
207 self.fcn_list = 0
208 return self
209 if len(field) == 1 and type(field[0]) == type(1):
210 # Be compatible with old profiler
211 field = [ {-1: "stdname", \
212 0:"calls", \
213 1:"time", \
214 2: "cumulative" } [ field[0] ] ]
215
216 sort_arg_defs = self.get_sort_arg_defs()
217 sort_tuple = ()
218 self.sort_type = ""
219 connector = ""
220 for word in field:
221 sort_tuple = sort_tuple + sort_arg_defs[word][0]
222 self.sort_type = self.sort_type + connector + \
223 sort_arg_defs[word][1]
224 connector = ", "
225
226 stats_list = []
227 for func in self.stats.keys():
228 cc, nc, tt, ct, callers = self.stats[func]
229 stats_list.append((cc, nc, tt, ct) + func_split(func) \
230 + (func_std_string(func), func,) )
231
232 stats_list.sort(TupleComp(sort_tuple).compare)
233
234 self.fcn_list = fcn_list = []
235 for tuple in stats_list:
236 fcn_list.append(tuple[-1])
237 return self
238
239
240 def reverse_order(self):
241 if self.fcn_list: self.fcn_list.reverse()
242 return self
243
244 def strip_dirs(self):
245 oldstats = self.stats
246 self.stats = newstats = {}
247 max_name_len = 0
248 for func in oldstats.keys():
249 cc, nc, tt, ct, callers = oldstats[func]
250 newfunc = func_strip_path(func)
251 if len(func_std_string(newfunc)) > max_name_len:
252 max_name_len = len(func_std_string(newfunc))
253 newcallers = {}
254 for func2 in callers.keys():
255 newcallers[func_strip_path(func2)] = \
256 callers[func2]
257
258 if newstats.has_key(newfunc):
259 newstats[newfunc] = add_func_stats( \
260 newstats[newfunc],\
261 (cc, nc, tt, ct, newcallers))
262 else:
263 newstats[newfunc] = (cc, nc, tt, ct, newcallers)
264 old_top = self.top_level
265 self.top_level = new_top = {}
266 for func in old_top.keys():
267 new_top[func_strip_path(func)] = None
268
269 self.max_name_len = max_name_len
270
271 self.fcn_list = None
272 self.all_callees = None
273 return self
Guido van Rossumadb31051994-06-23 11:42:52 +0000274
275
276
Tim Peters2344fae2001-01-15 00:50:52 +0000277 def calc_callees(self):
278 if self.all_callees: return
279 self.all_callees = all_callees = {}
280 for func in self.stats.keys():
281 if not all_callees.has_key(func):
282 all_callees[func] = {}
283 cc, nc, tt, ct, callers = self.stats[func]
284 for func2 in callers.keys():
285 if not all_callees.has_key(func2):
286 all_callees[func2] = {}
287 all_callees[func2][func] = callers[func2]
288 return
Guido van Rossumadb31051994-06-23 11:42:52 +0000289
Tim Peters2344fae2001-01-15 00:50:52 +0000290 #******************************************************************
291 # The following functions support actual printing of reports
292 #******************************************************************
Guido van Rossumadb31051994-06-23 11:42:52 +0000293
Tim Peters2344fae2001-01-15 00:50:52 +0000294 # Optional "amount" is either a line count, or a percentage of lines.
Guido van Rossumadb31051994-06-23 11:42:52 +0000295
Tim Peters2344fae2001-01-15 00:50:52 +0000296 def eval_print_amount(self, sel, list, msg):
297 new_list = list
298 if type(sel) == type(""):
299 new_list = []
300 for func in list:
301 if re.search(sel, func_std_string(func)):
302 new_list.append(func)
303 else:
304 count = len(list)
305 if type(sel) == type(1.0) and 0.0 <= sel < 1.0:
306 count = int (count * sel + .5)
307 new_list = list[:count]
308 elif type(sel) == type(1) and 0 <= sel < count:
309 count = sel
310 new_list = list[:count]
311 if len(list) != len(new_list):
312 msg = msg + " List reduced from " + `len(list)` \
313 + " to " + `len(new_list)` + \
314 " due to restriction <" + `sel` + ">\n"
Guido van Rossumadb31051994-06-23 11:42:52 +0000315
Tim Peters2344fae2001-01-15 00:50:52 +0000316 return new_list, msg
Guido van Rossumadb31051994-06-23 11:42:52 +0000317
318
319
Tim Peters2344fae2001-01-15 00:50:52 +0000320 def get_print_list(self, sel_list):
321 width = self.max_name_len
322 if self.fcn_list:
323 list = self.fcn_list[:]
324 msg = " Ordered by: " + self.sort_type + '\n'
325 else:
326 list = self.stats.keys()
327 msg = " Random listing order was used\n"
328
329 for selection in sel_list:
330 list,msg = self.eval_print_amount(selection, list, msg)
331
332 count = len(list)
333
334 if not list:
335 return 0, list
336 print msg
337 if count < len(self.stats):
338 width = 0
339 for func in list:
340 if len(func_std_string(func)) > width:
341 width = len(func_std_string(func))
342 return width+2, list
343
344 def print_stats(self, *amount):
345 for filename in self.files:
346 print filename
347 if self.files: print
348 indent = " "
349 for func in self.top_level.keys():
350 print indent, func_get_function_name(func)
351
352 print indent, self.total_calls, "function calls",
353 if self.total_calls != self.prim_calls:
354 print "(" + `self.prim_calls`, "primitive calls)",
355 print "in", fpformat.fix(self.total_tt, 3), "CPU seconds"
356 print
357 width, list = self.get_print_list(amount)
358 if list:
359 self.print_title()
360 for func in list:
361 self.print_line(func)
362 print
363 print
364 return self
Guido van Rossumadb31051994-06-23 11:42:52 +0000365
366
Tim Peters2344fae2001-01-15 00:50:52 +0000367 def print_callees(self, *amount):
368 width, list = self.get_print_list(amount)
369 if list:
370 self.calc_callees()
371
372 self.print_call_heading(width, "called...")
373 for func in list:
374 if self.all_callees.has_key(func):
375 self.print_call_line(width, \
376 func, self.all_callees[func])
377 else:
378 self.print_call_line(width, func, {})
379 print
380 print
381 return self
382
383 def print_callers(self, *amount):
384 width, list = self.get_print_list(amount)
385 if list:
386 self.print_call_heading(width, "was called by...")
387 for func in list:
388 cc, nc, tt, ct, callers = self.stats[func]
389 self.print_call_line(width, func, callers)
390 print
391 print
392 return self
393
394 def print_call_heading(self, name_size, column_title):
Eric S. Raymond373c55e2001-02-09 08:25:29 +0000395 print "Function ".ljust(name_size) + column_title
Guido van Rossumadb31051994-06-23 11:42:52 +0000396
397
Tim Peters2344fae2001-01-15 00:50:52 +0000398 def print_call_line(self, name_size, source, call_dict):
Eric S. Raymond373c55e2001-02-09 08:25:29 +0000399 print func_std_string(source).ljust(name_size),
Tim Peters2344fae2001-01-15 00:50:52 +0000400 if not call_dict:
401 print "--"
402 return
403 clist = call_dict.keys()
404 clist.sort()
405 name_size = name_size + 1
406 indent = ""
407 for func in clist:
408 name = func_std_string(func)
409 print indent*name_size + name + '(' \
410 + `call_dict[func]`+')', \
411 f8(self.stats[func][3])
412 indent = " "
413
414
415
416 def print_title(self):
Eric S. Raymond373c55e2001-02-09 08:25:29 +0000417 print 'ncalls'.rjust(9),
418 print 'tottime'.rjust(8),
419 print 'percall'.rjust(8),
420 print 'cumtime'.rjust(8),
421 print 'percall'.rjust(8),
Tim Peters2344fae2001-01-15 00:50:52 +0000422 print 'filename:lineno(function)'
423
424
425 def print_line(self, func): # hack : should print percentages
426 cc, nc, tt, ct, callers = self.stats[func]
427 c = `nc`
428 if nc != cc:
429 c = c + '/' + `cc`
Eric S. Raymond373c55e2001-02-09 08:25:29 +0000430 print c.rjust(9),
Tim Peters2344fae2001-01-15 00:50:52 +0000431 print f8(tt),
432 if nc == 0:
433 print ' '*8,
434 else:
435 print f8(tt/nc),
436 print f8(ct),
437 if cc == 0:
438 print ' '*8,
439 else:
440 print f8(ct/cc),
441 print func_std_string(func)
442
443
444 def ignore(self):
445 pass # has no return value, so use at end of line :-)
Guido van Rossumadb31051994-06-23 11:42:52 +0000446
447
Guido van Rossumadb31051994-06-23 11:42:52 +0000448class TupleComp:
Tim Peters2344fae2001-01-15 00:50:52 +0000449 """This class provides a generic function for comparing any two tuples.
450 Each instance records a list of tuple-indices (from most significant
451 to least significant), and sort direction (ascending or decending) for
452 each tuple-index. The compare functions can then be used as the function
453 argument to the system sort() function when a list of tuples need to be
454 sorted in the instances order."""
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000455
Tim Peters2344fae2001-01-15 00:50:52 +0000456 def __init__(self, comp_select_list):
457 self.comp_select_list = comp_select_list
Guido van Rossumadb31051994-06-23 11:42:52 +0000458
Tim Peters2344fae2001-01-15 00:50:52 +0000459 def compare (self, left, right):
460 for index, direction in self.comp_select_list:
461 l = left[index]
462 r = right[index]
463 if l < r:
464 return -direction
465 if l > r:
466 return direction
467 return 0
Guido van Rossumadb31051994-06-23 11:42:52 +0000468
Tim Peters2344fae2001-01-15 00:50:52 +0000469
Guido van Rossumadb31051994-06-23 11:42:52 +0000470
471#**************************************************************************
472
473def func_strip_path(func_name):
Tim Peters2344fae2001-01-15 00:50:52 +0000474 file, line, name = func_name
475 return os.path.basename(file), line, name
Guido van Rossumadb31051994-06-23 11:42:52 +0000476
477def func_get_function_name(func):
Tim Peters2344fae2001-01-15 00:50:52 +0000478 return func[2]
Guido van Rossumadb31051994-06-23 11:42:52 +0000479
480def func_std_string(func_name): # match what old profile produced
Tim Peters2344fae2001-01-15 00:50:52 +0000481 file, line, name = func_name
482 return file + ":" + `line` + "(" + name + ")"
Guido van Rossumadb31051994-06-23 11:42:52 +0000483
484def func_split(func_name):
Tim Peters2344fae2001-01-15 00:50:52 +0000485 return func_name
Guido van Rossumadb31051994-06-23 11:42:52 +0000486
487#**************************************************************************
488# The following functions combine statists for pairs functions.
489# The bulk of the processing involves correctly handling "call" lists,
Tim Peters2344fae2001-01-15 00:50:52 +0000490# such as callers and callees.
Guido van Rossumadb31051994-06-23 11:42:52 +0000491#**************************************************************************
492
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000493def add_func_stats(target, source):
Tim Peters2344fae2001-01-15 00:50:52 +0000494 """Add together all the stats for two profile entries."""
495 cc, nc, tt, ct, callers = source
496 t_cc, t_nc, t_tt, t_ct, t_callers = target
497 return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct, \
498 add_callers(t_callers, callers))
Guido van Rossumadb31051994-06-23 11:42:52 +0000499
500
Guido van Rossumadb31051994-06-23 11:42:52 +0000501def add_callers(target, source):
Tim Peters2344fae2001-01-15 00:50:52 +0000502 """Combine two caller lists in a single list."""
503 new_callers = {}
504 for func in target.keys():
505 new_callers[func] = target[func]
506 for func in source.keys():
507 if new_callers.has_key(func):
508 new_callers[func] = source[func] + new_callers[func]
509 else:
510 new_callers[func] = source[func]
511 return new_callers
Guido van Rossumadb31051994-06-23 11:42:52 +0000512
Guido van Rossumadb31051994-06-23 11:42:52 +0000513def count_calls(callers):
Tim Peters2344fae2001-01-15 00:50:52 +0000514 """Sum the caller statistics to get total number of calls received."""
515 nc = 0
516 for func in callers.keys():
517 nc = nc + callers[func]
518 return nc
Guido van Rossumadb31051994-06-23 11:42:52 +0000519
520#**************************************************************************
521# The following functions support printing of reports
522#**************************************************************************
523
524def f8(x):
Eric S. Raymond373c55e2001-02-09 08:25:29 +0000525 return fpformat.fix(x, 3).rjust(8)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000526
527#**************************************************************************
528# Statistics browser added by ESR, April 2001
529#**************************************************************************
530
531if __name__ == '__main__':
532 import cmd
Eric S. Raymond9cb98572001-04-14 01:48:41 +0000533 try:
534 import readline
Fred Drakee8187612001-05-11 19:21:41 +0000535 except ImportError:
Eric S. Raymond9cb98572001-04-14 01:48:41 +0000536 pass
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000537
538 class ProfileBrowser(cmd.Cmd):
539 def __init__(self, profile=None):
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000540 cmd.Cmd.__init__(self)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000541 self.prompt = "% "
542 if profile:
543 self.stats = Stats(profile)
544 else:
545 self.stats = None
546
547 def generic(self, fn, line):
548 args = line.split()
549 processed = []
550 for term in args:
551 try:
552 processed.append(int(term))
553 continue
554 except ValueError:
555 pass
556 try:
557 frac = float(term)
558 if frac > 1 or frac < 0:
559 print "Fraction argument mus be in [0, 1]"
560 continue
561 processed.append(frac)
562 continue
563 except ValueError:
564 pass
565 processed.append(term)
566 if self.stats:
567 apply(getattr(self.stats, fn), processed)
568 else:
569 print "No statistics object is loaded."
570 return 0
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000571 def generic_help(self):
572 print "Arguments may be:"
573 print "* An integer maximum number of entries to print."
574 print "* A decimal fractional number between 0 and 1, controlling"
575 print " what fraction of selected entries to print."
576 print "* A regular expression; only entries with function names"
577 print " that match it are printed."
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000578
579 def do_add(self, line):
580 self.stats.add(line)
581 return 0
582 def help_add(self):
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000583 print "Add profile info from given file to current statistics object."
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000584
585 def do_callees(self, line):
Eric S. Raymond3c1858a2001-04-14 15:16:05 +0000586 return self.generic('print_callees', line)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000587 def help_callees(self):
588 print "Print callees statistics from the current stat object."
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000589 self.generic_help()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000590
591 def do_callers(self, line):
Eric S. Raymond3c1858a2001-04-14 15:16:05 +0000592 return self.generic('print_callers', line)
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000593 def help_callers(self):
594 print "Print callers statistics from the current stat object."
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000595 self.generic_help()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000596
597 def do_EOF(self, line):
598 print ""
599 return 1
600 def help_EOF(self):
601 print "Leave the profile brower."
602
603 def do_quit(self, line):
604 return 1
605 def help_quit(self):
606 print "Leave the profile brower."
607
608 def do_read(self, line):
609 if line:
610 try:
611 self.stats = Stats(line)
612 except IOError, args:
613 print args[1]
614 return
615 self.prompt = line + "% "
Martin v. Löwis22adac52001-06-07 05:49:05 +0000616 elif len(self.prompt) > 2:
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000617 line = self.prompt[-2:]
618 else:
619 print "No statistics object is current -- cannot reload."
620 return 0
621 def help_read(self):
622 print "Read in profile data from a specified file."
623
624 def do_reverse(self, line):
625 self.stats.reverse_order()
626 return 0
627 def help_reverse(self):
628 print "Reverse the sort order of the profiling report."
629
630 def do_sort(self, line):
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000631 abbrevs = self.stats.get_sort_arg_defs().keys()
632 if line and not filter(lambda x,a=abbrevs: x not in a,line.split()):
633 apply(self.stats.sort_stats, line.split())
634 else:
635 print "Valid sort keys (unique prefixes are accepted):"
636 for (key, value) in Stats.sort_arg_dict_default.items():
637 print "%s -- %s" % (key, value[1])
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000638 return 0
639 def help_sort(self):
640 print "Sort profile data according to specified keys."
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000641 print "(Typing `sort' without arguments lists valid keys.)"
Martin v. Löwis27c430e2001-07-30 10:21:13 +0000642 def complete_sort(self, text, *args):
643 return [a for a in Stats.sort_arg_dict_default.keys() if a.startswith(text)]
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000644
645 def do_stats(self, line):
646 return self.generic('print_stats', line)
647 def help_stats(self):
648 print "Print statistics from the current stat object."
Eric S. Raymond53b809d2001-04-26 07:32:38 +0000649 self.generic_help()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000650
651 def do_strip(self, line):
Eric S. Raymond3c1858a2001-04-14 15:16:05 +0000652 self.stats.strip_dirs()
Eric S. Raymond4f3980d2001-04-13 00:23:01 +0000653 return 0
654 def help_strip(self):
655 print "Strip leading path information from filenames in the report."
656
657 def postcmd(self, stop, line):
658 if stop:
659 return stop
660 return None
661
662 import sys
663 print "Welcome to the profile statistics browser."
664 if len(sys.argv) > 1:
665 initprofile = sys.argv[1]
666 else:
667 initprofile = None
668 try:
669 ProfileBrowser(initprofile).cmdloop()
670 print "Goodbye."
671 except KeyboardInterrupt:
672 pass
673
674# That's all, folks.