Skip to content

Commit 19e16d6

Browse files
committed
Watch for readystate before initialising flamegraph UI
1 parent f7c5d07 commit 19e16d6

File tree

1 file changed

+100
-96
lines changed

1 file changed

+100
-96
lines changed

Lib/profiling/sampling/flamegraph.js

Lines changed: 100 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,89 @@
1+
function initFlamegraphUI() {
2+
main();
3+
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+
}
187
function main() {
288
const data = {{FLAMEGRAPH_DATA}}
389

@@ -9,7 +95,6 @@ function main() {
995
}
1096

1197
const pythonTooltip = flamegraph.tooltip.defaultFlamegraphTooltip();
12-
1398
pythonTooltip.show = function (d, element) {
1499
if (!this._tooltip) {
15100
this._tooltip = d3
@@ -232,93 +317,12 @@ function main() {
232317
populateStats(data);
233318
}
234319

235-
// Wait for libraries to load
236-
document.addEventListener("DOMContentLoaded", function () {
237-
main();
238320

239-
const infoBtn = document.getElementById("show-info-btn");
240-
const infoPanel = document.getElementById("info-panel");
241-
const closeBtn = document.getElementById("close-info-btn");
242-
const searchInput = document.getElementById("search-input");
243-
244-
if (infoBtn && infoPanel) {
245-
infoBtn.addEventListener("click", function () {
246-
const isOpen = infoPanel.style.display === "block";
247-
infoPanel.style.display = isOpen ? "none" : "block";
248-
});
249-
}
250-
if (closeBtn && infoPanel) {
251-
closeBtn.addEventListener("click", function () {
252-
infoPanel.style.display = "none";
253-
});
254-
}
255-
256-
// Add search functionality - wait for chart to be ready
257-
if (searchInput) {
258-
let searchTimeout;
259-
260-
function performSearch() {
261-
const searchTerm = searchInput.value.trim();
262-
263-
// Clear previous search highlighting
264-
d3.selectAll("#chart rect")
265-
.style("stroke", null)
266-
.style("stroke-width", null)
267-
.style("opacity", null);
268-
269-
if (searchTerm && searchTerm.length > 0) {
270-
// First, dim all rectangles
271-
d3.selectAll("#chart rect")
272-
.style("opacity", 0.3);
273-
274-
// Then highlight and restore opacity for matching nodes
275-
let matchCount = 0;
276-
d3.selectAll("#chart rect")
277-
.each(function(d) {
278-
if (d && d.data) {
279-
const name = d.data.name || "";
280-
const funcname = d.data.funcname || "";
281-
const filename = d.data.filename || "";
282-
283-
const matches = name.toLowerCase().includes(searchTerm.toLowerCase()) ||
284-
funcname.toLowerCase().includes(searchTerm.toLowerCase()) ||
285-
filename.toLowerCase().includes(searchTerm.toLowerCase());
286-
287-
if (matches) {
288-
matchCount++;
289-
d3.select(this)
290-
.style("opacity", 1)
291-
.style("stroke", "#ff6b35")
292-
.style("stroke-width", "2px")
293-
.style("stroke-dasharray", "3,3");
294-
}
295-
}
296-
});
297-
298-
// Update search input style based on results
299-
if (matchCount > 0) {
300-
searchInput.style.borderColor = "rgba(40, 167, 69, 0.8)";
301-
searchInput.style.boxShadow = "0 6px 20px rgba(40, 167, 69, 0.2)";
302-
} else {
303-
searchInput.style.borderColor = "rgba(220, 53, 69, 0.8)";
304-
searchInput.style.boxShadow = "0 6px 20px rgba(220, 53, 69, 0.2)";
305-
}
306-
} else {
307-
// Reset search input style
308-
searchInput.style.borderColor = "rgba(255, 255, 255, 0.2)";
309-
searchInput.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.1)";
310-
}
311-
}
312-
313-
searchInput.addEventListener("input", function() {
314-
clearTimeout(searchTimeout);
315-
searchTimeout = setTimeout(performSearch, 150);
316-
});
317-
318-
// Make search function globally accessible
319-
window.performSearch = performSearch;
320-
}
321-
});
321+
if (document.readyState === "loading") {
322+
document.addEventListener("DOMContentLoaded", initFlamegraphUI);
323+
} else {
324+
initFlamegraphUI();
325+
}
322326

323327
// Python color palette - cold to hot
324328
const pythonColors = [
@@ -334,10 +338,10 @@ const pythonColors = [
334338

335339
function populateStats(data) {
336340
const totalSamples = data.value || 0;
337-
341+
338342
// Collect all functions with their metrics, aggregated by function name
339343
const functionMap = new Map();
340-
344+
341345
function collectFunctions(node) {
342346
if (node.filename && node.funcname) {
343347
// Calculate direct samples (this node's value minus children's values)
@@ -346,10 +350,10 @@ function populateStats(data) {
346350
childrenValue = node.children.reduce((sum, child) => sum + child.value, 0);
347351
}
348352
const directSamples = Math.max(0, node.value - childrenValue);
349-
353+
350354
// Use file:line:funcname as key to ensure uniqueness
351355
const funcKey = `${node.filename}:${node.lineno || '?'}:${node.funcname}`;
352-
356+
353357
if (functionMap.has(funcKey)) {
354358
const existing = functionMap.get(funcKey);
355359
existing.directSamples += directSamples;
@@ -371,20 +375,20 @@ function populateStats(data) {
371375
});
372376
}
373377
}
374-
378+
375379
if (node.children) {
376380
node.children.forEach(child => collectFunctions(child));
377381
}
378382
}
379-
383+
380384
collectFunctions(data);
381-
385+
382386
// Convert map to array and get top 3 hotspots
383387
const hotSpots = Array.from(functionMap.values())
384388
.filter(f => f.directPercent > 0.5) // At least 0.5% to be significant
385389
.sort((a, b) => b.directPercent - a.directPercent)
386390
.slice(0, 3);
387-
391+
388392
// Populate the 3 cards
389393
for (let i = 0; i < 3; i++) {
390394
const num = i + 1;
@@ -395,7 +399,7 @@ function populateStats(data) {
395399
if (funcDisplay.length > 35) {
396400
funcDisplay = funcDisplay.substring(0, 32) + '...';
397401
}
398-
402+
399403
document.getElementById(`hotspot-file-${num}`).textContent = `${basename}:${hotspot.lineno}`;
400404
document.getElementById(`hotspot-func-${num}`).textContent = funcDisplay;
401405
document.getElementById(`hotspot-detail-${num}`).textContent = `${hotspot.directPercent.toFixed(1)}% samples (${hotspot.directSamples.toLocaleString()})`;

0 commit comments

Comments
 (0)