Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions external/Java.Interop/Documentation/EventPipeInteropEvents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Java.Interop EventPipe interop events

The .NET ↔ Java interop layer emits EventPipe events through a single provider:

- **Provider name:** `Java.Interop`

## Event catalog

| Event ID | Event name | Meaning |
|---|---|---|
| 1 | `DotNetWrapperCreated` | A managed wrapper for a Java object was created. |
| 2 | `JavaWrapperCreated` | A Java wrapper for a managed object was created. |
| 3 | `DotNetWrapperReleasedJavaReference` | A managed wrapper released its Java reference. |
| 4 | `JavaWrapperReleasedDotNetReference` | A Java wrapper released its managed reference. |
| 5 | `DotNetObjectOnlyReachableFromJava` | A managed object is only reachable from Java during bridge processing. |
| 6 | `JavaObjectOnlyReachableFromDotNet` | A Java object is only reachable from .NET during bridge processing. |

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.

There is some existing code in here for writing gref/lref logs. It is old and text-based. Does this replace or supplement this?


## Payload schema

All events contain:

- `managedType` (`string`)
- `javaType` (`string`)
- `jniIdentityHashCode` (`int`)
- `managedObjectHashCode` (`int`)
- `runtimeMode` (`string`)

Reachability events (`5`, `6`) additionally contain:

- `componentIndex` (`int`)
- `contextIndex` (`int`)
- `contextPointer` (`long`)

## Collecting events

Use `dotnet-trace` to capture the provider:

```bash
dotnet-trace collect --process-id <pid> --providers Java.Interop:0x3:4
```

`0x3` enables both wrapper lifecycle and reachability keywords, and `4` enables informational-level events.
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#nullable enable

using System.Diagnostics.Tracing;

