Merge pull request #2338 from BogdanovKirill/httpwritefix3
[mono.git] / mono / utils / mono-proclib.c
index ba488a8d72509e1bac00727dc3edff15525c1b9f..c4e20e4ed42400bad8e38c6ddb3a8ff100d13b87 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "config.h"
 #include "utils/mono-proclib.h"
+#include "utils/mono-time.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <process.h>
 #endif
 
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#if defined(_POSIX_VERSION)
 #include <sys/errno.h>
 #include <sys/param.h>
+#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SYSCTL_H
 #include <sys/sysctl.h>
+#endif
+#include <sys/resource.h>
+#endif
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
 #include <sys/proc.h>
 #if defined(__APPLE__)
 #include <mach/mach.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
@@ -98,7 +110,7 @@ mono_process_list (int *size)
                res = sysctl (mib, 4, NULL, &data_len, NULL, 0);
                if (res)
                        return NULL;
-               processes = malloc (data_len);
+               processes = (struct kinfo_proc *) malloc (data_len);
                res = sysctl (mib, 4, processes, &data_len, NULL, 0);
                if (res < 0) {
                        free (processes);
@@ -116,7 +128,7 @@ mono_process_list (int *size)
 #else
        res = data_len/sizeof (struct kinfo_proc);
 #endif /* KERN_PROC2 */
-       buf = g_realloc (buf, res * sizeof (void*));
+       buf = (void **) g_realloc (buf, res * sizeof (void*));
        for (i = 0; i < res; ++i)
                buf [i] = GINT_TO_POINTER (processes [i].kinfo_pid_member);
        free (processes);
@@ -149,7 +161,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);
        }
@@ -284,11 +296,42 @@ mono_process_get_name (gpointer pid, char *buf, int len)
 #endif
 }
 
+void
+mono_process_get_times (gpointer pid, gint64 *start_time, gint64 *user_time, gint64 *kernel_time)
+{
+       if (user_time)
+               *user_time = mono_process_get_data (pid, MONO_PROCESS_USER_TIME);
+
+       if (kernel_time)
+               *kernel_time = mono_process_get_data (pid, MONO_PROCESS_SYSTEM_TIME);
+
+       if (start_time) {
+               *start_time = 0;
+
+#if USE_SYSCTL && defined(kinfo_starttime_member)
+               {
+                       KINFO_PROC processi;
+
+                       if (sysctl_kinfo_proc (pid, &processi))
+                               *start_time = mono_100ns_datetime_from_timeval (processi.kinfo_starttime_member);
+               }
+#endif
+
+               if (*start_time == 0) {
+                       static guint64 boot_time = 0;
+                       if (!boot_time)
+                               boot_time = mono_100ns_datetime () - ((guint64)mono_msec_ticks ()) * 10000;
+
+                       *start_time = boot_time + mono_process_get_data (pid, MONO_PROCESS_ELAPSED);
+               }
+       }
+}
+
 /*
  * /proc/pid/stat format:
  * pid (cmdname) S 
  *     [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
- *     [10] utime stime cutime cstime prio nice threads 0 start_time vsize rss
+ *     [10] utime stime cutime cstime prio nice threads 0 start_time vsize
  *     [20] rss rsslim start_code end_code start_stack esp eip pending blocked sigign
  *     [30] sigcatch wchan 0 0 exit_signal cpu rt_prio policy
  */
@@ -309,16 +352,23 @@ get_process_stat_item (int pid, int pos, int sum, MonoProcessError *error)
        thread_array_t th_array;
        size_t i;
 
-       if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS)
-               RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
+       if (pid == getpid ()) {
+               /* task_for_pid () doesn't work on ios, even for the current process */
+               task = mach_task_self ();
+       } else {
+               if (task_for_pid (mach_task_self (), pid, &task) != KERN_SUCCESS)
+                       RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
+       }
 
