First set of licensing changes
[mono.git] / mono / utils / mono-proclib.c
index dea168417927c9934afc12fa1439459c8aaa4faa..c53af96454a79e84a98239ad450d0e9f8658154d 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2008-2011 Novell Inc
  * Copyright 2011 Xamarin Inc
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include "config.h"
@@ -14,6 +15,9 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_SCHED_GETAFFINITY
+#include <sched.h>
+#endif
 
 #ifdef HOST_WIN32
 #include <windows.h>
 #include <sys/user.h>
 #endif
 #ifdef HAVE_STRUCT_KINFO_PROC_KP_PROC
+#    define kinfo_starttime_member kp_proc.p_starttime
 #    define kinfo_pid_member kp_proc.p_pid
 #    define kinfo_name_member kp_proc.p_comm
 #elif defined(__OpenBSD__)
+// Can not figure out how to get the proc's start time on OpenBSD
+#    undef kinfo_starttime_member 
 #    define kinfo_pid_member p_pid
 #    define kinfo_name_member p_comm
 #else
+#define kinfo_starttime_member ki_start
 #define kinfo_pid_member ki_pid
 #define kinfo_name_member ki_comm
 #endif
@@ -157,7 +165,7 @@ mono_process_list (int *size)
                                count = 16;
                        else
                                count *= 2;
-                       buf = g_realloc (buf, count * sizeof (void*));
+                       buf = (void **)g_realloc (buf, count * sizeof (void*));
                }
                buf [i++] = GINT_TO_POINTER (pid);
        }
@@ -183,7 +191,7 @@ get_pid_status_item_buf (int pid, const char *item, char *rbuf, int blen, MonoPr
                        *error = MONO_PROCESS_ERROR_NOT_FOUND;
                return NULL;
        }
