<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\TypeDependencyAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\DependentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\GCSettings.CoreCLR.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\JitInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComTypes\IEnumerable.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComTypes\IEnumerator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\DynamicInterfaceCastableHelpers.cs" />
}
}
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern long GetILBytesJitted();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int GetMethodsJittedCount();
}
// Helper class to assist with unsafe pinning of arbitrary objects.
// It's used by VM code.
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System.Runtime
+{
+ public static partial class JitInfo
+ {
+ /// <summary>
+ /// Get the number of bytes of IL that have been compiled. If <paramref name="currentThread"/> is true,
+ /// then this value is scoped to the current thread, otherwise, this is a global value.
+ /// </summary>
+ /// <param name="currentThread">Whether the returned value should be specific to the current thread. Default: false</param>
+ /// <returns>The number of bytes of IL the JIT has compiled.</returns>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern long GetCompiledILBytes(bool currentThread = false);
+
+ /// <summary>
+ /// Get the number of methods that have been compiled. If <paramref name="currentThread"/> is true,
+ /// then this value is scoped to the current thread, otherwise, this is a global value.
+ /// </summary>
+ /// <param name="currentThread">Whether the returned value should be specific to the current thread. Default: false</param>
+ /// <returns>The number of methods the JIT has compiled.</returns>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern long GetCompiledMethodCount(bool currentThread = false);
+
+ // Normalized to 100ns ticks on vm side
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern long GetCompilationTimeInTicks(bool currentThread = false);
+ }
+}
\ No newline at end of file
QCFuncElement("_MemoryBarrierProcessWide", COMInterlocked::MemoryBarrierProcessWide)
FCFuncEnd()
+FCFuncStart(gJitInfoFuncs)
+ FCFuncElement("GetCompiledILBytes", GetCompiledILBytes)
+ FCFuncElement("GetCompiledMethodCount", GetCompiledMethodCount)
+ FCFuncElement("GetCompilationTimeInTicks", GetCompilationTimeInTicks)
+FCFuncEnd()
+
FCFuncStart(gVarArgFuncs)
FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_IntPtr_PtrVoid_RetVoid, VarArgsNative::Init2)
FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_IntPtr_RetVoid, VarArgsNative::Init)
QCFuncElement("AllocateTypeAssociatedMemory", RuntimeTypeHandle::AllocateTypeAssociatedMemory)
FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer)
FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo)
- FCFuncElement("GetILBytesJitted", GetJittedBytes)
- FCFuncElement("GetMethodsJittedCount", GetJittedMethodsCount)
FCFuncEnd()
FCFuncStart(gMngdFixedArrayMarshalerFuncs)
FCClassElement("InterfaceMarshaler", "System.StubHelpers", gInterfaceMarshalerFuncs)
#endif
FCClassElement("Interlocked", "System.Threading", gInterlockedFuncs)
+FCClassElement("JitInfo", "System.Runtime", gJitInfoFuncs)
#if TARGET_UNIX
FCClassElement("Kernel32", "", gPalKernel32Funcs)
#endif
#else // DACCESS_COMPILE
-uint64_t g_cbILJitted = 0;
-uint32_t g_cMethodsJitted = 0;
+Volatile<int64_t> g_cbILJitted = 0;
+Volatile<int64_t> g_cMethodsJitted = 0;
+Volatile<int64_t> g_c100nsTicksInJit = 0;
+thread_local int64_t t_cbILJittedForThread = 0;
+thread_local int64_t t_cMethodsJittedForThread = 0;
+thread_local int64_t t_c100nsTicksInJitForThread = 0;
+
+// This prevents tearing of 64 bit values on 32 bit systems
+static inline
+int64_t AtomicLoad64WithoutTearing(int64_t volatile *valueRef)
+{
+ WRAPPER_NO_CONTRACT;
+#if TARGET_64BIT
+ return VolatileLoad(valueRef);
+#else
+ return InterlockedCompareExchangeT((LONG64 volatile *)valueRef, (LONG64)0, (LONG64)0);
+#endif // TARGET_64BIT
+}
#ifndef CROSSGEN_COMPILE
-FCIMPL0(INT64, GetJittedBytes)
+FCIMPL1(INT64, GetCompiledILBytes, CLR_BOOL currentThread)
{
FCALL_CONTRACT;
- return g_cbILJitted;
+ return currentThread ? t_cbILJittedForThread : AtomicLoad64WithoutTearing(&g_cbILJitted);
}
FCIMPLEND
-FCIMPL0(INT32, GetJittedMethodsCount)
+FCIMPL1(INT64, GetCompiledMethodCount, CLR_BOOL currentThread)
{
FCALL_CONTRACT;
- return g_cMethodsJitted;
+ return currentThread ? t_cMethodsJittedForThread : AtomicLoad64WithoutTearing(&g_cMethodsJitted);
+}
+FCIMPLEND
+
+FCIMPL1(INT64, GetCompilationTimeInTicks, CLR_BOOL currentThread)
+{
+ FCALL_CONTRACT;
+
+ return currentThread ? t_c100nsTicksInJitForThread : AtomicLoad64WithoutTearing(&g_c100nsTicksInJit);
}
FCIMPLEND
#endif
MethodDesc* ftn = nativeCodeVersion.GetMethodDesc();
PCODE ret = NULL;
+ NormalizedTimer timer;
+ int64_t c100nsTicksInJit = 0;
COOPERATIVE_TRANSITION_BEGIN();
+ timer.Start();
+
#ifdef FEATURE_PREJIT
if (g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_ALL &&
printf(".");
#endif // _DEBUG
- FastInterlockExchangeAddLong((LONG64*)&g_cbILJitted, methodInfo.ILCodeSize);
- FastInterlockIncrement((LONG*)&g_cMethodsJitted);
+ timer.Stop();
+ c100nsTicksInJit = timer.Elapsed100nsTicks();
+
+ InterlockedExchangeAdd64((LONG64*)&g_c100nsTicksInJit, c100nsTicksInJit);
+ t_c100nsTicksInJitForThread += c100nsTicksInJit;
+
+ InterlockedExchangeAdd64((LONG64*)&g_cbILJitted, methodInfo.ILCodeSize);
+ t_cbILJittedForThread += methodInfo.ILCodeSize;
+
+ InterlockedIncrement64((LONG64*)&g_cMethodsJitted);
+ t_cMethodsJittedForThread++;
COOPERATIVE_TRANSITION_END();
return ret;
bool __stdcall TrackAllocationsEnabled();
-FCDECL0(INT64, GetJittedBytes);
-FCDECL0(INT32, GetJittedMethodsCount);
+
+extern Volatile<int64_t> g_cbILJitted;
+extern Volatile<int64_t> g_cMethodsJitted;
+extern Volatile<int64_t> g_c100nsTicksInJit;
+extern thread_local int64_t t_cbILJittedForThread;
+extern thread_local int64_t t_cMethodsJittedForThread;
+extern thread_local int64_t t_c100nsTicksInJitForThread;
+
+FCDECL1(INT64, GetCompiledILBytes, CLR_BOOL currentThread);
+FCDECL1(INT64, GetCompiledMethodCount, CLR_BOOL currentThread);
+FCDECL1(INT64, GetCompilationTimeInTicks, CLR_BOOL currentThread);
#endif // JITINTERFACE_H
}
#endif // !TARGET_UNIX
+Volatile<double> NormalizedTimer::s_frequency = -1.0;
+
#endif // !DACCESS_COMPILE
static BOOL nativeIsDigit(WCHAR c);
};
+// ======================================================================================
+// Simple, reusable 100ns timer for normalizing ticks. For use in Q/FCalls to avoid discrepency with
+// tick frequency between native and managed.
+class NormalizedTimer
+{
+private:
+ static const int64_t NormalizedTicksPerSecond = 10000000 /* 100ns ticks per second (1e7) */;
+ static Volatile<double> s_frequency;
+
+ LARGE_INTEGER startTimestamp;
+ LARGE_INTEGER stopTimestamp;
+
+#if _DEBUG
+ bool isRunning = false;
+#endif // _DEBUG
+
+public:
+ NormalizedTimer()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (s_frequency.Load() == -1)
+ {
+ double frequency;
+ LARGE_INTEGER qpfValue;
+ QueryPerformanceFrequency(&qpfValue);
+ frequency = static_cast<double>(qpfValue.QuadPart);
+ frequency /= NormalizedTicksPerSecond;
+ s_frequency.Store(frequency);
+ }
+
+ startTimestamp.QuadPart = 0;
+ startTimestamp.QuadPart = 0;
+ }
+
+ // ======================================================================================
+ // Start the timer
+ inline
+ void Start()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!isRunning);
+ QueryPerformanceCounter(&startTimestamp);
+
+#if _DEBUG
+ isRunning = true;
+#endif // _DEBUG
+ }
+
+ // ======================================================================================
+ // stop the timer. If called before starting, sets the start time to the same as the stop
+ inline
+ void Stop()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(isRunning);
+ QueryPerformanceCounter(&stopTimestamp);
+
+#if _DEBUG
+ isRunning = false;
+#endif // _DEBUG
+ }
+
+ // ======================================================================================
+ // Return elapsed ticks. This will stop a running timer.
+ // Will return 0 if called out of order.
+ // Only recalculated this value if it has been stopped/started since previous calculation.
+ inline
+ int64_t Elapsed100nsTicks()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!isRunning);
+ _ASSERTE(startTimestamp.QuadPart > 0);
+ _ASSERTE(stopTimestamp.QuadPart > 0);
+ return static_cast<int64_t>((stopTimestamp.QuadPart - startTimestamp.QuadPart) / s_frequency);
+ }
+};
+
#ifdef _DEBUG
#define FORCEINLINE_NONDEBUG
#else
public static bool IsNotMonoRuntime => !IsMonoRuntime;
public static bool IsMonoInterpreter => GetIsRunningOnMonoInterpreter();
public static bool IsMonoAOT => Environment.GetEnvironmentVariable("MONO_AOT_MODE") == "aot";
+ public static bool IsNotMonoAOT => Environment.GetEnvironmentVariable("MONO_AOT_MODE") != "aot";
public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"));
public static bool IsNetBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD"));
public static bool IsAndroid => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID"));
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Vector64_1.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Vector64DebugView_1.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Enums.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\JitInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\AssemblyLoadContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\LibraryNameVariation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.cs" />
private PollingCounter? _assemblyCounter;
private PollingCounter? _ilBytesJittedCounter;
private PollingCounter? _methodsJittedCounter;
+ private IncrementingPollingCounter? _jitTimeCounter;
public static void Initialize()
{
_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" };
- _ilBytesJittedCounter ??= new PollingCounter("il-bytes-jitted", this, () => System.Runtime.CompilerServices.RuntimeHelpers.GetILBytesJitted()) { DisplayName = "IL Bytes Jitted", DisplayUnits = "B" };
- _methodsJittedCounter ??= new PollingCounter("methods-jitted-count", this, () => System.Runtime.CompilerServices.RuntimeHelpers.GetMethodsJittedCount()) { DisplayName = "Number of Methods Jitted" };
+ _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" };
+ _jitTimeCounter ??= new IncrementingPollingCounter("time-in-jit", this, () => System.Runtime.JitInfo.GetCompilationTime().TotalMilliseconds) { DisplayName = "Time spent in JIT", DisplayUnits = "ms", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Runtime
+{
+ /// <summary>
+ /// A static class for getting information about the Just In Time compiler.
+ /// </summary>
+ public static partial class JitInfo
+ {
+ /// <summary>
+ /// Get the amount of time the JIT Compiler has spent compiling methods. If <paramref name="currentThread"/> is true,
+ /// then this value is scoped to the current thread, otherwise, this is a global value.
+ /// </summary>
+ /// <param name="currentThread">Whether the returned value should be specific to the current thread. Default: false</param>
+ /// <returns>The amount of time the JIT Compiler has spent compiling methods.</returns>
+ public static TimeSpan GetCompilationTime(bool currentThread = false)
+ {
+ // TimeSpan.FromTicks() takes 100ns ticks
+ return TimeSpan.FromTicks(GetCompilationTimeInTicks(currentThread));
+ }
+ }
+}
\ No newline at end of file
public static System.Runtime.GCLargeObjectHeapCompactionMode LargeObjectHeapCompactionMode { get { throw null; } set { } }
public static System.Runtime.GCLatencyMode LatencyMode { get { throw null; } set { } }
}
+ public static partial class JitInfo
+ {
+ public static long GetCompiledILBytes(bool currentThread=false) { throw null; }
+ public static long GetCompiledMethodCount(bool currentThread=false) { throw null; }
+ public static TimeSpan GetCompilationTime(bool currentThread=false) { throw null; }
+ }
public sealed partial class MemoryFailPoint : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, System.IDisposable
{
public MemoryFailPoint(int sizeInMegabytes) { }
<Compile Include="System\Reflection\TypeTests.Get.CornerCases.cs" />
<Compile Include="System\Reflection\TypeTests.GetMember.cs" />
<Compile Include="System\Runtime\DependentHandleTests.cs" />
+ <Compile Include="System\Runtime\JitInfoTests.cs" />
<Compile Include="System\Runtime\MemoryFailPointTests.cs" />
<Compile Include="System\Runtime\NgenServicingAttributesTests.cs" />
<Compile Include="System\Runtime\CompilerServices\AttributesTests.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.DotNet.XUnitExtensions;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Threading;
+using Xunit;
+
+namespace System.Runtime.Tests
+{
+ public class JitInfoTests
+ {
+ private long MakeAndInvokeDynamicSquareMethod(int input)
+ {
+ // example ref emit dynamic method from https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-define-and-execute-dynamic-methods
+ Type[] methodArgs = {typeof(int)};
+
+ DynamicMethod squareIt = new DynamicMethod(
+ "SquareIt",
+ typeof(long),
+ methodArgs,
+ typeof(JitInfoTests).Module);
+
+ ILGenerator il = squareIt.GetILGenerator();
+ il.Emit(OpCodes.Ldarg_0);
+ il.Emit(OpCodes.Conv_I8);
+ il.Emit(OpCodes.Dup);
+ il.Emit(OpCodes.Mul);
+ il.Emit(OpCodes.Ret);
+
+ Func<int, long> invokeSquareIt =
+ (Func<int, long>)
+ squareIt.CreateDelegate(typeof(Func<int, long>));
+
+ return invokeSquareIt(input);
+
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // JitInfo metrics will be 0 in AOT scenarios
+ public void JitInfoIsPopulated()
+ {
+ TimeSpan beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime();
+ long beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes();
+ long beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount();
+
+ long square = MakeAndInvokeDynamicSquareMethod(100);
+ Assert.True(square == 10000);
+
+ TimeSpan afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime();
+ long afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes();
+ long afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount();
+
+ if (PlatformDetection.IsMonoInterpreter)
+ {
+ // special case the Mono interpreter where compilation time may be >0 but before and after will most likely be the same
+ Assert.True(beforeCompilationTime >= TimeSpan.Zero, $"Compilation time not greater than 0! ({beforeCompilationTime})");
+ Assert.True(beforeCompiledILBytes >= 0, $"Compiled IL bytes not greater than 0! ({beforeCompiledILBytes})");
+ Assert.True(beforeCompiledMethodCount >= 0, $"Compiled method count not greater than 0! ({beforeCompiledMethodCount})");
+
+ Assert.True(afterCompilationTime >= beforeCompilationTime, $"CompilationTime: after not greater than before! (after: {afterCompilationTime}, before: {beforeCompilationTime})");
+ Assert.True(afterCompiledILBytes >= beforeCompiledILBytes, $"Compiled IL bytes: after not greater than before! (after: {afterCompiledILBytes}, before: {beforeCompiledILBytes})");
+ Assert.True(afterCompiledMethodCount >= beforeCompiledMethodCount, $"Compiled method count: after not greater than before! (after: {afterCompiledMethodCount}, before: {beforeCompiledMethodCount})");
+ }
+ else
+ {
+ Assert.True(beforeCompilationTime > TimeSpan.Zero, $"Compilation time not greater than 0! ({beforeCompilationTime})");
+ Assert.True(beforeCompiledILBytes > 0, $"Compiled IL bytes not greater than 0! ({beforeCompiledILBytes})");
+ Assert.True(beforeCompiledMethodCount > 0, $"Compiled method count not greater than 0! ({beforeCompiledMethodCount})");
+
+ Assert.True(afterCompilationTime > beforeCompilationTime, $"CompilationTime: after not greater than before! (after: {afterCompilationTime}, before: {beforeCompilationTime})");
+ Assert.True(afterCompiledILBytes > beforeCompiledILBytes, $"Compiled IL bytes: after not greater than before! (after: {afterCompiledILBytes}, before: {beforeCompiledILBytes})");
+ Assert.True(afterCompiledMethodCount > beforeCompiledMethodCount, $"Compiled method count: after not greater than before! (after: {afterCompiledMethodCount}, before: {beforeCompiledMethodCount})");
+ }
+ }
+
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsMonoAOT))] // JitInfo metrics will be 0 in AOT scenarios
+ public void JitInfoIsNotPopulated()
+ {
+ TimeSpan beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime();
+ long beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes();
+ long beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount();
+
+ long square = MakeAndInvokeDynamicSquareMethod(100);
+ Assert.True(square == 10000);
+
+ TimeSpan afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime();
+ long afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes();
+ long afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount();
+
+ Assert.True(beforeCompilationTime == TimeSpan.Zero, $"Before Compilation time not eqeual to 0! ({beforeCompilationTime})");
+ Assert.True(beforeCompiledILBytes == 0, $"Before Compiled IL bytes not eqeual to 0! ({beforeCompiledILBytes})");
+ Assert.True(beforeCompiledMethodCount == 0, $"Before Compiled method count not eqeual to 0! ({beforeCompiledMethodCount})");
+
+ Assert.True(afterCompilationTime == TimeSpan.Zero, $"After Compilation time not eqeual to 0! ({afterCompilationTime})");
+ Assert.True(afterCompiledILBytes == 0, $"After Compiled IL bytes not eqeual to 0! ({afterCompiledILBytes})");
+ Assert.True(afterCompiledMethodCount == 0, $"After Compiled method count not eqeual to 0! ({afterCompiledMethodCount})");
+ }
+
+ [Fact]
+ [SkipOnMono("Mono does not track thread specific JIT information")]
+ public void JitInfoCurrentThreadIsPopulated()
+ {
+ TimeSpan t1_beforeCompilationTime = TimeSpan.Zero;
+ long t1_beforeCompiledILBytes = 0;
+ long t1_beforeCompiledMethodCount = 0;
+
+ TimeSpan t1_afterCompilationTime = TimeSpan.Zero;
+ long t1_afterCompiledILBytes = 0;
+ long t1_afterCompiledMethodCount = 0;
+
+ TimeSpan t2_beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true);
+ long t2_beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true);
+ long t2_beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true);
+
+ var t1 = new Thread(() => {
+ t1_beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true);
+ t1_beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true);
+ t1_beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true);
+ long square = MakeAndInvokeDynamicSquareMethod(100);
+ Assert.True(square == 10000);
+ t1_afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true);
+ t1_afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true);
+ t1_afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true);
+ });
+
+ t1.Start();
+ t1.Join();
+
+ long square = MakeAndInvokeDynamicSquareMethod(100);
+ Assert.True(square == 10000);
+
+ TimeSpan t2_afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true);
+ long t2_afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true);
+ long t2_afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true);
+
+ Assert.True(t2_beforeCompilationTime > TimeSpan.Zero, $"Thread 2 Compilation time not greater than 0! ({t2_beforeCompilationTime})");
+ Assert.True(t2_beforeCompiledILBytes > 0, $"Thread 2 Compiled IL bytes not greater than 0! ({t2_beforeCompiledILBytes})");
+ Assert.True(t2_beforeCompiledMethodCount > 0, $"Thread 2 Compiled method count not greater than 0! ({t2_beforeCompiledMethodCount})");
+
+ Assert.True(t2_afterCompilationTime > t2_beforeCompilationTime, $"CompilationTime: after not greater than before! (after: {t2_afterCompilationTime}, before: {t2_beforeCompilationTime})");
+ Assert.True(t2_afterCompiledILBytes > t2_beforeCompiledILBytes, $"Compiled IL bytes: after not greater than before! (after: {t2_afterCompiledILBytes}, before: {t2_beforeCompiledILBytes})");
+ Assert.True(t2_afterCompiledMethodCount > t2_beforeCompiledMethodCount, $"Compiled method count: after not greater than before! (after: {t2_afterCompiledMethodCount}, before: {t2_beforeCompiledMethodCount})");
+
+ Assert.True(t1_beforeCompilationTime > TimeSpan.Zero, $"Thread 1 before compilation time not greater than 0! ({t1_beforeCompilationTime})");
+ Assert.True(t1_beforeCompiledILBytes > 0, $"Thread 1 before compiled IL bytes not greater than 0! ({t1_beforeCompiledILBytes})");
+ Assert.True(t1_beforeCompiledMethodCount > 0, $"Thread 1 before compiled method count not greater than 0! ({t1_beforeCompiledMethodCount})");
+
+ Assert.True(t1_afterCompilationTime > t1_beforeCompilationTime, $"Thread 1 compilation time: after not greater than before! (after: {t1_afterCompilationTime}, before: {t1_beforeCompilationTime})");
+ Assert.True(t1_afterCompiledILBytes > t1_beforeCompiledILBytes, $"Thread 1 compiled IL bytes: after not greater than before! (after: {t1_afterCompiledILBytes}, before: {t1_beforeCompiledILBytes})");
+ Assert.True(t1_afterCompiledMethodCount > t1_beforeCompiledMethodCount, $"Thread 1 compiled method count: after not greater than before! (after: {t1_afterCompiledMethodCount}, before: {t1_beforeCompiledMethodCount})");
+
+ Assert.True(t1_afterCompilationTime != t2_afterCompilationTime, $"Thread 1 compilation time: equal to other thread! (t1: {t1_afterCompilationTime}, t2: {t2_beforeCompilationTime})");
+ Assert.True(t1_afterCompiledILBytes != t2_afterCompiledILBytes, $"Thread 1 compiled IL bytes: equal to other thread! (t1: {t1_afterCompiledILBytes}, t2: {t2_beforeCompiledILBytes})");
+ Assert.True(t1_afterCompiledMethodCount != t2_afterCompiledMethodCount, $"Thread 1 compiled method count: equal to other thread! (t1: {t1_afterCompiledMethodCount}, t2: {t2_beforeCompiledMethodCount})");
+ }
+
+ [Fact]
+ [SkipOnCoreClr("CoreCLR does track thread specific JIT information")]
+ public void JitInfoCurrentThreadIsNotPopulated()
+ {
+ TimeSpan compilationTime = TimeSpan.Zero;
+ long compiledILBytes = 0;
+ long compiledMethodCount = 0;
+
+ compilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true);
+ compiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true);
+ compiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true);
+
+ Assert.True(compilationTime == TimeSpan.Zero, $"compilation time not equal to 0! ({compilationTime})");
+ Assert.True(compiledILBytes == 0, $"compiled IL bytes not equal to 0! ({compiledILBytes})");
+ Assert.True(compiledMethodCount == 0, $"compiled method count not equal to 0! ({compiledMethodCount})");
+ }
+ }
+}
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\DependentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\GCSettings.Mono.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\JitInfo.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\JitHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.Mono.cs" />
GC_LARGE_OBJECT_SIZE_BYTES,
GC_LAST_PERCENT_TIME_IN_GC,
JIT_IL_BYTES_JITTED,
- JIT_METHODS_JITTED
+ JIT_METHODS_JITTED,
+ JIT_TICKS_IN_JIT
}
#if FEATURE_PERFTRACING
return GetUninitializedObjectInternal(new RuntimeTypeHandle(rt).Value);
}
- internal static long GetILBytesJitted()
- {
- return (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_IL_BYTES_JITTED);
- }
-
- internal static int GetMethodsJittedCount()
- {
- return (int)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_METHODS_JITTED);
- }
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern unsafe void PrepareMethod(IntPtr method, IntPtr* instantiations, int ninst);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.Tracing;
+
+namespace System.Runtime
+{
+ public static partial class JitInfo
+ {
+ /// <summary>
+ /// Get the number of bytes of IL that have been compiled. If <paramref name="currentThread"/> is true,
+ /// then this value is scoped to the current thread, otherwise, this is a global value.
+ /// </summary>
+ /// <param name="currentThread">Whether the returned value should be specific to the current thread. Default: false</param>
+ /// <returns>The number of bytes of IL the JIT has compiled.</returns>
+ public static long GetCompiledILBytes(bool currentThread = false)
+ {
+ return currentThread ? 0 : (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_IL_BYTES_JITTED);
+ }
+
+ /// <summary>
+ /// Get the number of methods that have been compiled. If <paramref name="currentThread"/> is true,
+ /// then this value is scoped to the current thread, otherwise, this is a global value.
+ /// </summary>
+ /// <param name="currentThread">Whether the returned value should be specific to the current thread. Default: false</param>
+ /// <returns>The number of methods the JIT has compiled.</returns>
+ public static long GetCompiledMethodCount(bool currentThread = false)
+ {
+ return currentThread ? 0 : (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_METHODS_JITTED);
+ }
+
+ // normalized to 100ns ticks on vm side
+ private static long GetCompilationTimeInTicks(bool currentThread = false)
+ {
+ return currentThread ? 0 : (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_TICKS_IN_JIT);
+ }
+ }
+}
\ No newline at end of file
EP_RT_COUNTERS_GC_LARGE_OBJECT_SIZE_BYTES,
EP_RT_COUNTERS_GC_LAST_PERCENT_TIME_IN_GC,
EP_RT_COUNTERS_JIT_IL_BYTES_JITTED,
- EP_RT_COUNTERS_JIT_METOHODS_JITTED
+ EP_RT_COUNTERS_JIT_METHODS_JITTED,
+ EP_RT_COUNTERS_JIT_TICKS_IN_JIT
} EventPipeRuntimeCounters;
static
gint64 methods_compiled = 0;
gint64 cil_code_size_bytes = 0;
gint64 native_code_size_bytes = 0;
+ gint64 jit_time = 0;
if (mono_get_runtime_callbacks ()->get_jit_stats)
- mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes);
+ mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes, &jit_time);
return cil_code_size_bytes;
}
static
inline
-gint32
+gint64
get_methods_jitted (void)
{
gint64 methods_compiled = 0;
gint64 cil_code_size_bytes = 0;
gint64 native_code_size_bytes = 0;
+ gint64 jit_time = 0;
if (mono_get_runtime_callbacks ()->get_jit_stats)
- mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes);
- return (gint32)methods_compiled;
+ mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes, &jit_time);
+ return methods_compiled;
}
static
return excepion_count;
}
+static
+inline
+gint64
+get_ticks_in_jit (void)
+{
+ gint64 methods_compiled = 0;
+ gint64 cil_code_size_bytes = 0;
+ gint64 native_code_size_bytes = 0;
+ gint64 jit_time = 0;
+
+ if (mono_get_runtime_callbacks ()->get_jit_stats)
+ mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes, &jit_time);
+ return jit_time;
+}
+
guint64 ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetRuntimeCounterValue (gint32 id)
{
EventPipeRuntimeCounters counterID = (EventPipeRuntimeCounters)id;
return (guint64)gc_last_percent_time_in_gc ();
case EP_RT_COUNTERS_JIT_IL_BYTES_JITTED :
return (guint64)get_il_bytes_jitted ();
- case EP_RT_COUNTERS_JIT_METOHODS_JITTED :
+ case EP_RT_COUNTERS_JIT_METHODS_JITTED :
return (guint64)get_methods_jitted ();
+ case EP_RT_COUNTERS_JIT_TICKS_IN_JIT :
+ return (gint64)get_ticks_in_jit ();
default:
return 0;
}
void (*init_mem_manager)(MonoMemoryManager*);
void (*free_mem_manager)(MonoMemoryManager*);
void (*metadata_update_published) (MonoAssemblyLoadContext *alc, uint32_t generation);
- void (*get_jit_stats)(gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes);
+ void (*get_jit_stats)(gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes, gint64 *jit_time);
void (*get_exception_stats)(guint32 *exception_count);
// Same as compile_method, but returns a MonoFtnDesc in llvmonly mode
gpointer (*get_ftnptr)(MonoMethod *method, MonoError *error);
extern MonoJitStats mono_jit_stats;
static inline void
-get_jit_stats (gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes)
+get_jit_stats (gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes, gint64 *jit_time)
{
*methods_compiled = mono_jit_stats.methods_compiled;
*cil_code_size_bytes = mono_jit_stats.cil_code_size;
*native_code_size_bytes = mono_jit_stats.native_code_size;
+ *jit_time = mono_jit_stats.jit_time;
}
guint32
{
private readonly EventLevel _level = EventLevel.Verbose;
- public int MaxIncrement { get; private set; } = 0;
+ public double MaxIncrement { get; private set; } = 0;
public SimpleEventListener()
{
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
- int increment = 0;
+ double increment = 0;
bool isExceptionCounter = false;
for (int i = 0; i < eventData.Payload.Count; i++)
isExceptionCounter = true;
if (payload.Key.Equals("Increment"))
{
- increment = Int32.Parse(payload.Value.ToString());
+ increment = double.Parse(payload.Value.ToString());
}
}
if (isExceptionCounter)
{ "poh-size", false },
{ "assembly-count", false },
{ "il-bytes-jitted", false },
- { "methods-jitted-count", false }
+ { "methods-jitted-count", false },
+ { "time-in-jit", false }
};
}
private Dictionary<string, bool> observedRuntimeCounters;