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