Repository: jazzband/django-debug-toolbar

Issue:
KeyError in ProfilingPanel after upgrade to 1.4 
Hello! After upgrade django-debug-toolbar from 1.3.2 to 1.4 debug_toolbar.panels.profiling.ProfilingPanel stop working.
Traceback: http://dpaste.com/347D0XH



Discussion:
Most likely when someone submits a good patch.

Any more information about the error?

I am seeing it at `/admin/login` only?!

More precisely, I only encounter this problem in /admin/login when I'm not logged in.
The DjangoDebugToolbarStats class does not find the root function.

The issue exists for decorated view functions (for instance, `/admin/login`), which `ProfilingPanel` can't find root function for and hence to build hierarchy of function calls.

Did more investigation and found out that issue exists when decorated view has calls of decorated view in it, more than that the case exists when they wrapped with the same decorator. 
I did another tests and got the same result for this case. 

Eventually, we can't determine root function and hence, build calls tree. 
Continue to investigate how to determine root function, at the meantime as a temporary solution we could disable `ProfilePanel` for this cases which at least will prevent particular page within other Django Debug Toolbar panels from being broken.

Started getting this randomly after:

1. being logged into admin with a session
2. loaddata on user information
3. runserver_plus restarts
4. admin wants to go to login screen

then I get the KeyError issue from profiling

if I disable the ProfilingPanel and log back into admin, then re-enable ProfilingPanel, everything is fixed

Resolving patch:
diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py
--- a/debug_toolbar/panels/profiling.py
+++ b/debug_toolbar/panels/profiling.py
@@ -167,12 +167,13 @@ def generate_stats(self, request, response):
         self.stats = DjangoDebugToolbarStats(self.profiler)
         self.stats.calc_callees()
 
-        root = FunctionCall(self.stats, self.stats.get_root_func(), depth=0)
-
-        func_list = []
-        self.add_node(func_list,
-                      root,
-                      dt_settings.get_config()['PROFILER_MAX_DEPTH'],
-                      root.stats[3] / 8)
-
-        self.record_stats({'func_list': func_list})
+        root_func = self.stats.get_root_func()
+        # Ensure root function exists before continuing with function call analysis
+        if root_func:
+            root = FunctionCall(self.stats, root_func, depth=0)
+            func_list = []
+            self.add_node(func_list,
+                          root,
+                          dt_settings.get_config()['PROFILER_MAX_DEPTH'],
+                          root.stats[3] / 8)
+            self.record_stats({'func_list': func_list})


Test patch:
diff --git a/tests/panels/test_profiling.py b/tests/panels/test_profiling.py
--- a/tests/panels/test_profiling.py
+++ b/tests/panels/test_profiling.py
@@ -47,6 +47,24 @@ def test_listcomp_escaped(self):
         self.assertNotIn('<span class="djdt-func"><listcomp></span>', self.panel.content)
         self.assertIn('<span class="djdt-func">&lt;listcomp&gt;</span>', self.panel.content)
 
+    def test_generate_stats_no_profiler(self):
+        """
+        Test generating stats with no profiler.
+        """
+        self.assertIsNone(self.panel.generate_stats(self.request, self.response))
+
+    def test_generate_stats_no_root_func(self):
+        """
+        Test generating stats using profiler without root function.
+        """
+        self.panel.process_view(self.request, regular_view, ('profiling',), {})
+        self.panel.process_response(self.request, self.response)
+        self.panel.profiler.clear()
+        self.panel.profiler.enable()
+        self.panel.profiler.disable()
+        self.panel.generate_stats(self.request, self.response)
+        self.assertNotIn('func_list', self.panel.get_stats())
+
 
 @override_settings(DEBUG=True,
                    DEBUG_TOOLBAR_PANELS=['debug_toolbar.panels.profiling.ProfilingPanel'])