-       if (task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count) != KERN_SUCCESS) {
-               mach_port_deallocate (mach_task_self (), task);
+       if (task_info (task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count) != KERN_SUCCESS) {
+               if (pid != getpid ())
+                       mach_port_deallocate (mach_task_self (), task);
                RET_ERROR (MONO_PROCESS_ERROR_OTHER);
        }
        
        if (task_threads(task, &th_array, &th_count) != KERN_SUCCESS) {
-               mach_port_deallocate (mach_task_self (), task);
+               if (pid != getpid ())
+                       mach_port_deallocate (mach_task_self (), task);
                RET_ERROR (MONO_PROCESS_ERROR_OTHER);
        }
                
@@ -341,7 +391,8 @@ get_process_stat_item (int pid, int pos, int sum, MonoProcessError *error)
        for (i = 0; i < th_count; i++)
                mach_port_deallocate(task, th_array[i]);
 
-       mach_port_deallocate (mach_task_self (), task);
+       if (pid != getpid ())
+               mach_port_deallocate (mach_task_self (), task);
 
        process_user_time += t_info.user_time.seconds + t_info.user_time.microseconds / 1e6;
        process_system_time += t_info.system_time.seconds + t_info.system_time.microseconds / 1e6;
@@ -523,7 +574,7 @@ mono_process_get_data_with_error (gpointer pid, MonoProcessData data, MonoProces
        case MONO_PROCESS_FAULTS:
                return get_process_stat_item (rpid, 6, TRUE, error);
        case MONO_PROCESS_ELAPSED:
-               return get_process_stat_item (rpid, 18, FALSE, error) / get_user_hz ();
+               return get_process_stat_time (rpid, 18, FALSE, error);
        case MONO_PROCESS_PPID:
                return get_process_stat_time (rpid, 0, FALSE, error);
        case MONO_PROCESS_PAGED_BYTES:
@@ -563,6 +614,11 @@ mono_process_current_pid ()
 int
 mono_cpu_count (void)
 {
+#ifdef HOST_WIN32
+       SYSTEM_INFO info;
+       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
@@ -583,8 +639,8 @@ mono_cpu_count (void)
        if (count > 0)
                return count + 1;
 #endif
-#ifdef _SC_NPROCESSORS_ONLN
-       count = sysconf (_SC_NPROCESSORS_ONLN);
+#ifdef _SC_NPROCESSORS_CONF
+       count = sysconf (_SC_NPROCESSORS_CONF);
        if (count > 0)
                return count;
 #endif
@@ -598,12 +654,6 @@ mono_cpu_count (void)
                        return count;
        }
 #endif
-#ifdef HOST_WIN32
-       {
-               SYSTEM_INFO info;
-               GetSystemInfo (&info);
-               return info.dwNumberOfProcessors;
-       }
 #endif
        /* FIXME: warn */
        return 1;
@@ -704,3 +754,68 @@ mono_atexit (void (*func)(void))
        return atexit (func);
 #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)
+{
+       gint32 cpu_usage = 0;
+       gint64 cpu_total_time;
+       gint64 cpu_busy_time;
+
+#ifndef HOST_WIN32
+       struct rusage resource_usage;
+       gint64 current_time;
+       gint64 kernel_time;
+       gint64 user_time;
+
+       if (getrusage (RUSAGE_SELF, &resource_usage) == -1) {
+               g_error ("getrusage() failed, errno is %d (%s)\n", errno, strerror (errno));
+               return -1;
+       }
+
+       current_time = mono_100ns_ticks ();
+       kernel_time = resource_usage.ru_stime.tv_sec * 1000 * 1000 * 10 + resource_usage.ru_stime.tv_usec * 10;
+       user_time = resource_usage.ru_utime.tv_sec * 1000 * 1000 * 10 + resource_usage.ru_utime.tv_usec * 10;
+
+       cpu_busy_time = (user_time - (prev ? prev->user_time : 0)) + (kernel_time - (prev ? prev->kernel_time : 0));
+       cpu_total_time = (current_time - (prev ? prev->current_time : 0)) * mono_cpu_count ();
+
+       if (prev) {
+               prev->kernel_time = kernel_time;
+               prev->user_time = user_time;
+               prev->current_time = current_time;
+       }
+#else
+       guint64 idle_time;
+       guint64 kernel_time;
+       guint64 user_time;
+
+       if (!GetSystemTimes ((FILETIME*) &idle_time, (FILETIME*) &kernel_time, (FILETIME*) &user_time)) {
+               g_error ("GetSystemTimes() failed, error code is %d\n", GetLastError ());
+               return -1;
+       }
+
+       cpu_total_time = (gint64)((user_time - (prev ? prev->user_time : 0)) + (kernel_time - (prev ? prev->kernel_time : 0)));
+       cpu_busy_time = (gint64)(cpu_total_time - (idle_time - (prev ? prev->idle_time : 0)));
+
+       if (prev) {
+               prev->idle_time = idle_time;
+               prev->kernel_time = kernel_time;
+               prev->user_time = user_time;
+       }
+#endif
+
+       if (cpu_total_time > 0 && cpu_busy_time > 0)
+               cpu_usage = (gint32)(cpu_busy_time * 100 / cpu_total_time);
+
+       return cpu_usage;
+}