1import time
2
3from django.template.loader import render_to_string
4from django.templatetags.static import static
5from django.utils.translation import gettext_lazy as _
6
7from debug_toolbar.panels import Panel
8
9try:
10    import resource  # Not available on Win32 systems
11except ImportError:
12    resource = None
13
14
15class TimerPanel(Panel):
16    """
17    Panel that displays the time a response took in milliseconds.
18    """
19
20    def nav_subtitle(self):
21        stats = self.get_stats()
22        if hasattr(self, "_start_rusage"):
23            utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime
24            stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime
25            return _("CPU: %(cum)0.2fms (%(total)0.2fms)") % {
26                "cum": (utime + stime) * 1000.0,
27                "total": stats["total_time"],
28            }
29        elif "total_time" in stats:
30            return _("Total: %0.2fms") % stats["total_time"]
31        else:
32            return ""
33
34    has_content = resource is not None
35
36    title = _("Time")
37
38    template = "debug_toolbar/panels/timer.html"
39
40    @property
41    def content(self):
42        stats = self.get_stats()
43        rows = (
44            (_("User CPU time"), _("%(utime)0.3f msec") % stats),
45            (_("System CPU time"), _("%(stime)0.3f msec") % stats),
46            (_("Total CPU time"), _("%(total)0.3f msec") % stats),
47            (_("Elapsed time"), _("%(total_time)0.3f msec") % stats),
48            (
49                _("Context switches"),
50                _("%(vcsw)d voluntary, %(ivcsw)d involuntary") % stats,
51            ),
52        )
53        return render_to_string(self.template, {"rows": rows})
54
55    @property
56    def scripts(self):
57        scripts = super().scripts
58        scripts.append(static("debug_toolbar/js/timer.js"))
59        return scripts
60
61    def process_request(self, request):
62        self._start_time = time.time()
63        if self.has_content:
64            self._start_rusage = resource.getrusage(resource.RUSAGE_SELF)
65        return super().process_request(request)
66
67    def generate_stats(self, request, response):
68        stats = {}
69        if hasattr(self, "_start_time"):
70            stats["total_time"] = (time.time() - self._start_time) * 1000
71        if hasattr(self, "_start_rusage"):
72            self._end_rusage = resource.getrusage(resource.RUSAGE_SELF)
73            stats["utime"] = 1000 * self._elapsed_ru("ru_utime")
74            stats["stime"] = 1000 * self._elapsed_ru("ru_stime")
75            stats["total"] = stats["utime"] + stats["stime"]
76            stats["vcsw"] = self._elapsed_ru("ru_nvcsw")
77            stats["ivcsw"] = self._elapsed_ru("ru_nivcsw")
78            stats["minflt"] = self._elapsed_ru("ru_minflt")
79            stats["majflt"] = self._elapsed_ru("ru_majflt")
80            # these are documented as not meaningful under Linux.  If you're
81            # running BSD feel free to enable them, and add any others that I
82            # hadn't gotten to before I noticed that I was getting nothing but
83            # zeroes and that the docs agreed. :-(
84            #
85            #        stats['blkin'] = self._elapsed_ru('ru_inblock')
86            #        stats['blkout'] = self._elapsed_ru('ru_oublock')
87            #        stats['swap'] = self._elapsed_ru('ru_nswap')
88            #        stats['rss'] = self._end_rusage.ru_maxrss
89            #        stats['srss'] = self._end_rusage.ru_ixrss
90            #        stats['urss'] = self._end_rusage.ru_idrss
91            #        stats['usrss'] = self._end_rusage.ru_isrss
92
93        self.record_stats(stats)
94
95    def generate_server_timing(self, request, response):
96        stats = self.get_stats()
97
98        self.record_server_timing("utime", "User CPU time", stats.get("utime", 0))
99        self.record_server_timing("stime", "System CPU time", stats.get("stime", 0))
100        self.record_server_timing("total", "Total CPU time", stats.get("total", 0))
101        self.record_server_timing(
102            "total_time", "Elapsed time", stats.get("total_time", 0)
103        )
104
105    def _elapsed_ru(self, name):
106        return getattr(self._end_rusage, name) - getattr(self._start_rusage, name)
107