com.alicizax.unity.framework/Runtime/ABase/Event/EventDebugRegistry.cs

359 lines
13 KiB
C#
Raw Permalink Normal View History

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AlicizaX
{
internal enum EventDebugOperationKind : byte
{
Subscribe,
Unsubscribe,
Publish,
Resize,
2026-04-20 14:08:04 +08:00
Clear,
MutationRejected
}
internal readonly struct EventDebugSummary
{
internal readonly Type EventType;
internal readonly bool Initialized;
internal readonly int SubscriberCount;
internal readonly int PeakSubscriberCount;
internal readonly int Capacity;
2026-04-20 14:08:04 +08:00
internal readonly int ValueSubscriberCount;
internal readonly int InSubscriberCount;
internal readonly long PublishCount;
internal readonly long SubscribeCount;
internal readonly long UnsubscribeCount;
internal readonly int ResizeCount;
internal readonly int ClearCount;
2026-04-20 14:08:04 +08:00
internal readonly long MutationRejectedCount;
internal readonly int LastOperationFrame;
internal readonly long LastOperationTicksUtc;
internal EventDebugSummary(
Type eventType,
bool initialized,
int subscriberCount,
int peakSubscriberCount,
int capacity,
2026-04-20 14:08:04 +08:00
int valueSubscriberCount,
int inSubscriberCount,
long publishCount,
long subscribeCount,
long unsubscribeCount,
int resizeCount,
int clearCount,
2026-04-20 14:08:04 +08:00
long mutationRejectedCount,
int lastOperationFrame,
long lastOperationTicksUtc)
{
EventType = eventType;
Initialized = initialized;
SubscriberCount = subscriberCount;
PeakSubscriberCount = peakSubscriberCount;
Capacity = capacity;
2026-04-20 14:08:04 +08:00
ValueSubscriberCount = valueSubscriberCount;
InSubscriberCount = inSubscriberCount;
PublishCount = publishCount;
SubscribeCount = subscribeCount;
UnsubscribeCount = unsubscribeCount;
ResizeCount = resizeCount;
ClearCount = clearCount;
2026-04-20 14:08:04 +08:00
MutationRejectedCount = mutationRejectedCount;
LastOperationFrame = lastOperationFrame;
LastOperationTicksUtc = lastOperationTicksUtc;
}
}
internal readonly struct EventDebugSubscriberInfo
{
internal readonly int HandlerIndex;
internal readonly int Version;
internal readonly string DeclaringTypeName;
internal readonly string MethodName;
internal readonly string TargetTypeName;
internal readonly UnityEngine.Object UnityTarget;
internal readonly bool IsStatic;
internal readonly bool IsUnityObjectDestroyed;
2026-04-20 14:08:04 +08:00
internal readonly bool UsesInParameter;
internal readonly bool IsCompilerGeneratedTarget;
internal readonly bool IsCompilerGeneratedMethod;
internal EventDebugSubscriberInfo(
int handlerIndex,
int version,
string declaringTypeName,
string methodName,
string targetTypeName,
UnityEngine.Object unityTarget,
bool isStatic,
2026-04-20 14:08:04 +08:00
bool isUnityObjectDestroyed,
bool usesInParameter,
bool isCompilerGeneratedTarget,
bool isCompilerGeneratedMethod)
{
HandlerIndex = handlerIndex;
Version = version;
DeclaringTypeName = declaringTypeName;
MethodName = methodName;
TargetTypeName = targetTypeName;
UnityTarget = unityTarget;
IsStatic = isStatic;
IsUnityObjectDestroyed = isUnityObjectDestroyed;
2026-04-20 14:08:04 +08:00
UsesInParameter = usesInParameter;
IsCompilerGeneratedTarget = isCompilerGeneratedTarget;
IsCompilerGeneratedMethod = isCompilerGeneratedMethod;
}
}
internal readonly struct EventDebugOperationRecord
{
internal readonly Type EventType;
internal readonly EventDebugOperationKind OperationKind;
internal readonly int Frame;
internal readonly long TicksUtc;
internal readonly int SubscriberCount;
internal readonly int Capacity;
internal EventDebugOperationRecord(
Type eventType,
EventDebugOperationKind operationKind,
int frame,
long ticksUtc,
int subscriberCount,
int capacity)
{
EventType = eventType;
OperationKind = operationKind;
Frame = frame;
TicksUtc = ticksUtc;
SubscriberCount = subscriberCount;
Capacity = capacity;
}
}
internal static class EventDebugRegistry
{
private const int HistoryCapacity = 128;
private sealed class State
{
internal readonly Type EventType;
internal Func<int> SubscriberCountProvider;
internal Func<int> CapacityProvider;
2026-04-20 14:08:04 +08:00
internal Func<int> ValueSubscriberCountProvider;
internal Func<int> InSubscriberCountProvider;
internal Func<EventDebugSubscriberInfo[]> SubscribersProvider;
internal int PeakSubscriberCount;
internal long PublishCount;
internal long SubscribeCount;
internal long UnsubscribeCount;
internal int ResizeCount;
internal int ClearCount;
2026-04-20 14:08:04 +08:00
internal long MutationRejectedCount;
internal int LastOperationFrame;
internal long LastOperationTicksUtc;
internal State(Type eventType)
{
EventType = eventType;
}
}
private static readonly Dictionary<Type, State> _states = new();
private static readonly List<Type> _registrationOrder = new();
2026-04-20 14:08:04 +08:00
private static readonly EventDebugOperationRecord[] _history = new EventDebugOperationRecord[HistoryCapacity];
private static int _historyWriteIndex;
private static int _historyCount;
internal static void RegisterContainer<T>(
Func<int> subscriberCountProvider,
Func<int> capacityProvider,
2026-04-20 14:08:04 +08:00
Func<int> valueSubscriberCountProvider,
Func<int> inSubscriberCountProvider,
Func<EventDebugSubscriberInfo[]> subscribersProvider)
where T : struct, IEventArgs
{
Type eventType = typeof(T);
if (!_states.TryGetValue(eventType, out State state))
{
state = new State(eventType);
_states.Add(eventType, state);
_registrationOrder.Add(eventType);
}
state.SubscriberCountProvider = subscriberCountProvider;
state.CapacityProvider = capacityProvider;
2026-04-20 14:08:04 +08:00
state.ValueSubscriberCountProvider = valueSubscriberCountProvider;
state.InSubscriberCountProvider = inSubscriberCountProvider;
state.SubscribersProvider = subscribersProvider;
state.PeakSubscriberCount = Math.Max(state.PeakSubscriberCount, subscriberCountProvider());
}
internal static void RecordSubscribe<T>(int subscriberCount, int capacity) where T : struct, IEventArgs
{
State state = GetState<T>();
state.SubscribeCount++;
state.PeakSubscriberCount = Math.Max(state.PeakSubscriberCount, subscriberCount);
MarkOperation(state, EventDebugOperationKind.Subscribe, subscriberCount, capacity);
}
internal static void RecordUnsubscribe<T>(int subscriberCount, int capacity) where T : struct, IEventArgs
{
State state = GetState<T>();
state.UnsubscribeCount++;
MarkOperation(state, EventDebugOperationKind.Unsubscribe, subscriberCount, capacity);
}
internal static void RecordPublish<T>(int subscriberCount, int capacity) where T : struct, IEventArgs
{
State state = GetState<T>();
state.PublishCount++;
MarkOperation(state, EventDebugOperationKind.Publish, subscriberCount, capacity);
}
internal static void RecordResize<T>(int subscriberCount, int capacity) where T : struct, IEventArgs
{
State state = GetState<T>();
state.ResizeCount++;
MarkOperation(state, EventDebugOperationKind.Resize, subscriberCount, capacity);
}
internal static void RecordClear<T>(int subscriberCount, int capacity) where T : struct, IEventArgs
{
State state = GetState<T>();
state.ClearCount++;
MarkOperation(state, EventDebugOperationKind.Clear, subscriberCount, capacity);
}
2026-04-20 14:08:04 +08:00
internal static void RecordMutationRejected<T>(int subscriberCount, int capacity) where T : struct, IEventArgs
{
State state = GetState<T>();
state.MutationRejectedCount++;
MarkOperation(state, EventDebugOperationKind.MutationRejected, subscriberCount, capacity);
}
internal static EventDebugSummary[] GetSummaries()
{
int count = _registrationOrder.Count;
EventDebugSummary[] summaries = new EventDebugSummary[count];
for (int i = 0; i < count; i++)
{
summaries[i] = BuildSummary(_states[_registrationOrder[i]]);
}
return summaries;
}
internal static bool TryGetDetails(Type eventType, out EventDebugSummary summary, out EventDebugSubscriberInfo[] subscribers)
{
if (_states.TryGetValue(eventType, out State state))
{
summary = BuildSummary(state);
subscribers = state.SubscribersProvider?.Invoke() ?? Array.Empty<EventDebugSubscriberInfo>();
return true;
}
summary = default;
subscribers = Array.Empty<EventDebugSubscriberInfo>();
return false;
}
internal static EventDebugOperationRecord[] GetRecentOperations()
{
EventDebugOperationRecord[] result = new EventDebugOperationRecord[_historyCount];
for (int i = 0; i < _historyCount; i++)
{
int index = (_historyWriteIndex - 1 - i + _history.Length) % _history.Length;
result[i] = _history[index];
}
return result;
}
internal static void ResetStats()
{
foreach (Type eventType in _registrationOrder)
{
State state = _states[eventType];
state.PeakSubscriberCount = state.SubscriberCountProvider?.Invoke() ?? 0;
state.PublishCount = 0;
state.SubscribeCount = 0;
state.UnsubscribeCount = 0;
state.ResizeCount = 0;
state.ClearCount = 0;
2026-04-20 14:08:04 +08:00
state.MutationRejectedCount = 0;
state.LastOperationFrame = 0;
state.LastOperationTicksUtc = 0;
}
_historyWriteIndex = 0;
_historyCount = 0;
}
private static State GetState<T>() where T : struct, IEventArgs
{
Type eventType = typeof(T);
if (_states.TryGetValue(eventType, out State state))
{
return state;
}
throw new InvalidOperationException($"Event debug state is not registered for {eventType.FullName}.");
}
private static EventDebugSummary BuildSummary(State state)
{
int subscriberCount = state.SubscriberCountProvider?.Invoke() ?? 0;
int capacity = state.CapacityProvider?.Invoke() ?? 0;
2026-04-20 14:08:04 +08:00
int valueSubscriberCount = state.ValueSubscriberCountProvider?.Invoke() ?? 0;
int inSubscriberCount = state.InSubscriberCountProvider?.Invoke() ?? 0;
return new EventDebugSummary(
state.EventType,
state.SubscriberCountProvider != null,
subscriberCount,
state.PeakSubscriberCount,
capacity,
2026-04-20 14:08:04 +08:00
valueSubscriberCount,
inSubscriberCount,
state.PublishCount,
state.SubscribeCount,
state.UnsubscribeCount,
state.ResizeCount,
state.ClearCount,
2026-04-20 14:08:04 +08:00
state.MutationRejectedCount,
state.LastOperationFrame,
state.LastOperationTicksUtc);
}
private static void MarkOperation(State state, EventDebugOperationKind kind, int subscriberCount, int capacity)
{
int frame = Time.frameCount;
long ticksUtc = DateTime.UtcNow.Ticks;
state.LastOperationFrame = frame;
state.LastOperationTicksUtc = ticksUtc;
_history[_historyWriteIndex] = new EventDebugOperationRecord(
state.EventType,
kind,
frame,
ticksUtc,
subscriberCount,
capacity);
_historyWriteIndex = (_historyWriteIndex + 1) % _history.Length;
if (_historyCount < _history.Length)
{
_historyCount++;
}
}
}
}
#endif