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