Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 12 additions & 8 deletions Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ function getDisplayName(moduleName, filename) {
return filename;
}

function samplesToMilliseconds(samples, data) {
return (samples * data.stats.sample_interval_usec / 1000).toFixed(2);
Comment thread
ivonastojanovic marked this conversation as resolved.
}

function selectFlamegraphData() {
const baseData = isShowingElided ? elidedFlamegraphData : normalData;

Expand Down Expand Up @@ -246,12 +250,12 @@ function setupLogos() {
// Status Bar
// ============================================================================

function updateStatusBar(nodeData, rootValue) {
function updateStatusBar(nodeData, rootValue, data) {
const funcname = resolveString(nodeData.funcname) || resolveString(nodeData.name) || "--";
const filename = resolveString(nodeData.filename) || "";
const moduleName = resolveString(nodeData.module) || "";
const lineno = nodeData.lineno;
const timeMs = (nodeData.value / 1000).toFixed(2);
const timeMs = samplesToMilliseconds(nodeData.value, data);
const percent = rootValue > 0 ? ((nodeData.value / rootValue) * 100).toFixed(1) : "0.0";

const brandEl = document.getElementById('status-brand');
Expand Down Expand Up @@ -313,9 +317,9 @@ function createPythonTooltip(data) {
.style("opacity", 0);
}

const timeMs = (d.data.value / 1000).toFixed(2);
const timeMs = samplesToMilliseconds(d.data.value, data);
const selfSamples = d.data.self || 0;
const selfMs = (selfSamples / 1000).toFixed(2);
const selfMs = samplesToMilliseconds(selfSamples, data);
const percentage = ((d.data.value / data.value) * 100).toFixed(2);
const relativePercentage = Math.min(100, ((d.data.value / (zoomedNodeValue ?? data.value)) * 100)).toFixed(2);
const calls = d.data.calls || 0;
Expand Down Expand Up @@ -399,9 +403,9 @@ function createPythonTooltip(data) {
// Differential stats section
let diffSection = "";
if (d.data.diff !== undefined && d.data.baseline !== undefined) {
const baselineSelf = (d.data.baseline / 1000).toFixed(2);
const currentSelf = ((d.data.self_time || 0) / 1000).toFixed(2);
const diffMs = (d.data.diff / 1000).toFixed(2);
const baselineSelf = samplesToMilliseconds(d.data.baseline, data);
const currentSelf = samplesToMilliseconds(d.data.self_time || 0, data);
const diffMs = samplesToMilliseconds(d.data.diff, data);
const diffPct = d.data.diff_pct;
const sign = d.data.diff >= 0 ? "+" : "";
const diffClass = d.data.diff > 0 ? "regression" : (d.data.diff < 0 ? "improvement" : "neutral");
Expand Down Expand Up @@ -499,7 +503,7 @@ function createPythonTooltip(data) {
.style("opacity", 1);

// Update status bar
updateStatusBar(d.data, data.value);
updateStatusBar(d.data, data.value, data);
};

pythonTooltip.hide = function () {
Expand Down
2 changes: 1 addition & 1 deletion Lib/profiling/sampling/stack_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def export(self, filename):
class FlamegraphCollector(StackTraceCollector):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.stats = {}
self.stats = {"sample_interval_usec": self.sample_interval_usec}
self._root = {"samples": 0, "children": {}, "threads": set()}
self._total_samples = 0
self._sample_count = 0 # Track actual number of samples (not thread traces)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ def test_flamegraph_collector_basic(self):
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
self.assertEqual(data["stats"]["sample_interval_usec"], 1000)

def test_flamegraph_collector_export(self):
"""Test flamegraph HTML export functionality."""
Expand All @@ -513,7 +514,7 @@ def test_flamegraph_collector_export(self):
)
self.addCleanup(close_and_unlink, flamegraph_out)

collector = FlamegraphCollector(1000)
collector = FlamegraphCollector(10000)

# Create some test data (use Interpreter/Thread objects like runtime)
test_frames1 = [
Expand Down Expand Up @@ -569,6 +570,8 @@ def test_flamegraph_collector_export(self):
self.assertIn('"name":', content)
self.assertIn('"value":', content)
self.assertIn('"children":', content)
self.assertIn('"sample_interval_usec": 10000', content)
self.assertIn("samples * data.stats.sample_interval_usec / 1000", content)

def test_flamegraph_collector_empty_export_fails(self):
"""Test empty flamegraph export reports no output."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix the time units in Tachyon flame graph tooltips by accounting for the
sampling interval when converting samples to milliseconds.
Loading