Skip to content

Commit 7c4d792

Browse files
committed
Break up large functions in flamegraph.js
1 parent 7546036 commit 7c4d792

File tree

1 file changed

+129
-146
lines changed

1 file changed

+129
-146
lines changed

Lib/profiling/sampling/flamegraph.js

Lines changed: 129 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,27 @@
1-
function initFlamegraphUI() {
2-
main();
1+
const EMBEDDED_DATA = {{FLAMEGRAPH_DATA}};
32

4-
const infoBtn = document.getElementById("show-info-btn");
5-
const infoPanel = document.getElementById("info-panel");
6-
const closeBtn = document.getElementById("close-info-btn");
7-
const searchInput = document.getElementById("search-input");
8-
9-
if (infoBtn && infoPanel) {
10-
infoBtn.addEventListener("click", function () {
11-
const isOpen = infoPanel.style.display === "block";
12-
infoPanel.style.display = isOpen ? "none" : "block";
13-
});
14-
}
15-
if (closeBtn && infoPanel) {
16-
closeBtn.addEventListener("click", function () {
17-
infoPanel.style.display = "none";
18-
});
19-
}
20-
21-
// Add search functionality - wait for chart to be ready
22-
if (searchInput) {
23-
let searchTimeout;
24-
25-
function performSearch() {
26-
const searchTerm = searchInput.value.trim();
27-
28-
// Clear previous search highlighting
29-
d3.selectAll("#chart rect")
30-
.style("stroke", null)
31-
.style("stroke-width", null)
32-
.style("opacity", null);
33-
34-
if (searchTerm && searchTerm.length > 0) {
35-
// First, dim all rectangles
36-
d3.selectAll("#chart rect")
37-
.style("opacity", 0.3);
38-
39-
// Then highlight and restore opacity for matching nodes
40-
let matchCount = 0;
41-
d3.selectAll("#chart rect")
42-
.each(function(d) {
43-
if (d && d.data) {
44-
const name = d.data.name || "";
45-
const funcname = d.data.funcname || "";
46-
const filename = d.data.filename || "";
47-
48-
const matches = name.toLowerCase().includes(searchTerm.toLowerCase()) ||
49-
funcname.toLowerCase().includes(searchTerm.toLowerCase()) ||
50-
filename.toLowerCase().includes(searchTerm.toLowerCase());
51-
52-
if (matches) {
53-
matchCount++;
54-
d3.select(this)
55-
.style("opacity", 1)
56-
.style("stroke", "#ff6b35")
57-
.style("stroke-width", "2px")
58-
.style("stroke-dasharray", "3,3");
59-
}
60-
}
61-
});
62-
63-
// Update search input style based on results
64-
if (matchCount > 0) {
65-
searchInput.style.borderColor = "rgba(40, 167, 69, 0.8)";
66-
searchInput.style.boxShadow = "0 6px 20px rgba(40, 167, 69, 0.2)";
67-
} else {
68-
searchInput.style.borderColor = "rgba(220, 53, 69, 0.8)";
69-
searchInput.style.boxShadow = "0 6px 20px rgba(220, 53, 69, 0.2)";
70-
}
71-
} else {
72-
// Reset search input style
73-
searchInput.style.borderColor = "rgba(255, 255, 255, 0.2)";
74-
searchInput.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.1)";
75-
}
76-
}
77-
78-
searchInput.addEventListener("input", function() {
79-
clearTimeout(searchTimeout);
80-
searchTimeout = setTimeout(performSearch, 150);
81-
});
82-
83-
// Make search function globally accessible
84-
window.performSearch = performSearch;
85-
}
86-
}
87-
function main() {
88-
const data = {{FLAMEGRAPH_DATA}}
3+
// Python color palette - cold to hot
4+
const pythonColors = [
5+
"#fff4bf", // Coldest - light yellow (<1%)
6+
"#ffec9e", // Cold - yellow (1-3%)
7+
"#ffe47d", // Cool - golden yellow (3-6%)
8+
"#ffdc5c", // Medium - golden (6-12%)
9+
"#ffd43b", // Warm - Python gold (12-18%)
10+
"#5592cc", // Hot - light blue (18-35%)
11+
"#4584bb", // Very hot - medium blue (35-60%)
12+
"#3776ab", // Hottest - Python blue (≥60%)
13+
];
8914

15+
function ensureLibraryLoaded() {
9016
if (typeof flamegraph === "undefined") {
9117
console.error("d3-flame-graph library not loaded");
9218
document.getElementById("chart").innerHTML =
9319
'<h2 style="text-align: center; color: #d32f2f;">Error: d3-flame-graph library failed to load</h2>';
9420
throw new Error("d3-flame-graph library failed to load");
9521
}
22+
}
9623

24+
function createPythonTooltip(data) {
9725
const pythonTooltip = flamegraph.tooltip.defaultFlamegraphTooltip();
9826
pythonTooltip.show = function (d, element) {
9927
if (!this._tooltip) {
@@ -266,76 +194,141 @@ function main() {
266194
this._tooltip.transition().duration(200).style("opacity", 0);
267195
}
268196
};
197+
return pythonTooltip;
198+
}
269199

270-
// Store root value globally for color mapping
271-
let globalRootValue = data.value;
272-
// Store data globally for resize handling
273-
window.flamegraphData = data;
274-
275-
// Create the flamegraph with proper color mapping
200+
function createFlamegraph(tooltip, rootValue) {
276201
let chart = flamegraph()
277202
.width(window.innerWidth - 80)
278203
.cellHeight(20)
279204
.transitionDuration(300)
280205
.minFrameSize(1)
281-
.tooltip(pythonTooltip)
206+
.tooltip(tooltip)
282207
.inverted(true)
283208
.setColorMapper(function (d) {
284-
// Use the stored global root value
285-
const percentage = d.data.value / globalRootValue;
286-
287-
// Realistic thresholds for profiling data based on debug output
209+
const percentage = d.data.value / rootValue;
288210
let colorIndex;
289-
if (percentage >= 0.6)
290-
colorIndex = 7; // Hottest - ≥60% (like your 100%, 80%)
291-
else if (percentage >= 0.35)
292-
colorIndex = 6; // Very hot - 35-60% (like your 50%)
293-
else if (percentage >= 0.18)
294-
colorIndex = 5; // Hot - 18-35% (like your 30%, 25%, 20%)
295-
else if (percentage >= 0.12)
296-
colorIndex = 4; // Warm - 12-18% (like your 15%)
297-
else if (percentage >= 0.06)
298-
colorIndex = 3; // Medium - 6-12%
299-
else if (percentage >= 0.03)
300-
colorIndex = 2; // Cool - 3-6% (like your 5%)
301-
else if (percentage >= 0.01)
302-
colorIndex = 1; // Cold - 1-3%
303-
else colorIndex = 0; // Coldest - <1%
304-
305-
const color = pythonColors[colorIndex];
306-
307-
return color;
211+
if (percentage >= 0.6) colorIndex = 7;
212+
else if (percentage >= 0.35) colorIndex = 6;
213+
else if (percentage >= 0.18) colorIndex = 5;
214+
else if (percentage >= 0.12) colorIndex = 4;
215+
else if (percentage >= 0.06) colorIndex = 3;
216+
else if (percentage >= 0.03) colorIndex = 2;
217+
else if (percentage >= 0.01) colorIndex = 1;
218+
else colorIndex = 0; // <1%
219+
return pythonColors[colorIndex];
308220
});
221+
return chart;
222+
}
309223

310-
// Render the flamegraph
224+
function renderFlamegraph(chart, data) {
311225
d3.select("#chart").datum(data).call(chart);
226+
window.flamegraphChart = chart; // for controls
227+
window.flamegraphData = data; // for resize/search
228+
populateStats(data);
229+
}
312230

313-
// Make chart globally accessible for controls
314-
window.flamegraphChart = chart;
231+
function attachPanelControls() {
232+
const infoBtn = document.getElementById("show-info-btn");
233+
const infoPanel = document.getElementById("info-panel");
234+
const closeBtn = document.getElementById("close-info-btn");
235+
if (infoBtn && infoPanel) {
236+
infoBtn.addEventListener("click", function () {
237+
const isOpen = infoPanel.style.display === "block";
238+
infoPanel.style.display = isOpen ? "none" : "block";
239+
});
240+
}
241+
if (closeBtn && infoPanel) {
242+
closeBtn.addEventListener("click", function () {
243+
infoPanel.style.display = "none";
244+
});
245+
}
246+
}
315247

316-
// Populate stats cards
317-
populateStats(data);
248+
function updateSearchHighlight(searchTerm, searchInput) {
249+
d3.selectAll("#chart rect")
250+
.style("stroke", null)
251+
.style("stroke-width", null)
252+
.style("opacity", null);
253+
if (searchTerm && searchTerm.length > 0) {
254+
d3.selectAll("#chart rect").style("opacity", 0.3);
255+
let matchCount = 0;
256+
d3.selectAll("#chart rect").each(function (d) {
257+
if (d && d.data) {
258+
const name = d.data.name || "";
259+
const funcname = d.data.funcname || "";
260+
const filename = d.data.filename || "";
261+
const term = searchTerm.toLowerCase();
262+
const matches =
263+
name.toLowerCase().includes(term) ||
264+
funcname.toLowerCase().includes(term) ||
265+
filename.toLowerCase().includes(term);
266+
if (matches) {
267+
matchCount++;
268+
d3.select(this)
269+
.style("opacity", 1)
270+
.style("stroke", "#ff6b35")
271+
.style("stroke-width", "2px")
272+
.style("stroke-dasharray", "3,3");
273+
}
274+
}
275+
});
276+
if (searchInput) {
277+
if (matchCount > 0) {
278+
searchInput.style.borderColor = "rgba(40, 167, 69, 0.8)";
279+
searchInput.style.boxShadow = "0 6px 20px rgba(40, 167, 69, 0.2)";
280+
} else {
281+
searchInput.style.borderColor = "rgba(220, 53, 69, 0.8)";
282+
searchInput.style.boxShadow = "0 6px 20px rgba(220, 53, 69, 0.2)";
283+
}
284+
}
285+
} else if (searchInput) {
286+
searchInput.style.borderColor = "rgba(255, 255, 255, 0.2)";
287+
searchInput.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.1)";
288+
}
318289
}
319290

291+
function initSearchHandlers() {
292+
const searchInput = document.getElementById("search-input");
293+
if (!searchInput) return;
294+
let searchTimeout;
295+
function performSearch() {
296+
const term = searchInput.value.trim();
297+
updateSearchHighlight(term, searchInput);
298+
}
299+
searchInput.addEventListener("input", function () {
300+
clearTimeout(searchTimeout);
301+
searchTimeout = setTimeout(performSearch, 150);
302+
});
303+
window.performSearch = performSearch;
304+
}
305+
306+
function handleResize(chart, data) {
307+
window.addEventListener("resize", function () {
308+
if (chart && data) {
309+
const newWidth = window.innerWidth - 80;
310+
chart.width(newWidth);
311+
d3.select("#chart").datum(data).call(chart);
312+
}
313+
});
314+
}
315+
316+
function initFlamegraph() {
317+
ensureLibraryLoaded();
318+
const tooltip = createPythonTooltip(EMBEDDED_DATA);
319+
const chart = createFlamegraph(tooltip, EMBEDDED_DATA.value);
320+
renderFlamegraph(chart, EMBEDDED_DATA);
321+
attachPanelControls();
322+
initSearchHandlers();
323+
handleResize(chart, EMBEDDED_DATA);
324+
}
320325

321326
if (document.readyState === "loading") {
322-
document.addEventListener("DOMContentLoaded", initFlamegraphUI);
327+
document.addEventListener("DOMContentLoaded", initFlamegraph);
323328
} else {
324-
initFlamegraphUI();
329+
initFlamegraph();
325330
}
326331

327-
// Python color palette - cold to hot
328-
const pythonColors = [
329-
"#fff4bf", // Coldest - light yellow (<1%)
330-
"#ffec9e", // Cold - yellow (1-3%)
331-
"#ffe47d", // Cool - golden yellow (3-6%)
332-
"#ffdc5c", // Medium - golden (6-12%)
333-
"#ffd43b", // Warm - Python gold (12-18%)
334-
"#5592cc", // Hot - light blue (18-35%)
335-
"#4584bb", // Very hot - medium blue (35-60%)
336-
"#3776ab", // Hottest - Python blue (≥60%)
337-
];
338-
339332
function populateStats(data) {
340333
const totalSamples = data.value || 0;
341334

@@ -450,13 +443,3 @@ function clearSearch() {
450443
}
451444
}
452445

453-
// Handle window resize
454-
window.addEventListener("resize", function () {
455-
if (window.flamegraphChart && window.flamegraphData) {
456-
const newWidth = window.innerWidth - 80;
457-
window.flamegraphChart.width(newWidth);
458-
d3.select("#chart")
459-
.datum(window.flamegraphData)
460-
.call(window.flamegraphChart);
461-
}
462-
});

0 commit comments

Comments
 (0)