Skip to content

[browser][coreCLR] Fix IP resolution for interpreted methods on WASM#127370

Open
pavelsavara wants to merge 2 commits intodotnet:mainfrom
pavelsavara:browser_EP_coreclr_prep2
Open

[browser][coreCLR] Fix IP resolution for interpreted methods on WASM#127370
pavelsavara wants to merge 2 commits intodotnet:mainfrom
pavelsavara:browser_EP_coreclr_prep2

Conversation

@pavelsavara
Copy link
Copy Markdown
Member

@pavelsavara pavelsavara commented Apr 24, 2026

Motivation

The sampling profiler does not work on WASM because the EventPipe code paths that resolve instruction pointers (IPs) to method information assume the traditional InterpreterPrecode mechanism. On WASM, CoreCLR uses portable entrypoints (FEATURE_PORTABLE_ENTRYPOINTS) instead of interpreter precodes, so the existing code silently returns wrong or zero addresses, causing the profiler to fail to attribute samples to managed methods.

Split from #126324 for smaller reviews

Changes

src/coreclr/vm/prestub.cpp

When emitting the ETW MethodJitted event for interpreted methods, the code needs to extract the InterpByteCodeStart pointer to use as the native code start address. On non-WASM platforms this goes through InterpreterPrecode::FromEntryPointByteCodeAddr. On WASM (FEATURE_PORTABLE_ENTRYPOINTS), this path doesn't exist — interpreter data is stored via PortableEntryPoint. The fix adds a conditional that uses PortableEntryPoint::GetInterpreterData(pCode) on WASM instead.

src/coreclr/vm/eventtrace.cpp

MethodAndStartAddressToEECodeInfoPointer resolves a MethodDesc + optional code address into a pointer suitable for ETW method events. The existing helper GetInterpreterCodeFromInterpreterPrecodeIfPresent is a no-op when FEATURE_PORTABLE_ENTRYPOINTS is defined (it just returns its input unchanged). The fix adds a fallback: when no explicit native code start address was provided, it queries MethodDesc::GetInterpreterCode() directly and returns the InterpByteCodeStart pointer. This allows the sampling profiler to correctly map sampled IPs back to interpreted methods on WASM.

@pavelsavara pavelsavara added this to the 11.0.0 milestone Apr 24, 2026
@pavelsavara pavelsavara self-assigned this Apr 24, 2026
Copilot AI review requested due to automatic review settings April 24, 2026 08:49
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes EventPipe/ETW instruction-pointer (IP) to managed-method resolution for interpreted methods on WASM, where CoreCLR uses FEATURE_PORTABLE_ENTRYPOINTS instead of interpreter precodes. This enables the sampling profiler to correctly attribute samples to interpreted managed methods.

Changes:

  • Update MethodDesc::JitCompileCodeLockedEventWrapper to derive the interpreted-method “native start address” from PortableEntryPoint interpreter data when FEATURE_PORTABLE_ENTRYPOINTS is enabled.
  • Update MethodAndStartAddressToEECodeInfoPointer to fall back to MethodDesc::GetInterpreterCode() (returning the InterpByteCodeStart pointer) when no explicit start address is supplied on portable-entrypoint interpreter builds.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/coreclr/vm/prestub.cpp Uses PortableEntryPoint::GetInterpreterData to compute the method start address for interpreted-method ETW events under portable entrypoints.
src/coreclr/vm/eventtrace.cpp Adds a portable-entrypoints interpreter fallback so method events can resolve InterpByteCodeStart when no explicit start address is provided.

Comment thread src/coreclr/vm/eventtrace.cpp Outdated
@@ -810,8 +810,12 @@ PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, J
if (isInterpreterCode)
{
// If this is interpreter code, we need to get the native code start address from the interpreter Precode
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

The comment mentions getting the native start address from the interpreter Precode, but in the FEATURE_PORTABLE_ENTRYPOINTS branch the data comes from PortableEntryPoint interpreter data. Update the comment to reflect both cases; optionally, you could avoid duplicating the extraction logic by using MethodDesc::GetInterpreterCode() (which JitCompileCodeLocked already sets) when isInterpreterCode is true.

Suggested change
// If this is interpreter code, we need to get the native code start address from the interpreter Precode
// If this is interpreter code, get the native code start address from the
// interpreter entrypoint data: PortableEntryPoint interpreter data when
// FEATURE_PORTABLE_ENTRYPOINTS is enabled, otherwise the interpreter Precode.

Copilot uses AI. Check for mistakes.
@pavelsavara pavelsavara changed the title [browser] Fix IP resolution for interpreted methods on WASM [browser][coreCLR] Fix IP resolution for interpreted methods on WASM Apr 24, 2026
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 24, 2026 08:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

PTR_InterpByteCodeStart pInterpCode = pMethodDesc->GetInterpreterCode();
if (pInterpCode != NULL)
{
return dac_cast<TADDR>(pInterpCode);
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

In the portable-entrypoints + interpreter fallback, the returned address should be encoded consistently with other interpreter code addresses (e.g., JitCompileCodeLockedEventWrapper passes PINSTRToPCODE(dac_cast<TADDR>(interpreterCode)) to ETW). Returning dac_cast<TADDR>(pInterpCode) bypasses PINSTRToPCODE, which can cause mismatches on architectures where PCODE carries tagging (e.g., ARM THUMB bit) and may break comparisons/method event correlation. Consider returning PINSTRToPCODE(dac_cast<TADDR>(pInterpCode)) (or equivalent) instead of the raw pointer value.

Suggested change
return dac_cast<TADDR>(pInterpCode);
return PINSTRToPCODE(dac_cast<TADDR>(pInterpCode));

Copilot uses AI. Check for mistakes.
@pavelsavara pavelsavara marked this pull request as ready for review April 24, 2026 11:46
}

return GetInterpreterCodeFromInterpreterPrecodeIfPresent(start);
start = GetInterpreterCodeFromInterpreterPrecodeIfPresent(start);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be fixed in GetInterpreterCodeFromInterpreterPrecodeIfPresent instead?

Also, GetInterpreterCodeFromInterpreterPrecodeIfPresent may want to be renamed to GetInterpreterCodeFromEntryPointIfPresent to better represent what it does.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants