Skip to content

Commit 3242031

Browse files
committed
Allow function output in CSV format, and custom sort ordering
1 parent 1a33eba commit 3242031

File tree

1 file changed

+77
-28
lines changed

1 file changed

+77
-28
lines changed

statprof.py

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
import signal
107107
import sys
108108

109-
from collections import defaultdict
109+
from collections import defaultdict, namedtuple
110110
from contextlib import contextmanager
111111

112112

@@ -285,15 +285,33 @@ def reset(frequency=None):
285285
state.reset(frequency)
286286

287287

288+
class DisplayFormats:
289+
ByLine = 0
290+
ByMethod = 1
291+
CSV = 2
292+
293+
294+
class SortOrders:
295+
BySelf = 0
296+
ByCumulative = 1
297+
298+
299+
def sort_key(ordering):
300+
if ordering == SortOrders.BySelf:
301+
return lambda x: x.self_secs_in_proc
302+
elif ordering == SortOrders.ByCumulative:
303+
return lambda x: x.cum_secs_in_proc
304+
305+
288306
@contextmanager
289-
def profile(verbose=True):
307+
def profile(verbose=True, fp=None, format=DisplayFormats.ByLine, sort_order=SortOrders.BySelf):
290308
start()
291309
try:
292310
yield
293311
finally:
294312
stop()
295313
if verbose:
296-
display()
314+
display(fp, format, sort_order)
297315

298316

299317
###########################################################################
@@ -311,10 +329,11 @@ def __init__(self, call_data):
311329
self.filepath = call_data.key.filename
312330
self.filename = basename
313331
self.function = call_data.key.name
314-
self.name = '%s:%d:%s' % (self.filename, self.lineno, self.function)
332+
self.name = '%s:%d:%s' % (self.filepath, self.lineno, self.function)
315333
self.pcnt_time_in_proc = self_samples / nsamples * 100
316-
self.cum_secs_in_proc = cum_samples * secs_per_sample
317334
self.self_secs_in_proc = self_samples * secs_per_sample
335+
self.cum_time_in_proc = cum_samples / nsamples * 100
336+
self.cum_secs_in_proc = cum_samples * secs_per_sample
318337
self.num_calls = None
319338
self.self_secs_per_call = None
320339
self.cum_secs_per_call = None
@@ -326,12 +345,7 @@ def display(self, fp):
326345
self.name))
327346

328347

329-
class DisplayFormats:
330-
ByLine = 0
331-
ByMethod = 1
332-
333-
334-
def display(fp=None, format=0):
348+
def display(fp=None, format=DisplayFormats.ByLine, sort_order=SortOrders.BySelf):
335349
'''Print statistics, either to stdout or the given file object.'''
336350

337351
if fp is None:
@@ -342,9 +356,11 @@ def display(fp=None, format=0):
342356
return
343357

344358
if format == DisplayFormats.ByLine:
345-
display_by_line(fp)
359+
display_by_line(fp, sort_order=sort_order)
346360
elif format == DisplayFormats.ByMethod:
347-
display_by_method(fp)
361+
display_by_method(fp, sort_order=sort_order)
362+
elif format == DisplayFormats.CSV:
363+
display_by_csv(fp, sort_order=sort_order)
348364
else:
349365
raise Exception("Invalid display format")
350366

@@ -353,11 +369,11 @@ def display(fp=None, format=0):
353369
fp.write('Total time: %f seconds\n' % state.accumulated_time)
354370

355371

356-
def display_by_line(fp):
372+
def display_by_line(fp, sort_order):
357373
'''Print the profiler data with each sample line represented
358374
as one row in a table. Sorted by self-time per line.'''
359375
l = [CallStats(x) for x in _itervalues(CallData.all_calls)]
360-
l.sort(reverse=True, key=lambda x: x.self_secs_in_proc)
376+
l.sort(reverse=True, key=sort_key(sort_order))
361377

362378
fp.write('%5.5s %10.10s %7.7s %-8.8s\n' %
363379
('% ', 'cumulative', 'self', ''))
@@ -384,7 +400,11 @@ def get_line_source(filename, lineno):
384400

385401
return ""
386402

387-
def display_by_method(fp):
403+
404+
FunctionData = namedtuple('FunctionData', 'fname, cum_secs_in_proc, self_secs_in_proc, pcnt_time_in_proc, samples')
405+
406+
407+
def display_by_method(fp, sort_order):
388408
'''Print the profiler data with each sample function represented
389409
as one row in a table. Important lines within that function are
390410
output as nested rows. Sorted by self-time per line.'''
@@ -397,7 +417,7 @@ def display_by_method(fp):
397417

398418
grouped = defaultdict(list)
399419
for call in calldata:
400-
grouped[call.filename + ":" + call.function].append(call)
420+
grouped[call.filepath + ":" + call.function].append(call)
401421

402422
# compute sums for each function
403423
functiondata = []
@@ -409,21 +429,23 @@ def display_by_method(fp):
409429
total_cum_sec += sample.cum_secs_in_proc
410430
total_self_sec += sample.self_secs_in_proc
411431
total_percent += sample.pcnt_time_in_proc
412-
functiondata.append((fname,
413-
total_cum_sec,
414-
total_self_sec,
415-
total_percent,
416-
samples))
432+
functiondata.append(FunctionData(
433+
fname,
434+
total_cum_sec,
435+
total_self_sec,
436+
total_percent,
437+
samples
438+
))
417439

418440
# sort by total self sec
419-
functiondata.sort(reverse=True, key=lambda x: x[2])
441+
functiondata.sort(reverse=True, key=sort_key(sort_order))
420442

421443
for function in functiondata:
422-
fp.write('%6.2f %9.2f %9.2f %s\n' % (function[3], # total percent
423-
function[1], # total cum sec
424-
function[2], # total self sec
425-
function[0])) # file:function
426-
function[4].sort(reverse=True, key=lambda i: i.self_secs_in_proc)
444+
fp.write('%6.2f %9.2f %9.2f %s\n' % (function.pcnt_time_in_proc,
445+
function.cum_secs_in_proc,
446+
function.self_secs_in_proc,
447+
function.fname)) # file:function
448+
function[4].sort(reverse=True, key=sort_key(sort_order))
427449
for call in function[4]:
428450
# only show line numbers for significant locations ( > 1% time spent)
429451
if call.pcnt_time_in_proc > 1:
@@ -435,3 +457,30 @@ def display_by_method(fp):
435457
call.self_secs_in_proc,
436458
call.lineno,
437459
source))
460+
461+
def display_by_csv(fp, sort_order):
462+
import csv
463+
writer = csv.writer(fp)
464+
writer.writerow((
465+
"File Path",
466+
"Line Number",
467+
"Function",
468+
"Self (%)",
469+
"Self (sec)",
470+
"Cumulative (%)",
471+
"Cumulative (sec)",
472+
))
473+
for row in sorted(
474+
(CallStats(x) for x in CallData.all_calls.itervalues()),
475+
key=sort_key(sort_order),
476+
reverse=True
477+
):
478+
writer.writerow((
479+
row.filepath,
480+
row.lineno,
481+
row.function,
482+
row.pcnt_time_in_proc,
483+
row.self_secs_in_proc,
484+
row.cum_time_in_proc,
485+
row.cum_secs_in_proc,
486+
))

0 commit comments

Comments
 (0)