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