Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabling RuntimeEventSource in NativeAOT #85424

Merged
merged 11 commits into from
May 11, 2023
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 @@ -960,6 +960,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 @@ -968,4 +980,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>
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

using MethodBase = System.Reflection.MethodBase;

Expand Down Expand Up @@ -95,6 +96,10 @@ private enum RhEHFrameType
RH_EH_FIRST_RETHROW_FRAME = 2,
}

// Performance metric to count the number of exceptions thrown
private static uint s_exceptionCount;
internal static uint GetExceptionCount() => s_exceptionCount;

[RuntimeExport("AppendExceptionStackFrame")]
private static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, int flags)
{
Expand All @@ -112,6 +117,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)
Interlocked.Increment(ref s_exceptionCount);
Comment on lines +120 to +122
Copy link
Member

Choose a reason for hiding this comment

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

I'm going to assume this matches how CoreCLR counts these wrt rethrow or ExceptionDispatchInfo.Throw.


// 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)
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
{
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 @@ -35,6 +35,15 @@ public static Assembly Load(string assemblyString)
return Load(name);
}

// Performance metric to count the number of assemblies
// Caching since in NativeAOT, the number will be the same
private static uint? s_assemblyCount;
internal static uint GetAssemblyCount()
{
s_assemblyCount ??= (uint)AppDomain.CurrentDomain.GetAssemblies().Length;
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
return (uint)s_assemblyCount;
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
}

[Obsolete("Assembly.LoadWithPartialName has been deprecated. Use Assembly.Load() instead.")]
public static Assembly LoadWithPartialName(string partialName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,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
1 change: 1 addition & 0 deletions src/coreclr/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ void FreeTrackerMemory(ExceptionTracker* pTracker, TrackerMemoryType mem)
static inline void UpdatePerformanceMetrics(CrawlFrame *pcfThisFrame, BOOL bIsRethrownException, BOOL bIsNewException)
{
WRAPPER_NO_CONTRACT;
// This should ideally use a threadsafe mechanism for greater accuracy, see https://github.com/dotnet/runtime/issues/85997
g_exceptionCount++;

// Fire an exception thrown ETW event when an exception occurs
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()
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
{
if (EventSource.IsSupported)
{
Initialize();
}
}
#pragma warning restore CA2255
#endif

public static void Initialize()
{
// initializing more than once may lead to missing events
Expand Down Expand Up @@ -108,7 +122,6 @@ 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" };
_exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.GetExceptionCount()) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
_gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" };
Expand All @@ -118,7 +131,6 @@ protected override void OnEventCommand(EventCommandEventArgs command)
_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" };
_assemblyCounter ??= new PollingCounter("assembly-count", this, () => System.Reflection.Assembly.GetAssemblyCount()) { DisplayName = "Number of Assemblies Loaded" };
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
#endif // !NATIVEAOT

_ilBytesJittedCounter ??= new PollingCounter("il-bytes-jitted", this, () => System.Runtime.JitInfo.GetCompiledILBytes()) { DisplayName = "IL Bytes Jitted", DisplayUnits = "B" };
_methodsJittedCounter ??= new PollingCounter("methods-jitted-count", this, () => System.Runtime.JitInfo.GetCompiledMethodCount()) { DisplayName = "Number of Methods Jitted" };
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>