Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
48ac250
Add .prof File Download Support to Profiling Panel
andoriyaprashant Jun 18, 2025
49f29eb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 18, 2025
7ec7f25
Merge branch 'main' of https://github.com/django-commons/django-debug…
JohananOppongAmoateng Dec 17, 2025
fe4e305
Refactor profiling panel and add download functionality for .prof files
JohananOppongAmoateng Dec 17, 2025
5db558d
Add profiling data download functionality and related settings
JohananOppongAmoateng Dec 17, 2025
74ddf5e
Add tests for profiling stats generation and download functionality
JohananOppongAmoateng Dec 17, 2025
5bbb211
fix
JohananOppongAmoateng Dec 17, 2025
a285d82
fix
JohananOppongAmoateng Dec 17, 2025
2bd5cfe
fix
JohananOppongAmoateng Dec 17, 2025
e698cde
Merge branch 'main' into download-prof-files
JohananOppongAmoateng Dec 17, 2025
66e2d50
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 17, 2025
3896fe3
Merge branch 'main' of https://github.com/django-commons/django-debug…
JohananOppongAmoateng Feb 7, 2026
09f2ba0
address problems
JohananOppongAmoateng Feb 7, 2026
9829c58
docs
JohananOppongAmoateng Feb 7, 2026
cfc244b
address review concerns
JohananOppongAmoateng Feb 8, 2026
df258a2
Merge branch 'main' into download-prof-files
tim-schilling May 7, 2026
efbd2b9
Clean up PR.
tim-schilling May 7, 2026
9cbdf04
Generate the prof file on the fly.
tim-schilling May 7, 2026
05a505c
Merge branch 'main' into download-prof-files
tim-schilling May 9, 2026
34dde67
Name the profile file based on the request being made.
tim-schilling May 9, 2026
bdb6bf5
Remove the mocks from the profiling panel's integration tests.
tim-schilling May 9, 2026
776e4ba
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 9, 2026
25fa939
Remove unexpected addition of uv.lock.
tim-schilling May 9, 2026
f1c86c2
Switch to a proper FileResponse response.
tim-schilling May 10, 2026
d03fec3
Remove extra change.
tim-schilling May 10, 2026
7235ab8
Convert to a NamedTuple class.
tim-schilling May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add .prof File Download Support to Profiling Panel
  • Loading branch information
andoriyaprashant committed Jun 18, 2025
commit 48ac250cf25bf23efb904fc5770c5c688a695d94
13 changes: 11 additions & 2 deletions debug_toolbar/panels/profiling.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from colorsys import hsv_to_rgb
from pstats import Stats
import tempfile

from django.conf import settings
from django.utils.html import format_html
Expand Down Expand Up @@ -168,8 +169,11 @@ def generate_stats(self, request, response):
self.stats = Stats(self.profiler)
self.stats.calc_callees()

root_func = cProfile.label(super().process_request.__code__)
prof_file_path = os.path.join(tempfile.gettempdir(), next(tempfile._get_candidate_names()) + ".prof")
self.profiler.dump_stats(prof_file_path)
self.prof_file_path = prof_file_path

root_func = cProfile.label(super().process_request.__code__)
if root_func in self.stats.stats:
root = FunctionCall(self.stats, root_func, depth=0)
func_list = []
Expand All @@ -182,4 +186,9 @@ def generate_stats(self, request, response):
dt_settings.get_config()["PROFILER_MAX_DEPTH"],
cum_time_threshold,
)
self.record_stats({"func_list": func_list})
self.record_stats({
"func_list": func_list,
"prof_file_path": self.prof_file_path
})


11 changes: 10 additions & 1 deletion debug_toolbar/panels/sql/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,16 @@ def _last_executed_query(self, sql, params):
# process during the .last_executed_query() call.
self.db._djdt_logger = None
try:
return self.db.ops.last_executed_query(self.cursor, sql, params)
# Handle executemany: take the first set of parameters for formatting
Comment thread
JohananOppongAmoateng marked this conversation as resolved.
Outdated
if isinstance(params, (list, tuple)) and len(params) > 0 and isinstance(params[0], (list, tuple)):
sample_params = params[0]
else:
sample_params = params

try:
return self.db.ops.last_executed_query(self.cursor, sql, sample_params)
except Exception:
return sql
finally:
self.db._djdt_logger = self.logger

Expand Down
9 changes: 9 additions & 0 deletions debug_toolbar/templates/debug_toolbar/panels/profiling.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{% load i18n %}

{% if prof_file_path %}
<div style="margin-bottom: 10px;">
<a href="{% url 'debug_toolbar_download_prof_file' %}?path={{ prof_file_path|urlencode }}" class="djDebugButton">
Download .prof file
</a>
</div>
{% endif %}

<table>
<thead>
<tr>
Expand Down
12 changes: 11 additions & 1 deletion debug_toolbar/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
from django.urls import path
from debug_toolbar import APP_NAME
from debug_toolbar import views as debug_toolbar_views
from debug_toolbar.toolbar import DebugToolbar
from debug_toolbar import APP_NAME

app_name = APP_NAME
urlpatterns = DebugToolbar.get_urls()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrealted change.

urlpatterns = DebugToolbar.get_urls() + [
path(
"download_prof_file/",
debug_toolbar_views.download_prof_file,
name="debug_toolbar_download_prof_file"
),
]
19 changes: 18 additions & 1 deletion debug_toolbar/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from django.http import JsonResponse
import os
from django.http import JsonResponse, FileResponse, Http404
from django.utils.html import escape
from django.utils.translation import gettext as _

from django.views.decorators.http import require_GET

from debug_toolbar._compat import login_not_required
from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar
from debug_toolbar.toolbar import DebugToolbar
Expand All @@ -25,3 +28,17 @@ def render_panel(request):
content = panel.content
scripts = panel.scripts
return JsonResponse({"content": content, "scripts": scripts})


Comment thread
JohananOppongAmoateng marked this conversation as resolved.
Outdated
@require_GET
def download_prof_file(request):
file_path = request.GET.get("path")
print("Serving .prof file:", file_path)
if not file_path or not os.path.exists(file_path):
print("File does not exist:", file_path)
raise Http404("File not found.")

response = FileResponse(open(file_path, 'rb'), content_type='application/octet-stream')
response['Content-Disposition'] = f'attachment; filename="{os.path.basename(file_path)}"'
return response