Skip to content

Replace 4 counter track components with a single generic TrackCounter#5944

Open
fatadel wants to merge 3 commits intofirefox-devtools:mainfrom
fatadel:generic-counters-5752-pr-2
Open

Replace 4 counter track components with a single generic TrackCounter#5944
fatadel wants to merge 3 commits intofirefox-devtools:mainfrom
fatadel:generic-counters-5752-pr-2

Conversation

@fatadel
Copy link
Copy Markdown
Contributor

@fatadel fatadel commented Apr 10, 2026

Collapse the separate Memory, Power, ProcessCPU, and Bandwidth track implementations into a single TrackCounter component that renders any counter type using CounterDisplayConfig from PR #5912.

The LocalTrack union type is simplified from 8 variants to 5 by replacing 'memory', 'power', 'process-cpu', and 'bandwidth' with a single 'counter' type. The component branches on display.graphType for canvas drawing (accumulated vs rate) and on display.unit for tooltip rendering (bytes, pWh, percent, etc.).

Track index ordering (for URL backward compatibility) is handled by a category-based mapping function. Display ordering uses the new display.sortWeight field.

This is the second (last) PR for issue #5752.

Make counters self-describing in terms of rendering by adding `display`
field of `CounterDisplayConfig` type. The value is derived from a
counter's `category` and `name` fields. This data is sufficient to
understand how a counter should be rendered allowing us to remove
hardcoded logic for each counter.

This is the first PR for issue firefox-devtools#5752.
@fatadel fatadel changed the title Generic counters 5752 pr 2 Replace 4 counter track components with a single generic TrackCounter Apr 10, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 87.07865% with 46 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.28%. Comparing base (4308a73) to head (8491d2d).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/components/timeline/TrackCounterGraph.tsx 93.46% 16 Missing and 1 partial ⚠️
src/profile-logic/tracks.ts 71.92% 16 Missing ⚠️
src/selectors/app.tsx 0.00% 8 Missing ⚠️
src/test/fixtures/profiles/tracks.ts 50.00% 3 Missing ⚠️
src/components/timeline/TrackCounter.tsx 84.61% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5944      +/-   ##
==========================================
- Coverage   85.37%   85.28%   -0.10%     
==========================================
  Files         322      316       -6     
  Lines       32069    31647     -422     
  Branches     8814     8763      -51     
==========================================
- Hits        27378    26989     -389     
+ Misses       4260     4227      -33     
  Partials      431      431              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fatadel fatadel requested a review from canova April 10, 2026 12:58
@canova
Copy link
Copy Markdown
Member

canova commented Apr 10, 2026

Nice!

It looks like the order of the tacks have changed with this PR, is this intentional?
Before:
Screenshot 2026-04-10 at 15 25 05
After:
Screenshot 2026-04-10 at 15 25 13

Compositor is above the other counter tracks.

Copy link
Copy Markdown
Member

@canova canova left a comment

Choose a reason for hiding this comment

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

I'm a bit worried about the way we decide how we figure out which tooltip to show. Currently it works, but it looks brittle considering that another counter could have similar fields. I'm still leaning towards defining tooltip fields inside the profile format. But @fqueze was saying that it was difficult to make it generic. I think I would be okay with losing the l10n part of the tooltips to make it generic, so we can define all the things like the label and what it should display. What do you think?

Comment thread src/components/timeline/TrackCounterGraph.tsx
Comment thread src/components/timeline/TrackCounterGraph.tsx
@canova
Copy link
Copy Markdown
Member

canova commented Apr 10, 2026

I was thinking about something like this that we can define in the counter schema:

 type CounterTooltipDataSource =
    | 'count'            // samples.count[i]
    | 'rate'             // count / sampleTimeDelta
    | 'cpu-ratio'        // rate / maxCounterSampleCountPerMs
    | 'accumulated'      // accumulatedCounts[i] - minCount
    | 'count-range'      // total range across the visible graph
    | 'selection-total'  // sum over the preview selection
    | 'sample-number';   // samples.number[i] — row is omitted when null

  type CounterTooltipFormatter = 'bytes' | 'bytes-per-second' | 'percent' | 'number';

  type CounterTooltipRow =
    | { type: 'value'; source: CounterTooltipDataSource; format: CounterTooltipFormatter; label: string }
    | { type: 'separator' };

  // In CounterDisplayConfig:
  tooltipRows: CounterTooltipRow[];

And then we could use that like:

  tooltipRows: [
    { type: 'value', source: 'accumulated',  format: 'bytes',  label: 'relative memory at this time' },
    { type: 'value', source: 'count-range',  format: 'bytes',  label: 'memory range in graph' },
    { type: 'value', source: 'sample-number', format: 'number', label: 'allocations/deallocations since previous sample' },
  ]

But not so sure if it works with all the counters that we have.

@fqueze
Copy link
Copy Markdown
Contributor

fqueze commented Apr 10, 2026

I'm a bit worried about the way we decide how we figure out which tooltip to show. Currently it works, but it looks brittle considering that another counter could have similar fields. I'm still leaning towards defining tooltip fields inside the profile format. But @fqueze was saying that it was difficult to make it generic. I think I would be okay with losing the l10n part of the tooltips to make it generic, so we can define all the things like the label and what it should display. What do you think?

Ideally we would have a generic implementation defined in the counter schema inside the profile JSON, and we can have specific overrides for specific counters (eg power, to show the CO2e values), similar to how network markers are handled differently.

