Merge pull request #1588 from BrzVlad/feature-aot-wbarrier
[mono.git] / mono / utils / mono-proclib.c
1 /*
2  * Copyright 2008-2011 Novell Inc
3  * Copyright 2011 Xamarin Inc
4  */
5
6 #include "config.h"
7 #include "utils/mono-proclib.h"
8 #include "utils/mono-time.h"
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17
18 #ifdef HOST_WIN32
19 #include <windows.h>
20 #include <process.h>
21 #include <Winbase.h>
22 #endif
23
24 #if defined(_POSIX_VERSION)
25 #include <sys/errno.h>
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/sysctl.h>
29 #include <sys/resource.h>
30 #endif
31 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
32 #include <sys/proc.h>
33 #if defined(__APPLE__)
34 #include <mach/mach.h>
35 #endif
36 #ifdef HAVE_SYS_USER_H
37 #include <sys/user.h>
38 #endif
39 #ifdef HAVE_STRUCT_KINFO_PROC_KP_PROC
40 #    define kinfo_pid_member kp_proc.p_pid
41 #    define kinfo_name_member kp_proc.p_comm
42 #elif defined(__OpenBSD__)
43 #    define kinfo_pid_member p_pid
44 #    define kinfo_name_member p_comm
45 #else
46 #define kinfo_pid_member ki_pid
47 #define kinfo_name_member ki_comm
48 #endif
49 #define USE_SYSCTL 1
50 #endif
51
52 /**
53  * mono_process_list:
54  * @size: a pointer to a location where the size of the returned array is stored
55  *
56  * Return an array of pid values for the processes currently running on the system.
57  * The size of the array is stored in @size.
58  */
59 gpointer*
60 mono_process_list (int *size)
61 {
62 #if USE_SYSCTL
63         int res, i;
64 #ifdef KERN_PROC2
65         int mib [6];
66         size_t data_len = sizeof (struct kinfo_proc2) * 400;
67         struct kinfo_proc2 *processes = malloc (data_len);
68 #else
69         int mib [4];
70         size_t data_len = sizeof (struct kinfo_proc) * 16;
71         struct kinfo_proc *processes;
72         int limit = 8;
73 #endif /* KERN_PROC2 */
74         void **buf = NULL;
75
76         if (size)
77                 *size = 0;
78
79 #ifdef KERN_PROC2
80         if (!processes)
81                 return NULL;
82
83         mib [0] = CTL_KERN;
84         mib [1] = KERN_PROC2;
85         mib [2] = KERN_PROC_ALL;
86         mib [3] = 0;
87         mib [4] = sizeof(struct kinfo_proc2);
88         mib [5] = 400; /* XXX */
89
90         res = sysctl (mib, 6, processes, &data_len, NULL, 0);
91         if (res < 0) {
92                 free (processes);
93                 return NULL;
94         }
95 #else
96         processes = NULL;
97         while (limit) {
98                 mib [0] = CTL_KERN;
99                 mib [1] = KERN_PROC;
100                 mib [2] = KERN_PROC_ALL;
101                 mib [3] = 0;
102
103                 res = sysctl (mib, 4, NULL, &data_len, NULL, 0);
104                 if (res)
105                         return NULL;
106                 processes = malloc (data_len);
107                 res = sysctl (mib, 4, processes, &data_len, NULL, 0);
108                 if (res < 0) {
109                         free (processes);
110                         if (errno != ENOMEM)
111                                 return NULL;
112                         limit --;
113                 } else {
114                         break;
115                 }
116         }
117 #endif /* KERN_PROC2 */
118
119 #ifdef KERN_PROC2
120         res = data_len/sizeof (struct kinfo_proc2);
121 #else
122         res = data_len/sizeof (struct kinfo_proc);
123 #endif /* KERN_PROC2 */
124         buf = g_realloc (buf, res * sizeof (void*));
125         for (i = 0; i < res; ++i)
126                 buf [i] = GINT_TO_POINTER (processes [i].kinfo_pid_member);
127         free (processes);
128         if (size)
129                 *size = res;
130         return buf;
131 #elif defined(__HAIKU__)
132         /* FIXME: Add back the code from 9185fcc305e43428d0f40f3ee37c8a405d41c9ae */
133         g_assert_not_reached ();
134         return NULL;
135 #else
136         const char *name;
137         void **buf = NULL;
138         int count = 0;
139         int i = 0;
140         GDir *dir = g_dir_open ("/proc/", 0, NULL);
141         if (!dir) {
142                 if (size)
143                         *size = 0;
144                 return NULL;
145         }
146         while ((name = g_dir_read_name (dir))) {
147                 int pid;
148                 char *nend;
149                 pid = strtol (name, &nend, 10);
150                 if (pid <= 0 || nend == name || *nend)
151                         continue;
152                 if (i >= count) {
153                         if (!count)
154                                 count = 16;
155                         else
156                                 count *= 2;
157                         buf = g_realloc (buf, count * sizeof (void*));
158                 }
159                 buf [i++] = GINT_TO_POINTER (pid);
160         }
161         g_dir_close (dir);
162         if (size)
163                 *size = i;
164         return buf;
165 #endif
166 }
167
168 static G_GNUC_UNUSED char*
169 get_pid_status_item_buf (int pid, const char *item, char *rbuf, int blen, MonoProcessError *error)
170 {
171         char buf [256];
172         char *s;
173         FILE *f;
174         int len = strlen (item);
175
176         g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
177         f = fopen (buf, "r");
178         if (!f) {
179                 if (error)
180                         *error = MONO_PROCESS_ERROR_NOT_FOUND;
181                 return NULL;
182         }
183         while ((s = fgets (buf, blen, f))) {
184                 if (*item != *buf)
185                         continue;
186                 if (strncmp (buf, item, len))
187                         continue;
188                 s = buf + len;
189                 while (g_ascii_isspace (*s)) s++;
190                 if (*s++ != ':')
191                         continue;
192                 while (g_ascii_isspace (*s)) s++;
193                 fclose (f);
194                 len = strlen (s);
195                 strncpy (rbuf, s, MIN (len, blen));
196                 rbuf [MIN (len, blen) - 1] = 0;
197                 if (error)
198                         *error = MONO_PROCESS_ERROR_NONE;
199                 return rbuf;
200         }
201         fclose (f);
202         if (error)
203                 *error = MONO_PROCESS_ERROR_OTHER;
204         return NULL;
205 }
206
207 /**
208  * mono_process_get_name:
209  * @pid: pid of the process
210  * @buf: byte buffer where to store the name of the prcoess
211  * @len: size of the buffer @buf
212  *
213  * Return the name of the process identified by @pid, storing it
214  * inside @buf for a maximum of len bytes (including the terminating 0).
215  */
216 char*
217 mono_process_get_name (gpointer pid, char *buf, int len)
218 {
219 #if USE_SYSCTL
220         int res;
221 #ifdef KERN_PROC2
222         int mib [6];
223         size_t data_len = sizeof (struct kinfo_proc2);
224         struct kinfo_proc2 processi;
225 #else
226         int mib [4];
227         size_t data_len = sizeof (struct kinfo_proc);
228         struct kinfo_proc processi;
229 #endif /* KERN_PROC2 */
230
231         memset (buf, 0, len);
232
233 #ifdef KERN_PROC2
234         mib [0] = CTL_KERN;
235         mib [1] = KERN_PROC2;
236         mib [2] = KERN_PROC_PID;
237         mib [3] = GPOINTER_TO_UINT (pid);
238         mib [4] = sizeof(struct kinfo_proc2);
239         mib [5] = 400; /* XXX */
240
241         res = sysctl (mib, 6, &processi, &data_len, NULL, 0);
242
243         if (res < 0 || data_len != sizeof (struct kinfo_proc2)) {
244                 return buf;
245         }
246 #else
247         mib [0] = CTL_KERN;
248         mib [1] = KERN_PROC;
249         mib [2] = KERN_PROC_PID;
250         mib [3] = GPOINTER_TO_UINT (pid);
251         
252         res = sysctl (mib, 4, &processi, &data_len, NULL, 0);
253         if (res < 0 || data_len != sizeof (struct kinfo_proc)) {
254                 return buf;
255         }
256 #endif /* KERN_PROC2 */
257         strncpy (buf, processi.kinfo_name_member, len - 1);
258         return buf;
259 #else
260         char fname [128];
261         FILE *file;
262         char *p;
263         int r;
264         sprintf (fname, "/proc/%d/cmdline", GPOINTER_TO_INT (pid));
265         buf [0] = 0;
266         file = fopen (fname, "r");
267         if (!file)
268                 return buf;
269         r = fread (buf, 1, len - 1, file);
270         fclose (file);
271         buf [r] = 0;
272         p = strrchr (buf, '/');
273         if (p)
274                 return p + 1;
275         if (r == 0) {
276                 return get_pid_status_item_buf (GPOINTER_TO_INT (pid), "Name", buf, len, NULL);
277         }
278         return buf;
279 #endif
280 }
281
282 /*
283  * /proc/pid/stat format:
284  * pid (cmdname) S 
285  *      [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
286  *      [10] utime stime cutime cstime prio nice threads 0 start_time vsize rss
287  *      [20] rss rsslim start_code end_code start_stack esp eip pending blocked sigign
288  *      [30] sigcatch wchan 0 0 exit_signal cpu rt_prio policy
289  */
290
291 #define RET_ERROR(err) do {     \
292                 if (error) *error = (err);      \
293                 return 0;                       \
294         } while (0)
295
296 static gint64
297 get_process_stat_item (int pid, int pos, int sum, MonoProcessError *error)
298 {
299 #if defined(__APPLE__) 
300         double process_user_time = 0, process_system_time = 0;//, process_percent = 0;
301         task_t task;
302         struct task_basic_info t_info;
303         mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT, th_count;
304         thread_array_t th_array;
305         size_t i;
306
307         if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS)
308                 RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
309
310         if (task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count) != KERN_SUCCESS) {
311                 mach_port_deallocate (mach_task_self (), task);
312                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
313         }
314         
315         if (task_threads(task, &th_array, &th_count) != KERN_SUCCESS) {
316                 mach_port_deallocate (mach_task_self (), task);
317                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
318         }
319                 
320         for (i = 0; i < th_count; i++) {
321                 double thread_user_time, thread_system_time;//, thread_percent;
322                 
323                 struct thread_basic_info th_info;
324                 mach_msg_type_number_t th_info_count = THREAD_BASIC_INFO_COUNT;
325                 if (thread_info(th_array[i], THREAD_BASIC_INFO, (thread_info_t)&th_info, &th_info_count) == KERN_SUCCESS) {
326                         thread_user_time = th_info.user_time.seconds + th_info.user_time.microseconds / 1e6;
327                         thread_system_time = th_info.system_time.seconds + th_info.system_time.microseconds / 1e6;
328                         //thread_percent = (double)th_info.cpu_usage / TH_USAGE_SCALE;
329                         
330                         process_user_time += thread_user_time;
331                         process_system_time += thread_system_time;
332                         //process_percent += th_percent;
333                 }
334         }
335         
336         for (i = 0; i < th_count; i++)
337                 mach_port_deallocate(task, th_array[i]);
338
339         mach_port_deallocate (mach_task_self (), task);
340
341         process_user_time += t_info.user_time.seconds + t_info.user_time.microseconds / 1e6;
342         process_system_time += t_info.system_time.seconds + t_info.system_time.microseconds / 1e6;
343     
344         if (pos == 10 && sum == TRUE)
345                 return (gint64)((process_user_time + process_system_time) * 10000000);
346         else if (pos == 10)
347                 return (gint64)(process_user_time * 10000000);
348         else if (pos == 11)
349                 return (gint64)(process_system_time * 10000000);
350                 
351         return 0;
352 #else
353         char buf [512];
354         char *s, *end;
355         FILE *f;
356         int len, i;
357         gint64 value;
358
359         g_snprintf (buf, sizeof (buf), "/proc/%d/stat", pid);
360         f = fopen (buf, "r");
361         if (!f)
362                 RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
363         len = fread (buf, 1, sizeof (buf), f);
364         fclose (f);
365         if (len <= 0)
366                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
367         s = strchr (buf, ')');
368         if (!s)
369                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
370         s++;
371         while (g_ascii_isspace (*s)) s++;
372         if (!*s)
373                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
374         /* skip the status char */
375         while (*s && !g_ascii_isspace (*s)) s++;
376         if (!*s)
377                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
378         for (i = 0; i < pos; ++i) {
379                 while (g_ascii_isspace (*s)) s++;
380                 if (!*s)
381                         RET_ERROR (MONO_PROCESS_ERROR_OTHER);
382                 while (*s && !g_ascii_isspace (*s)) s++;
383                 if (!*s)
384                         RET_ERROR (MONO_PROCESS_ERROR_OTHER);
385         }
386         /* we are finally at the needed item */
387         value = strtoul (s, &end, 0);
388         /* add also the following value */
389         if (sum) {
390                 while (g_ascii_isspace (*s)) s++;
391                 if (!*s)
392                         RET_ERROR (MONO_PROCESS_ERROR_OTHER);
393                 value += strtoul (s, &end, 0);
394         }
395         if (error)
396                 *error = MONO_PROCESS_ERROR_NONE;
397         return value;
398 #endif
399 }
400
401 static int
402 get_user_hz (void)
403 {
404         static int user_hz = 0;
405         if (user_hz == 0) {
406 #ifdef _SC_CLK_TCK
407                 user_hz = sysconf (_SC_CLK_TCK);
408 #endif
409                 if (user_hz == 0)
410                         user_hz = 100;
411         }
412         return user_hz;
413 }
414
415 static gint64
416 get_process_stat_time (int pid, int pos, int sum, MonoProcessError *error)
417 {
418         gint64 val = get_process_stat_item (pid, pos, sum, error);
419 #if defined(__APPLE__)
420         return val;
421 #else
422         /* return 100ns ticks */
423         return (val * 10000000) / get_user_hz ();
424 #endif
425 }
426
427 static gint64
428 get_pid_status_item (int pid, const char *item, MonoProcessError *error, int multiplier)
429 {
430 #if defined(__APPLE__)
431         // ignore the multiplier
432         
433         gint64 ret;
434         task_t task;
435         struct task_basic_info t_info;
436         mach_msg_type_number_t th_count = TASK_BASIC_INFO_COUNT;
437
438         if (pid == getpid ()) {
439                 /* task_for_pid () doesn't work on ios, even for the current process */
440                 task = mach_task_self ();
441         } else {
442                 if (task_for_pid (mach_task_self (), pid, &task) != KERN_SUCCESS)
443                         RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
444         }
445         
446         if (task_info (task, TASK_BASIC_INFO, (task_info_t)&t_info, &th_count) != KERN_SUCCESS) {
447                 if (pid != getpid ())
448                         mach_port_deallocate (mach_task_self (), task);
449                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
450         }
451
452         if (strcmp (item, "VmRSS") == 0 || strcmp (item, "VmHWM") == 0 || strcmp (item, "VmData") == 0)
453                 ret = t_info.resident_size;
454         else if (strcmp (item, "VmSize") == 0 || strcmp (item, "VmPeak") == 0)
455                 ret = t_info.virtual_size;
456         else if (strcmp (item, "Threads") == 0)
457                 ret = th_count;
458         else
459                 ret = 0;
460
461         if (pid != getpid ())
462                 mach_port_deallocate (mach_task_self (), task);
463         
464         return ret;
465 #else
466         char buf [64];
467         char *s;
468
469         s = get_pid_status_item_buf (pid, item, buf, sizeof (buf), error);
470         if (s)
471                 return ((gint64) atol (s)) * multiplier;
472         return 0;
473 #endif
474 }
475
476 /**
477  * mono_process_get_data:
478  * @pid: pid of the process
479  * @data: description of data to return
480  *
481  * Return a data item of a process like user time, memory use etc,
482  * according to the @data argumet.
483  */
484 gint64
485 mono_process_get_data_with_error (gpointer pid, MonoProcessData data, MonoProcessError *error)
486 {
487         gint64 val;
488         int rpid = GPOINTER_TO_INT (pid);
489
490         if (error)
491                 *error = MONO_PROCESS_ERROR_OTHER;
492
493         switch (data) {
494         case MONO_PROCESS_NUM_THREADS:
495                 return get_pid_status_item (rpid, "Threads", error, 1);
496         case MONO_PROCESS_USER_TIME:
497                 return get_process_stat_time (rpid, 10, FALSE, error);
498         case MONO_PROCESS_SYSTEM_TIME:
499                 return get_process_stat_time (rpid, 11, FALSE, error);
500         case MONO_PROCESS_TOTAL_TIME:
501                 return get_process_stat_time (rpid, 10, TRUE, error);
502         case MONO_PROCESS_WORKING_SET:
503                 return get_pid_status_item (rpid, "VmRSS", error, 1024);
504         case MONO_PROCESS_WORKING_SET_PEAK:
505                 val = get_pid_status_item (rpid, "VmHWM", error, 1024);
506                 if (val == 0)
507                         val = get_pid_status_item (rpid, "VmRSS", error, 1024);
508                 return val;
509         case MONO_PROCESS_PRIVATE_BYTES:
510                 return get_pid_status_item (rpid, "VmData", error, 1024);
511         case MONO_PROCESS_VIRTUAL_BYTES:
512                 return get_pid_status_item (rpid, "VmSize", error, 1024);
513         case MONO_PROCESS_VIRTUAL_BYTES_PEAK:
514                 val = get_pid_status_item (rpid, "VmPeak", error, 1024);
515                 if (val == 0)
516                         val = get_pid_status_item (rpid, "VmSize", error, 1024);
517                 return val;
518         case MONO_PROCESS_FAULTS:
519                 return get_process_stat_item (rpid, 6, TRUE, error);
520         case MONO_PROCESS_ELAPSED:
521                 return get_process_stat_item (rpid, 18, FALSE, error) / get_user_hz ();
522         case MONO_PROCESS_PPID:
523                 return get_process_stat_time (rpid, 0, FALSE, error);
524         case MONO_PROCESS_PAGED_BYTES:
525                 return get_pid_status_item (rpid, "VmSwap", error, 1024);
526
527                 /* Nothing yet */
528         case MONO_PROCESS_END:
529                 return 0;
530         }
531         return 0;
532 }
533
534 gint64
535 mono_process_get_data (gpointer pid, MonoProcessData data)
536 {
537         MonoProcessError error;
538         return mono_process_get_data_with_error (pid, data, &error);
539 }
540
541 int
542 mono_process_current_pid ()
543 {
544 #if defined(HAVE_UNISTD_H)
545         return (int) getpid ();
546 #elif defined(HOST_WIN32)
547         return (int) GetCurrentProcessId ();
548 #else
549 #error getpid
550 #endif
551 }
552
553 /**
554  * mono_cpu_count:
555  *
556  * Return the number of processors on the system.
557  */
558 int
559 mono_cpu_count (void)
560 {
561         int count = 0;
562 #ifdef PLATFORM_ANDROID
563         /* Android tries really hard to save power by powering off CPUs on SMP phones which
564          * means the normal way to query cpu count returns a wrong value with userspace API.
565          * Instead we use /sys entries to query the actual hardware CPU count.
566          */
567         char buffer[8] = {'\0'};
568         int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
569         /* Format of the /sys entry is a cpulist of indexes which in the case
570          * of present is always of the form "0-(n-1)" when there is more than
571          * 1 core, n being the number of CPU cores in the system. Otherwise
572          * the value is simply 0
573          */
574         if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
575                 count = strtol (((char*)buffer) + 2, NULL, 10);
576         if (present != -1)
577                 close (present);
578         if (count > 0)
579                 return count + 1;
580 #endif
581 #ifdef _SC_NPROCESSORS_ONLN
582         count = sysconf (_SC_NPROCESSORS_ONLN);
583         if (count > 0)
584                 return count;
585 #endif
586 #ifdef USE_SYSCTL
587         {
588                 int mib [2];
589                 size_t len = sizeof (int);
590                 mib [0] = CTL_HW;
591                 mib [1] = HW_NCPU;
592                 if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
593                         return count;
594         }
595 #endif
596 #ifdef HOST_WIN32
597         {
598                 SYSTEM_INFO info;
599                 GetSystemInfo (&info);
600                 return info.dwNumberOfProcessors;
601         }
602 #endif
603         /* FIXME: warn */
604         return 1;
605 }
606
607 static void
608 get_cpu_times (int cpu_id, gint64 *user, gint64 *systemt, gint64 *irq, gint64 *sirq, gint64 *idle)
609 {
610         char buf [256];
611         char *s;
612         int hz = get_user_hz ();
613         guint64 user_ticks = 0, nice_ticks = 0, system_ticks = 0, idle_ticks = 0, irq_ticks = 0, sirq_ticks = 0;
614         FILE *f = fopen ("/proc/stat", "r");
615         if (!f)
616                 return;
617         if (cpu_id < 0)
618                 hz *= mono_cpu_count ();
619         while ((s = fgets (buf, sizeof (buf), f))) {
620                 char *data = NULL;
621                 if (cpu_id < 0 && strncmp (s, "cpu", 3) == 0 && g_ascii_isspace (s [3])) {
622                         data = s + 4;
623                 } else if (cpu_id >= 0 && strncmp (s, "cpu", 3) == 0 && strtol (s + 3, &data, 10) == cpu_id) {
624                         if (data == s + 3)
625                                 continue;
626                         data++;
627                 } else {
628                         continue;
629                 }
630                 
631                 user_ticks = strtoull (data, &data, 10);
632                 nice_ticks = strtoull (data, &data, 10);
633                 system_ticks = strtoull (data, &data, 10);
634                 idle_ticks = strtoull (data, &data, 10);
635                 /* iowait_ticks = strtoull (data, &data, 10); */
636                 irq_ticks = strtoull (data, &data, 10);
637                 sirq_ticks = strtoull (data, &data, 10);
638                 break;
639         }
640         fclose (f);
641
642         if (user)
643                 *user = (user_ticks + nice_ticks) * 10000000 / hz;
644         if (systemt)
645                 *systemt = (system_ticks) * 10000000 / hz;
646         if (irq)
647                 *irq = (irq_ticks) * 10000000 / hz;
648         if (sirq)
649                 *sirq = (sirq_ticks) * 10000000 / hz;
650         if (idle)
651                 *idle = (idle_ticks) * 10000000 / hz;
652 }
653
654 /**
655  * mono_cpu_get_data:
656  * @cpu_id: processor number or -1 to get a summary of all the processors
657  * @data: type of data to retrieve
658  *
659  * Get data about a processor on the system, like time spent in user space or idle time.
660  */
661 gint64
662 mono_cpu_get_data (int cpu_id, MonoCpuData data, MonoProcessError *error)
663 {
664         gint64 value = 0;
665
666         if (error)
667                 *error = MONO_PROCESS_ERROR_NONE;
668         switch (data) {
669         case MONO_CPU_USER_TIME:
670                 get_cpu_times (cpu_id, &value, NULL, NULL, NULL, NULL);
671                 break;
672         case MONO_CPU_PRIV_TIME:
673                 get_cpu_times (cpu_id, NULL, &value, NULL, NULL, NULL);
674                 break;
675         case MONO_CPU_INTR_TIME:
676                 get_cpu_times (cpu_id, NULL, NULL, &value, NULL, NULL);
677                 break;
678         case MONO_CPU_DCP_TIME:
679                 get_cpu_times (cpu_id, NULL, NULL, NULL, &value, NULL);
680                 break;
681         case MONO_CPU_IDLE_TIME:
682                 get_cpu_times (cpu_id, NULL, NULL, NULL, NULL, &value);
683                 break;
684
685         case MONO_CPU_END:
686                 /* Nothing yet */
687                 return 0;
688         }
689         return value;
690 }
691
692 int
693 mono_atexit (void (*func)(void))
694 {
695 #ifdef PLATFORM_ANDROID
696         /* Some versions of android libc doesn't define atexit () */
697         return 0;
698 #else
699         return atexit (func);
700 #endif
701 }
702
703 gint32
704 mono_cpu_usage (MonoCpuUsageState *prev)
705 {
706         gint32 cpu_usage = 0;
707         gint64 cpu_total_time;
708         gint64 cpu_busy_time;
709
710 #ifndef HOST_WIN32
711         struct rusage resource_usage;
712         gint64 current_time;
713         gint64 kernel_time;
714         gint64 user_time;
715
716         if (getrusage (RUSAGE_SELF, &resource_usage) == -1) {
717                 g_error ("getrusage() failed, errno is %d (%s)\n", errno, strerror (errno));
718                 return -1;
719         }
720
721         current_time = mono_100ns_ticks ();
722         kernel_time = resource_usage.ru_stime.tv_sec * 1000 * 1000 * 10 + resource_usage.ru_stime.tv_usec * 10;
723         user_time = resource_usage.ru_utime.tv_sec * 1000 * 1000 * 10 + resource_usage.ru_utime.tv_usec * 10;
724
725         cpu_busy_time = (user_time - (prev ? prev->user_time : 0)) + (kernel_time - (prev ? prev->kernel_time : 0));
726         cpu_total_time = (current_time - (prev ? prev->current_time : 0)) * mono_cpu_count ();
727
728         if (prev) {
729                 prev->kernel_time = kernel_time;
730                 prev->user_time = user_time;
731                 prev->current_time = current_time;
732         }
733 #else
734         guint64 idle_time;
735         guint64 kernel_time;
736         guint64 user_time;
737
738         if (!GetSystemTimes ((FILETIME*) &idle_time, (FILETIME*) &kernel_time, (FILETIME*) &user_time)) {
739                 g_error ("GetSystemTimes() failed, error code is %d\n", GetLastError ());
740                 return -1;
741         }
742
743         cpu_total_time = (gint64)((user_time - (prev ? prev->user_time : 0)) + (kernel_time - (prev ? prev->kernel_time : 0)));
744         cpu_busy_time = (gint64)(cpu_total_time - (idle_time - (prev ? prev->idle_time : 0)));
745
746         if (prev) {
747                 prev->idle_time = idle_time;
748                 prev->kernel_time = kernel_time;
749                 prev->user_time = user_time;
750         }
751 #endif
752
753         if (cpu_total_time > 0 && cpu_busy_time > 0)
754                 cpu_usage = (gint32)(cpu_busy_time * 100 / cpu_total_time);
755
756         g_assert (cpu_usage >= 0);
757         g_assert (cpu_usage <= 100);
758
759         return cpu_usage;
760 }