[xbuild] Fix assembly name comparison when resolving references.
[mono.git] / mono / utils / mono-proclib.c
1 #include "config.h"
2 #include "utils/mono-proclib.h"
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #ifdef HAVE_UNISTD_H
8 #include <unistd.h>
9 #endif
10
11 #ifdef HOST_WIN32
12 #include <windows.h>
13 #endif
14
15 /* FIXME: bsds untested */
16 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <sys/sysctl.h>
20 #include <sys/proc.h>
21 #if defined(__APPLE__)
22 #include <mach/mach.h>
23 #endif
24 #ifdef HAVE_SYS_USER_H
25 #include <sys/user.h>
26 #endif
27 #ifdef HAVE_STRUCT_KINFO_PROC_KP_PROC
28 #  ifdef KERN_PROC2
29 #    define kinfo_pid_member p_pid
30 #    define kinfo_name_member p_comm
31 #  else
32 #    define kinfo_pid_member kp_proc.p_pid
33 #    define kinfo_name_member kp_proc.p_comm
34 #  endif
35 #else
36 #define kinfo_pid_member ki_pid
37 #define kinfo_name_member ki_comm
38 #endif
39 #define USE_SYSCTL 1
40 #endif
41
42 /**
43  * mono_process_list:
44  * @size: a pointer to a location where the size of the returned array is stored
45  *
46  * Return an array of pid values for the processes currently running on the system.
47  * The size of the array is stored in @size.
48  */
49 gpointer*
50 mono_process_list (int *size)
51 {
52 #if USE_SYSCTL
53         int res, i;
54 #ifdef KERN_PROC2
55         int mib [6];
56         size_t data_len = sizeof (struct kinfo_proc2) * 400;
57         struct kinfo_proc2 *processes = malloc (data_len);
58 #else
59         int mib [4];
60         size_t data_len = sizeof (struct kinfo_proc) * 400;
61         struct kinfo_proc *processes = malloc (data_len);
62 #endif /* KERN_PROC2 */
63         void **buf = NULL;
64
65         if (size)
66                 *size = 0;
67         if (!processes)
68                 return NULL;
69
70 #ifdef KERN_PROC2
71         mib [0] = CTL_KERN;
72         mib [1] = KERN_PROC2;
73         mib [2] = KERN_PROC_ALL;
74         mib [3] = 0;
75         mib [4] = sizeof(struct kinfo_proc2);
76         mib [5] = 400; /* XXX */
77
78         res = sysctl (mib, 6, processes, &data_len, NULL, 0);
79 #else
80         mib [0] = CTL_KERN;
81         mib [1] = KERN_PROC;
82         mib [2] = KERN_PROC_ALL;
83         mib [3] = 0;
84         
85         res = sysctl (mib, 4, processes, &data_len, NULL, 0);
86 #endif /* KERN_PROC2 */
87
88         if (res < 0) {
89                 free (processes);
90                 return NULL;
91         }
92 #ifdef KERN_PROC2
93         res = data_len/sizeof (struct kinfo_proc2);
94 #else
95         res = data_len/sizeof (struct kinfo_proc);
96 #endif /* KERN_PROC2 */
97         buf = g_realloc (buf, res * sizeof (void*));
98         for (i = 0; i < res; ++i)
99                 buf [i] = GINT_TO_POINTER (processes [i].kinfo_pid_member);
100         free (processes);
101         if (size)
102                 *size = res;
103         return buf;
104 #else
105         const char *name;
106         void **buf = NULL;
107         int count = 0;
108         int i = 0;
109         GDir *dir = g_dir_open ("/proc/", 0, NULL);
110         if (!dir) {
111                 if (size)
112                         *size = 0;
113                 return NULL;
114         }
115         while ((name = g_dir_read_name (dir))) {
116                 int pid;
117                 char *nend;
118                 pid = strtol (name, &nend, 10);
119                 if (pid <= 0 || nend == name || *nend)
120                         continue;
121                 if (i >= count) {
122                         if (!count)
123                                 count = 16;
124                         else
125                                 count *= 2;
126                         buf = g_realloc (buf, count * sizeof (void*));
127                 }
128                 buf [i++] = GINT_TO_POINTER (pid);
129         }
130         g_dir_close (dir);
131         if (size)
132                 *size = i;
133         return buf;
134 #endif
135 }
136
137 static char*
138 get_pid_status_item_buf (int pid, const char *item, char *rbuf, int blen, MonoProcessError *error)
139 {
140         char buf [256];
141         char *s;
142         FILE *f;
143         int len = strlen (item);
144
145         g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
146         f = fopen (buf, "r");
147         if (!f) {
148                 if (error)
149                         *error = MONO_PROCESS_ERROR_NOT_FOUND;
150                 return NULL;
151         }
152         while ((s = fgets (buf, blen, f))) {
153                 if (*item != *buf)
154                         continue;
155                 if (strncmp (buf, item, len))
156                         continue;
157                 s = buf + len;
158                 while (g_ascii_isspace (*s)) s++;
159                 if (*s++ != ':')
160                         continue;
161                 while (g_ascii_isspace (*s)) s++;
162                 fclose (f);
163                 len = strlen (s);
164                 strncpy (rbuf, s, MIN (len, blen));
165                 rbuf [MIN (len, blen) - 1] = 0;
166                 if (error)
167                         *error = MONO_PROCESS_ERROR_NONE;
168                 return rbuf;
169         }
170         fclose (f);
171         if (error)
172                 *error = MONO_PROCESS_ERROR_OTHER;
173         return NULL;
174 }
175
176 /**
177  * mono_process_get_name:
178  * @pid: pid of the process
179  * @buf: byte buffer where to store the name of the prcoess
180  * @len: size of the buffer @buf
181  *
182  * Return the name of the process identified by @pid, storing it
183  * inside @buf for a maximum of len bytes (including the terminating 0).
184  */
185 char*
186 mono_process_get_name (gpointer pid, char *buf, int len)
187 {
188 #if USE_SYSCTL
189         int res;
190 #ifdef KERN_PROC2
191         int mib [6];
192         size_t data_len = sizeof (struct kinfo_proc2);
193         struct kinfo_proc2 processi;
194 #else
195         int mib [4];
196         size_t data_len = sizeof (struct kinfo_proc);
197         struct kinfo_proc processi;
198 #endif /* KERN_PROC2 */
199
200         memset (buf, 0, len);
201
202 #ifdef KERN_PROC2
203         mib [0] = CTL_KERN;
204         mib [1] = KERN_PROC2;
205         mib [2] = KERN_PROC_PID;
206         mib [3] = GPOINTER_TO_UINT (pid);
207         mib [4] = sizeof(struct kinfo_proc2);
208         mib [5] = 400; /* XXX */
209
210         res = sysctl (mib, 6, &processi, &data_len, NULL, 0);
211
212         if (res < 0 || data_len != sizeof (struct kinfo_proc2)) {
213                 return buf;
214         }
215 #else
216         mib [0] = CTL_KERN;
217         mib [1] = KERN_PROC;
218         mib [2] = KERN_PROC_PID;
219         mib [3] = GPOINTER_TO_UINT (pid);
220         
221         res = sysctl (mib, 4, &processi, &data_len, NULL, 0);
222         if (res < 0 || data_len != sizeof (struct kinfo_proc)) {
223                 return buf;
224         }
225 #endif /* KERN_PROC2 */
226         strncpy (buf, processi.kinfo_name_member, len - 1);
227         return buf;
228 #else
229         char fname [128];
230         FILE *file;
231         char *p;
232         int r;
233         sprintf (fname, "/proc/%d/cmdline", GPOINTER_TO_INT (pid));
234         buf [0] = 0;
235         file = fopen (fname, "r");
236         if (!file)
237                 return buf;
238         r = fread (buf, 1, len - 1, file);
239         fclose (file);
240         buf [r] = 0;
241         p = strrchr (buf, '/');
242         if (p)
243                 return p + 1;
244         if (r == 0) {
245                 return get_pid_status_item_buf (GPOINTER_TO_INT (pid), "Name", buf, len, NULL);
246         }
247         return buf;
248 #endif
249 }
250
251 /*
252  * /proc/pid/stat format:
253  * pid (cmdname) S 
254  *      [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
255  *      [10] utime stime cutime cstime prio nice threads 0 start_time vsize rss
256  *      [20] rss rsslim start_code end_code start_stack esp eip pending blocked sigign
257  *      [30] sigcatch wchan 0 0 exit_signal cpu rt_prio policy
258  */
259
260 #define RET_ERROR(err) do {     \
261                 if (error) *error = (err);      \
262                 return 0;                       \
263         } while (0)
264
265 static gint64
266 get_process_stat_item (int pid, int pos, int sum, MonoProcessError *error)
267 {
268 #if defined(__APPLE__) 
269         double process_user_time = 0, process_system_time = 0;//, process_percent = 0;
270         task_t task;
271
272         if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS)
273                 RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
274         
275         struct task_basic_info t_info;
276         mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT, th_count;
277
278         if (task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count) != KERN_SUCCESS) {
279                 mach_port_deallocate (mach_task_self (), task);
280                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
281         }
282         
283         thread_array_t th_array;
284
285         if (task_threads(task, &th_array, &th_count) != KERN_SUCCESS) {
286                 mach_port_deallocate (mach_task_self (), task);
287                 RET_ERROR (MONO_PROCESS_ERROR_OTHER);
288         }
289                 
290         size_t i;
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         if (task_for_pid (mach_task_self (), pid, &task) != KERN_SUCCESS)
408                 RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
409
410         struct task_basic_info t_info;
411         mach_msg_type_number_t th_count = TASK_BASIC_INFO_COUNT;
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
425         mach_port_deallocate (mach_task_self (), task);
426         
427         return ret;
428 #else
429         char buf [64];
430         char *s;
431
432         s = get_pid_status_item_buf (pid, item, buf, sizeof (buf), error);
433         if (s)
434                 return atoi (s) * multiplier;
435         return 0;
436 #endif
437 }
438
439 /**
440  * mono_process_get_data:
441  * @pid: pid of the process
442  * @data: description of data to return
443  *
444  * Return a data item of a process like user time, memory use etc,
445  * according to the @data argumet.
446  */
447 gint64
448 mono_process_get_data_with_error (gpointer pid, MonoProcessData data, MonoProcessError *error)
449 {
450         gint64 val;
451         int rpid = GPOINTER_TO_INT (pid);
452
453         if (error)
454                 *error = MONO_PROCESS_ERROR_OTHER;
455
456         switch (data) {
457         case MONO_PROCESS_NUM_THREADS:
458                 return get_pid_status_item (rpid, "Threads", error, 1);
459         case MONO_PROCESS_USER_TIME:
460                 return get_process_stat_time (rpid, 10, FALSE, error);
461         case MONO_PROCESS_SYSTEM_TIME:
462                 return get_process_stat_time (rpid, 11, FALSE, error);
463         case MONO_PROCESS_TOTAL_TIME:
464                 return get_process_stat_time (rpid, 10, TRUE, error);
465         case MONO_PROCESS_WORKING_SET:
466                 return get_pid_status_item (rpid, "VmRSS", error, 1024);
467         case MONO_PROCESS_WORKING_SET_PEAK:
468                 val = get_pid_status_item (rpid, "VmHWM", error, 1024);
469                 if (val == 0)
470                         val = get_pid_status_item (rpid, "VmRSS", error, 1024);
471                 return val;
472         case MONO_PROCESS_PRIVATE_BYTES:
473                 return get_pid_status_item (rpid, "VmData", error, 1024);
474         case MONO_PROCESS_VIRTUAL_BYTES:
475                 return get_pid_status_item (rpid, "VmSize", error, 1024);
476         case MONO_PROCESS_VIRTUAL_BYTES_PEAK:
477                 val = get_pid_status_item (rpid, "VmPeak", error, 1024);
478                 if (val == 0)
479                         val = get_pid_status_item (rpid, "VmSize", error, 1024);
480                 return val;
481         case MONO_PROCESS_FAULTS:
482                 return get_process_stat_item (rpid, 6, TRUE, error);
483         case MONO_PROCESS_ELAPSED:
484                 return get_process_stat_item (rpid, 18, FALSE, error) / get_user_hz ();
485         case MONO_PROCESS_PPID:
486                 return get_process_stat_time (rpid, 0, FALSE, error);
487
488                 /* Nothing yet */
489         case MONO_PROCESS_END:
490                 return 0;
491         }
492         return 0;
493 }
494
495 gint64
496 mono_process_get_data (gpointer pid, MonoProcessData data)
497 {
498         MonoProcessError error;
499         return mono_process_get_data_with_error (pid, data, &error);
500 }
501
502 /**
503  * mono_cpu_count:
504  *
505  * Return the number of processors on the system.
506  */
507 int
508 mono_cpu_count (void)
509 {
510         int count;
511 #ifdef _SC_NPROCESSORS_ONLN
512         count = sysconf (_SC_NPROCESSORS_ONLN);
513         if (count > 0)
514                 return count;
515 #endif
516 #ifdef USE_SYSCTL
517         {
518                 int mib [2];
519                 size_t len = sizeof (int);
520                 mib [0] = CTL_HW;
521                 mib [1] = HW_NCPU;
522                 if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
523                         return count;
524         }
525 #endif
526 #ifdef HOST_WIN32
527         {
528                 SYSTEM_INFO info;
529                 GetSystemInfo (&info);
530                 return info.dwNumberOfProcessors;
531         }
532 #endif
533         /* FIXME: warn */
534         return 1;
535 }
536
537 static void
538 get_cpu_times (int cpu_id, gint64 *user, gint64 *systemt, gint64 *irq, gint64 *sirq, gint64 *idle)
539 {
540         char buf [256];
541         char *s;
542         int hz = get_user_hz ();
543         long long unsigned int user_ticks, nice_ticks, system_ticks, idle_ticks, iowait_ticks, irq_ticks, sirq_ticks;
544         FILE *f = fopen ("/proc/stat", "r");
545         if (!f)
546                 return;
547         if (cpu_id < 0)
548                 hz *= mono_cpu_count ();
549         while ((s = fgets (buf, sizeof (buf), f))) {
550                 char *data = NULL;
551                 if (cpu_id < 0 && strncmp (s, "cpu", 3) == 0 && g_ascii_isspace (s [3])) {
552                         data = s + 4;
553                 } else if (cpu_id >= 0 && strncmp (s, "cpu", 3) == 0 && strtol (s + 3, &data, 10) == cpu_id) {
554                         if (data == s + 3)
555                                 continue;
556                         data++;
557                 } else {
558                         continue;
559                 }
560                 sscanf (data, "%Lu %Lu %Lu %Lu %Lu %Lu %Lu", &user_ticks, &nice_ticks, &system_ticks, &idle_ticks, &iowait_ticks, &irq_ticks, &sirq_ticks);
561         }
562         fclose (f);
563
564         if (user)
565                 *user = (user_ticks + nice_ticks) * 10000000 / hz;
566         if (systemt)
567                 *systemt = (system_ticks) * 10000000 / hz;
568         if (irq)
569                 *irq = (irq_ticks) * 10000000 / hz;
570         if (sirq)
571                 *sirq = (sirq_ticks) * 10000000 / hz;
572         if (idle)
573                 *idle = (idle_ticks) * 10000000 / hz;
574 }
575
576 /**
577  * mono_cpu_get_data:
578  * @cpu_id: processor number or -1 to get a summary of all the processors
579  * @data: type of data to retrieve
580  *
581  * Get data about a processor on the system, like time spent in user space or idle time.
582  */
583 gint64
584 mono_cpu_get_data (int cpu_id, MonoCpuData data, MonoProcessError *error)
585 {
586         gint64 value = 0;
587
588         if (error)
589                 *error = MONO_PROCESS_ERROR_NONE;
590         switch (data) {
591         case MONO_CPU_USER_TIME:
592                 get_cpu_times (cpu_id, &value, NULL, NULL, NULL, NULL);
593                 break;
594         case MONO_CPU_PRIV_TIME:
595                 get_cpu_times (cpu_id, NULL, &value, NULL, NULL, NULL);
596                 break;
597         case MONO_CPU_INTR_TIME:
598                 get_cpu_times (cpu_id, NULL, NULL, &value, NULL, NULL);
599                 break;
600         case MONO_CPU_DCP_TIME:
601                 get_cpu_times (cpu_id, NULL, NULL, NULL, &value, NULL);
602                 break;
603         case MONO_CPU_IDLE_TIME:
604                 get_cpu_times (cpu_id, NULL, NULL, NULL, NULL, &value);
605                 break;
606
607         case MONO_CPU_END:
608                 /* Nothing yet */
609                 return 0;
610         }
611         return value;
612 }
613