Introduce History Navigation and caching of searches
This Pr introduces two new major concepts:
- Caching
- History navigation
This allows for a much smoother experience working with ghc-debug and large heaps.
Caching
Cache 'TreeMode' data in ghc-debug-brick
Adds a caching layer that allows us to cache the result of expensive heap traversals.
- Extract async retainer heap traversals in separate module
- Decouple file persistence from heap profile census
- Decouple input handling from heap traversals
- Instead of immediately traversing the heap, send an event that allows us to check whether this request was already cached.
- If there is a cache result, just serve this, otherwise perform the heap traversal.
The caching is implemented via a simple map keyed by the parameters of the individual heap traversals.
When changing the result size, only the retainer search will have cache misses. The other heap traversals do perform a full search regardless the result size, thus, the results are reusable even if the result size changes.
Strictly speaking, the retainer search results should only be a cache miss if the search limit has been increased. We will leave that as future work.
History
Add History navigation capabilities
Allow the user to go back to previous searches and back to the current
one via Alt + RArrow and Alt + LArrow.
To make this easier to implement, we turn GcRoots into a normal
treeMode that is no longer stored in OperationalState directly.
We further introduce the new type HistoryNavigator which abstracts
away the logic of navigating the previous pages.
It stores a list of commands in a list zipper like fashion, for simple
traversal.
The HistoryNavigator is never empty, as we always view something.
When we go backwards on the stack and perform a new search, we create a
new future. This should be the same behaviour as common browsers
implement.
At the moment, each action is responsible for making it is pushed onto the history stack correctly.
We abstract the cache layer into its own type for consistency.