diff --git a/iommi/profiling.py b/iommi/profiling.py
index 536b826e..e601ca7d 100644
--- a/iommi/profiling.py
+++ b/iommi/profiling.py
@@ -2,6 +2,7 @@
import cProfile
import os
+import pstats
import subprocess
import sys
from io import StringIO
@@ -10,8 +11,12 @@
from django.http import StreamingHttpResponse
-from iommi._web_compat import HttpResponse
-from ._web_compat import settings
+from iommi._web_compat import (
+ HttpResponse,
+ settings,
+)
+from iommi.debug import src_debug_url_builder
+
MEDIA_PREFIXES = ['/static/']
@@ -35,6 +40,99 @@ def should_profile(request):
return ('_iommi_prof' in request.GET or '_iommi_prof' in request.POST) and ((not disabled and is_staff) or settings.DEBUG)
+def strip_extra_path(s, token):
+ if token not in s:
+ return s
+ pre, _, post = s.rpartition(' ')
+ post = post[post.rindex(token) + len(token) :]
+ return f'{pre} {post}'
+
+
+class HTMLStats(pstats.Stats):
+ def print_title(self):
+ print('''
+
+
+ ncalls |
+ tottime |
+ percall |
+ cumtime |
+ percall |
+ function |
+ filename |
+ lineno |
+
+
+ ''', file=self.stream)
+
+ def print_stats(self, *amount):
+ for filename in self.files:
+ print(filename, file=self.stream)
+ if self.files:
+ print(file=self.stream)
+ indent = ' ' * 8
+ for func in self.top_level:
+ print(indent, func[2], file=self.stream)
+
+ print(indent, self.total_calls, "function calls", end=' ', file=self.stream)
+ if self.total_calls != self.prim_calls:
+ print("(%d primitive calls)" % self.prim_calls, end=' ', file=self.stream)
+ print("in %.3f seconds" % self.total_tt, file=self.stream)
+ print(file=self.stream)
+
+ # this call prints...
+ width, list = self.get_print_list(amount)
+
+ print('
', file=self.stream)
+ if list:
+ self.print_title()
+ limit = 280
+ for func in list[:limit]:
+ self.print_line(func)
+ print(file=self.stream)
+ print(file=self.stream)
+
+ print('
', file=self.stream)
+ return self
+
+ def print_line(self, func):
+ path, line_number, function_name = func
+
+ base_dir = str(settings.BASE_DIR)
+ should_bold = base_dir in path and '/site-packages/' not in path
+ nice_path = path.replace(base_dir, '')
+ nice_path = strip_extra_path(nice_path, '/site-packages')
+ nice_path = strip_extra_path(nice_path, '/Python.framework/Versions')
+
+ if should_bold:
+ print(f'', file=self.stream)
+ else:
+ print(f'
', file=self.stream)
+
+ def f8(x):
+ return "%8.3f" % x
+
+ cc, nc, tt, ct, callers = self.stats[func]
+ c = str(nc)
+ if nc != cc:
+ c = c + '/' + str(cc)
+ print(f'{c} | ', file=self.stream)
+ print(f'{f8(tt)} | ', file=self.stream)
+ if nc == 0:
+ print(' | ', file=self.stream)
+ else:
+ print(f'{f8(tt/nc)} | ', file=self.stream)
+ print(f'{f8(ct)} | ', file=self.stream)
+ if cc == 0:
+ print(' | ', file=self.stream)
+ else:
+ print(f'{f8(ct/cc)} | ', file=self.stream)
+ print(f'{function_name} | ', file=self.stream)
+ print(f'{nice_path} | ', file=self.stream)
+ print(f'{line_number} | ', file=self.stream)
+ print(f'
', file=self.stream)
+
+
class Middleware:
def __init__(self, get_response):
self.get_response = get_response
@@ -70,7 +168,7 @@ def __call__(self, request):
import pstats
s = StringIO()
- ps = pstats.Stats(*request._iommi_prof, stream=s)
+ ps = HTMLStats(*request._iommi_prof, stream=s)
prof_command = request.GET.get('_iommi_prof')
@@ -130,36 +228,37 @@ def __call__(self, request):
ps = ps.sort_stats(prof_command or 'cumulative')
ps.print_stats()
- stats_str = s.getvalue()
-
- limit = 280
- result = []
-
- def strip_extra_path(s, token):
- if token not in s:
- return s
- pre, _, post = s.rpartition(' ')
- post = post[post.rindex(token) + len(token) :]
- return f'{pre} {post}'
-
- base_dir = str(settings.BASE_DIR)
- for line in stats_str.split("\n")[:limit]:
- should_bold = base_dir in line and '/site-packages/' not in line
- line = line.replace(base_dir, '')
- line = strip_extra_path(line, '/site-packages')
- line = strip_extra_path(line, '/Python.framework/Versions')
- if should_bold:
- line = f'{line}'
-
- line = line.replace(' ', ' ')
- result.append(line)
+ result = s.getvalue()
# language=html
start_html = '''
@@ -177,13 +287,11 @@ def strip_extra_path(s, token):
graph
snakeviz
-
-
+
+
'''
- lines_html = "
\n".join(result)
- end_html = '
'
- response.content = start_html + lines_html + end_html
+ response.content = start_html.strip() + result
response['Content-Type'] = 'text/html'