Open Bug 1925105 Opened 8 months ago Updated 2 months ago

Missing interpreter frames in profiles of resumed async functions (interpreter and baseline interpreter)

Categories

(Core :: JavaScript Engine, defect, P2)

defect

Tracking

()

People

(Reporter: mstange, Unassigned)

References

(Blocks 1 open bug)

Details

(Whiteboard: [sp3])

Attachments

(1 file, 1 obsolete file)

For profiling with native profilers such as samply or perf, Spidermonkey supports entering "stub" functions for the C++ interpreter and the Baseline interpreter. However, it seems that when resuming an async functions, we sometimes don't enter these stubs.
Example profile: https://share.firefox.dev/3UfMbPV

This makes it hard to account the correct sample count to these async functions. It also makes it hard to get an accurate sense of the async/await overhead tracked in bug 1924185.

Markus, can you name which frames we are supposed to look at in the flame graph?

Flags: needinfo?(mstange.moz)
Blocks: speedometer3
Severity: -- → S3
Priority: -- → P2
Whiteboard: [js-perf-next]

The callees of the Baseline: AsyncFunctionNext frame are the frames that are missing JS function names.

I'll try to get a reduced testcase.

Flags: needinfo?(mstange.moz)
Flags: needinfo?(mstange.moz)
Whiteboard: [js-perf-next] → [js-perf-next][sp3]
Whiteboard: [js-perf-next][sp3] → [sp3]
Attached file testcase (obsolete) —

samply profile of this testcase: https://share.firefox.dev/428alAq

The profile is missing the frame for myAsyncFunction(). It's unclear from the profile what is calling foo() and bar().

This profile was collected with samply record -r 4000 PERF_SPEW_DIR=. IONPERF=func MOZ_DISABLE_CONTENT_SANDBOX=1 JIT_OPTION_enableICFramePointers=true JIT_OPTION_onlyInlineSelfHosted=true JIT_OPTION_emitInterpreterEntryTrampoline=true python3 ./mach run on macOS.

Flags: needinfo?(mstange.moz)
Attached file better testcase

Actually the previous testcase wasn't good because it was calling foo() and bar() with await.

Here's a testcase with regular calls from myAsyncFunction() to foo() and to bar(). There's no interpreter trampoline frame for myAsyncFunction() in the profile: https://share.firefox.dev/3RGLBsL

Attachment #9478187 - Attachment is obsolete: true

I took a quick look at this. It's a non-trivial fix.

To resume a generator / async function, we call into self-hosted code (GeneratorNext for generators, AsyncFunctionNext for async functions). Those functions contain a call to the resumeGenerator intrinsic, which the parser turns into JSOp::Resume. The effect of JSOp::Resume is to set up the stack frame for the function being resumed, and then jump to the right place in the code. This plays out in different ways depending on the tier of the result, but the key fact for every tier is that we never actually call the resumed function. We always set up a fake stack frame manually. This means that we can't reuse our existing interpreter entry trampolines, which call the wrapped function.

Instead, we would have to generate a second set of resume trampolines, or at least add an additional path in the trampoline for async functions / generators that would resume the function instead of calling it. Then we could make the resume code call into those trampolines.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: