Skip to content

Conversation

lkollar
Copy link
Contributor

@lkollar lkollar commented Aug 31, 2025

Add --flamegraph option to the sampling profiler to produce a self-contained HTML report.

@lkollar lkollar marked this pull request as ready for review August 31, 2025 19:46
Copy link
Member

@AA-Turner AA-Turner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brief review of the JS code. Please disregard any feedback that is irrelevant due to use of the D3 framework -- I am primarily used to working with vanilla/framework-less JS.

On formatting, obviously PEP 8 doesn't apply as it's not Python code, but it would still be good to keep to the spirit of it, and wrap at 79 characters or thereabouts for readability.

I've had advice/reviews in the past that from a security perspective it's best to avoid constructing HTML in JS directly and instead use e.g. document.createElement(). I don't know enough about the D3/JS interactions to know if this applies here, though. There was one place that looked like it was performing HTML escaping in JavaScript, which seems like something we might consider doing in the Python layer before the data gets to JS?

A

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially use the SVG instead? https://github.com/python/devguide/blob/main/_static/python-logo.svg?short_path=f69d24a

The SVG is ~18x smaller, but it hasn't been minified.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add .min.js to the list of generated files in .gitattributes?

Comment on lines +19 to +46
.style("position", "absolute")
.style("padding", "20px")
.style("background", "white")
.style("color", "#2e3338")
.style("border-radius", "8px")
.style("font-size", "14px")
.style("border", "1px solid #e9ecef")
.style("box-shadow", "0 8px 30px rgba(0, 0, 0, 0.15)")
.style("z-index", "1000")
.style("pointer-events", "none")
.style("font-weight", "400")
.style("line-height", "1.5")
.style("max-width", "500px")
.style("font-family", "'Source Sans Pro', sans-serif")
.style("opacity", 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to apply these styles via a class, instead of inline?

}

const pythonTooltip = flamegraph.tooltip.defaultFlamegraphTooltip();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

const sourceLines = source
.map(
(line) =>
`<div style="font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; font-size: 12px; color: ${line.startsWith("→") ? "#3776ab" : "#5a6c7d"}; white-space: pre; line-height: 1.4; padding: 2px 0;">${line.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</div>`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, is it possible to use a class instead of inline styles?

Should we escape the HTML (line.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) whilst we're still in Python?

For the color codes, could we use CSS variables so that if we change them in the future, we only need to change it in one place?

Comment on lines 87 to 94
${
calls > 0
? `
<span style="color: #5a6c7d; font-weight: 500;">Function Calls:</span>
<strong style="color: #2e3338;">${calls.toLocaleString()}</strong>
`
: ""
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps extract to local variables instead of nested substitutions?

}

// Wait for libraries to load
document.addEventListener("DOMContentLoaded", function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that DOMContentLoaded may fire before the script loads, so the suggested pattern is to check for document.readyState. I don't know if this applies here or not, though.

Comment on lines 240 to 241
// Add search functionality - wait for chart to be ready
if (searchInput) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this block be extracted to a helper function?

def _create_flamegraph_html(self, data):
data_json = json.dumps(data)

template_dir = pathlib.Path(__file__).parent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use importlib.resources here? I believe e.g. for the Windows embedable builds, the stdlib is distributed as a zip file so modules might not have a __file__ attribute.

@pablogsal
Copy link
Member

I've had advice/reviews in the past that from a security perspective it's best to avoid constructing HTML in JS directly and instead use e.g. document.createElement(). I don't know enough about the D3/JS interactions to know if this applies here, though. There was one place that looked like it was performing HTML escaping in JavaScript, which seems like something we might consider doing in the Python layer before the data gets to JS?

Since this is a static template there are no advantages of doing this and having the web parts in Python is a bit messy specially since we cannot use something like Jinja. Having all the web parts in JS or the template itself is a common practice for this case and keeps the Python code focused just on the data part and not in creating HTML. Also since this is just a static file served with no dependencies so there are no concerns aboutsecurity or injection so I prefer to maximize maintainability.

I will give it a pass with prettier to format it as you indicate :)

@lkollar
Copy link
Contributor Author

lkollar commented Sep 9, 2025

Thanks for the review @AA-Turner. I think I addressed everything except for the comments around constructing HTML in JS. I also ended up breaking up the JS functions a bit in 7c4d792 as it was getting really unwieldy and I think this will make maintenance easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants