Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(FlameGraph): Keep focus whenever the profile data changes #325

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

grafakus
Copy link
Contributor

@grafakus grafakus commented Dec 19, 2024

✨ Description

Related issue(s): resolves #260, related (draft) PR in Grafana grafana/grafana#98356

This PR introduces a new keepFocusOnDataChange prop to the FlameGraph component.

📖 Summary of the changes

In order to simplify the implementation of the fix, the whole @grafana/flamegraph package has been copy-pasted. Once agreed on the definitive solution, I'll update the PR in the Grafana repository.

I've commented the changes the diff tab (mainly in src/tmp/grafana-flamegraph/src/FlameGraphContainer.tsx).

🧪 How to test?

  • Manually:
    • Go to the "Flame graph" or the "Diff flame graph" view
    • Click on a flame graph node
    • Click on "Focus block"
    • Change the time range or click on the time picker refresh button
    • The block should stay focused and the percentage should update or drop to 0 if the function didn't consume any resource for the new time range

The same should occur for the sandwich view

@github-actions github-actions bot added the feat label Dec 19, 2024
Copy link
Contributor

github-actions bot commented Dec 19, 2024

Unit test coverage

Lines Statements Branches Functions
Coverage: 10%
11.12% (550/4943) 7.71% (137/1776) 8.24% (120/1455)

@grafakus grafakus self-assigned this Dec 19, 2024
@@ -38,16 +38,17 @@
"@emotion/css": "11.10.6",
"@grafana/data": "11.3.2",
"@grafana/faro-web-sdk": "^1.10.0",
"@grafana/flamegraph": "11.3.2",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed and added all its dependencies instead.

@@ -14,6 +13,7 @@ import { PyroscopeLogo } from '@shared/ui/PyroscopeLogo';
import React, { useEffect, useMemo } from 'react';
import { Unsubscribable } from 'rxjs';

import { FlameGraph } from '../../../../tmp/grafana-flamegraph/src/';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TS was complaining when I tried aliasing @gfrafana/flamegraph. Webpack didn't 🤷🏾‍♂️

Comment on lines +76 to +79
* Whether or not to keep any focused item when the profile data changes.
*/
keepFocusOnDataChange?: boolean;
};
Copy link
Contributor Author

@grafakus grafakus Dec 19, 2024

Choose a reason for hiding this comment

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

👆🏾 The change

@ifrost
Copy link
Contributor

ifrost commented Jan 7, 2025

From the feedback:

"I would assume we want to keep the state of the flame graph and other tools that exist to help understand the flame graph. Here's a quick video:"

Shouldn't we lock entire FlameGraph state when users starts drilling down? And maybe show some sort of indication that there's new data coming in and the graph needs refreshing? I found it a bit confusing that the graph tries to keep the focus also when I change the time range. The block that is in focus may not exist anymore.

}

if (focusedItem) {
const percentValue = totalTicks > 0 ? Math.round(10000 * (focusedItem.item.value / totalTicks)) / 100 : 0;
Copy link
Contributor Author

@grafakus grafakus Jan 14, 2025

Choose a reason for hiding this comment

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

A small change to prevent NaN or 0 values to appear. A tooltip has also been added to remind which function is focused, as well as some minor CSS alignment changes below.

verticalAlign: 'text-bottom',
margin: theme.spacing(0, 0.5),
}),
metadata: css({
Copy link
Contributor Author

@grafakus grafakus Jan 14, 2025

Choose a reason for hiding this comment

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

This fixes the alignment

setSandwichItem(undefined);
}, [setSandwichItem]);

useEffect(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The change.

setRangeMin(item.start / totalViewTicks);
setRangeMax((item.start + item.value) / totalViewTicks);
} else {
setFocusedItemData({
Copy link
Contributor Author

@grafakus grafakus Jan 14, 2025

Choose a reason for hiding this comment

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

Edge case when the function is not present in the data received:

image

ifrost
ifrost previously approved these changes Jan 15, 2025
Copy link
Contributor

@ifrost ifrost left a comment

Choose a reason for hiding this comment

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

Works great!

My only doubt is about FlameGraphCanvas vs FlameGraphContainer responsibilities. Canvas sets range when user clicks while container sets range to keep the focus. Also container is responsible for resetting range when focus is reset. Just a gut feeling but maybe setting range based on focused item should be fully controlled by the container 🤔 otherwise we end up with similar code in both:

setRangeMin(clickedItemData.item.start / totalViewTicks);
            setRangeMax((clickedItemData.item.start + clickedItemData.item.value) / totalViewTicks);
            onItemFocused(clickedItemData);

Anyway, probably something to tackle separately. Good stuff!

@ifrost
Copy link
Contributor

ifrost commented Jan 15, 2025

What's the plan?

@grafakus
Copy link
Contributor Author

grafakus commented Jan 15, 2025

My only doubt is about FlameGraphCanvas vs FlameGraphContainer responsibilities.
Just a gut feeling but maybe setting range based on focused item should be fully controlled by the container 🤔 otherwise we end up with similar code in both
Anyway, probably something to tackle separately

Thanks for the testing and review! I agree to your suggestion, it seems a good improvement to the code. We have a couple of issues for the flame graph package in our backlog, we will have many opportunities to refactor and clarify the responsibilities of the components in the future.

@grafakus
Copy link
Contributor Author

grafakus commented Jan 15, 2025

What's the plan?

I've updated grafana/grafana#98356 and will ping Joey and André to see if they want to have a look at it. Then I'd suggest to merge this PR so the functionality does not depend on Grafana's release cycle and has a chance to hit production faster.

@grafakus grafakus requested a review from ifrost January 15, 2025 12:33
<Icon className={styles.sandwichMarkerIcon} name={'arrow-down'} />
let canvas = null;

if (levelsCallers?.length) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is made to prevent runtime errors (as seen in prod) when either:

  • levelsCallers[levelsCallers.length - 1] is undefined
  • levels[0] is undefined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Flame graph view: (auto)refreshing should preserve UI context
2 participants