Skip to content
Merged
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/GCHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ COOP_PINVOKE_HELPER(int32_t, RhGetGeneration, (OBJECTREF obj))
return GCHeapUtilities::GetGCHeap()->WhichGeneration(obj);
}

COOP_PINVOKE_HELPER(int64_t, RhGetGenerationSize, (int32_t gen))
{
return (int64_t)(GCHeapUtilities::GetGCHeap()->GetLastGCGenerationSize(gen));
}

COOP_PINVOKE_HELPER(int64_t, RhGetLastGCPercentTimeInGC, ())
{
return GCHeapUtilities::GetGCHeap()->GetLastGCPercentTimeInGC();
}


COOP_PINVOKE_HELPER(int32_t, RhGetGcLatencyMode, ())
{
return GCHeapUtilities::GetGCHeap()->GetGcLatencyMode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,38 @@
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Threading.LockHolder</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Diagnostics.Tracing.CounterPayload</Target>
<Left>ref/net8.0/System.Private.CoreLib.dll</Left>
<Right>lib/net8.0/System.Private.CoreLib.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Diagnostics.Tracing.IncrementingCounterPayload</Target>
<Left>ref/net8.0/System.Private.CoreLib.dll</Left>
<Right>lib/net8.0/System.Private.CoreLib.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Diagnostics.Tracing.IncrementingPollingCounterPayloadType</Target>
<Left>ref/net8.0/System.Private.CoreLib.dll</Left>
<Right>lib/net8.0/System.Private.CoreLib.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Diagnostics.Tracing.PollingPayloadType</Target>
<Left>ref/net8.0/System.Private.CoreLib.dll</Left>
<Right>lib/net8.0/System.Private.CoreLib.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.Diagnostics.Tracing.RuntimeEventSource.LogAppContextSwitch(System.String,System.Int32)</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.Diagnostics.Tracing.RuntimeEventSource.ProcessorCount(System.Int32)</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.ModuleHandle.#ctor(System.Reflection.Module)</Target>
Expand All @@ -972,6 +1004,18 @@
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.TypedReference.get_IsNull</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.GC.GetGenerationSize(System.Int32)</Target>
<Left>ref/net8.0/System.Private.CoreLib.dll</Left>
<Right>lib/net8.0/System.Private.CoreLib.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.GC.GetLastGCPercentTimeInGC</Target>
<Left>ref/net8.0/System.Private.CoreLib.dll</Left>
<Right>lib/net8.0/System.Private.CoreLib.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0014</DiagnosticId>
<Target>M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)-&gt;object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute]</Target>
Expand All @@ -980,4 +1024,4 @@
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant``1(System.IntPtr)-&gt;T?:[T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute]</Target>
</Suppression>
</Suppressions>
</Suppressions>
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ private enum RhEHFrameType
RH_EH_FIRST_RETHROW_FRAME = 2,
}

// Performance metric to count the number of exceptions thrown
private static int s_exceptionCount;
internal static int ExceptionCount => s_exceptionCount;

[RuntimeExport("AppendExceptionStackFrame")]
private static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, int flags)
{
Expand All @@ -112,6 +116,10 @@ private static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, in
bool isFirstFrame = (flags & (int)RhEHFrameType.RH_EH_FIRST_FRAME) != 0;
bool isFirstRethrowFrame = (flags & (int)RhEHFrameType.RH_EH_FIRST_RETHROW_FRAME) != 0;

// track count for metrics
if(isFirstFrame && !isFirstRethrowFrame)
s_exceptionCount++;

// When we're throwing an exception object, we first need to clear its stacktrace with two exceptions:
// 1. Don't clear if we're rethrowing with `throw;`.
// 2. Don't clear if we're throwing through ExceptionDispatchInfo.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ public static int GetGeneration(object obj)
return RuntimeImports.RhGetGeneration(obj);
}

public static int GetGenerationSize(int gen)
{
return RuntimeImports.RhGetGenerationSize(gen);
}

public static int GetLastGCPercentTimeInGC()
{
return RuntimeImports.RhGetLastGCPercentTimeInGC();
}

