From dfee3848866f2193576d2bdd44f0c8bcd10c63a4 Mon Sep 17 00:00:00 2001 From: Dave McLain Date: Thu, 21 Apr 2011 20:12:42 -0500 Subject: Creating a profiling panel from the version panel for basic stuff --- debug_toolbar/panels/profiling.py | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 debug_toolbar/panels/profiling.py (limited to 'debug_toolbar/panels/profiling.py') diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py new file mode 100644 index 0000000..302f444 --- /dev/null +++ b/debug_toolbar/panels/profiling.py @@ -0,0 +1,59 @@ +import sys + +import django +from django.conf import settings +from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ + +import debug_toolbar +from debug_toolbar.panels import DebugPanel + + +class VersionDebugPanel(DebugPanel): + """ + Panel that displays the Django version. + """ + name = 'Profiling' + has_content = True + + def nav_title(self): + return _('Versions') + + def nav_subtitle(self): + return 'Django %s' % django.get_version() + + def url(self): + return '' + + def title(self): + return _('Versions') + + def content(self): + versions = {} + for app in settings.INSTALLED_APPS + ['django']: + name = app.split('.')[-1].replace('_', ' ').capitalize() + __import__(app) + app = sys.modules[app] + if hasattr(app, 'get_version'): + get_version = app.get_version + if callable(get_version): + version = get_version() + else: + version = get_version + elif hasattr(app, 'VERSION'): + version = app.VERSION + elif hasattr(app, '__version__'): + version = app.__version__ + else: + continue + if isinstance(version, (list, tuple)): + version = '.'.join(str(o) for o in version) + versions[name] = version + + context = self.context.copy() + context.update({ + 'versions': versions, + 'paths': sys.path, + }) + + return render_to_string('debug_toolbar/panels/versions.html', context) -- cgit v1.2.3 From 3929bf2066e108695546e2dd8db14579a8710126 Mon Sep 17 00:00:00 2001 From: Dave McLain Date: Fri, 22 Apr 2011 01:24:57 -0500 Subject: first pass at a profiler panel --- debug_toolbar/panels/profiling.py | 169 ++++++++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 33 deletions(-) (limited to 'debug_toolbar/panels/profiling.py') diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 302f444..af7137f 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,15 +1,120 @@ -import sys - import django from django.conf import settings from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ - +from django.utils.safestring import mark_safe import debug_toolbar from debug_toolbar.panels import DebugPanel +import sys +import cProfile +from pstats import Stats, f8, func_std_string +from cStringIO import StringIO +from django.conf import settings +from colorsys import hsv_to_rgb + +class DjangoDebugToolbarStats(Stats): + __root = None + + def get_root_func(self): + if self.__root is None: + for func, (cc, nc, tt, ct, callers) in self.stats.iteritems(): + if len(callers) == 0: + self.__root = func + break + return self.__root + + def print_call_tree_node(self, function, depth, max_depth, cum_filter=0.1): + self.print_line(function, depth=depth) + if depth < max_depth: + for called in self.all_callees[function].keys(): + if self.stats[called][3] >= cum_filter: + self.print_call_tree_node(called, depth+1, max_depth, cum_filter=cum_filter) -class VersionDebugPanel(DebugPanel): +class FunctionCall(object): + def __init__(self, statobj, func, depth=0, stats=None, css_id='djDebugProfileCall_0', hsv=(0,0.5,1)): + self.statobj = statobj + self.func = func + if stats: + self.stats = stats + else: + self.stats = statobj.stats[func][:4] + self.depth = depth + self.id = css_id + self.hsv=hsv + + def parent_classes(self): + return self.parent_classes + + def background(self): + print self.hsv + r,g,b = hsv_to_rgb(*self.hsv) + return 'rgb(%f%%,%f%%,%f%%)' %(r*100, g*100, b*100) + + def func_std_string(self): # match what old profile produced + func_name = self.func + if func_name[:2] == ('~', 0): + # special case for built-in functions + name = func_name[2] + if name.startswith('<') and name.endswith('>'): + return '{%s}' % name[1:-1] + else: + return name + else: + file_name, line_num, method = self.func + if file_name.startswith(settings.PROJECT_ROOT): + file_name='PROJECT' + file_name[len(settings.PROJECT_ROOT):] + idx = file_name.find('/site-packages/') + if idx > -1: + file_name=file_name[idx+14:] + return "%s:%d(%s)" % (file_name, line_num, method) + + def subfuncs(self): + i=0 + h,s,v = self.hsv + count = len(self.statobj.all_callees[self.func]) + for func, stats in self.statobj.all_callees[self.func].iteritems(): + i += 1 + h1 = h + (i/count)/(self.depth+1) + if stats[3] == 0: + s1 = 0 + else: + s1 = s*(stats[3]/self.stats[3]) + yield FunctionCall(self.statobj, + func, + self.depth+1, + stats=stats, + css_id=self.id + '_' + str(i), + hsv=(h1,s1,1)) + + def as_row(self): + cc, nc, tt, ct = self.stats + if nc != cc: + c = str(nc) + '/' + str(cc) + else: + c = str(nc) + + if nc != 0: + ttdivnc = tt/nc + else: + ttdivnc = 0 + + if cc == 0: + ctdivcc = 0 + else: + ctdivcc = ct/cc + indent = 5*self.depth + funcstr = self.func_std_string() + out = """ + %(c)s + %(tt)8.3f
(%(ttdivnc)8.3f) + %(ct)8.3f
(%(ctdivcc)8.3f) + %(funcstr)s + """ % locals() + return mark_safe(out) + + +class ProfilingDebugPanel(DebugPanel): """ Panel that displays the Django version. """ @@ -17,43 +122,41 @@ class VersionDebugPanel(DebugPanel): has_content = True def nav_title(self): - return _('Versions') - - def nav_subtitle(self): - return 'Django %s' % django.get_version() + return _('Profiling') def url(self): return '' def title(self): - return _('Versions') + return _('Profiling') - def content(self): - versions = {} - for app in settings.INSTALLED_APPS + ['django']: - name = app.split('.')[-1].replace('_', ' ').capitalize() - __import__(app) - app = sys.modules[app] - if hasattr(app, 'get_version'): - get_version = app.get_version - if callable(get_version): - version = get_version() - else: - version = get_version - elif hasattr(app, 'VERSION'): - version = app.VERSION - elif hasattr(app, '__version__'): - version = app.__version__ - else: - continue - if isinstance(version, (list, tuple)): - version = '.'.join(str(o) for o in version) - versions[name] = version + def process_view(self, request, view_func, view_args, view_kwargs): + self.profiler = cProfile.Profile() + args = (request,) + view_args + return self.profiler.runcall(view_func, *args, **view_kwargs) + + def process_response(self, request, response): + self.profiler.create_stats() + self.stats = DjangoDebugToolbarStats(self.profiler) + return response + def add_node(self, func_list, func, max_depth, cum_time=0.1): + func_list.append(func) + if func.depth < max_depth: + for subfunc in func.subfuncs(): + if subfunc.stats[3] >= cum_time: + self.add_node(func_list, subfunc, max_depth, cum_time=cum_time) + + def content(self): + + self.stats.calc_callees() + root = FunctionCall(self.stats, self.stats.get_root_func(), depth=0) + + func_list = [] + self.add_node(func_list, root, 10, root.stats[3]/8) context = self.context.copy() context.update({ - 'versions': versions, - 'paths': sys.path, + 'func_list': func_list, }) - return render_to_string('debug_toolbar/panels/versions.html', context) + return render_to_string('debug_toolbar/panels/profiling.html', context) -- cgit v1.2.3 From 38019ea2f5222ac97a7c5b67074a8a3b2ecf1ba0 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 22 Apr 2011 13:07:41 -0700 Subject: Kill off requirement of PROJECT_ROOT and clean up lint violations in profiling panel --- debug_toolbar/panels/profiling.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'debug_toolbar/panels/profiling.py') diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index af7137f..465a0a3 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,16 +1,10 @@ -import django -from django.conf import settings from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe -import debug_toolbar from debug_toolbar.panels import DebugPanel -import sys import cProfile -from pstats import Stats, f8, func_std_string -from cStringIO import StringIO -from django.conf import settings +from pstats import Stats from colorsys import hsv_to_rgb class DjangoDebugToolbarStats(Stats): @@ -62,8 +56,6 @@ class FunctionCall(object): return name else: file_name, line_num, method = self.func - if file_name.startswith(settings.PROJECT_ROOT): - file_name='PROJECT' + file_name[len(settings.PROJECT_ROOT):] idx = file_name.find('/site-packages/') if idx > -1: file_name=file_name[idx+14:] -- cgit v1.2.3 From 5fc8d35d17b691727c08e9c2b55aa655f516850f Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 22 Apr 2011 13:08:44 -0700 Subject: Remove print statement --- debug_toolbar/panels/profiling.py | 1 - 1 file changed, 1 deletion(-) (limited to 'debug_toolbar/panels/profiling.py') diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 465a0a3..97f4865 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -41,7 +41,6 @@ class FunctionCall(object): return self.parent_classes def background(self): - print self.hsv r,g,b = hsv_to_rgb(*self.hsv) return 'rgb(%f%%,%f%%,%f%%)' %(r*100, g*100, b*100) -- cgit v1.2.3 From 249247f9c7a5027d444471526859ee86c06a1f1a Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 22 Apr 2011 14:47:36 -0700 Subject: Initial refactor of toggline so that profiling matches sql --- debug_toolbar/panels/profiling.py | 70 +++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 28 deletions(-) (limited to 'debug_toolbar/panels/profiling.py') diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 97f4865..d0ea2c1 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,3 +1,5 @@ +from __future__ import division + from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe @@ -26,7 +28,7 @@ class DjangoDebugToolbarStats(Stats): self.print_call_tree_node(called, depth+1, max_depth, cum_filter=cum_filter) class FunctionCall(object): - def __init__(self, statobj, func, depth=0, stats=None, css_id='djDebugProfileCall_0', hsv=(0,0.5,1)): + def __init__(self, statobj, func, depth=0, stats=None, id=0, parent_ids=[], hsv=(0,0.5,1)): self.statobj = statobj self.func = func if stats: @@ -34,8 +36,9 @@ class FunctionCall(object): else: self.stats = statobj.stats[func][:4] self.depth = depth - self.id = css_id - self.hsv=hsv + self.id = id + self.parent_ids = parent_ids + self.hsv = hsv def parent_classes(self): return self.parent_classes @@ -58,7 +61,15 @@ class FunctionCall(object): idx = file_name.find('/site-packages/') if idx > -1: file_name=file_name[idx+14:] - return "%s:%d(%s)" % (file_name, line_num, method) + + file_path, file_name = file_name.rsplit('/', 1) + + return mark_safe('{0}/{1} in {3}({2})'.format( + file_path, + file_name, + line_num, + method, + )) def subfuncs(self): i=0 @@ -75,35 +86,38 @@ class FunctionCall(object): func, self.depth+1, stats=stats, - css_id=self.id + '_' + str(i), + id=str(self.id) + '_' + str(i), + parent_ids=self.parent_ids + [self.id], hsv=(h1,s1,1)) - def as_row(self): + def count(self): + return self.stats[1] + + def tottime(self): + return self.stats[2] + + def cumtime(self): cc, nc, tt, ct = self.stats - if nc != cc: - c = str(nc) + '/' + str(cc) - else: - c = str(nc) - - if nc != 0: - ttdivnc = tt/nc - else: - ttdivnc = 0 - + return self.stats[3] + + def tottime_per_call(self): + cc, nc, tt, ct = self.stats + + if nc == 0: + return 0 + + return tt/nc + + def cumtime_per_call(self): + cc, nc, tt, ct = self.stats + if cc == 0: - ctdivcc = 0 - else: - ctdivcc = ct/cc - indent = 5*self.depth - funcstr = self.func_std_string() - out = """ - %(c)s - %(tt)8.3f
(%(ttdivnc)8.3f) - %(ct)8.3f
(%(ctdivcc)8.3f) - %(funcstr)s - """ % locals() - return mark_safe(out) + return 0 + + return ct/cc + def indent(self): + return 16 * self.depth class ProfilingDebugPanel(DebugPanel): """ -- cgit v1.2.3 From a3e8ce8eb1c4e2d1482a22d3a7e0dba7f4ff3201 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 22 Apr 2011 14:59:59 -0700 Subject: Basic support for toggling everywhere correctly --- debug_toolbar/panels/profiling.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'debug_toolbar/panels/profiling.py') diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index d0ea2c1..68aafb7 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -147,9 +147,11 @@ class ProfilingDebugPanel(DebugPanel): def add_node(self, func_list, func, max_depth, cum_time=0.1): func_list.append(func) + func.has_subfuncs = False if func.depth < max_depth: for subfunc in func.subfuncs(): if subfunc.stats[3] >= cum_time: + func.has_subfuncs = True self.add_node(func_list, subfunc, max_depth, cum_time=cum_time) def content(self): -- cgit v1.2.3