Skip to content

Add profiler-cli for querying profiles#5963

Open
canova wants to merge 8 commits intofirefox-devtools:mainfrom
canova:pq-cli
Open

Add profiler-cli for querying profiles#5963
canova wants to merge 8 commits intofirefox-devtools:mainfrom
canova:pq-cli

Conversation

@canova
Copy link
Copy Markdown
Member

@canova canova commented Apr 23, 2026

Fixes #5953 (we can move follow-ups that we have there once this lands).

This PR is the rebased, squashed and split version of #5663. I also did some reviews on top and updated the code further to fix some issues.

(This PR currently depends on #5968. Please review that one first)

It adds a new command-line tool, profiler-cli, for querying Firefox Profiler profiles from a terminal. The CLI is published as a separate npm package (@firefox-devtools/profiler-cli, alpha) and is the intended entry point for most users

What's in here:

New src/profile-query/ library: Reusable ProfileQuerier class that loads a profile (file, profiler.firefox.com URL, or share link) and exposes methods for profile/thread info, samples (top-down / bottom-up / hot functions), markers, functions, zoom (view range) stack, and per-thread filter stack. All query methods return structured result objects; formatting lives in the CLI layer.

New profiler-cli/ package: Node CLI (profiler-cli, short alias pq) with a persistent daemon architecture so subsequent commands against the same profile are fast. Sessions are stored under ~/.profiler-cli/.

How to do manual testing locally:

  • Pull the branch
  • Run yarn install and yarn build-profiler-cli
  • Add a temporary alias (so it doesn't affect the existing installation that you might have): alias profiler-cli="node $(pwd)/profiler-cli/dist/profiler-cli.js" && alias pq="node $(pwd)/profiler-cli/dist/profiler-cli.js"
  • Then you can use pq and profiler-cli in this terminal session.

@canova canova added the cli Issues related to the profiler CLI label Apr 23, 2026
@canova canova self-assigned this Apr 23, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 56.05880% with 1106 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.73%. Comparing base (c155fdf) to head (d1ebfbb).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/profile-query/index.ts 38.56% 223 Missing ⚠️
src/profile-query/formatters/page-load.ts 0.46% 216 Missing ⚠️
src/profile-query/formatters/marker-info.ts 75.04% 133 Missing ⚠️
src/profile-query/filter-stack.ts 0.00% 103 Missing ⚠️
src/profile-query/loader.ts 0.00% 92 Missing ⚠️
src/profile-query/formatters/thread-info.ts 51.97% 73 Missing ⚠️
src/profile-query/formatters/profile-info.ts 0.00% 58 Missing ⚠️
src/profile-logic/transforms.ts 58.46% 54 Missing ⚠️
src/profile-query/function-annotate.ts 62.99% 47 Missing ⚠️
src/selectors/profile.ts 46.34% 22 Missing ⚠️
... and 12 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5963      +/-   ##
==========================================
- Coverage   85.33%   83.73%   -1.61%     
==========================================
  Files         323      327       +4     
  Lines       32262    34266    +2004     
  Branches     8895     9484     +589     
==========================================
+ Hits        27532    28692    +1160     
- Misses       4298     5146     +848     
+ Partials      432      428       -4     

☔ 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.

@canova canova marked this pull request as ready for review April 23, 2026 10:38
@canova canova requested review from a team and fatadel as code owners April 23, 2026 10:38
@canova canova requested review from mstange and removed request for a team April 23, 2026 10:38
@canova
Copy link
Copy Markdown
Member Author

canova commented Apr 23, 2026

This is ready for review!
(Removing the l10n review request as this ftl change is reviewed already in #5943 which this PR depends)

@flodolo
Copy link
Copy Markdown
Contributor

flodolo commented Apr 23, 2026

(Removing the l10n review request as this ftl change is reviewed already in #5943 which this PR depends)

Was too fast 😓

@canova canova force-pushed the pq-cli branch 3 times, most recently from 90b59aa to 11d68ca Compare April 24, 2026 09:25
'function-include filter requires a non-empty filter string.'
);
}
const funcIndexes = new Set(filter.split(',').map(Number));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

surprised we have a comma-separated string of numbers here - we have proper arrays for things like merge-call-node, why not here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's mainly because the filter-samples transform accepts a string as a filter, and the idea was to make it more generic so it can accept a bunch of filterType as an argument. But it kinda restricts us in this case:

/**
* Filter the samples in the thread by the filter.
* Currently it only supports filtering by the marker name but can be extended
* to support more filters in the future.
*/
'filter-samples': {
readonly type: 'filter-samples';
// Expand this type when you need to support more than just the marker.
readonly filterType: FilterSamplesType;
readonly filter: string;
};

But that's not necessarily a restriction that we have to follow, we can always change it something like this instead:

'filter-samples':
    | { readonly type: 'filter-samples'; readonly filterType: 'marker-search' | 'outside-marker'; readonly filter: string }
    | { readonly type: 'filter-samples'; readonly filterType: 'function-include' | 'stack-prefix'; readonly funcIndexes: number[] }
    | { readonly type: 'filter-samples'; readonly filterType: 'stack-suffix'; readonly funcIndex: number }

I don't know if we should do it here in a follow-up as it wouldn't need any upgrader, let me know what you think.

Comment thread src/profile-query/function-annotate.ts Outdated
}
}
}
// Memoize bottom-up: does this stack contain any frame for funcIndex?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this function is computing a bunch of things that we already have code to compute. Haven't reviewed in detail yet.

Copy link
Copy Markdown
Member Author

@canova canova Apr 27, 2026

Choose a reason for hiding this comment

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

Yeah, I reviewed that one more time and improved it. Put it as a separate commit so it's easier to review: d1ebfbb

Comment thread src/profile-query/function-annotate.ts Outdated
}
}

// Assembly annotation
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We don't really have enough information to make Assembly annotation work properly. We only have the mapping between source line and assembly instruction for instructions that were sampled. The symbolication API doesn't give us this information for the rest of the instructions yet.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, but I wanted to do exactly what assembly view in the browser does, and I believe this behavior matches it. When I look at the assembly view in the browser I only see a single line in there with all the sample information summed up. And I use the same selectors to fetch this information. So I think it makes sense to keep them in sync. WDYT?

Comment thread src/utils/slice-tree.ts
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

export type Slice = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This whole file could use some comments. I wrote this code but I don't like it very much - and that includes the name! It's supposed to make a tree representation of the information you'd get from looking at a visual CPU usage graph, by applying a threshold operation to the entire time series, and then gradually increasing the threshold and seeing where "above water level" things split into separate islands. I suppose we can land it without comments and then I'll make a PR to add them

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah yeah, that's a good point. I kept it as is for now.

@canova
Copy link
Copy Markdown
Member Author

canova commented Apr 27, 2026

Thanks for the first look @mstange! I merged the "idle filtering" PR and rebased this PR on top of the recent main, so you'll see 2 less commits here.

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

Labels

cli Issues related to the profiler CLI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement and land the MVP of the profiler CLI

3 participants