106
106
import signal
107
107
import sys
108
108
109
- from collections import defaultdict
109
+ from collections import defaultdict , namedtuple
110
110
from contextlib import contextmanager
111
111
112
112
@@ -285,15 +285,33 @@ def reset(frequency=None):
285
285
state .reset (frequency )
286
286
287
287
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
+
288
306
@contextmanager
289
- def profile (verbose = True ):
307
+ def profile (verbose = True , fp = None , format = DisplayFormats . ByLine , sort_order = SortOrders . BySelf ):
290
308
start ()
291
309
try :
292
310
yield
293
311
finally :
294
312
stop ()
295
313
if verbose :
296
- display ()
314
+ display (fp , format , sort_order )
297
315
298
316
299
317
###########################################################################
@@ -311,10 +329,11 @@ def __init__(self, call_data):
311
329
self .filepath = call_data .key .filename
312
330
self .filename = basename
313
331
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 )
315
333
self .pcnt_time_in_proc = self_samples / nsamples * 100
316
- self .cum_secs_in_proc = cum_samples * secs_per_sample
317
334
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
318
337
self .num_calls = None
319
338
self .self_secs_per_call = None
320
339
self .cum_secs_per_call = None
@@ -326,12 +345,7 @@ def display(self, fp):
326
345
self .name ))
327
346
328
347
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 ):
335
349
'''Print statistics, either to stdout or the given file object.'''
336
350
337
351
if fp is None :
@@ -342,9 +356,11 @@ def display(fp=None, format=0):
342
356
return
343
357
344
358
if format == DisplayFormats .ByLine :
345
- display_by_line (fp )
359
+ display_by_line (fp , sort_order = sort_order )
346
360
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 )
348
364
else :
349
365
raise Exception ("Invalid display format" )
350
366
@@ -353,11 +369,11 @@ def display(fp=None, format=0):
353
369
fp .write ('Total time: %f seconds\n ' % state .accumulated_time )
354
370
355
371
356
- def display_by_line (fp ):
372
+ def display_by_line (fp , sort_order ):
357
373
'''Print the profiler data with each sample line represented
358
374
as one row in a table. Sorted by self-time per line.'''
359
375
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 ) )
361
377
362
378
fp .write ('%5.5s %10.10s %7.7s %-8.8s\n ' %
363
379
('% ' , 'cumulative' , 'self' , '' ))
@@ -384,7 +400,11 @@ def get_line_source(filename, lineno):
384
400
385
401
return ""
386
402
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 ):
388
408
'''Print the profiler data with each sample function represented
389
409
as one row in a table. Important lines within that function are
390
410
output as nested rows. Sorted by self-time per line.'''
@@ -397,7 +417,7 @@ def display_by_method(fp):
397
417
398
418
grouped = defaultdict (list )
399
419
for call in calldata :
400
- grouped [call .filename + ":" + call .function ].append (call )
420
+ grouped [call .filepath + ":" + call .function ].append (call )
401
421
402
422
# compute sums for each function
403
423
functiondata = []
@@ -409,21 +429,23 @@ def display_by_method(fp):
409
429
total_cum_sec += sample .cum_secs_in_proc
410
430
total_self_sec += sample .self_secs_in_proc
411
431
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
+ ))
417
439
418
440
# 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 ) )
420
442
421
443
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 ) )
427
449
for call in function [4 ]:
428
450
# only show line numbers for significant locations ( > 1% time spent)
429
451
if call .pcnt_time_in_proc > 1 :
@@ -435,3 +457,30 @@ def display_by_method(fp):
435
457
call .self_secs_in_proc ,
436
458
call .lineno ,
437
459
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