-       while ((s = fgets (buf, blen, f))) {
+       while ((s = fgets (buf, sizeof (buf), f))) {
                if (*item != *buf)
                        continue;
                if (strncmp (buf, item, len))
@@ -304,12 +312,12 @@ mono_process_get_times (gpointer pid, gint64 *start_time, gint64 *user_time, gin
        if (start_time) {
                *start_time = 0;
 
-#if USE_SYSCTL
+#if USE_SYSCTL && defined(kinfo_starttime_member)
                {
                        KINFO_PROC processi;
 
                        if (sysctl_kinfo_proc (pid, &processi))
-                               *start_time = mono_100ns_datetime_from_timeval (processi.kp_proc.p_starttime);
+                               *start_time = mono_100ns_datetime_from_timeval (processi.kinfo_starttime_member);
                }
 #endif
 
@@ -615,12 +623,12 @@ mono_cpu_count (void)
        GetSystemInfo (&info);
        return info.dwNumberOfProcessors;
 #else
-       int count = 0;
 #ifdef PLATFORM_ANDROID
        /* Android tries really hard to save power by powering off CPUs on SMP phones which
         * means the normal way to query cpu count returns a wrong value with userspace API.
         * Instead we use /sys entries to query the actual hardware CPU count.
         */
+       int count = 0;
        char buffer[8] = {'\0'};
        int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
        /* Format of the /sys entry is a cpulist of indexes which in the case
@@ -635,13 +643,95 @@ mono_cpu_count (void)
        if (count > 0)
                return count + 1;
 #endif
+
+#if defined(HOST_ARM) || defined (HOST_ARM64)
+
+/*
+ * Recap from Alexander Köplinger <alex.koeplinger@outlook.com>:
+ *
+ * When we merged the change from PR #2722, we started seeing random failures on ARM in
+ * the MonoTests.System.Threading.ThreadPoolTests.SetAndGetMaxThreads and
+ * MonoTests.System.Threading.ManualResetEventSlimTests.Constructor_Defaults tests. Both
+ * of those tests are dealing with Environment.ProcessorCount to verify some implementation
+ * details.
+ *
+ * It turns out that on the Jetson TK1 board we use on public Jenkins and on ARM kernels
+ * in general, the value returned by sched_getaffinity (or _SC_NPROCESSORS_ONLN) doesn't
+ * contain CPUs/cores that are powered off for power saving reasons. This is contrary to
+ * what happens on x86, where even cores in deep-sleep state are returned [1], [2]. This
+ * means that we would get a processor count of 1 at one point in time and a higher value
+ * when load increases later on as the system wakes CPUs.
+ *
+ * Various runtime pieces like the threadpool and also user code however relies on the
+ * value returned by Environment.ProcessorCount e.g. for deciding how many parallel tasks
+ * to start, thereby limiting the performance when that code thinks we only have one CPU.
+ *
+ * Talking to a few people, this was the reason why we changed to _SC_NPROCESSORS_CONF in
+ * mono#1688 and why we added a special case for Android in mono@de3addc to get the "real"
+ * number of processors in the system.
+ *
+ * Because of those issues Android/Dalvik also switched from _ONLN to _SC_NPROCESSORS_CONF
+ * for the Java API Runtime.availableProcessors() too [3], citing:
+ * > Traditionally this returned the number currently online, but many mobile devices are
+ * able to take unused cores offline to save power, so releases newer than Android 4.2 (Jelly
+ * Bean) return the maximum number of cores that could be made available if there were no
+ * power or heat constraints.
+ *
+ * The problem with sticking to _SC_NPROCESSORS_CONF however is that it breaks down in
+ * constrained environments like Docker or with an explicit CPU affinity set by the Linux
+ * `taskset` command, They'd get a higher CPU count than can be used, start more threads etc.
+ * which results in unnecessary context switches and overloaded systems. That's why we need
+ * to respect sched_getaffinity.
+ *
+ * So while in an ideal world we would be able to rely on sched_getaffinity/_SC_NPROCESSORS_ONLN
+ * to return the number of theoretically available CPUs regardless of power saving measures
+ * everywhere, we can't do this on ARM.
+ *
+ * I think the pragmatic solution is the following:
+ * * use sched_getaffinity (+ fallback to _SC_NPROCESSORS_ONLN in case of error) on x86. This
+ * ensures we're inline with what OpenJDK [4] and CoreCLR [5] do
+ * * use _SC_NPROCESSORS_CONF exclusively on ARM (I think we could eventually even get rid of
+ * the PLATFORM_ANDROID special case)
+ *
+ * Helpful links:
+ *
+ * [1] https://sourceware.org/ml/libc-alpha/2013-07/msg00383.html
+ * [2] https://lists.01.org/pipermail/powertop/2012-September/000433.html
+ * [3] https://android.googlesource.com/platform/libcore/+/750dc634e56c58d1d04f6a138734ac2b772900b5%5E1..750dc634e56c58d1d04f6a138734ac2b772900b5/
+ * [4] https://bugs.openjdk.java.net/browse/JDK-6515172
+ * [5] https://github.com/dotnet/coreclr/blob/7058273693db2555f127ce16e6b0c5b40fb04867/src/pal/src/misc/sysinfo.cpp#L148
+ */
+
 #ifdef _SC_NPROCESSORS_CONF
-       count = sysconf (_SC_NPROCESSORS_CONF);
-       if (count > 0)
-               return count;
+       {
+               int count = sysconf (_SC_NPROCESSORS_CONF);
+               if (count > 0)
+                       return count;
+       }
+#endif
+
+#else
+
+#ifdef HAVE_SCHED_GETAFFINITY
+       {
+               cpu_set_t set;
+               if (sched_getaffinity (mono_process_current_pid (), sizeof (set), &set) == 0)
+                       return CPU_COUNT (&set);
+       }
+#endif
+#ifdef _SC_NPROCESSORS_ONLN
+       {
+               int count = sysconf (_SC_NPROCESSORS_ONLN);
+               if (count > 0)
+                       return count;
+       }
 #endif
+
+#endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
+
 #ifdef USE_SYSCTL
        {
+               int count;
                int mib [2];
                size_t len = sizeof (int);
                mib [0] = CTL_HW;
@@ -650,7 +740,7 @@ mono_cpu_count (void)
                        return count;
        }
 #endif
-#endif
+#endif /* HOST_WIN32 */
        /* FIXME: warn */
        return 1;
 }
@@ -751,6 +841,15 @@ mono_atexit (void (*func)(void))
 #endif
 }
 
+/*
+ * This function returns the cpu usage in percentage,
+ * normalized on the number of cores.
+ *
+ * Warning : the percentage returned can be > 100%. This
+ * might happens on systems like Android which, for
+ * battery and performance reasons, shut down cores and
+ * lie about the number of active cores.
+ */
 gint32
 mono_cpu_usage (MonoCpuUsageState *prev)
 {
@@ -804,8 +903,5 @@ mono_cpu_usage (MonoCpuUsageState *prev)
        if (cpu_total_time > 0 && cpu_busy_time > 0)
                cpu_usage = (gint32)(cpu_busy_time * 100 / cpu_total_time);
 
-       g_assert (cpu_usage >= 0);
-       g_assert (cpu_usage <= 100);
-
        return cpu_usage;
 }