https://github.com/python/cpython/commit/4ff8b07a3d907c5a755a862a86496ed6c6fb2f3d
commit: 4ff8b07a3d907c5a755a862a86496ed6c6fb2f3d
branch: main
author: ivonastojanovic <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-04-04T20:55:05+01:00
summary:

gh-142927: Show self time in flamegraph tooltip (#147706)

We already show self time in differential flamegraphs, but it should
be included in regular flamegraphs as well. Display the time spent
in the function body excluding callees, not just the total inclusive
time.

files:
M Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
M Lib/profiling/sampling/stack_collector.py
M Lib/test/test_profiling/test_sampling_profiler/test_collectors.py

diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js 
b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
index 166c03d03fbe5b..d7a8890d4a1ad9 100644
--- a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
+++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
@@ -292,6 +292,8 @@ function createPythonTooltip(data) {
     }
 
     const timeMs = (d.data.value / 1000).toFixed(2);
+    const selfSamples = d.data.self || 0;
+    const selfMs = (selfSamples / 1000).toFixed(2);
     const percentage = ((d.data.value / data.value) * 100).toFixed(2);
     const calls = d.data.calls || 0;
     const childCount = d.children ? d.children.length : 0;
@@ -403,9 +405,14 @@ function createPythonTooltip(data) {
         ${fileLocationHTML}
       </div>
       <div class="tooltip-stats">
-        <span class="tooltip-stat-label">Execution Time:</span>
+        <span class="tooltip-stat-label">Total Time:</span>
         <span class="tooltip-stat-value">${timeMs} ms</span>
 
+        ${selfSamples > 0 ? `
+          <span class="tooltip-stat-label">Self Time:</span>
+          <span class="tooltip-stat-value">${selfMs} ms</span>
+        ` : ''}
+
         <span class="tooltip-stat-label">Percentage:</span>
         <span class="tooltip-stat-value accent">${percentage}%</span>
 
@@ -1271,6 +1278,7 @@ function accumulateInvertedNode(parent, stackFrame, leaf, 
isDifferential) {
     const newNode = {
       name: stackFrame.name,
       value: 0,
+      self: 0,
       children: {},
       filename: stackFrame.filename,
       lineno: stackFrame.lineno,
@@ -1293,6 +1301,7 @@ function accumulateInvertedNode(parent, stackFrame, leaf, 
isDifferential) {
 
   const node = parent.children[key];
   node.value += leaf.value;
+  node.self += stackFrame.self || 0;
   if (leaf.threads) {
     leaf.threads.forEach(t => node.threads.add(t));
   }
diff --git a/Lib/profiling/sampling/stack_collector.py 
b/Lib/profiling/sampling/stack_collector.py
index 31102d3eb0ffa6..461ce95a25874b 100644
--- a/Lib/profiling/sampling/stack_collector.py
+++ b/Lib/profiling/sampling/stack_collector.py
@@ -207,6 +207,7 @@ def convert_children(children, min_samples):
                 child_entry = {
                     "name": name_idx,
                     "value": samples,
+                    "self": node.get("self", 0),
                     "children": [],
                     "filename": filename_idx,
                     "lineno": func[1],
diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py 
b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
index 86fb9d4c05b3bc..503430ddf02163 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
@@ -435,12 +435,14 @@ def test_flamegraph_collector_basic(self):
         strings = data.get("strings", [])
         name = resolve_name(data, strings)
         self.assertTrue(name.startswith("Program Root: "))
-        self.assertIn("func2 (file.py:20)", name)  # formatted name
+        self.assertIn("func2 (file.py:20)", name)
+        self.assertEqual(data["self"], 0)  # non-leaf: no self time
         children = data.get("children", [])
         self.assertEqual(len(children), 1)
         child = children[0]
         self.assertIn("func1 (file.py:10)", resolve_name(child, strings))
         self.assertEqual(child["value"], 1)
+        self.assertEqual(child["self"], 1)  # leaf: all time is self
 
     def test_flamegraph_collector_export(self):
         """Test flamegraph HTML export functionality."""

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to