-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
feat(profiling): add ability to search for frames #31723
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
Conversation
| // where flamegraphRenderer is not null (see check on L86) | ||
| if (flamegraphRenderer === null) { | ||
| return; | ||
| if (flamegraphRenderer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you change these to use arrow functions, I think the scoping rules ensures that flamegraphRenderer is non null after the check from above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think my comment was wrong and why I removed it. Imagine if we at some point clear the flamegraph renderer but forget to clear the handlers, then our scheduler may still call the draw call and it would throw because flamegraphRenderer is now null
| newScheduler.on('searchResults', (results: Record<string, FlamegraphFrame>) => { | ||
| newScheduler.unregisterBeforeFrameCallback(drawRectangles); | ||
|
|
||
| function newDrawRectangles() { | ||
| if (flamegraphRenderer) { | ||
| flamegraphRenderer.draw(results); | ||
| } | ||
| } | ||
| newScheduler.registerBeforeFrameCallback(newDrawRectangles); | ||
|
|
||
| newScheduler.onDispose(() => | ||
| newScheduler.unregisterBeforeFrameCallback(newDrawRectangles) | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a big fan of this approach, would storing the results as a state be cleaner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was debating between that and storing the results. The difference here is that if we pass searchresults to this effect, we end up recreating all the renderers (unless we split each renderer to it's own useEffect init), but here we instead just replace the draw call. I think we need to figure out a good sort of "swap" mechanism here, I'm not even sure about the whole canvasScheduler and how well that will scale tbh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have some ideas about this, I'd love to hear them. We want a system that allows us to enqueue calls in some order and replace them. Maybe the easiest way here is to just isolate each renderer to it's own effect
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a pass over in #31639 where I lifted all the renderers out of useEffect using useMemo. In that scenario, I think if we used a ref to store the search results to avoid recreating everything, the .draw immediately after should accomplish what we want here. It's quite a refactor for an already large PR, so I'm happy to leave this as is and resolve this in a follow up.
| for (let i = 0; i < allFrames.length; i++) { | ||
| const frame = allFrames[i]; | ||
|
|
||
| if (userQuery.test(frame.frame.name.trim())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this going to cause problems when the global flag is set in the regex? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test#using_test_on_a_regex_with_the_global_flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it will indeed. Wdyt would be a good way of handling it? we could disable /g flag, but we need to add a warning for it then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could recreate the regex on every iteration or manually reset lastIndex
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I went with in the end, I was a bit worried we may have a large performance overhead, but then I read that it seems that regexp objects are pretty heavily optimized and cached. Not going to worry about it until it becomes a problem
fff0334 to
64af0cd
Compare
64af0cd to
d59c70b
Compare
Zylphrex
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly just missing dependencies, otherwise, looks good to me
| } | ||
| } | ||
|
|
||
| return results; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: could just return at the end of the if block instead of returning in both the try and catch
| threshold: 0.3, | ||
| includeMatches: true, | ||
| }); | ||
| }, [allFrames.length]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be allFrames?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most def
| canvasPoolManager.dispatch('zoomIntoFrame', [frame]); | ||
| setSelectedNode(frame); | ||
| }, | ||
| [canvasPoolManager] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should setSelectedNode be in the dependencies here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not necessary, react guarantees that the setState fn is stable between rerenders (https://reactjs.org/docs/hooks-reference.html)
Note
React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to > omit from the useEffect or useCallback dependency list.
| setSearchResults(results); | ||
| canvasPoolManager.dispatch('searchResults', [results]); | ||
| }, | ||
| [searchIndex, frames, canvasPoolManager] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missing setSearchResults and allFrames
| setOpen(false); | ||
| } | ||
| }, | ||
| [open, setSearchResults] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add setOpen?
| onPreviousSearchClick(); | ||
| } | ||
| }, | ||
| [onNextSearchClick, onPreviousSearchClick, setSearchResults] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add setOpen?
|
@Zylphrex I think I'll add the react-hooks-exhaustive dependencies to our eslint config, the linting should have caught this. But good eye 👁️ |
size-limit report 📦
|
* feat(flamegraph): add view select menu * feat(profiling): use buttonbar * feat(profiling): add search * style(lint): Auto commit lint changes * style(lint): Auto commit lint changes * feat(profiling): decouple search fn * fix(flamegraphsearch): missing hook deps Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
* feat(profiling): flamegraph * feat(profiling): add flamegraph renderer and tests * fix(profiling): ignore unused renderer in test * fix(profiling): ignore unused renderer in test * feat(profiling): add util hooks * ref(hook): add usememowithprevious tests * feat(profiling): zoom view * fix(flamegraphrenderer): adjust for profiles that do not start at 0 and trim text more accurately * feat(profiling): add tooltip component * fix(flamegraph): remove merge marker * test(utils): fix tests * fix(tooltip): code review * feat(tooltip): add useDevicePixelRatio hook * feat(profiling): add view select menu (#31698) * feat(flamegraph): add view select menu * feat(profiling): use buttonbar * fix(viewselectmenu): use locale * test(gridrenderer): update tests * feat(profiling): add ability to search for frames (#31723) * feat(flamegraph): add view select menu * feat(profiling): use buttonbar * feat(profiling): add search * style(lint): Auto commit lint changes * style(lint): Auto commit lint changes * feat(profiling): decouple search fn * fix(flamegraphsearch): missing hook deps Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com> * fix(flamegraphtooltip): update deps Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Still very rough - I didnt take most of the design, but the functionality is there (we need to make it more bulletproof and add some limitations). I dont want to optimize anything early, but once we have some traces that we can stress test the search with, we should have a good idea of where the bottlenecks are.
