Add EventPipe interop instrumentation for wrapper lifecycle and GC-bridge reachability - #12258
Add EventPipe interop instrumentation for wrapper lifecycle and GC-bridge reachability#12258jkoritzinsky with Copilot wants to merge 9 commits into
Conversation
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces EventPipe instrumentation across the .NET↔Java interop layer to make wrapper lifecycle and GC-bridge reachability transitions observable in production traces via a new Java.Interop EventSource provider.
Changes:
- Added a new
Java.InteropEventSource (InteropEventSource) with stable event IDs/keywords and a documented payload schema. - Instrumented wrapper creation/release in Java.Interop value managers + Mono.Android runtime, and added GC-bridge reachability transition events with correlation fields.
- Added unit tests (Java.Interop) and runtime-facing tests (Mono.Android) plus documentation for capturing with
dotnet-trace.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/InteropEventSourceRuntimeTests.cs | Runtime tests validating lifecycle events are emitted on-device/in-runtime. |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapValueManager.cs | Emits wrapper creation events for trimmable typemap value manager paths. |
| src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalRegisteredPeers.cs | Emits wrapper release + GC-bridge reachability events during bridge processing. |
| src/Mono.Android/Android.Runtime/AndroidRuntime.cs | Emits wrapper creation events for AndroidValueManager.CreatePeer. |
| external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/InteropEventSourceTests.cs | Unit tests validating EventSource emission and payload shape. |
| external/Java.Interop/src/Java.Interop/PublicAPI.Unshipped.txt | Declares new public API surface for InteropEventSource. |
| external/Java.Interop/src/Java.Interop/Java.Interop/ManagedPeer.cs | Emits JavaWrapperCreated during managed-peer construction from Java. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs | Emits wrapper creation events in reflection-based value manager. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs | Emits wrapper release events during peer disposal. |
| external/Java.Interop/src/Java.Interop/Java.Interop/InteropEventSource.cs | New provider implementation + event definitions/keywords. |
| external/Java.Interop/Documentation/EventPipeInteropEvents.md | Event catalog, payload schema, and dotnet-trace collection instructions. |
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
…ava-interop Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
|
This helps — I've been investigating dotnet/runtime#131370 (Android frame drops from the GC bridge) and I think interop visibility is important if we get apps negatively impacted by the bridge. I suspect promotion cost has the potential to get in the way for games / high responsive graphic apps. A couple questions:
The bridge's cost tracks promoted bytes, not peer count — it force-promotes each peer's whole If we add one, these two fields are what would let the two sides join: "this bridge round promoted
I built a probe that has millions of peer registrations over a 40 s run. Each event carries two strings, Repro and scripts: https://github.com/steveisok/android-gcbridge-investigation |
I think
On the runtime side, we only can track which bridge objects die, not which ones continue to live unless we add additional tracking both over here and in dotnet/runtime.
I think adding a sampling counter would make sense. |
|
@copilot please add EventCounter-based aggregate metrics (that can be enabled separately from the fine-grained events) for the following scenarios:
|
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Implemented this in 79f463a. The aggregate metrics now come from a separate |
| ManagedObjectsOnlyReachableFromJavaCount, | ||
| JavaObjectsOnlyReachableFromManagedCount + 1, | ||
| BridgeObjectsAliveAfterProcessingCount, | ||
| BridgeObjectsUnreachableAfterProcessingCount + 1) |
There was a problem hiding this comment.
@copilot It looks like ManagedObjectsOnlyReachableFromJavaCount and BridgeObjectsAliveAfterProcessingCount are identical here. (and the same for the other two options).
Please remove the BridgeObjectsAliveAfterProcessingCount and BridgeObjectsUnreachableAfterProcessingCount metrics.
There was a problem hiding this comment.
Addressed in 9cc31d8. The duplicate BridgeObjectsAliveAfterProcessingCount and BridgeObjectsUnreachableAfterProcessingCount metrics are removed, and the remaining counter API/tests/docs now only expose the two distinct reachability counts.
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
This adds first-class EventPipe visibility into the .NET↔Java interop layer so wrapper lifecycle and cross-runtime reachability transitions can be observed in production traces. The instrumentation covers wrapper creation/release on both sides and GC-bridge reachability state transitions with correlation data.
New interop EventSource provider
Java.InteropDotNetWrapperCreatedJavaWrapperCreatedDotNetWrapperReleasedJavaReferenceJavaWrapperReleasedDotNetReferenceDotNetObjectOnlyReachableFromJavaJavaObjectOnlyReachableFromDotNetWrapper creation instrumentation
.NET wrapper for Java objectevents from:ReflectionJniValueManager.CreatePeerTrimmableTypeMapValueManager.CreatePeerAndroidRuntime.AndroidValueManager.CreatePeerJava wrapper for .NET objectevents from:ManagedPeer.ConstructConstructPeerCorepaths where Java peer references are establishedWrapper release instrumentation
.NET wrapper releases Java referencefrom disposal/finalization paths:JniValueManager.DisposePeerJavaMarshalRegisteredPeers.FinalizePeerJava wrapper releases .NET referencefrom registered-peer removal:JavaMarshalRegisteredPeers.RemovePeerGC-bridge reachability instrumentation
JavaMarshalRegisteredPeersbridge processing:.NET object only reachable from JavaJava object only reachable from .NETcomponentIndex,contextIndex,contextPointer) for trace-side reconstruction.Low-overhead event gating
InteropEventSource.IsEnabled()before payload-heavy work (e.g., Java type resolution) to avoid unnecessary overhead when no listener is attached.API, tests, and docs
InteropEventSource.dotnet-traceusage documentation.