/// <summary>
/// Returns the current generation number of the target
/// of a specified <see cref="System.WeakReference"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ internal static void RhWaitForPendingFinalizers(bool allowReentrantWait)
[RuntimeImport(RuntimeLibrary, "RhGetGeneration")]
internal static extern int RhGetGeneration(object obj);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhGetGenerationSize")]
internal static extern int RhGetGenerationSize(int gen);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhGetLastGCPercentTimeInGC")]
internal static extern int RhGetLastGCPercentTimeInGC();

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhGetGcLatencyMode")]
internal static extern GCLatencyMode RhGetGcLatencyMode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
namespace System.Diagnostics.Tracing
{
[EventData]
internal sealed class CounterPayload : IEnumerable<KeyValuePair<string, object?>>
#if NATIVEAOT
public // On NativeAOT, this must be public to prevent it from getting reflection blocked.
#else
internal
#endif
sealed class CounterPayload : IEnumerable<KeyValuePair<string, object?>>
{
public string? Name { get; set; }

Expand Down Expand Up @@ -68,7 +73,12 @@ IEnumerator IEnumerable.GetEnumerator()
}

[EventData]
internal sealed class IncrementingCounterPayload : IEnumerable<KeyValuePair<string, object?>>
#if NATIVEAOT
public // On NativeAOT, this must be public to prevent it from getting reflection blocked.
#else
internal
#endif
sealed class IncrementingCounterPayload : IEnumerable<KeyValuePair<string, object?>>
{
public string? Name { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ internal override void WritePayload(float intervalSec, int pollingIntervalMillis
/// This is the payload that is sent in the with EventSource.Write
/// </summary>
[EventData]
internal sealed class IncrementingPollingCounterPayloadType
#if NATIVEAOT
public // On NativeAOT, this must be public to prevent it from getting reflection blocked.
#else
internal
#endif
sealed class IncrementingPollingCounterPayloadType
{
public IncrementingPollingCounterPayloadType(IncrementingCounterPayload payload) { Payload = payload; }
public IncrementingCounterPayload Payload { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ internal override void WritePayload(float intervalSec, int pollingIntervalMillis
/// This is the payload that is sent in the with EventSource.Write
/// </summary>
[EventData]
internal sealed class PollingPayloadType
#if NATIVEAOT
public // On NativeAOT, this must be public to prevent it from getting reflection blocked.
#else
internal
#endif
sealed class PollingPayloadType
{
public PollingPayloadType(CounterPayload payload) { Payload = payload; }
public CounterPayload Payload { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Threading;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace System.Diagnostics.Tracing
{
Expand Down Expand Up @@ -35,8 +36,6 @@ public static class Keywords
private IncrementingPollingCounter? _allocRateCounter;
private PollingCounter? _timerCounter;
private PollingCounter? _fragmentationCounter;

#if !NATIVEAOT // TODO shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase
private PollingCounter? _committedCounter;
private IncrementingPollingCounter? _exceptionCounter;
private PollingCounter? _gcTimeCounter;
Expand All @@ -46,12 +45,27 @@ public static class Keywords
private PollingCounter? _lohSizeCounter;
private PollingCounter? _pohSizeCounter;
private PollingCounter? _assemblyCounter;
#endif // !NATIVEAOT

private PollingCounter? _ilBytesJittedCounter;
private PollingCounter? _methodsJittedCounter;
private IncrementingPollingCounter? _jitTimeCounter;

#if NATIVEAOT
/// <summary>
/// If EventSource feature is enabled, RuntimeEventSource needs to be initialized
/// In CoreCLR, this is done via StartupHookProvider.CoreCLR.cs
/// </summary>
#pragma warning disable CA2255
[ModuleInitializer]
internal static void NativeAOTtartupHook()
{
if (EventSource.IsSupported)
{
Initialize();
}
}
#pragma warning restore CA2255
#endif

public static void Initialize()
{
// initializing more than once may lead to missing events
Expand All @@ -71,13 +85,23 @@ private enum EventId : int
}

[Event((int)EventId.AppContextSwitch, Level = EventLevel.Informational, Keywords = Keywords.AppContext)]
internal void LogAppContextSwitch(string switchName, int value)
#if NATIVEAOT
public
#else
internal
#endif
void LogAppContextSwitch(string switchName, int value)
{
base.WriteEvent((int)EventId.AppContextSwitch, switchName, value);
}

[Event((int)EventId.ProcessorCount, Level = EventLevel.Informational, Keywords = Keywords.ProcessorCount)]
internal void ProcessorCount(int processorCount)
#if NATIVEAOT
public
#else
internal
#endif
void ProcessorCount(int processorCount)
{
base.WriteEvent((int)EventId.ProcessorCount, processorCount);
}
Expand Down Expand Up @@ -108,15 +132,21 @@ protected override void OnEventCommand(EventCommandEventArgs command)
return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 100d / gcInfo.HeapSizeBytes : 0;
}) { DisplayName = "GC Fragmentation", DisplayUnits = "%" };

#if !NATIVEAOT // TODO
_committedCounter ??= new PollingCounter("gc-committed", this, () => ((double)GC.GetGCMemoryInfo().TotalCommittedBytes / 1_000_000)) { DisplayName = "GC Committed Bytes", DisplayUnits = "MB" };
#if NATIVEAOT
_exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.ExceptionCount) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
#else
_exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.GetExceptionCount()) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
#endif // !NATIVEAOT
_gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" };
_gen0SizeCounter ??= new PollingCounter("gen-0-size", this, () => GC.GetGenerationSize(0)) { DisplayName = "Gen 0 Size", DisplayUnits = "B" };
_gen1SizeCounter ??= new PollingCounter("gen-1-size", this, () => GC.GetGenerationSize(1)) { DisplayName = "Gen 1 Size", DisplayUnits = "B" };
_gen2SizeCounter ??= new PollingCounter("gen-2-size", this, () => GC.GetGenerationSize(2)) { DisplayName = "Gen 2 Size", DisplayUnits = "B" };
_lohSizeCounter ??= new PollingCounter("loh-size", this, () => GC.GetGenerationSize(3)) { DisplayName = "LOH Size", DisplayUnits = "B" };
_pohSizeCounter ??= new PollingCounter("poh-size", this, () => GC.GetGenerationSize(4)) { DisplayName = "POH (Pinned Object Heap) Size", DisplayUnits = "B" };
#if NATIVEAOT
_assemblyCounter ??= new PollingCounter("assembly-count", this, () => AppDomain.CurrentDomain.GetAssemblies().Length) { DisplayName = "Number of Assemblies Loaded" };
#else
_assemblyCounter ??= new PollingCounter("assembly-count", this, () => System.Reflection.Assembly.GetAssemblyCount()) { DisplayName = "Number of Assemblies Loaded" };
#endif // !NATIVEAOT

Expand Down
15 changes: 15 additions & 0 deletions src/tests/tracing/eventcounter/runtimecounters.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,24 @@
<JitOptimizationSensitive>true</JitOptimizationSensitive>
<!-- This test has a secondary thread with an infinite loop -->
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
<EventSourceSupport Condition="'$(TestBuildMode)' == 'nativeaot'">true</EventSourceSupport>
</PropertyGroup>
<ItemGroup>
<Compile Include="runtimecounters.cs" />
<ProjectReference Include="../common/common.csproj" />
</ItemGroup>

<!-- Hack to get NativeAOT assemblies into IlcReference
In CoreCLR tests, these assemblies get copied to CORE_ROOT, which NativeAOT doesn't use
-->
<Import Project="$(RepoRoot)eng/liveBuilds.targets" Condition="'$(TestBuildMode)' == 'nativeaot'" />
<!-- Get all the *.dll files that has IsNative != "true"-->
<Target Name="GetRequiredNativeAOTAssemblies"
DependsOnTargets="ResolveLibrariesRuntimeFilesFromLocalBuild"
BeforeTargets="ComputeIlcCompileInputs"
Condition="'$(TestBuildMode)' == 'nativeaot'">
<ItemGroup>
<IlcReference Include="@(LibrariesRuntimeFiles)" Condition="'%(Extension)' == '.dll' and '%(LibrariesRuntimeFiles.IsNative)' != 'true'"/>
</ItemGroup>
</Target>
</Project>