#include <mono/utils/mono-membar.h>
#include <mono/utils/mono-mutex.h>
#include <mono/utils/mono-signal-handler.h>
+ #include <mono/utils/mono-proclib.h>
/* The process' environment strings */
#if defined(__APPLE__) && !defined (__arm__) && !defined (__aarch64__)
* signal handler)
*/
static struct MonoProcess *mono_processes = NULL;
-static volatile gint32 mono_processes_read_lock = 0;
static volatile gint32 mono_processes_cleaning_up = 0;
static mono_mutex_t mono_processes_mutex;
static void mono_processes_cleanup (void);
/* Not sure if w32 allows NULLs here or not */
return FALSE;
- if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process))
- /* This is a pseudo handle, so just fail for now
- */
- return FALSE;
-
+ if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
+ gpointer pid = GINT_TO_POINTER (WAPI_HANDLE_TO_PID(process));
+ gint64 start_ticks, user_ticks, kernel_ticks;
+
+ mono_process_get_times (pid, &start_ticks, &user_ticks, &kernel_ticks);
+
+ _wapi_guint64_to_filetime (start_ticks, create_time);
+ _wapi_guint64_to_filetime (user_ticks, kernel_time);
+ _wapi_guint64_to_filetime (kernel_ticks, user_time);
+
+ return TRUE;
+ }
+
process_handle = lookup_process_handle (process);
if (!process_handle) {
DEBUG ("%s: Can't find process %p", __func__, process);
{
struct MonoProcess *mp;
struct MonoProcess *prev = NULL;
- struct MonoProcess *candidate = NULL;
+ GSList *finished = NULL;
+ GSList *l;
gpointer unref_handle;
- int spin;
DEBUG ("%s", __func__);
if (InterlockedCompareExchange (&mono_processes_cleaning_up, 1, 0) != 0)
return;
- mp = mono_processes;
- while (mp != NULL) {
- if (mp->pid == 0 && mp->handle != NULL) {
+ for (mp = mono_processes; mp; mp = mp->next) {
+ if (mp->pid == 0 && mp->handle) {
/* This process has exited and we need to remove the artifical ref
* on the handle */
mono_mutex_lock (&mono_processes_mutex);
mono_mutex_unlock (&mono_processes_mutex);
if (unref_handle)
_wapi_handle_unref (unref_handle);
- continue;
}
- mp = mp->next;
}
/*
* asynchronously. The handler requires that the mono_processes list
* remain valid.
*/
- mp = mono_processes;
- spin = 0;
- while (mp != NULL) {
- if ((mp->handle_count == 0 && mp->pid == 0) || candidate != NULL) {
- if (spin > 0) {
- _wapi_handle_spin (spin);
- spin <<= 1;
- }
+ mono_mutex_lock (&mono_processes_mutex);
- /* We've found a candidate */
- mono_mutex_lock (&mono_processes_mutex);
+ mp = mono_processes;
+ while (mp) {
+ if (mp->handle_count == 0 && mp->freeable) {
/*
+ * Unlink the entry.
* This code can run parallel with the sigchld handler, but the
* modifications it makes are safe.
*/
- if (candidate == NULL) {
- /* unlink it */
- if (mp == mono_processes) {
- mono_processes = mp->next;
- } else {
- prev->next = mp->next;
- }
- candidate = mp;
- }
-
- /* It's still safe to traverse the structure.*/
- mono_memory_barrier ();
+ if (mp == mono_processes)
+ mono_processes = mp->next;
+ else
+ prev->next = mp->next;
+ finished = g_slist_prepend (finished, mp);
- if (mono_processes_read_lock != 0) {
- /* The sigchld handler is watching us. Spin a bit and try again */
- if (spin == 0) {
- spin = 1;
- } else if (spin >= 8) {
- /* Just give up for now */
- mono_mutex_unlock (&mono_processes_mutex);
- break;
- }
- } else {
- /* We've modified the list of processes, and we know the sigchld handler
- * isn't executing, so even if it executes at any moment, it'll see the
- * new version of the list. So now we can free the candidate. */
- DEBUG ("%s: freeing candidate %p", __func__, candidate);
- mp = candidate->next;
- MONO_SEM_DESTROY (&candidate->exit_sem);
- g_free (candidate);
- candidate = NULL;
- }
+ mp = mp->next;
+ } else {
+ prev = mp;
+ mp = mp->next;
+ }
+ }
- mono_mutex_unlock (&mono_processes_mutex);
+ mono_memory_barrier ();
- continue;
- }
- spin = 0;
- prev = mp;
- mp = mp->next;
+ for (l = finished; l; l = l->next) {
+ /*
+ * All the entries in the finished list are unlinked from mono_processes, and
+ * they have the 'finished' flag set, which means the sigchld handler is done
+ * accessing them.
+ */
+ mp = l->data;
+ MONO_SEM_DESTROY (&mp->exit_sem);
+ g_free (mp);
}
+ g_slist_free (finished);
+
+ mono_mutex_unlock (&mono_processes_mutex);
DEBUG ("%s done", __func__);
DEBUG ("SIG CHILD handler for pid: %i\n", info->si_pid);
- InterlockedIncrement (&mono_processes_read_lock);
-
do {
do {
pid = waitpid (-1, &status, WNOHANG);
break;
DEBUG ("child ended: %i", pid);
- p = mono_processes;
- while (p != NULL) {
+
+ /*
+ * This can run concurrently with the code in the rest of this module.
+ */
+ for (p = mono_processes; p; p = p->next) {
if (p->pid == pid) {
- p->pid = 0; /* this pid doesn't exist anymore, clear it */
- p->status = status;
- MONO_SEM_POST (&p->exit_sem);
break;
}
- p = p->next;
+ }
+ if (p) {
+ p->pid = 0; /* this pid doesn't exist anymore, clear it */
+ p->status = status;
+ MONO_SEM_POST (&p->exit_sem);
+ mono_memory_barrier ();
+ /* Mark this as freeable, the pointer becomes invalid afterwards */
+ p->freeable = TRUE;
}
} while (1);
- InterlockedDecrement (&mono_processes_read_lock);
-
DEBUG ("SIG CHILD handler: done looping.");
}
#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>
return NULL;
}
- /**
- * mono_process_get_name:
- * @pid: pid of the process
- * @buf: byte buffer where to store the name of the prcoess
- * @len: size of the buffer @buf
- *
- * Return the name of the process identified by @pid, storing it
- * inside @buf for a maximum of len bytes (including the terminating 0).
- */
- char*
- mono_process_get_name (gpointer pid, char *buf, int len)
- {
#if USE_SYSCTL
- int res;
+
#ifdef KERN_PROC2
- int mib [6];
- size_t data_len = sizeof (struct kinfo_proc2);
- struct kinfo_proc2 processi;
+ #define KINFO_PROC struct kinfo_proc2
#else
- int mib [4];
- size_t data_len = sizeof (struct kinfo_proc);
- struct kinfo_proc processi;
- #endif /* KERN_PROC2 */
+ #define KINFO_PROC struct kinfo_proc
+ #endif
- memset (buf, 0, len);
+ static gboolean
+ sysctl_kinfo_proc (gpointer pid, KINFO_PROC* processi)
+ {
+ int res;
+ size_t data_len = sizeof (KINFO_PROC);
#ifdef KERN_PROC2
+ int mib [6];
mib [0] = CTL_KERN;
mib [1] = KERN_PROC2;
mib [2] = KERN_PROC_PID;
mib [3] = GPOINTER_TO_UINT (pid);
- mib [4] = sizeof(struct kinfo_proc2);
+ mib [4] = sizeof(KINFO_PROC);
mib [5] = 400; /* XXX */
- res = sysctl (mib, 6, &processi, &data_len, NULL, 0);
-
- if (res < 0 || data_len != sizeof (struct kinfo_proc2)) {
- return buf;
- }
+ res = sysctl (mib, 6, processi, &data_len, NULL, 0);
#else
+ int mib [4];
mib [0] = CTL_KERN;
mib [1] = KERN_PROC;
mib [2] = KERN_PROC_PID;
mib [3] = GPOINTER_TO_UINT (pid);
-
- res = sysctl (mib, 4, &processi, &data_len, NULL, 0);
- if (res < 0 || data_len != sizeof (struct kinfo_proc)) {
- return buf;
- }
+
+ res = sysctl (mib, 4, processi, &data_len, NULL, 0);
#endif /* KERN_PROC2 */
- strncpy (buf, processi.kinfo_name_member, len - 1);
+
+ if (res < 0 || data_len != sizeof (KINFO_PROC))
+ return FALSE;
+
+ return TRUE;
+ }
+ #endif /* USE_SYSCTL */
+
+ /**
+ * mono_process_get_name:
+ * @pid: pid of the process
+ * @buf: byte buffer where to store the name of the prcoess
+ * @len: size of the buffer @buf
+ *
+ * Return the name of the process identified by @pid, storing it
+ * inside @buf for a maximum of len bytes (including the terminating 0).
+ */
+ char*
+ mono_process_get_name (gpointer pid, char *buf, int len)
+ {
+ #if USE_SYSCTL
+ KINFO_PROC processi;
+
+ memset (buf, 0, len);
+
+ if (sysctl_kinfo_proc (pid, &processi))
+ strncpy (buf, processi.kinfo_name_member, len - 1);
+
return buf;
#else
char fname [128];
#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
+ {
+ KINFO_PROC processi;
+
+ if (sysctl_kinfo_proc (pid, &processi))
+ *start_time = mono_100ns_datetime_from_timeval (processi.kp_proc.p_starttime);
+ }
+ #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
*/
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:
return atexit (func);
#endif
}
+
+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);
+
+ g_assert (cpu_usage >= 0);
+ g_assert (cpu_usage <= 100);
+
+ return cpu_usage;
+}