Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
9 changes: 8 additions & 1 deletion 9 Lib/profiling/sampling/_flamegraph_assets/flamegraph.css
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ body.resizing-sidebar {
}

/* View Mode Section */
.view-mode-section {
display: flex;
flex-direction: column;
gap: 8px;
}

.view-mode-section .section-content {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -1067,7 +1073,8 @@ body.resizing-sidebar {
-------------------------------------------------------------------------- */

#toggle-invert .toggle-track.on,
#toggle-elided .toggle-track.on {
#toggle-elided .toggle-track.on,
#toggle-path-display .toggle-track.on {
background: #8e44ad;
border-color: #8e44ad;
box-shadow: 0 0 8px rgba(142, 68, 173, 0.3);
Expand Down
72 changes: 59 additions & 13 deletions 72 Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ let normalData = null;
let invertedData = null;
let currentThreadFilter = 'all';
let isInverted = false;
let useModuleNames = true;

// Heat colors are now defined in CSS variables (--heat-1 through --heat-8)
// and automatically switch with theme changes - no JS color arrays needed!
Expand Down Expand Up @@ -64,6 +65,12 @@ function resolveStringIndices(node, table) {
if (typeof resolved.funcname === 'number') {
resolved.funcname = resolveString(resolved.funcname, table);
}
if (typeof resolved.module === 'number') {
resolved.module = resolveString(resolved.module, table);
}
if (typeof resolved.label === 'number') {
resolved.label = resolveString(resolved.label, table);
}

if (Array.isArray(resolved.source)) {
resolved.source = resolved.source.map(index =>
Expand All @@ -78,6 +85,19 @@ function resolveStringIndices(node, table) {
return resolved;
}

// Escape HTML special characters
function escapeHtml(str) {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

// Get display path based on user preference (module or full path)
function getDisplayName(moduleName, filename) {
if (useModuleNames) {
return moduleName || filename;
}
return filename;
}

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

Expand Down Expand Up @@ -228,6 +248,7 @@ function setupLogos() {
function updateStatusBar(nodeData, rootValue) {
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 percent = rootValue > 0 ? ((nodeData.value / rootValue) * 100).toFixed(1) : "0.0";
Expand All @@ -249,8 +270,8 @@ function updateStatusBar(nodeData, rootValue) {

const fileEl = document.getElementById('status-file');
if (fileEl && filename && filename !== "~") {
const basename = filename.split('/').pop();
fileEl.textContent = lineno ? `${basename}:${lineno}` : basename;
const displayName = getDisplayName(moduleName, filename);
fileEl.textContent = lineno ? `${displayName}:${lineno}` : displayName;
}

const funcEl = document.getElementById('status-func');
Expand Down Expand Up @@ -301,6 +322,8 @@ function createPythonTooltip(data) {

const funcname = resolveString(d.data.funcname) || resolveString(d.data.name);
const filename = resolveString(d.data.filename) || "";
const moduleName = resolveString(d.data.module) || "";
const displayName = escapeHtml(useModuleNames ? (moduleName || filename) : filename);
const isSpecialFrame = filename === "~";

// Build source section
Expand All @@ -309,7 +332,7 @@ function createPythonTooltip(data) {
const sourceLines = source
.map((line) => {
const isCurrent = line.startsWith("→");
const escaped = line.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
const escaped = escapeHtml(line);
return `<div class="tooltip-source-line${isCurrent ? ' current' : ''}">${escaped}</div>`;
})
.join("");
Expand Down Expand Up @@ -369,7 +392,7 @@ function createPythonTooltip(data) {
}

const fileLocationHTML = isSpecialFrame ? "" : `
<div class="tooltip-location">${filename}${d.data.lineno ? ":" + d.data.lineno : ""}</div>`;
<div class="tooltip-location">${displayName}${d.data.lineno ? ":" + d.data.lineno : ""}</div>`;

// Differential stats section
let diffSection = "";
Expand Down Expand Up @@ -586,6 +609,7 @@ function createFlamegraph(tooltip, rootValue, data) {
.minFrameSize(1)
.tooltip(tooltip)
.inverted(true)
.getName(d => resolveString(useModuleNames ? d.data.label : d.data.name) || resolveString(d.data.name) || '')
.setColorMapper(function (d) {
if (d.depth === 0) return 'transparent';

Expand Down Expand Up @@ -628,25 +652,25 @@ function updateSearchHighlight(searchTerm, searchInput) {
const name = resolveString(d.data.name) || "";
const funcname = resolveString(d.data.funcname) || "";
const filename = resolveString(d.data.filename) || "";
const moduleName = resolveString(d.data.module) || "";
const displayName = getDisplayName(moduleName, filename);
const lineno = d.data.lineno;
const term = searchTerm.toLowerCase();

// Check if search term looks like file:line pattern
// Check if search term looks like path:line pattern
const fileLineMatch = term.match(/^(.+):(\d+)$/);
let matches = false;

if (fileLineMatch) {
// Exact file:line matching
const searchFile = fileLineMatch[1];
const searchLine = parseInt(fileLineMatch[2], 10);
const basename = filename.split('/').pop().toLowerCase();
matches = basename.includes(searchFile) && lineno === searchLine;
matches = displayName.toLowerCase().includes(searchFile) && lineno === searchLine;
} else {
// Regular substring search
matches =
name.toLowerCase().includes(term) ||
funcname.toLowerCase().includes(term) ||
filename.toLowerCase().includes(term);
displayName.toLowerCase().includes(term);
}

if (matches) {
Expand Down Expand Up @@ -1047,6 +1071,7 @@ function populateStats(data) {

let filename = resolveString(node.filename);
let funcname = resolveString(node.funcname);
let moduleName = resolveString(node.module);

if (!filename || !funcname) {
const nameStr = resolveString(node.name);
Expand All @@ -1061,6 +1086,7 @@ function populateStats(data) {

filename = filename || 'unknown';
funcname = funcname || 'unknown';
moduleName = moduleName || 'unknown';

if (filename !== 'unknown' && funcname !== 'unknown' && node.value > 0) {
let childrenValue = 0;
Expand All @@ -1077,12 +1103,14 @@ function populateStats(data) {
existing.directPercent = (existing.directSamples / totalSamples) * 100;
if (directSamples > existing.maxSingleSamples) {
existing.filename = filename;
existing.module = moduleName;
existing.lineno = node.lineno || '?';
existing.maxSingleSamples = directSamples;
}
} else {
functionMap.set(funcKey, {
filename: filename,
module: moduleName,
lineno: node.lineno || '?',
funcname: funcname,
directSamples,
Expand Down Expand Up @@ -1117,6 +1145,7 @@ function populateStats(data) {
const h = hotSpots[i];
const filename = h.filename || 'unknown';
const lineno = h.lineno ?? '?';
const moduleName = h.module || 'unknown';
const isSpecialFrame = filename === '~' && (lineno === 0 || lineno === '?');

let funcDisplay = h.funcname || 'unknown';
Expand All @@ -1127,8 +1156,8 @@ function populateStats(data) {
if (isSpecialFrame) {
fileEl.textContent = '--';
} else {
const basename = filename !== 'unknown' ? filename.split('/').pop() : 'unknown';
fileEl.textContent = `${basename}:${lineno}`;
const displayName = getDisplayName(moduleName, filename);
fileEl.textContent = `${displayName}:${lineno}`;
}
}
if (percentEl) percentEl.textContent = `${h.directPercent.toFixed(1)}%`;
Expand All @@ -1144,8 +1173,11 @@ function populateStats(data) {
if (card) {
if (i < hotSpots.length && hotSpots[i]) {
const h = hotSpots[i];
const basename = h.filename !== 'unknown' ? h.filename.split('/').pop() : '';
const searchTerm = basename && h.lineno !== '?' ? `${basename}:${h.lineno}` : h.funcname;
const moduleName = h.module || 'unknown';
const filename = h.filename || 'unknown';
const displayName = getDisplayName(moduleName, filename);
const hasValidLocation = displayName !== 'unknown' && h.lineno !== '?';
const searchTerm = hasValidLocation ? `${displayName}:${h.lineno}` : h.funcname;
card.dataset.searchterm = searchTerm;
card.onclick = () => searchForHotspot(searchTerm);
card.style.cursor = 'pointer';
Expand Down Expand Up @@ -1277,10 +1309,12 @@ function accumulateInvertedNode(parent, stackFrame, leaf, isDifferential) {
if (!parent.children[key]) {
const newNode = {
name: stackFrame.name,
label: stackFrame.label,
value: 0,
self: 0,
children: {},
filename: stackFrame.filename,
module: stackFrame.module,
lineno: stackFrame.lineno,
funcname: stackFrame.funcname,
source: stackFrame.source,
Expand Down Expand Up @@ -1375,6 +1409,7 @@ function generateInvertedFlamegraph(data) {

const invertedRoot = {
name: data.name,
label: data.label,
value: data.value,
children: {},
stats: data.stats,
Expand All @@ -1399,6 +1434,12 @@ function toggleInvert() {
updateFlamegraphView();
}

function togglePathDisplay() {
useModuleNames = !useModuleNames;
updateToggleUI('toggle-path-display', useModuleNames);
updateFlamegraphView();
}

// ============================================================================
// Initialization
// ============================================================================
Expand Down Expand Up @@ -1446,6 +1487,11 @@ function initFlamegraph() {
if (toggleInvertBtn) {
toggleInvertBtn.addEventListener('click', toggleInvert);
}

const togglePathDisplayBtn = document.getElementById('toggle-path-display');
if (togglePathDisplayBtn) {
togglePathDisplayBtn.addEventListener('click', togglePathDisplay);
}
}

// Keyboard shortcut: Enter/Space activates toggle switches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ <h3 class="section-title">View Mode</h3>
<span class="toggle-label" data-text="Elided" title="Code paths that existed in baseline but are missing from current profile">Elided</span>
</div>

<div class="toggle-switch" id="toggle-path-display" title="Toggle between module names and full file paths" tabindex="0">
<span class="toggle-label" data-text="File Paths">File Paths</span>
<div class="toggle-track on"></div>
<span class="toggle-label active" data-text="Module Names">Module Names</span>
</div>

<div class="toggle-switch" id="toggle-invert" title="Toggle between standard and inverted flamegraph view" tabindex="0">
<span class="toggle-label active" data-text="Flamegraph">Flamegraph</span>
<div class="toggle-track"></div>
Expand Down
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.