[System.Native] Add SystemNative_GetCpuUtilization (dotnet/corefx#34806)
authorFilip Navara <filip.navara@gmail.com>
Fri, 25 Jan 2019 14:37:13 +0000 (15:37 +0100)
committerStephen Toub <stoub@microsoft.com>
Fri, 25 Jan 2019 14:37:13 +0000 (09:37 -0500)
* Add SystemNative_GetCpuUtilization.

* Update sysconf call to match CoreCLR.

Commit migrated from https://github.com/dotnet/corefx/commit/26a4a61c99b2e2cbec52f10a230fd6436807f6e6

src/libraries/Native/Unix/System.Native/pal_time.c
src/libraries/Native/Unix/System.Native/pal_time.h

index 94cc61a..33f168f 100644 (file)
@@ -12,6 +12,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/time.h>
+#include <sys/resource.h>
 #if HAVE_MACH_ABSOLUTE_TIME
 #include <mach/mach_time.h>
 #endif
@@ -152,3 +153,82 @@ int32_t SystemNative_GetTimebaseInfo(uint32_t* numer, uint32_t* denom)
     }
     return 1;
 }
+
+#if defined(_ARM_) || defined(_ARM64_)
+#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF
+#else
+#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_ONLN
+#endif
+
+int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo)
+{
+    static long numProcessors = 0;
+
+    if (numProcessors <= 0)
+    {
+        numProcessors = sysconf(SYSCONF_GET_NUMPROCS);
+        if (numProcessors <= 0)
+        {
+            return 0;
+        }
+    }
+
+    uint64_t kernelTime = 0;
+    uint64_t userTime = 0;
+
+    struct rusage resUsage;
+    if (getrusage(RUSAGE_SELF, &resUsage) == -1)
+    {
+        assert(false);
+        return 0;
+    }
+    else
+    {
+        kernelTime = ((uint64_t)(resUsage.ru_stime.tv_sec) * SecondsToNanoSeconds) + (uint64_t)(resUsage.ru_stime.tv_usec);
+        userTime = ((uint64_t)(resUsage.ru_utime.tv_sec) * SecondsToNanoSeconds) + (uint64_t)(resUsage.ru_utime.tv_usec);
+    }
+
+    uint64_t timestamp;
+    uint64_t resolution;
+
+    if (!SystemNative_GetTimestamp(&timestamp) || !SystemNative_GetTimestampResolution(&resolution))
+    {
+        return 0;
+    }
+
+    uint64_t currentTime = timestamp * SecondsToNanoSeconds / resolution;
+
+    uint64_t lastRecordedCurrentTime = previousCpuInfo->lastRecordedCurrentTime;
+    uint64_t lastRecordedKernelTime = previousCpuInfo->lastRecordedKernelTime;
+    uint64_t lastRecordedUserTime = previousCpuInfo->lastRecordedUserTime;
+
+    uint64_t cpuTotalTime = 0;
+    if (currentTime > lastRecordedCurrentTime)
+    {
+        // cpuTotalTime is based on clock time. Since multiple threads can run in parallel,
+        // we need to scale cpuTotalTime cover the same amount of total CPU time.
+        // rusage time is already scaled across multiple processors.
+        cpuTotalTime = (currentTime - lastRecordedCurrentTime);
+        cpuTotalTime *= (uint64_t)numProcessors;
+    }
+
+    uint64_t cpuBusyTime = 0;
+    if (userTime >= lastRecordedUserTime && kernelTime >= lastRecordedKernelTime)
+    {
+        cpuBusyTime = (userTime - lastRecordedUserTime) + (kernelTime - lastRecordedKernelTime);
+    }
+
+    int32_t cpuUtilization = 0;
+    if (cpuTotalTime > 0 && cpuBusyTime > 0)
+    {
+        cpuUtilization = (int32_t)(cpuBusyTime / cpuTotalTime);
+    }
+
+    assert(cpuUtilization >= 0 && cpuUtilization <= 100);
+
+    previousCpuInfo->lastRecordedCurrentTime = currentTime;
+    previousCpuInfo->lastRecordedUserTime = userTime;
+    previousCpuInfo->lastRecordedKernelTime = kernelTime;
+
+    return cpuUtilization;
+}
index a0deba8..ebeee54 100644 (file)
@@ -13,6 +13,14 @@ typedef struct TimeSpec
     int64_t tv_nsec; // nanoseconds
 } TimeSpec;
 
+typedef struct ProcessCpuInformation
+{
+    uint64_t lastRecordedCurrentTime;
+    uint64_t lastRecordedKernelTime;
+    uint64_t lastRecordedUserTime;
+} ProcessCpuInformation;
+
+
 /**
  * Sets the last access and last modified time of a file
  *
@@ -37,3 +45,12 @@ DLLEXPORT int32_t SystemNative_GetTimestamp(uint64_t* timestamp);
 DLLEXPORT int32_t SystemNative_GetAbsoluteTime(uint64_t* timestamp);
 
 DLLEXPORT int32_t SystemNative_GetTimebaseInfo(uint32_t* numer, uint32_t* denom);
+
+/**
+ * The main purpose of this function is to compute the overall CPU utilization
+ * for the CLR thread pool to regulate the number of worker threads.
+ * Since there is no consistent API on Unix to get the CPU utilization
+ * from a user process, getrusage and gettimeofday are used to
+ * compute the current process's CPU utilization instead.
+ */
+DLLEXPORT int32_t SystemNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo);