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

Support inline callstack symbolication for local Firefox builds #3727

Merged
merged 6 commits into from
Dec 14, 2021

Conversation

mstange
Copy link
Contributor

@mstange mstange commented Dec 7, 2021

This PR makes it so that we will get inline callstacks when profiling local Firefox builds. It hooks up symbolication to the new querySymbolicationApi entry point from the WebChannel.

Example profile: https://share.firefox.dev/31CAqLf

Fixes #1516.

@mstange mstange requested a review from julienw December 7, 2021 05:49
@mstange mstange self-assigned this Dec 7, 2021
@codecov
Copy link

codecov bot commented Dec 7, 2021

Codecov Report

Merging #3727 (586e8d9) into main (1e45932) will increase coverage by 0.01%.
The diff coverage is 93.44%.

❗ Current head 586e8d9 differs from pull request most recent head 2fd7f3b. Consider uploading reports for the commit 2fd7f3b to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3727      +/-   ##
==========================================
+ Coverage   88.70%   88.72%   +0.01%     
==========================================
  Files         263      263              
  Lines       22100    22129      +29     
  Branches     5660     5666       +6     
==========================================
+ Hits        19604    19634      +30     
+ Misses       2313     2312       -1     
  Partials      183      183              
Impacted Files Coverage Δ
src/profile-logic/mozilla-symbolication-api.js 13.25% <40.00%> (-1.04%) ⬇️
src/profile-logic/symbol-store.js 93.75% <97.05%> (-0.07%) ⬇️
src/actions/receive-profile.js 86.48% <100.00%> (+0.27%) ⬆️
src/app-logic/browser-connection.js 86.66% <100.00%> (+1.30%) ⬆️
src/components/timeline/FullTimeline.js 67.56% <0.00%> (ø)
src/app-logic/web-channel.js 96.66% <0.00%> (+3.33%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1e45932...2fd7f3b. Read the comment docs.

Copy link
Contributor

@julienw julienw left a comment

Choose a reason for hiding this comment

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

Thanks, let's move forward with that!
That's a pretty big change in our symbolication code, and this all works fine!


const responseJson = _ensureIsAPIResultV5(await response.json());
const responseJson = _ensureIsAPIResultV5(
await callback('/symbolicate/v5', JSON.stringify(body))
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we should pass body and let the callback call JSON.stringify if necessary only.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would be an option. I don't have an opinion on it. Both callees currently need JSON.stringify.

);
}

return querySymbolicationApiViaWebChannel(path, requestJson);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering what Firefox does with the path later on? I think it passes it along all the way to the wasm program, but I wonder why it's useful?
(probably not for this PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's useful so that we can add more APIs without having to add more methods to the WebChannel. I want to add two new APIs: A way to get source code, and a way to get assembly code.
I've already landed a /source/v1 API in profiler-get-symbols (but not integrated it into Firefox yet): mstange/profiler-get-symbols#24

// No symbols for this library, it'll need to try the next option.
const { request, error } = response;
failedRequests.push(request);
(errorMap.get(request) ?? []).push(error);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is that working? If the request isn't in errorMap yet, how does it get there?
(same remark for the same line below)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, maybe I should add a comment. request is guaranteed to be in the errorMap - we initialize errorMap with all requests. The ?? [] is just there because it's the shortest way I could think of to appease Flow. In Rust I would use .unwrap() here to say "I'm sure that this exists and I'm willing to risk a panic if my reasoning about this code is wrong". In JS I could throw an exception, but that would be three extra lines.
Or maybe I should use ensureExists from the flow helpers? But that one takes an error message...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added two comments.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or maybe I should use ensureExists from the flow helpers? But that one takes an error message...

The error message isn't mandatory, the tool adds a default one if it's missing.

Copy link
Contributor

Choose a reason for hiding this comment

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

So that would have been my preference :-)

// Some libraries are still remaining, try option 3 for them.
// Option 3 is symbolProvider.requestSymbolsFromBrowser.
const option3ResponsePromise =
this._symbolProvider.requestSymbolsFromBrowser(option3Requests);
Copy link
Contributor

Choose a reason for hiding this comment

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

The name of the functions requestSymbolsFromBrowser vs requestSymbolTableFromBrowser are very close, do you have an idea to make them more descriptive? Maybe requestSymbolsFromWebChannel vs requestSymbolTableFromFrameScript? But for Android we're not using the framescript, are we?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the names are quite close indeed. I'm hoping we can remove the "symbol table" variants once we've converted Android (i.e. fixed bug 1735897).

As for the WebChannel / frame script distinction vs requestSymbols / requestSymbolTable, the relationship is as follows:

SymbolStore --> requestSymbolsFromBrowser --> WebChannel querySymbolicationApi (if available)
      |
      `-------> requestSymbolTableFromBrowser --> WebChannel getSymbolTable (if available)
                                   |
                                   `-----> otherwise frame script getSymbolTable

requests: LibSymbolicationRequest[],
symbolsUrl: string
callback: (path: string, requestJson: string) => Promise<MixedObject>
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the name callback isn't descriptive enough, what do you think of renaming it symbolFetcherCallback or something similar?
Also please describe it a bit in the comment above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestions, will do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've renamed this to querySymbolicationApiCallback and also created a type QuerySymbolicationApiCallback for it.

The errors created here are later accumulated into one big
SymbolsNotFoundError, once all options have failed.
At that point we also still know the lib. There is no need to
embed the lib into the error here, too.

We have an instanceof SymbolsNotFoundError check, but that check only
ever sees the combined error. It doesn't look at the individual input
errors. So this doesn't change any behavior.
It now receives a callback so that it supports both doing a fetch request to a server
and sending the request elsewhere, for example to the browser.
This calls through into BrowserConnection.querySymbolicationApi.
The request and response JSON are processed by MozillaSymbolicationAPI.
We insert this as the new option 3, before getting symbol tables (which
is now option 4).
This gives us rich symbol information with file + line + inline data.
We keep option 4 for old Firefox versions and for Android system libraries.
@mstange mstange merged commit 46ddaf8 into firefox-devtools:main Dec 14, 2021
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.

Proposal for a new geckoProfiler content API for symbolication
2 participants