From 4289268732a1e906555eee047c053ee31b99b28d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Wed, 12 Jan 2022 09:34:50 +0000 Subject: [PATCH] Optimize render_stacktrace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is called by the SQL panel to render each frame of each stack trace. Previously it used a Django template, which is slow. By generating the HTML with pure Python code, we can avoid much of this overhead. I tried this change on a demo app I built that has 96 total queries, due to N+1 queries. The queries execute in 13ms due to use of SQLite, so the whole view is a negligible concern. Nearly all the view runtime is the toolbar itself. Without this change, the runtime is ~1300ms; with the change it’s ~1100ms. That's a saving of **15%**. I also checked the appearance of the generated HTML hasn’t changed. --- .../debug_toolbar/panels/sql_stacktrace.html | 4 -- debug_toolbar/utils.py | 47 ++++++++++--------- 2 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html b/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html deleted file mode 100644 index 426783b93..000000000 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html +++ /dev/null @@ -1,4 +0,0 @@ -{% for s in stacktrace %}{{s.0}}/{{s.1}} in {{s.3}}({{s.2}}) - {{s.4}} - {% if show_locals %}
{{s.5|pprint}}
{% endif %} -{% endfor %} diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index c662ab113..57965c457 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -3,12 +3,12 @@ import re import sys from importlib import import_module -from itertools import chain +from pprint import pformat import django from django.core.exceptions import ImproperlyConfigured from django.template import Node -from django.template.loader import render_to_string +from django.utils.html import format_html from django.utils.safestring import mark_safe from debug_toolbar import settings as dt_settings @@ -69,26 +69,31 @@ def tidy_stacktrace(stack): def render_stacktrace(trace): - stacktrace = [] - for frame in trace: - params = (v for v in chain(frame[0].rsplit(os.path.sep, 1), frame[1:])) - params_dict = {str(idx): v for idx, v in enumerate(params)} - try: - stacktrace.append(params_dict) - except KeyError: - # This frame doesn't have the expected format, so skip it and move - # on to the next one - continue - - return mark_safe( - render_to_string( - "debug_toolbar/panels/sql_stacktrace.html", - { - "stacktrace": stacktrace, - "show_locals": dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"], - }, + show_locals = dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"] + html = "" + for abspath, lineno, func, code, locals_ in trace: + directory, filename = abspath.rsplit(os.path.sep, 1) + html += format_html( + ( + '{}/' + + '{} in' + + ' {}' + + '({})\n' + + ' {}\n' + ), + directory, + filename, + func, + lineno, + code, ) - ) + if show_locals: + html += format_html( + '
{}
\n', + pformat(locals_), + ) + html += "\n" + return mark_safe(html) def get_template_info():