-
-
Notifications
You must be signed in to change notification settings - Fork 146
/
profiler.py
129 lines (102 loc) · 3.83 KB
/
profiler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
try:
import cProfile as profile
except ImportError:
import profile
import functools
import pstats
from flask import current_app
from flask_debugtoolbar.panels import DebugPanel
from flask_debugtoolbar.utils import format_fname
class ProfilerDebugPanel(DebugPanel):
"""
Panel that displays the time a response took with cProfile output.
"""
name = 'Profiler'
user_activate = True
def __init__(self, jinja_env, context={}):
DebugPanel.__init__(self, jinja_env, context=context)
if current_app.config.get('DEBUG_TB_PROFILER_ENABLED'):
self.is_active = True
self.dump_filename = current_app.config.get(
"DEBUG_TB_PROFILER_DUMP_FILENAME"
)
def has_content(self):
return bool(self.profiler)
def process_request(self, request):
if not self.is_active:
return
self.profiler = profile.Profile()
self.stats = None
def process_view(self, request, view_func, view_kwargs):
if self.is_active:
func = functools.partial(self.profiler.runcall, view_func)
functools.update_wrapper(func, view_func)
return func
def process_response(self, request, response):
if not self.is_active:
return False
if self.profiler is not None:
self.profiler.disable()
try:
stats = pstats.Stats(self.profiler)
except TypeError:
self.is_active = False
return False
function_calls = []
for func in stats.sort_stats(1).fcn_list:
current = {}
info = stats.stats[func]
# Number of calls
if info[0] != info[1]:
current['ncalls'] = '%d/%d' % (info[1], info[0])
else:
current['ncalls'] = info[1]
# Total time
current['tottime'] = info[2] * 1000
# Quotient of total time divided by number of calls
if info[1]:
current['percall'] = info[2] * 1000 / info[1]
else:
current['percall'] = 0
# Cumulative time
current['cumtime'] = info[3] * 1000
# Quotient of the cumulative time divided by the number of
# primitive calls.
if info[0]:
current['percall_cum'] = info[3] * 1000 / info[0]
else:
current['percall_cum'] = 0
# Filename
filename = pstats.func_std_string(func)
current['filename_long'] = filename
current['filename'] = format_fname(filename)
function_calls.append(current)
self.stats = stats
self.function_calls = function_calls
if self.dump_filename:
if callable(self.dump_filename):
filename = self.dump_filename()
else:
filename = self.dump_filename
self.profiler.dump_stats(filename)
return response
def title(self):
if not self.is_active:
return "Profiler not active"
return 'View: %.2fms' % (float(self.stats.total_tt) * 1000,)
def nav_title(self):
return 'Profiler'
def nav_subtitle(self):
if not self.is_active:
return "in-active"
return 'View: %.2fms' % (float(self.stats.total_tt) * 1000,)
def url(self):
return ''
def content(self):
if not self.is_active:
return "The profiler is not activated, activate it to use it"
context = {
'stats': self.stats,
'function_calls': self.function_calls,
}
return self.render('panels/profiler.html', context)