namespace Java.Interop
{
public static class InteropEventSource
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated
{
internal const string ProviderName = "Java.Interop";
const string UnknownValue = "Unknown";

static readonly InteropEventSourceImplementation source = new InteropEventSourceImplementation ();

public static bool IsEnabled ()
{
return source.IsEnabled ();
}
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated

public static void DotNetWrapperCreated (
string? managedType,
string? javaType,
int jniIdentityHashCode,
int managedObjectHashCode,
string? runtimeMode)
{
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated
source.DotNetWrapperCreated (
GetPayloadValue (managedType),
GetPayloadValue (javaType),
jniIdentityHashCode,
managedObjectHashCode,
GetPayloadValue (runtimeMode));
}

public static void JavaWrapperCreated (
string? managedType,
string? javaType,
int jniIdentityHashCode,
int managedObjectHashCode,
string? runtimeMode)
{
source.JavaWrapperCreated (
GetPayloadValue (managedType),
GetPayloadValue (javaType),
jniIdentityHashCode,
managedObjectHashCode,
GetPayloadValue (runtimeMode));
}

public static void DotNetWrapperReleasedJavaReference (
string? managedType,
string? javaType,
int jniIdentityHashCode,
int managedObjectHashCode,
string? runtimeMode)
{
source.DotNetWrapperReleasedJavaReference (
GetPayloadValue (managedType),
GetPayloadValue (javaType),
jniIdentityHashCode,
managedObjectHashCode,
GetPayloadValue (runtimeMode));
}

public static void JavaWrapperReleasedDotNetReference (
string? managedType,
string? javaType,
int jniIdentityHashCode,
int managedObjectHashCode,
string? runtimeMode)
{
source.JavaWrapperReleasedDotNetReference (
GetPayloadValue (managedType),
GetPayloadValue (javaType),
jniIdentityHashCode,
managedObjectHashCode,
GetPayloadValue (runtimeMode));
}

public static void DotNetObjectOnlyReachableFromJava (
string? managedType,
string? javaType,
int jniIdentityHashCode,
int managedObjectHashCode,
string? runtimeMode,
int componentIndex,
int contextIndex,
long contextPointer)
{
source.DotNetObjectOnlyReachableFromJava (
GetPayloadValue (managedType),
GetPayloadValue (javaType),
jniIdentityHashCode,
managedObjectHashCode,
GetPayloadValue (runtimeMode),
componentIndex,
contextIndex,
contextPointer);
}

public static void JavaObjectOnlyReachableFromDotNet (
string? managedType,
string? javaType,
int jniIdentityHashCode,
int managedObjectHashCode,
string? runtimeMode,
int componentIndex,
int contextIndex,
long contextPointer)
{
source.JavaObjectOnlyReachableFromDotNet (
GetPayloadValue (managedType),
GetPayloadValue (javaType),
jniIdentityHashCode,
managedObjectHashCode,
GetPayloadValue (runtimeMode),
componentIndex,
contextIndex,
contextPointer);
}

static string GetPayloadValue (string? value)
{
return value ?? UnknownValue;
}

[EventSource (Name = ProviderName)]
sealed class InteropEventSourceImplementation : EventSource
{
public static class Keywords
{
public const EventKeywords WrapperLifecycle = (EventKeywords) 0x1;
public const EventKeywords Reachability = (EventKeywords) 0x2;
}

[Event (1, Level = EventLevel.Informational, Keywords = Keywords.WrapperLifecycle)]
public void DotNetWrapperCreated (string managedType, string javaType, int jniIdentityHashCode, int managedObjectHashCode, string runtimeMode)
{
WriteEvent (1, managedType, javaType, jniIdentityHashCode, managedObjectHashCode, runtimeMode);
}

[Event (2, Level = EventLevel.Informational, Keywords = Keywords.WrapperLifecycle)]
public void JavaWrapperCreated (string managedType, string javaType, int jniIdentityHashCode, int managedObjectHashCode, string runtimeMode)
{
WriteEvent (2, managedType, javaType, jniIdentityHashCode, managedObjectHashCode, runtimeMode);
}

[Event (3, Level = EventLevel.Informational, Keywords = Keywords.WrapperLifecycle)]
public void DotNetWrapperReleasedJavaReference (string managedType, string javaType, int jniIdentityHashCode, int managedObjectHashCode, string runtimeMode)
{
WriteEvent (3, managedType, javaType, jniIdentityHashCode, managedObjectHashCode, runtimeMode);
}

[Event (4, Level = EventLevel.Informational, Keywords = Keywords.WrapperLifecycle)]
public void JavaWrapperReleasedDotNetReference (string managedType, string javaType, int jniIdentityHashCode, int managedObjectHashCode, string runtimeMode)
{
WriteEvent (4, managedType, javaType, jniIdentityHashCode, managedObjectHashCode, runtimeMode);
}

[Event (5, Level = EventLevel.Informational, Keywords = Keywords.Reachability)]
public void DotNetObjectOnlyReachableFromJava (string managedType, string javaType, int jniIdentityHashCode, int managedObjectHashCode, string runtimeMode, int componentIndex, int contextIndex, long contextPointer)
{
WriteEvent (5, managedType, javaType, jniIdentityHashCode, managedObjectHashCode, runtimeMode, componentIndex, contextIndex, contextPointer);
}

[Event (6, Level = EventLevel.Informational, Keywords = Keywords.Reachability)]
public void JavaObjectOnlyReachableFromDotNet (string managedType, string javaType, int jniIdentityHashCode, int managedObjectHashCode, string runtimeMode, int componentIndex, int contextIndex, long contextPointer)
{
WriteEvent (6, managedType, javaType, jniIdentityHashCode, managedObjectHashCode, runtimeMode, componentIndex, contextIndex, contextPointer);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ public virtual void DisposePeer (IJavaPeerable value)
var h = value.PeerReference;
if (!h.IsValid)
return;
if (InteropEventSource.IsEnabled ()) {
var javaType = JniEnvironment.Types.GetJniTypeNameFromInstance (h);
InteropEventSource.DotNetWrapperReleasedJavaReference (
value.GetType ().FullName,
javaType,
value.JniIdentityHashCode,
RuntimeHelpers.GetHashCode (value),
Runtime.GetType ().FullName);
}
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated

DisposePeer (h, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ protected override void ConstructPeerCore (IJavaPeerable peer, ref JniObjectRefe

peer.SetPeerReference (newRef);
peer.SetJniIdentityHashCode (JniSystem.IdentityHashCode (newRef));
if (!peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Activatable) && InteropEventSource.IsEnabled ()) {
EmitJavaWrapperCreatedEvent (peer, newRef);
}
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated

var o = Runtime.ObjectReferenceManager;
if (o.LogGlobalReferenceMessages) {
Expand All @@ -94,6 +97,34 @@ protected override void ConstructPeerCore (IJavaPeerable peer, ref JniObjectRefe
}
}

void EmitDotNetWrapperCreatedEvent (IJavaPeerable peer)
{
JniObjectReference reference = peer.PeerReference;
var javaType = reference.IsValid ? JniEnvironment.Types.GetJniTypeNameFromInstance (reference) : null;
InteropEventSource.DotNetWrapperCreated (
peer.GetType ().FullName,
javaType,
peer.JniIdentityHashCode,
RuntimeHelpers.GetHashCode (peer),
GetRuntimeMode ());
}

void EmitJavaWrapperCreatedEvent (IJavaPeerable peer, JniObjectReference reference)
{
var javaType = reference.IsValid ? JniEnvironment.Types.GetJniTypeNameFromInstance (reference) : null;
InteropEventSource.JavaWrapperCreated (
peer.GetType ().FullName,
javaType,
peer.JniIdentityHashCode,
RuntimeHelpers.GetHashCode (peer),
GetRuntimeMode ());
}

string GetRuntimeMode ()
{
return Runtime.GetType ().FullName ?? "Unknown";
}
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated

// This base method implementation is NOT reachable in trimmable typemap - it is featureswitch guarded
public override IJavaPeerable? CreatePeer (
ref JniObjectReference reference,
Expand Down Expand Up @@ -143,6 +174,9 @@ protected override void ConstructPeerCore (IJavaPeerable peer, ref JniObjectRefe
JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType));
}
peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
if (InteropEventSource.IsEnabled ()) {
EmitDotNetWrapperCreatedEvent (peer);
}
return peer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
Expand Down Expand Up @@ -105,6 +106,15 @@ static void Construct (

var typeSig = new JniTypeSignature (JniEnvironment.Types.GetJniTypeNameFromInstance (r_self));
var type = GetTypeFromSignature (runtime.TypeManager, typeSig);
if (InteropEventSource.IsEnabled ()) {
var managedObjectHashCode = self != null ? RuntimeHelpers.GetHashCode (self) : 0;
InteropEventSource.JavaWrapperCreated (
type.FullName,
typeSig.SimpleReference,
runtime.ValueManager.GetJniIdentityHashCode (r_self),
managedObjectHashCode,
runtime.GetType ().FullName);
}
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated

if (type.IsGenericTypeDefinition) {
throw new NotSupportedException (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,11 @@ override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetTypesForSimpleRefer
override Java.Interop.JniRuntime.ReflectionJniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> void
override Java.Interop.JniRuntime.ReflectionJniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan<char> methods) -> void
virtual Java.Interop.JniRuntime.ReflectionJniValueManager.TryConstructPeer(Java.Interop.IJavaPeerable! self, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> bool
static Java.Interop.InteropEventSource.DotNetObjectOnlyReachableFromJava(string? managedType, string? javaType, int jniIdentityHashCode, int managedObjectHashCode, string? runtimeMode, int componentIndex, int contextIndex, long contextPointer) -> void
static Java.Interop.InteropEventSource.DotNetWrapperCreated(string? managedType, string? javaType, int jniIdentityHashCode, int managedObjectHashCode, string? runtimeMode) -> void
static Java.Interop.InteropEventSource.DotNetWrapperReleasedJavaReference(string? managedType, string? javaType, int jniIdentityHashCode, int managedObjectHashCode, string? runtimeMode) -> void
static Java.Interop.InteropEventSource.IsEnabled() -> bool
static Java.Interop.InteropEventSource.JavaObjectOnlyReachableFromDotNet(string? managedType, string? javaType, int jniIdentityHashCode, int managedObjectHashCode, string? runtimeMode, int componentIndex, int contextIndex, long contextPointer) -> void
static Java.Interop.InteropEventSource.JavaWrapperCreated(string? managedType, string? javaType, int jniIdentityHashCode, int managedObjectHashCode, string? runtimeMode) -> void
static Java.Interop.InteropEventSource.JavaWrapperReleasedDotNetReference(string? managedType, string? javaType, int jniIdentityHashCode, int managedObjectHashCode, string? runtimeMode) -> void
Java.Interop.InteropEventSource
Loading
Loading