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