From: Sung Yoon Whang Date: Thu, 4 Apr 2019 03:40:50 +0000 (-0700) Subject: Add CPU runtime counter (#23680) X-Git-Tag: accepted/tizen/unified/20190813.215958~53^2~21 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3dbfa1e039d8fbb45d0d9eadffa768ba2b2e89dc;p=platform%2Fupstream%2Fcoreclr.git Add CPU runtime counter (#23680) * Add cpu counter * Fix windows build * Make the counter just return current CPU usage as % * Add Unix * Fix unix build * Some cleanup * rename * fixing some build errors * some cleanup * remove unused using * more cleanup * newline * Address PR feedback * more pr feedback * More feedback --- diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index e8cb0b8..3a8c2e7 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -365,6 +365,7 @@ + @@ -373,6 +374,7 @@ + diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs new file mode 100644 index 0000000..22ec793 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal extern static bool GetProcessTimes(IntPtr handleProcess, out long creation, out long exit, out long kernel, out long user); + } +} \ No newline at end of file diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimes.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimes.cs new file mode 100644 index 0000000..ff26978 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimes.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal extern static bool GetSystemTimes(out long idle, out long kernel, out long user); + } +} \ No newline at end of file diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 79a5866..f2b23c6 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -1012,8 +1012,10 @@ + + @@ -1176,7 +1178,7 @@ - + diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs index 010c2ea..31effe3 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs @@ -20,6 +20,7 @@ namespace System.Diagnostics.Tracing private IncrementingPollingCounter _gen1GCCounter; private IncrementingPollingCounter _gen2GCCounter; private IncrementingPollingCounter _exceptionCounter; + private PollingCounter _cpuTimeCounter; private const int EnabledPollingIntervalMilliseconds = 1000; // 1 second @@ -41,6 +42,7 @@ namespace System.Diagnostics.Tracing // overhead by at all times even when counters aren't enabled. // On disable, PollingCounters will stop polling for values so it should be fine to leave them around. + _cpuTimeCounter = _cpuTimeCounter ?? new PollingCounter("CPU Usage", this, () => RuntimeEventSourceHelper.GetCpuUsage()); _gcHeapSizeCounter = _gcHeapSizeCounter ?? new PollingCounter("GC Heap Size", this, () => GC.GetTotalMemory(false)); _gen0GCCounter = _gen0GCCounter ?? new IncrementingPollingCounter("Gen 0 GC Count", this, () => GC.CollectionCount(0)); _gen1GCCounter = _gen1GCCounter ?? new IncrementingPollingCounter("Gen 1 GC Count", this, () => GC.CollectionCount(1)); diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs new file mode 100644 index 0000000..ff1eff4 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Unix.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + + +namespace System.Diagnostics.Tracing +{ + internal sealed class RuntimeEventSourceHelper + { + private static Interop.Sys.ProcessCpuInformation cpuInfo; + + internal static int GetCpuUsage() + { + return Interop.Sys.GetCpuUtilization(ref cpuInfo); + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs new file mode 100644 index 0000000..778961e --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSourceHelper.Windows.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; + + +namespace System.Diagnostics.Tracing +{ + internal sealed class RuntimeEventSourceHelper + { + private static long prevProcUserTime = 0; + private static long prevProcKernelTime = 0; + private static long prevSystemUserTime = 0; + private static long prevSystemKernelTime = 0; + + internal static int GetCpuUsage() + { + // Returns the current process' CPU usage as a percentage + + int cpuUsage; + + if (!Interop.Kernel32.GetProcessTimes(Interop.Kernel32.GetCurrentProcess(), out _, out _, out long procKernelTime, out long procUserTime)) + { + return 0; + } + + if (!Interop.Kernel32.GetSystemTimes(out _, out long systemUserTime, out long systemKernelTime)) + { + return 0; + } + + if (prevSystemUserTime == 0 && prevSystemKernelTime == 0) // These may be 0 when we report CPU usage for the first time, in which case we should just return 0. + { + cpuUsage = 0; + } + else + { + long totalProcTime = (procUserTime - prevProcUserTime) + (procKernelTime - prevProcKernelTime); + long totalSystemTime = (systemUserTime - prevSystemUserTime) + (systemKernelTime - prevSystemKernelTime); + cpuUsage = (int)(totalProcTime * 100 / totalSystemTime); + } + + prevProcUserTime = procUserTime; + prevProcKernelTime = procKernelTime; + prevSystemUserTime = systemUserTime; + prevSystemKernelTime = systemKernelTime; + + return cpuUsage; + } + } +}