Skip to content

Commit ed2cc66

Browse files
committed
Merge branch 'line_profiler_int' of git://github.com/dmclain/django-debug-toolbar into line_profiler_int
2 parents 57e1779 + cf82001 commit ed2cc66

File tree

2 files changed

+57
-14
lines changed

2 files changed

+57
-14
lines changed

debug_toolbar/panels/profiling.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
from django.utils.safestring import mark_safe
66
from debug_toolbar.panels import DebugPanel
77

8+
try:
9+
from line_profiler import LineProfiler, show_func
10+
DJ_PROFILE_USE_LINE_PROFILER = True
11+
except ImportError:
12+
DJ_PROFILE_USE_LINE_PROFILER = False
13+
14+
15+
from cStringIO import StringIO
816
import cProfile
917
from pstats import Stats
1018
from colorsys import hsv_to_rgb
@@ -20,13 +28,6 @@ def get_root_func(self):
2028
break
2129
return self.__root
2230

23-
def print_call_tree_node(self, function, depth, max_depth, cum_filter=0.1):
24-
self.print_line(function, depth=depth)
25-
if depth < max_depth:
26-
for called in self.all_callees[function].keys():
27-
if self.stats[called][3] >= cum_filter:
28-
self.print_call_tree_node(called, depth+1, max_depth, cum_filter=cum_filter)
29-
3031
class FunctionCall(object):
3132
def __init__(self, statobj, func, depth=0, stats=None, id=0, parent_ids=[], hsv=(0,0.5,1)):
3233
self.statobj = statobj
@@ -39,6 +40,7 @@ def __init__(self, statobj, func, depth=0, stats=None, id=0, parent_ids=[], hsv=
3940
self.id = id
4041
self.parent_ids = parent_ids
4142
self.hsv = hsv
43+
self._line_stats_text = None
4244

4345
def parent_classes(self):
4446
return self.parent_classes
@@ -118,6 +120,18 @@ def cumtime_per_call(self):
118120

119121
def indent(self):
120122
return 16 * self.depth
123+
124+
def line_stats_text(self):
125+
if self._line_stats_text is None:
126+
lstats = self.statobj.line_stats
127+
if self.func in lstats.timings:
128+
out = StringIO()
129+
fn, lineno, name = self.func
130+
show_func(fn, lineno, name, lstats.timings[self.func], lstats.unit, stream=out)
131+
self._line_stats_text = out.getvalue()
132+
else:
133+
self._line_stats_text = False
134+
return self._line_stats_text
121135

122136
class ProfilingDebugPanel(DebugPanel):
123137
"""
@@ -135,23 +149,43 @@ def url(http://github.com/self):
135149
def title(self):
136150
return _('Profiling')
137151

152+
def _unwrap_closure_and_profile(self, func):
153+
if not hasattr(func, 'func_code'):
154+
return
155+
self.line_profiler.add_function(func)
156+
if func.func_closure:
157+
for cell in func.func_closure:
158+
if hasattr(cell.cell_contents, 'func_code'):
159+
self._unwrap_closure_and_profile(cell.cell_contents)
160+
138161
def process_view(self, request, view_func, view_args, view_kwargs):
139162
__traceback_hide__ = True
140163
self.profiler = cProfile.Profile()
141164
args = (request,) + view_args
142-
return self.profiler.runcall(view_func, *args, **view_kwargs)
165+
if DJ_PROFILE_USE_LINE_PROFILER:
166+
self.line_profiler = LineProfiler()
167+
self._unwrap_closure_and_profile(view_func)
168+
self.line_profiler.enable_by_count()
169+
out = self.profiler.runcall(view_func, *args, **view_kwargs)
170+
self.line_profiler.disable_by_count()
171+
else:
172+
self.line_profiler = None
173+
out = self.profiler.runcall(view_func, *args, **view_kwargs)
174+
return out
143175

144176
def process_response(self, request, response):
145177
self.profiler.create_stats()
146178
self.stats = DjangoDebugToolbarStats(self.profiler)
179+
if DJ_PROFILE_USE_LINE_PROFILER:
180+
self.stats.line_stats = self.line_profiler.get_stats()
147181
return response
148182

149183
def add_node(self, func_list, func, max_depth, cum_time=0.1):
150184
func_list.append(func)
151185
func.has_subfuncs = False
152186
if func.depth < max_depth:
153187
for subfunc in func.subfuncs():
154-
if subfunc.stats[3] >= cum_time:
188+
if subfunc.stats[3] >= cum_time or (subfunc.func in self.stats.line_stats.timings):
155189
func.has_subfuncs = True
156190
self.add_node(func_list, subfunc, max_depth, cum_time=cum_time)
157191

debug_toolbar/templates/debug_toolbar/panels/profiling.html

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
<thead>
55
<tr>
66
<th>{% trans "Call" %}</th>
7-
<th>{% trans "TotTime" %}</th>
8-
<th>{% trans "Per" %}</th>
97
<th>{% trans "CumTime" %}</th>
108
<th>{% trans "Per" %}</th>
9+
<th>{% trans "TotTime" %}</th>
10+
<th>{% trans "Per" %}</th>
1111
<th>{% trans "Count" %}</th>
1212
</tr>
1313
</thead>
@@ -25,12 +25,21 @@
2525
<span class="stack">{{ call.func_std_string }}</span>
2626
</div>
2727
</td>
28-
<td>{{ call.tottime|floatformat:3 }}</td>
29-
<td>{{ call.tottime_per_call|floatformat:3 }}</td>
3028
<td>{{ call.cumtime|floatformat:3 }}</td>
3129
<td>{{ call.cumtime_per_call|floatformat:3 }}</td>
30+
<td>{{ call.tottime|floatformat:3 }}</td>
31+
<td>{{ call.tottime_per_call|floatformat:3 }}</td>
3232
<td>{{ call.count }}</td>
3333
</tr>
34+
{% if call.line_stats_text %}
35+
<tr>
36+
<td colspan='6'>
37+
<pre style='font-family:Courier, monospace'>
38+
{{ call.line_stats_text }}
39+
</pre>
40+
</td>
41+
</tr>
42+
{% endif %}
3443
{% endfor %}
3544
</tbody>
36-
</table>
45+
</table>

0 commit comments

Comments
 (0)