Enabling RuntimeEventSource in NativeAOT (#85424)
authorLakshan Fernando <lakshanf@hotmail.com>
Thu, 11 May 2023 12:11:29 +0000 (05:11 -0700)
committerGitHub <noreply@github.com>
Thu, 11 May 2023 12:11:29 +0000 (05:11 -0700)
* Enabling RuntimeEventSource for counters

* enable exception and assembly count

* FB

* make the exception count thread safe

* revert reflection blocking workaround and open an issue for the CoreCLR exceptioncount fix

* Missed syncing to branch before syncing with main

* Update src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Assembly.NativeAot.cs

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
* FB

* Removing unnecessary whitespace chang

---------

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
src/coreclr/nativeaot/Runtime/GCHelpers.cpp
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.NativeAot.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Assembly.NativeAot.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs
src/tests/tracing/eventcounter/runtimecounters.csproj

index d795d46..40220bf 100644 (file)
@@ -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();
index 0c974b5..bbe9a1b 100644 (file)
@@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Runtime;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Threading;
 
 using MethodBase = System.Reflection.MethodBase;
 
@@ -95,6 +96,10 @@ namespace System
             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)
         {
@@ -112,6 +117,10 @@ namespace System
                 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);
+
                 // 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.
index 9a42524..4837437 100644 (file)
@@ -70,6 +70,16 @@ namespace System
             return RuntimeImports.RhGetGeneration(obj);
         }
 
+        internal static int GetGenerationSize(int gen)
+        {
+            return RuntimeImports.RhGetGenerationSize(gen);
+        }
+
+        internal static int GetLastGCPercentTimeInGC()
+        {
+            return RuntimeImports.RhGetLastGCPercentTimeInGC();
+        }
+
         /// <summary>
         /// Returns the current generation number of the target
         /// of a specified <see cref="System.WeakReference"/>.
index 396db84..db77f72 100644 (file)
@@ -35,6 +35,16 @@ namespace System.Reflection
             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()
+        {
+            if (s_assemblyCount == 0)
+                s_assemblyCount = (uint)Internal.Reflection.Core.Execution.ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder.GetLoadedAssemblies().Count;
+            return s_assemblyCount;
+        }
+
         [Obsolete("Assembly.LoadWithPartialName has been deprecated. Use Assembly.Load() instead.")]
         public static Assembly LoadWithPartialName(string partialName)
         {
index 54ac631..ebc9fdb 100644 (file)
@@ -98,6 +98,14 @@ namespace System.Runtime
         [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();
index 5c01e52..2bed9b8 100644 (file)
@@ -3,6 +3,7 @@
 
 using System.Threading;
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
 
 namespace System.Diagnostics.Tracing
 {
@@ -35,8 +36,6 @@ namespace System.Diagnostics.Tracing
         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;
@@ -46,12 +45,17 @@ namespace System.Diagnostics.Tracing
         private PollingCounter? _lohSizeCounter;
         private PollingCounter? _pohSizeCounter;
         private PollingCounter? _assemblyCounter;
-#endif // !NATIVEAOT
-
         private PollingCounter? _ilBytesJittedCounter;
         private PollingCounter? _methodsJittedCounter;
         private IncrementingPollingCounter? _jitTimeCounter;
 
+#if NATIVEAOT
+        // If EventSource feature is enabled, RuntimeEventSource needs to be initialized for NativeAOT
+        // In CoreCLR, this is done via StartupHookProvider.CoreCLR.cs
+#pragma warning disable CA2255
+        [ModuleInitializer]
+#pragma warning restore CA2255
+#endif
         public static void Initialize()
         {
             // initializing more than once may lead to missing events
@@ -108,7 +112,6 @@ namespace System.Diagnostics.Tracing
                     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) };
                 _gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" };
@@ -118,7 +121,6 @@ namespace System.Diagnostics.Tracing
                 _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" };
-#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" };
index 0bd3317..5c364fa 100644 (file)
@@ -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>