2 # Class for profiling python code.
3 # Author: Sjoerd Mullender
4 # Hacked somewhat by: Guido van Rossum
6 # See the accompanying document profile.doc for more information.
21 self
.profile_func
= None
24 def profile(self
, funcname
):
25 if not self
.profile_func
:
26 self
.profile_func
= {}
27 self
.profile_func
[funcname
] = 1
29 def trace_dispatch(self
, frame
, event
, arg
):
31 funcname
= codehack
.getcodename(frame
.f_code
)
32 if self
.profile_func
and not self
.profiling
:
33 if self
.profile_func
.has_key(funcname
):
38 if frame
.f_locals
.has_key('__key'):
39 key
= frame
.f_locals
['__key']
41 lineno
= codehack
.getlineno(frame
.f_code
)
42 filename
= frame
.f_code
.co_filename
43 key
= filename
+ ':' + `lineno`
+ '(' + funcname
+ ')'
44 frame
.f_locals
['__key'] = key
45 self
.call_level
= depth(frame
)
46 self
.cur_frame
= frame
49 s0
= 'call: ' + key
+ ' depth: ' + `self
.call_level`
+ ' time: ' + `t`
51 if pframe
.f_locals
.has_key('__key'):
52 pkey
= pframe
.f_locals
['__key']
54 pkey
= pframe
.f_code
.co_filename
+ \
56 `codehack
.getlineno(pframe
.f_code
)` \
58 codehack
.getcodename(pframe
.f_code
) \
60 pframe
.f_locals
['__key'] = pkey
62 s1
= 'parent: ' + pkey
63 if pframe
.f_locals
.has_key('__start_time'):
64 st
= pframe
.f_locals
['__start_time']
65 nc
, tt
, ct
, callers
, callees
= \
68 s1
= s1
+' before: st='+`st`
+' nc='+`nc`
+' tt='+`tt`
+' ct='+`ct`
69 if callers
.has_key(key
):
70 callers
[key
] = callers
[key
] + 1
74 s1
= s1
+' after: st='+`st`
+' nc='+`nc`
+' tt='+`tt
+(t
-st
)`
+' ct='+`ct`
75 self
.timings
[pkey
] = nc
, tt
+ (t
- st
), ct
, callers
, callees
76 if self
.timings
.has_key(key
):
77 nc
, tt
, ct
, callers
, callees
= self
.timings
[key
]
79 nc
, tt
, ct
, callers
, callees
= 0, 0, 0, {}, {}
81 s0
= s0
+' before: nc='+`nc`
+' tt='+`tt`
+' ct='+`ct`
82 s0
= s0
+' after: nc='+`nc
+1`
+' tt='+`tt`
+' ct='+`ct`
84 if callees
.has_key(pkey
):
85 callees
[pkey
] = callees
[pkey
] + 1
88 self
.timings
[key
] = nc
+ 1, tt
, ct
, callers
, callees
89 frame
.f_locals
['__start_time'] = t
96 if not self
.profiling
:
98 if self
.profile_func
.has_key( \
99 codehack
.getcodename(frame
.f_code
)):
101 self
.call_level
= depth(frame
)
102 self
.cur_frame
= frame
103 pframe
= frame
.f_back
108 self
.handle_return(pframe
, frame
, s0
)
110 if event
== 'exception':
111 if self
.profile_func
and not self
.profiling
:
113 call_level
= depth(frame
)
114 if call_level
< self
.call_level
:
115 if call_level
<> self
.call_level
- 1:
116 print 'heh!',call_level
,self
.call_level
121 self
.handle_return(self
.cur_frame
, frame
, s0
)
122 self
.call_level
= call_level
123 self
.cur_frame
= frame
125 print 'profile.Profile.dispatch: unknown debugging event:',
129 def handle_return(self
, pframe
, frame
, s0
):
132 if frame
.f_locals
.has_key('__key'):
133 key
= frame
.f_locals
['__key']
135 funcname
= codehack
.getcodename(frame
.f_code
)
136 lineno
= codehack
.getlineno(frame
.f_code
)
137 filename
= frame
.f_code
.co_filename
138 key
= filename
+ ':' + `lineno`
+ '(' + funcname
+ ')'
139 frame
.f_locals
['__key'] = key
141 s0
= s0
+ key
+ ' depth: ' + `self
.call_level`
+ ' time: ' + `t`
143 if pframe
.f_locals
.has_key('__key'):
144 pkey
= pframe
.f_locals
['__key']
146 funcname
= codehack
.getcodename(frame
.f_code
)
147 lineno
= codehack
.getlineno(frame
.f_code
)
148 filename
= frame
.f_code
.co_filename
149 pkey
= filename
+ ':' + `lineno`
+ '(' + funcname
+ ')'
150 pframe
.f_locals
['__key'] = pkey
153 if pframe
.f_locals
.has_key('__start_time') and \
154 self
.timings
.has_key(pkey
):
155 st
= pframe
.f_locals
['__start_time']
156 nc
, tt
, ct
, callers
, callees
= \
159 s1
= s1
+' before: st='+`st`
+' nc='+`nc`
+' tt='+`tt`
+' ct='+`ct`
160 s1
= s1
+' after: st='+`t`
+' nc='+`nc`
+' tt='+`tt`
+' ct='+`ct
+(t
-st
)`
161 self
.timings
[pkey
] = \
162 nc
, tt
, ct
+ (t
- st
), callers
, callees
163 pframe
.f_locals
['__start_time'] = t
164 if self
.timings
.has_key(key
):
165 nc
, tt
, ct
, callers
, callees
= self
.timings
[key
]
167 nc
, tt
, ct
, callers
, callees
= 0, 0, 0, {}, {}
168 if frame
.f_locals
.has_key('__start_time'):
169 st
= frame
.f_locals
['__start_time']
173 s0
= s0
+' before: st='+`st`
+' nc='+`nc`
+' tt='+`tt`
+' ct='+`ct`
174 s0
= s0
+' after: nc='+`nc`
+' tt='+`tt
+(t
-st
)`
+' ct='+`ct
+(t
-st
)`
177 self
.timings
[key
] = \
178 nc
, tt
+ (t
- st
), ct
+ (t
- st
), callers
, callees
180 def print_stats(self
):
181 # Print in reverse order by ct
184 for key
in self
.timings
.keys():
185 nc
, tt
, ct
, callers
, callees
= self
.timings
[key
]
188 list.append(ct
, tt
, nc
, key
)
191 for ct
, tt
, nc
, key
in list:
192 print_line(nc
, tt
, ct
, os
.path
.basename(key
))
194 def dump_stats(self
, file):
196 marshal
.dump(self
.timings
, f
)
199 # The following two methods can be called by clients to use
200 # a profiler to profile a statement, given as a string.
204 dict = __main__
.__dict
__
205 self
.runctx(cmd
, dict, dict)
207 def runctx(self
, cmd
, globals, locals):
208 sys
.setprofile(self
.trace_dispatch
)
210 exec(cmd
+ '\n', globals, locals)
214 # This method is more useful to profile a single function call.
216 def runcall(self
, func
, *args
):
217 sys
.setprofile(self
.trace_dispatch
)
232 def __init__(self
, file):
234 self
.stats
= marshal
.load(f
)
236 self
.stats_list
= None
238 def print_stats(self
):
241 for i
in range(len(self
.stats_list
)):
242 nc
, tt
, ct
, callers
, callees
, key
= \
244 print_line(nc
, tt
, ct
, key
)
246 for key
in self
.stats
.keys():
247 nc
, tt
, ct
, callers
, callees
= self
.stats
[key
]
248 print_line(nc
, tt
, ct
, key
)
250 def print_callers(self
):
252 for i
in range(len(self
.stats_list
)):
253 nc
, tt
, ct
, callers
, callees
, key
= \
256 for func
in callers
.keys():
257 print func
+'('+`callers
[func
]`
+')',
260 for key
in self
.stats
.keys():
261 nc
, tt
, ct
, callers
, callees
= self
.stats
[key
]
263 for func
in callers
.keys():
264 print func
+'('+`callers
[func
]`
+')',
267 def print_callees(self
):
269 for i
in range(len(self
.stats_list
)):
270 nc
, tt
, ct
, callers
, callees
, key
= \
273 for func
in callees
.keys():
274 print func
+'('+`callees
[func
]`
+')',
277 for key
in self
.stats
.keys():
278 nc
, tt
, ct
, callers
, callees
= self
.stats
[key
]
280 for func
in callees
.keys():
281 print func
+'('+`callees
[func
]`
+')',
284 def sort_stats(self
, field
):
286 for key
in self
.stats
.keys():
289 for i
in range(len(t
)):
298 stats_list
.append(nt
)
301 for i
in range(len(stats_list
)):
307 nt
= nt
[:field
] + t
[0:1] + nt
[field
:]
308 self
.stats_list
.append(nt
)
310 def reverse_order(self
):
311 self
.stats_list
.reverse()
313 def strip_dirs(self
):
315 for key
in self
.stats
.keys():
316 nc
, tt
, ct
, callers
, callees
= self
.stats
[key
]
317 newkey
= os
.path
.basename(key
)
319 for c
in callers
.keys():
320 newcallers
[os
.path
.basename(c
)] = callers
[c
]
322 for c
in callees
.keys():
323 newcallees
[os
.path
.basename(c
)] = callees
[c
]
324 newstats
[newkey
] = nc
, tt
, ct
, newcallers
, newcallees
325 self
.stats
= newstats
326 self
.stats_list
= None
329 print string
.rjust('ncalls', 8),
330 print string
.rjust('tottime', 8),
331 print string
.rjust('percall', 8),
332 print string
.rjust('cumtime', 8),
333 print string
.rjust('percall', 8),
334 print 'filename:lineno(function)'
336 def print_line(nc
, tt
, ct
, key
):
337 print string
.rjust(`nc`
, 8),
351 return string
.rjust(fpformat
.fix(x
, 3), 8)
353 # simplified user interface
354 def run(statement
, *args
):
363 prof
.dump_stats(args
[0])
365 # test command with debugging
370 prof
.run('import x; x.main()')
377 run('import x; x.main()')
381 for dirname
in sys
.path
:
382 fullname
= os
.path
.join(dirname
, 'profile.doc')
383 if os
.path
.exists(fullname
):
384 sts
= os
.system('${PAGER-more} '+fullname
)
385 if sts
: print '*** Pager exit status:', sts
388 print 'Sorry, can\'t find the help file "profile.doc"',
389 print 'along the Python search path'