@canova
Copy link
Copy Markdown
Member

canova commented Apr 10, 2026

Yeah that sounds good. Also, adding context here after talking about it in sync. It would make sense to keep a display.tooltipType (or named something like this), that holds 'memory', 'power' etc. as a first step. And then we can think about making it more generic as a follow-up.

  Collapse the separate Memory, Power, ProcessCPU, and Bandwidth track
  implementations into a single TrackCounter component that renders any
  counter type using CounterDisplayConfig from PR firefox-devtools#5912.

  The LocalTrack union type is simplified from 8 variants to 5 by
  replacing 'memory', 'power', 'process-cpu', and 'bandwidth' with a
  single 'counter' type. The component branches on display.graphType
  for canvas drawing (accumulated vs rate) and on display.unit for
  tooltip rendering (bytes, pWh, percent, etc.).

  Track index ordering (for URL backward compatibility) is handled by
  a category-based mapping function. Display ordering uses the new
  display.sortWeight field.

  Part of firefox-devtools#5752.
@fatadel fatadel force-pushed the generic-counters-5752-pr-2 branch from cc362ba to 925b5f7 Compare April 13, 2026 08:33
@fatadel
Copy link
Copy Markdown
Contributor Author

fatadel commented Apr 13, 2026

Nice!

It looks like the order of the tacks have changed with this PR, is this intentional? Before: %img% After: %img%

Compositor is above the other counter tracks.

No, this was not intentional. I have realized that I missed it because the profile I've used did not have the Compositor track.
The issue was that the new display.sortWeight for counters uses values in multiples of 10 (10, 20, 30, etc.), however, the old values (LOCAL_TRACK_DISPLAY_ORDER) for everything else were all < 10 (basically, from a different value space). Now I've fixed it by bringing everything to the same value space (multiples of 10).

@fatadel
Copy link
Copy Markdown
Contributor Author

fatadel commented Apr 13, 2026

Thanks for the feedback on tooltip rendering, @fqueze and @canova ! After thinking about it and consulting with Claude, here's the approach I'd like to propose. It combines @fqueze's idea of "generic default + specific overrides" with @canova's suggestion of a tooltipType field.

The concept: generic by default, with opt-in overrides for rich tooltips

We add an optional tooltipType field to CounterDisplayConfig:

export type CounterDisplayConfig = {
// ... existing fields ...

// When set, the frontend uses a dedicated tooltip implementation for this                                                                                                                                                                              
// counter type. When absent/null, the frontend renders a generic tooltip
// based on the counter's unit (e.g., bytes → formatBytes, percent →                                                                                                                                                                                    
// formatPercent).                                                                                                                                                                                                                                      
tooltipType?: 'memory' | 'power' | 'bandwidth' | 'process-cpu' | null;

};

The logic in the component is:

  1. If tooltipType is set → use the dedicated tooltip implementation (these are the existing tooltips we already have, preserved as-is)
  2. If tooltipType is absent/null → render a generic tooltip that formats the value based on unit

Wdyt about it @fqueze @canova ?

@canova
Copy link
Copy Markdown
Member

canova commented Apr 14, 2026

(apparently the comment I wrote in the morning wasn't sent, and I lost the message. rewriting)

I've thought about it a bit more and discussed with @fqueze and I think it might make sense to not add tooltipType at all actually. Because this field will be replaced by the more generic version of tooltip representation soon (either my solution above or a better one), so we might as well not add this for tooltipType for now.

In the code where we decide which tooltip to show, instead of this field, we can look at the counter.category and counter.name to decide which tooltip to share. Since we already deduct this tooltipType by looking at the counter names and categories, we can skip tooltipType all together. And once we have more generic ways to handle the tooltips, this custom handling would be removed.

This would prevent us from adding an intermediate display field that will be removed soon. WDYT?

@fatadel
Copy link
Copy Markdown
Contributor Author

fatadel commented Apr 15, 2026

(apparently the comment I wrote in the morning wasn't sent, and I lost the message. rewriting)

I've thought about it a bit more and discussed with @fqueze and I think it might make sense to not add tooltipType at all actually. Because this field will be replaced by the more generic version of tooltip representation soon (either my solution above or a better one), so we might as well not add this for tooltipType for now.

In the code where we decide which tooltip to show, instead of this field, we can look at the counter.category and counter.name to decide which tooltip to share. Since we already deduct this tooltipType by looking at the counter names and categories, we can skip tooltipType all together. And once we have more generic ways to handle the tooltips, this custom handling would be removed.

This would prevent us from adding an intermediate display field that will be removed soon. WDYT?

Yeah, it makes sense. Instead of adding a tooltipType field to the format that we'd just remove later, we can use counter.category and counter.name directly in the tooltip rendering code to decide which custom tooltip to show.
I will change it now!

# Conflicts:
#	src/app-logic/constants.ts
#	src/components/timeline/TrackBandwidthGraph.tsx
#	src/components/timeline/TrackMemoryGraph.tsx
#	src/components/timeline/TrackPowerGraph.tsx
#	src/profile-logic/process-profile.ts
#	src/profile-logic/processed-profile-versioning.ts
#	src/test/components/TrackBandwidth.test.tsx
#	src/test/components/TrackMemory.test.tsx
#	src/test/fixtures/profiles/processed-profile.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants