mono-proclib.c 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150
  1. /**
  2. * \file
  3. * Copyright 2008-2011 Novell Inc
  4. * Copyright 2011 Xamarin Inc
  5. * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  6. */
  7. #include "config.h"
  8. #include "utils/mono-proclib.h"
  9. #include "utils/mono-time.h"
  10. #include "utils/mono-errno.h"
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <fcntl.h>
  15. #ifdef HAVE_UNISTD_H
  16. #include <unistd.h>
  17. #endif
  18. #ifdef HAVE_SCHED_GETAFFINITY
  19. #include <sched.h>
  20. #endif
  21. #include <utils/mono-mmap.h>
  22. #include <utils/strenc-internals.h>
  23. #include <utils/strenc.h>
  24. #include <utils/mono-error-internals.h>
  25. #include <utils/mono-io-portability.h>
  26. #include <utils/mono-logger-internals.h>
  27. #if defined(_POSIX_VERSION)
  28. #ifdef HAVE_SYS_ERRNO_H
  29. #include <sys/errno.h>
  30. #endif
  31. #ifdef HAVE_SYS_PARAM_H
  32. #include <sys/param.h>
  33. #endif
  34. #include <errno.h>
  35. #ifdef HAVE_SYS_TYPES_H
  36. #include <sys/types.h>
  37. #endif
  38. #ifdef HAVE_SYS_SYSCTL_H
  39. #include <sys/sysctl.h>
  40. #endif
  41. #ifdef HAVE_SYS_RESOURCE_H
  42. #include <sys/resource.h>
  43. #endif
  44. #endif
  45. #if defined(__HAIKU__)
  46. #include <os/kernel/OS.h>
  47. #endif
  48. #if defined(_AIX)
  49. #include <procinfo.h>
  50. #endif
  51. #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
  52. #include <sys/proc.h>
  53. #if defined(__APPLE__)
  54. #include <mach/mach.h>
  55. #endif
  56. #ifdef HAVE_SYS_USER_H
  57. #include <sys/user.h>
  58. #endif
  59. #ifdef HAVE_STRUCT_KINFO_PROC_KP_PROC
  60. # define kinfo_starttime_member kp_proc.p_starttime
  61. # define kinfo_pid_member kp_proc.p_pid
  62. # define kinfo_name_member kp_proc.p_comm
  63. #elif defined(__NetBSD__)
  64. # define kinfo_starttime_member p_ustart_sec
  65. # define kinfo_pid_member p_pid
  66. # define kinfo_name_member p_comm
  67. #elif defined(__OpenBSD__)
  68. // Can not figure out how to get the proc's start time on OpenBSD
  69. # undef kinfo_starttime_member
  70. # define kinfo_pid_member p_pid
  71. # define kinfo_name_member p_comm
  72. #else
  73. #define kinfo_starttime_member ki_start
  74. #define kinfo_pid_member ki_pid
  75. #define kinfo_name_member ki_comm
  76. #endif
  77. #define USE_SYSCTL 1
  78. #endif
  79. #ifdef HAVE_SCHED_GETAFFINITY
  80. # ifndef GLIBC_HAS_CPU_COUNT
  81. static int
  82. CPU_COUNT(cpu_set_t *set)
  83. {
  84. int i, count = 0;
  85. for (int i = 0; i < CPU_SETSIZE; i++)
  86. if (CPU_ISSET(i, set))
  87. count++;
  88. return count;
  89. }
  90. # endif
  91. #endif
  92. /**
  93. * mono_process_list:
  94. * \param size a pointer to a location where the size of the returned array is stored
  95. * \returns an array of pid values for the processes currently running on the system.
  96. * The size of the array is stored in \p size.
  97. */
  98. gpointer*
  99. mono_process_list (int *size)
  100. {
  101. #if USE_SYSCTL
  102. int res, i;
  103. #ifdef KERN_PROC2
  104. int mib [6];
  105. size_t data_len = sizeof (struct kinfo_proc2) * 400;
  106. struct kinfo_proc2 *processes = g_malloc (data_len);
  107. #else
  108. int mib [4];
  109. size_t data_len = sizeof (struct kinfo_proc) * 16;
  110. struct kinfo_proc *processes;
  111. int limit = 8;
  112. #endif /* KERN_PROC2 */
  113. void **buf = NULL;
  114. if (size)
  115. *size = 0;
  116. #ifdef KERN_PROC2
  117. if (!processes)
  118. return NULL;
  119. mib [0] = CTL_KERN;
  120. mib [1] = KERN_PROC2;
  121. mib [2] = KERN_PROC_ALL;
  122. mib [3] = 0;
  123. mib [4] = sizeof(struct kinfo_proc2);
  124. mib [5] = 400; /* XXX */
  125. res = sysctl (mib, 6, processes, &data_len, NULL, 0);
  126. if (res < 0) {
  127. g_free (processes);
  128. return NULL;
  129. }
  130. #else
  131. processes = NULL;
  132. while (limit) {
  133. mib [0] = CTL_KERN;
  134. mib [1] = KERN_PROC;
  135. mib [2] = KERN_PROC_ALL;
  136. mib [3] = 0;
  137. res = sysctl (mib, 3, NULL, &data_len, NULL, 0);
  138. if (res)
  139. return NULL;
  140. processes = (struct kinfo_proc *) g_malloc (data_len);
  141. res = sysctl (mib, 3, processes, &data_len, NULL, 0);
  142. if (res < 0) {
  143. g_free (processes);
  144. if (errno != ENOMEM)
  145. return NULL;
  146. limit --;
  147. } else {
  148. break;
  149. }
  150. }
  151. #endif /* KERN_PROC2 */
  152. #ifdef KERN_PROC2
  153. res = data_len/sizeof (struct kinfo_proc2);
  154. #else
  155. res = data_len/sizeof (struct kinfo_proc);
  156. #endif /* KERN_PROC2 */
  157. buf = (void **) g_realloc (buf, res * sizeof (void*));
  158. for (i = 0; i < res; ++i)
  159. buf [i] = GINT_TO_POINTER (processes [i].kinfo_pid_member);
  160. g_free (processes);
  161. if (size)
  162. *size = res;
  163. return buf;
  164. #elif defined(__HAIKU__)
  165. int32 cookie = 0;
  166. int32 i = 0;
  167. team_info ti;
  168. system_info si;
  169. get_system_info(&si);
  170. void **buf = g_calloc(si.used_teams, sizeof(void*));
  171. while (get_next_team_info(&cookie, &ti) == B_OK && i < si.used_teams) {
  172. buf[i++] = GINT_TO_POINTER (ti.team);
  173. }
  174. *size = i;
  175. return buf;
  176. #elif defined(_AIX)
  177. void **buf = NULL;
  178. struct procentry64 *procs = NULL;
  179. int count = 0;
  180. int i = 0;
  181. pid_t pid = 1; // start at 1, 0 is a null process (???)
  182. // count number of procs + compensate for new ones forked in while we do it.
  183. // (it's not an atomic operation) 1000000 is the limit IBM ps seems to use
  184. // when I inspected it under truss. the second call we do to getprocs64 will
  185. // then only allocate what we need, instead of allocating some obscenely large
  186. // array on the heap.
  187. count = getprocs64(NULL, sizeof (struct procentry64), NULL, 0, &pid, 1000000);
  188. if (count < 1)
  189. goto cleanup;
  190. count += 10;
  191. pid = 1; // reset the pid cookie
  192. // 5026 bytes is the ideal size for the C struct. you may not like it, but
  193. // this is what peak allocation looks like
  194. procs = g_calloc (count, sizeof (struct procentry64));
  195. // the man page recommends you do this in a loop, but you can also just do it
  196. // in one shot; again, like what ps does. let the returned count (in case it's
  197. // less) be what we then allocate the array of pids from (in case of ANOTHER
  198. // system-wide race condition with processes)
  199. count = getprocs64 (procs, sizeof (struct procentry64), NULL, 0, &pid, count);
  200. if (count < 1 || procs == NULL)
  201. goto cleanup;
  202. buf = g_calloc (count, sizeof (void*));
  203. for (i = 0; i < count; i++) {
  204. buf[i] = GINT_TO_POINTER (procs[i].pi_pid);
  205. }
  206. *size = i;
  207. cleanup:
  208. g_free (procs);
  209. return buf;
  210. #else
  211. const char *name;
  212. void **buf = NULL;
  213. int count = 0;
  214. int i = 0;
  215. GDir *dir = g_dir_open ("/proc/", 0, NULL);
  216. if (!dir) {
  217. if (size)
  218. *size = 0;
  219. return NULL;
  220. }
  221. while ((name = g_dir_read_name (dir))) {
  222. int pid;
  223. char *nend;
  224. pid = strtol (name, &nend, 10);
  225. if (pid <= 0 || nend == name || *nend)
  226. continue;
  227. if (i >= count) {
  228. if (!count)
  229. count = 16;
  230. else
  231. count *= 2;
  232. buf = (void **)g_realloc (buf, count * sizeof (void*));
  233. }
  234. buf [i++] = GINT_TO_POINTER (pid);
  235. }
  236. g_dir_close (dir);
  237. if (size)
  238. *size = i;
  239. return buf;
  240. #endif
  241. }
  242. static G_GNUC_UNUSED char*
  243. get_pid_status_item_buf (int pid, const char *item, char *rbuf, int blen, MonoProcessError *error)
  244. {
  245. char buf [256];
  246. char *s;
  247. FILE *f;
  248. size_t len = strlen (item);
  249. g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
  250. f = fopen (buf, "r");
  251. if (!f) {
  252. if (error)
  253. *error = MONO_PROCESS_ERROR_NOT_FOUND;
  254. return NULL;
  255. }
  256. while ((s = fgets (buf, sizeof (buf), f))) {
  257. if (*item != *buf)
  258. continue;
  259. if (strncmp (buf, item, len))
  260. continue;
  261. s = buf + len;
  262. while (g_ascii_isspace (*s)) s++;
  263. if (*s++ != ':')
  264. continue;
  265. while (g_ascii_isspace (*s)) s++;
  266. fclose (f);
  267. len = strlen (s);
  268. memcpy (rbuf, s, MIN (len, blen));
  269. rbuf [MIN (len, blen) - 1] = 0;
  270. if (error)
  271. *error = MONO_PROCESS_ERROR_NONE;
  272. return rbuf;
  273. }
  274. fclose (f);
  275. if (error)
  276. *error = MONO_PROCESS_ERROR_OTHER;
  277. return NULL;
  278. }
  279. #if USE_SYSCTL
  280. #ifdef KERN_PROC2
  281. #define KINFO_PROC struct kinfo_proc2
  282. #else
  283. #define KINFO_PROC struct kinfo_proc
  284. #endif
  285. static gboolean
  286. sysctl_kinfo_proc (gpointer pid, KINFO_PROC* processi)
  287. {
  288. int res;
  289. size_t data_len = sizeof (KINFO_PROC);
  290. #ifdef KERN_PROC2
  291. int mib [6];
  292. mib [0] = CTL_KERN;
  293. mib [1] = KERN_PROC2;
  294. mib [2] = KERN_PROC_PID;
  295. mib [3] = GPOINTER_TO_UINT (pid);
  296. mib [4] = sizeof(KINFO_PROC);
  297. mib [5] = 400; /* XXX */
  298. res = sysctl (mib, 6, processi, &data_len, NULL, 0);
  299. #else
  300. int mib [4];
  301. mib [0] = CTL_KERN;
  302. mib [1] = KERN_PROC;
  303. mib [2] = KERN_PROC_PID;
  304. mib [3] = GPOINTER_TO_UINT (pid);
  305. res = sysctl (mib, 4, processi, &data_len, NULL, 0);
  306. #endif /* KERN_PROC2 */
  307. if (res < 0 || data_len != sizeof (KINFO_PROC))
  308. return FALSE;
  309. return TRUE;
  310. }
  311. #endif /* USE_SYSCTL */
  312. /**
  313. * mono_process_get_name:
  314. * \param pid pid of the process
  315. * \param buf byte buffer where to store the name of the prcoess
  316. * \param len size of the buffer \p buf
  317. * \returns the name of the process identified by \p pid, storing it
  318. * inside \p buf for a maximum of len bytes (including the terminating 0).
  319. */
  320. char*
  321. mono_process_get_name (gpointer pid, char *buf, int len)
  322. {
  323. #if USE_SYSCTL
  324. KINFO_PROC processi;
  325. memset (buf, 0, len);
  326. if (sysctl_kinfo_proc (pid, &processi))
  327. memcpy (buf, processi.kinfo_name_member, len - 1);
  328. return buf;
  329. #elif defined(_AIX)
  330. struct procentry64 proc;
  331. pid_t newpid = GPOINTER_TO_INT (pid);
  332. if (getprocs64 (&proc, sizeof (struct procentry64), NULL, 0, &newpid, 1) == 1) {
  333. g_strlcpy (buf, proc.pi_comm, len - 1);
  334. }
  335. return buf;
  336. #else
  337. char fname [128];
  338. FILE *file;
  339. char *p;
  340. size_t r;
  341. sprintf (fname, "/proc/%d/cmdline", GPOINTER_TO_INT (pid));
  342. buf [0] = 0;
  343. file = fopen (fname, "r");
  344. if (!file)
  345. return buf;
  346. r = fread (buf, 1, len - 1, file);
  347. fclose (file);
  348. buf [r] = 0;
  349. p = strrchr (buf, '/');
  350. if (p)
  351. return p + 1;
  352. if (r == 0) {
  353. return get_pid_status_item_buf (GPOINTER_TO_INT (pid), "Name", buf, len, NULL);
  354. }
  355. return buf;
  356. #endif
  357. }
  358. void
  359. mono_process_get_times (gpointer pid, gint64 *start_time, gint64 *user_time, gint64 *kernel_time)
  360. {
  361. if (user_time)
  362. *user_time = mono_process_get_data (pid, MONO_PROCESS_USER_TIME);
  363. if (kernel_time)
  364. *kernel_time = mono_process_get_data (pid, MONO_PROCESS_SYSTEM_TIME);
  365. if (start_time) {
  366. *start_time = 0;
  367. #if USE_SYSCTL && defined(kinfo_starttime_member)
  368. {
  369. KINFO_PROC processi;
  370. if (sysctl_kinfo_proc (pid, &processi)) {
  371. #if defined(__NetBSD__)
  372. struct timeval tv;
  373. tv.tv_sec = processi.kinfo_starttime_member;
  374. tv.tv_usec = processi.p_ustart_usec;
  375. *start_time = mono_100ns_datetime_from_timeval(tv);
  376. #else
  377. *start_time = mono_100ns_datetime_from_timeval (processi.kinfo_starttime_member);
  378. #endif
  379. }
  380. }
  381. #endif
  382. if (*start_time == 0) {
  383. static guint64 boot_time = 0;
  384. if (!boot_time)
  385. boot_time = mono_100ns_datetime () - mono_msec_boottime () * 10000;
  386. *start_time = boot_time + mono_process_get_data (pid, MONO_PROCESS_ELAPSED);
  387. }
  388. }
  389. }
  390. /*
  391. * /proc/pid/stat format:
  392. * pid (cmdname) S
  393. * [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
  394. * [10] utime stime cutime cstime prio nice threads 0 start_time vsize
  395. * [20] rss rsslim start_code end_code start_stack esp eip pending blocked sigign
  396. * [30] sigcatch wchan 0 0 exit_signal cpu rt_prio policy
  397. */
  398. #define RET_ERROR(err) do { \
  399. if (error) *error = (err); \
  400. return 0; \
  401. } while (0)
  402. static gint64
  403. get_process_stat_item (int pid, int pos, int sum, MonoProcessError *error)
  404. {
  405. #if defined(__APPLE__)
  406. double process_user_time = 0, process_system_time = 0;//, process_percent = 0;
  407. task_t task;
  408. struct task_basic_info t_info;
  409. mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT, th_count;
  410. thread_array_t th_array;
  411. size_t i;
  412. kern_return_t ret;
  413. if (pid == getpid ()) {
  414. /* task_for_pid () doesn't work on ios, even for the current process */
  415. task = mach_task_self ();
  416. } else {
  417. do {
  418. ret = task_for_pid (mach_task_self (), pid, &task);
  419. } while (ret == KERN_ABORTED);
  420. if (ret != KERN_SUCCESS)
  421. RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
  422. }
  423. do {
  424. ret = task_info (task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
  425. } while (ret == KERN_ABORTED);
  426. if (ret != KERN_SUCCESS) {
  427. if (pid != getpid ())
  428. mach_port_deallocate (mach_task_self (), task);
  429. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  430. }
  431. do {
  432. ret = task_threads (task, &th_array, &th_count);
  433. } while (ret == KERN_ABORTED);
  434. if (ret != KERN_SUCCESS) {
  435. if (pid != getpid ())
  436. mach_port_deallocate (mach_task_self (), task);
  437. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  438. }
  439. for (i = 0; i < th_count; i++) {
  440. double thread_user_time, thread_system_time;//, thread_percent;
  441. struct thread_basic_info th_info;
  442. mach_msg_type_number_t th_info_count = THREAD_BASIC_INFO_COUNT;
  443. do {
  444. ret = thread_info(th_array[i], THREAD_BASIC_INFO, (thread_info_t)&th_info, &th_info_count);
  445. } while (ret == KERN_ABORTED);
  446. if (ret == KERN_SUCCESS) {
  447. thread_user_time = th_info.user_time.seconds + th_info.user_time.microseconds / 1e6;
  448. thread_system_time = th_info.system_time.seconds + th_info.system_time.microseconds / 1e6;
  449. //thread_percent = (double)th_info.cpu_usage / TH_USAGE_SCALE;
  450. process_user_time += thread_user_time;
  451. process_system_time += thread_system_time;
  452. //process_percent += th_percent;
  453. }
  454. }
  455. for (i = 0; i < th_count; i++)
  456. mach_port_deallocate(task, th_array[i]);
  457. if (pid != getpid ())
  458. mach_port_deallocate (mach_task_self (), task);
  459. process_user_time += t_info.user_time.seconds + t_info.user_time.microseconds / 1e6;
  460. process_system_time += t_info.system_time.seconds + t_info.system_time.microseconds / 1e6;
  461. if (pos == 10 && sum == TRUE)
  462. return (gint64)((process_user_time + process_system_time) * 10000000);
  463. else if (pos == 10)
  464. return (gint64)(process_user_time * 10000000);
  465. else if (pos == 11)
  466. return (gint64)(process_system_time * 10000000);
  467. return 0;
  468. #else
  469. char buf [512];
  470. char *s, *end;
  471. FILE *f;
  472. size_t len;
  473. int i;
  474. gint64 value;
  475. g_snprintf (buf, sizeof (buf), "/proc/%d/stat", pid);
  476. f = fopen (buf, "r");
  477. if (!f)
  478. RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
  479. len = fread (buf, 1, sizeof (buf), f);
  480. fclose (f);
  481. if (len <= 0)
  482. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  483. s = strchr (buf, ')');
  484. if (!s)
  485. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  486. s++;
  487. while (g_ascii_isspace (*s)) s++;
  488. if (!*s)
  489. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  490. /* skip the status char */
  491. while (*s && !g_ascii_isspace (*s)) s++;
  492. if (!*s)
  493. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  494. for (i = 0; i < pos; ++i) {
  495. while (g_ascii_isspace (*s)) s++;
  496. if (!*s)
  497. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  498. while (*s && !g_ascii_isspace (*s)) s++;
  499. if (!*s)
  500. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  501. }
  502. /* we are finally at the needed item */
  503. value = strtoul (s, &end, 0);
  504. /* add also the following value */
  505. if (sum) {
  506. while (g_ascii_isspace (*s)) s++;
  507. if (!*s)
  508. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  509. value += strtoul (s, &end, 0);
  510. }
  511. if (error)
  512. *error = MONO_PROCESS_ERROR_NONE;
  513. return value;
  514. #endif
  515. }
  516. static int
  517. get_user_hz (void)
  518. {
  519. static int user_hz = 0;
  520. if (user_hz == 0) {
  521. #if defined (_SC_CLK_TCK) && defined (HAVE_SYSCONF)
  522. user_hz = sysconf (_SC_CLK_TCK);
  523. #endif
  524. if (user_hz == 0)
  525. user_hz = 100;
  526. }
  527. return user_hz;
  528. }
  529. static gint64
  530. get_process_stat_time (int pid, int pos, int sum, MonoProcessError *error)
  531. {
  532. gint64 val = get_process_stat_item (pid, pos, sum, error);
  533. #if defined(__APPLE__)
  534. return val;
  535. #else
  536. /* return 100ns ticks */
  537. return (val * 10000000) / get_user_hz ();
  538. #endif
  539. }
  540. static gint64
  541. get_pid_status_item (int pid, const char *item, MonoProcessError *error, int multiplier)
  542. {
  543. #if defined(__APPLE__)
  544. // ignore the multiplier
  545. gint64 ret;
  546. task_t task;
  547. task_vm_info_data_t t_info;
  548. mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT;
  549. kern_return_t mach_ret;
  550. if (pid == getpid ()) {
  551. /* task_for_pid () doesn't work on ios, even for the current process */
  552. task = mach_task_self ();
  553. } else {
  554. do {
  555. mach_ret = task_for_pid (mach_task_self (), pid, &task);
  556. } while (mach_ret == KERN_ABORTED);
  557. if (mach_ret != KERN_SUCCESS)
  558. RET_ERROR (MONO_PROCESS_ERROR_NOT_FOUND);
  559. }
  560. do {
  561. mach_ret = task_info (task, TASK_VM_INFO, (task_info_t)&t_info, &info_count);
  562. } while (mach_ret == KERN_ABORTED);
  563. if (mach_ret != KERN_SUCCESS) {
  564. if (pid != getpid ())
  565. mach_port_deallocate (mach_task_self (), task);
  566. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  567. }
  568. if(strcmp (item, "VmData") == 0)
  569. ret = t_info.internal + t_info.compressed;
  570. else if (strcmp (item, "VmRSS") == 0)
  571. ret = t_info.resident_size;
  572. else if(strcmp (item, "VmHWM") == 0)
  573. ret = t_info.resident_size_peak;
  574. else if (strcmp (item, "VmSize") == 0 || strcmp (item, "VmPeak") == 0)
  575. ret = t_info.virtual_size;
  576. else if (strcmp (item, "Threads") == 0) {
  577. struct task_basic_info t_info;
  578. mach_msg_type_number_t th_count = TASK_BASIC_INFO_COUNT;
  579. do {
  580. mach_ret = task_info (task, TASK_BASIC_INFO, (task_info_t)&t_info, &th_count);
  581. } while (mach_ret == KERN_ABORTED);
  582. if (mach_ret != KERN_SUCCESS) {
  583. if (pid != getpid ())
  584. mach_port_deallocate (mach_task_self (), task);
  585. RET_ERROR (MONO_PROCESS_ERROR_OTHER);
  586. }
  587. ret = th_count;
  588. } else if (strcmp (item, "VmSwap") == 0)
  589. ret = t_info.compressed;
  590. else
  591. ret = 0;
  592. if (pid != getpid ())
  593. mach_port_deallocate (mach_task_self (), task);
  594. return ret;
  595. #else
  596. char buf [64];
  597. char *s;
  598. s = get_pid_status_item_buf (pid, item, buf, sizeof (buf), error);
  599. if (s)
  600. return ((gint64) atol (s)) * multiplier;
  601. return 0;
  602. #endif
  603. }
  604. /**
  605. * mono_process_get_data:
  606. * \param pid pid of the process
  607. * \param data description of data to return
  608. * \returns a data item of a process like user time, memory use etc,
  609. * according to the \p data argumet.
  610. */
  611. gint64
  612. mono_process_get_data_with_error (gpointer pid, MonoProcessData data, MonoProcessError *error)
  613. {
  614. gint64 val;
  615. int rpid = GPOINTER_TO_INT (pid);
  616. if (error)
  617. *error = MONO_PROCESS_ERROR_OTHER;
  618. switch (data) {
  619. case MONO_PROCESS_NUM_THREADS:
  620. return get_pid_status_item (rpid, "Threads", error, 1);
  621. case MONO_PROCESS_USER_TIME:
  622. return get_process_stat_time (rpid, 10, FALSE, error);
  623. case MONO_PROCESS_SYSTEM_TIME:
  624. return get_process_stat_time (rpid, 11, FALSE, error);
  625. case MONO_PROCESS_TOTAL_TIME:
  626. return get_process_stat_time (rpid, 10, TRUE, error);
  627. case MONO_PROCESS_WORKING_SET:
  628. return get_pid_status_item (rpid, "VmRSS", error, 1024);
  629. case MONO_PROCESS_WORKING_SET_PEAK:
  630. val = get_pid_status_item (rpid, "VmHWM", error, 1024);
  631. if (val == 0)
  632. val = get_pid_status_item (rpid, "VmRSS", error, 1024);
  633. return val;
  634. case MONO_PROCESS_PRIVATE_BYTES:
  635. return get_pid_status_item (rpid, "VmData", error, 1024);
  636. case MONO_PROCESS_VIRTUAL_BYTES:
  637. return get_pid_status_item (rpid, "VmSize", error, 1024);
  638. case MONO_PROCESS_VIRTUAL_BYTES_PEAK:
  639. val = get_pid_status_item (rpid, "VmPeak", error, 1024);
  640. if (val == 0)
  641. val = get_pid_status_item (rpid, "VmSize", error, 1024);
  642. return val;
  643. case MONO_PROCESS_FAULTS:
  644. return get_process_stat_item (rpid, 6, TRUE, error);
  645. case MONO_PROCESS_ELAPSED:
  646. return get_process_stat_time (rpid, 18, FALSE, error);
  647. case MONO_PROCESS_PPID:
  648. return get_process_stat_time (rpid, 0, FALSE, error);
  649. case MONO_PROCESS_PAGED_BYTES:
  650. return get_pid_status_item (rpid, "VmSwap", error, 1024);
  651. /* Nothing yet */
  652. case MONO_PROCESS_END:
  653. return 0;
  654. }
  655. return 0;
  656. }
  657. gint64
  658. mono_process_get_data (gpointer pid, MonoProcessData data)
  659. {
  660. MonoProcessError error;
  661. return mono_process_get_data_with_error (pid, data, &error);
  662. }
  663. #ifndef HOST_WIN32
  664. int
  665. mono_process_current_pid ()
  666. {
  667. #if defined(HAVE_UNISTD_H)
  668. return (int) getpid ();
  669. #else
  670. #error getpid
  671. #endif
  672. }
  673. #endif /* !HOST_WIN32 */
  674. /**
  675. * mono_cpu_count:
  676. * \returns the number of processors on the system.
  677. */
  678. #ifndef HOST_WIN32
  679. int
  680. mono_cpu_count (void)
  681. {
  682. #ifdef HOST_ANDROID
  683. /* Android tries really hard to save power by powering off CPUs on SMP phones which
  684. * means the normal way to query cpu count returns a wrong value with userspace API.
  685. * Instead we use /sys entries to query the actual hardware CPU count.
  686. */
  687. int count = 0;
  688. char buffer[8] = {'\0'};
  689. int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
  690. /* Format of the /sys entry is a cpulist of indexes which in the case
  691. * of present is always of the form "0-(n-1)" when there is more than
  692. * 1 core, n being the number of CPU cores in the system. Otherwise
  693. * the value is simply 0
  694. */
  695. if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
  696. count = strtol (((char*)buffer) + 2, NULL, 10);
  697. if (present != -1)
  698. close (present);
  699. if (count > 0)
  700. return count + 1;
  701. #endif
  702. #if defined(HOST_ARM) || defined (HOST_ARM64)
  703. /*
  704. * Recap from Alexander Köplinger <alex.koeplinger@outlook.com>:
  705. *
  706. * When we merged the change from PR #2722, we started seeing random failures on ARM in
  707. * the MonoTests.System.Threading.ThreadPoolTests.SetAndGetMaxThreads and
  708. * MonoTests.System.Threading.ManualResetEventSlimTests.Constructor_Defaults tests. Both
  709. * of those tests are dealing with Environment.ProcessorCount to verify some implementation
  710. * details.
  711. *
  712. * It turns out that on the Jetson TK1 board we use on public Jenkins and on ARM kernels
  713. * in general, the value returned by sched_getaffinity (or _SC_NPROCESSORS_ONLN) doesn't
  714. * contain CPUs/cores that are powered off for power saving reasons. This is contrary to
  715. * what happens on x86, where even cores in deep-sleep state are returned [1], [2]. This
  716. * means that we would get a processor count of 1 at one point in time and a higher value
  717. * when load increases later on as the system wakes CPUs.
  718. *
  719. * Various runtime pieces like the threadpool and also user code however relies on the
  720. * value returned by Environment.ProcessorCount e.g. for deciding how many parallel tasks
  721. * to start, thereby limiting the performance when that code thinks we only have one CPU.
  722. *
  723. * Talking to a few people, this was the reason why we changed to _SC_NPROCESSORS_CONF in
  724. * mono#1688 and why we added a special case for Android in mono@de3addc to get the "real"
  725. * number of processors in the system.
  726. *
  727. * Because of those issues Android/Dalvik also switched from _ONLN to _SC_NPROCESSORS_CONF
  728. * for the Java API Runtime.availableProcessors() too [3], citing:
  729. * > Traditionally this returned the number currently online, but many mobile devices are
  730. * able to take unused cores offline to save power, so releases newer than Android 4.2 (Jelly
  731. * Bean) return the maximum number of cores that could be made available if there were no
  732. * power or heat constraints.
  733. *
  734. * The problem with sticking to _SC_NPROCESSORS_CONF however is that it breaks down in
  735. * constrained environments like Docker or with an explicit CPU affinity set by the Linux
  736. * `taskset` command, They'd get a higher CPU count than can be used, start more threads etc.
  737. * which results in unnecessary context switches and overloaded systems. That's why we need
  738. * to respect sched_getaffinity.
  739. *
  740. * So while in an ideal world we would be able to rely on sched_getaffinity/_SC_NPROCESSORS_ONLN
  741. * to return the number of theoretically available CPUs regardless of power saving measures
  742. * everywhere, we can't do this on ARM.
  743. *
  744. * I think the pragmatic solution is the following:
  745. * * use sched_getaffinity (+ fallback to _SC_NPROCESSORS_ONLN in case of error) on x86. This
  746. * ensures we're inline with what OpenJDK [4] and CoreCLR [5] do
  747. * * use _SC_NPROCESSORS_CONF exclusively on ARM (I think we could eventually even get rid of
  748. * the HOST_ANDROID special case)
  749. *
  750. * Helpful links:
  751. *
  752. * [1] https://sourceware.org/ml/libc-alpha/2013-07/msg00383.html
  753. * [2] https://lists.01.org/pipermail/powertop/2012-September/000433.html
  754. * [3] https://android.googlesource.com/platform/libcore/+/750dc634e56c58d1d04f6a138734ac2b772900b5%5E1..750dc634e56c58d1d04f6a138734ac2b772900b5/
  755. * [4] https://bugs.openjdk.java.net/browse/JDK-6515172
  756. * [5] https://github.com/dotnet/coreclr/blob/7058273693db2555f127ce16e6b0c5b40fb04867/src/pal/src/misc/sysinfo.cpp#L148
  757. */
  758. #if defined (_SC_NPROCESSORS_CONF) && defined (HAVE_SYSCONF)
  759. {
  760. int count = sysconf (_SC_NPROCESSORS_CONF);
  761. if (count > 0)
  762. return count;
  763. }
  764. #endif
  765. #else
  766. #ifdef HAVE_SCHED_GETAFFINITY
  767. {
  768. cpu_set_t set;
  769. if (sched_getaffinity (mono_process_current_pid (), sizeof (set), &set) == 0)
  770. return CPU_COUNT (&set);
  771. }
  772. #endif
  773. #if defined (_SC_NPROCESSORS_ONLN) && defined (HAVE_SYSCONF)
  774. {
  775. int count = sysconf (_SC_NPROCESSORS_ONLN);
  776. if (count > 0)
  777. return count;
  778. }
  779. #endif
  780. #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
  781. #ifdef USE_SYSCTL
  782. {
  783. int count;
  784. int mib [2];
  785. size_t len = sizeof (int);
  786. mib [0] = CTL_HW;
  787. mib [1] = HW_NCPU;
  788. if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
  789. return count;
  790. }
  791. #endif
  792. /* FIXME: warn */
  793. return 1;
  794. }
  795. #endif /* !HOST_WIN32 */
  796. static void
  797. get_cpu_times (int cpu_id, gint64 *user, gint64 *systemt, gint64 *irq, gint64 *sirq, gint64 *idle)
  798. {
  799. char buf [256];
  800. char *s;
  801. int uhz = get_user_hz ();
  802. guint64 user_ticks = 0, nice_ticks = 0, system_ticks = 0, idle_ticks = 0, irq_ticks = 0, sirq_ticks = 0;
  803. FILE *f = fopen ("/proc/stat", "r");
  804. if (!f)
  805. return;
  806. if (cpu_id < 0)
  807. uhz *= mono_cpu_count ();
  808. while ((s = fgets (buf, sizeof (buf), f))) {
  809. char *data = NULL;
  810. if (cpu_id < 0 && strncmp (s, "cpu", 3) == 0 && g_ascii_isspace (s [3])) {
  811. data = s + 4;
  812. } else if (cpu_id >= 0 && strncmp (s, "cpu", 3) == 0 && strtol (s + 3, &data, 10) == cpu_id) {
  813. if (data == s + 3)
  814. continue;
  815. data++;
  816. } else {
  817. continue;
  818. }
  819. user_ticks = strtoull (data, &data, 10);
  820. nice_ticks = strtoull (data, &data, 10);
  821. system_ticks = strtoull (data, &data, 10);
  822. idle_ticks = strtoull (data, &data, 10);
  823. /* iowait_ticks = strtoull (data, &data, 10); */
  824. irq_ticks = strtoull (data, &data, 10);
  825. sirq_ticks = strtoull (data, &data, 10);
  826. break;
  827. }
  828. fclose (f);
  829. if (user)
  830. *user = (user_ticks + nice_ticks) * 10000000 / uhz;
  831. if (systemt)
  832. *systemt = (system_ticks) * 10000000 / uhz;
  833. if (irq)
  834. *irq = (irq_ticks) * 10000000 / uhz;
  835. if (sirq)
  836. *sirq = (sirq_ticks) * 10000000 / uhz;
  837. if (idle)
  838. *idle = (idle_ticks) * 10000000 / uhz;
  839. }
  840. /**
  841. * mono_cpu_get_data:
  842. * \param cpu_id processor number or -1 to get a summary of all the processors
  843. * \param data type of data to retrieve
  844. * Get data about a processor on the system, like time spent in user space or idle time.
  845. */
  846. gint64
  847. mono_cpu_get_data (int cpu_id, MonoCpuData data, MonoProcessError *error)
  848. {
  849. gint64 value = 0;
  850. if (error)
  851. *error = MONO_PROCESS_ERROR_NONE;
  852. switch (data) {
  853. case MONO_CPU_USER_TIME:
  854. get_cpu_times (cpu_id, &value, NULL, NULL, NULL, NULL);
  855. break;
  856. case MONO_CPU_PRIV_TIME:
  857. get_cpu_times (cpu_id, NULL, &value, NULL, NULL, NULL);
  858. break;
  859. case MONO_CPU_INTR_TIME:
  860. get_cpu_times (cpu_id, NULL, NULL, &value, NULL, NULL);
  861. break;
  862. case MONO_CPU_DCP_TIME:
  863. get_cpu_times (cpu_id, NULL, NULL, NULL, &value, NULL);
  864. break;
  865. case MONO_CPU_IDLE_TIME:
  866. get_cpu_times (cpu_id, NULL, NULL, NULL, NULL, &value);
  867. break;
  868. case MONO_CPU_END:
  869. /* Nothing yet */
  870. return 0;
  871. }
  872. return value;
  873. }
  874. int
  875. mono_atexit (void (*func)(void))
  876. {
  877. #if defined(HOST_ANDROID) || !defined(HAVE_ATEXIT)
  878. /* Some versions of android libc doesn't define atexit () */
  879. return 0;
  880. #else
  881. return atexit (func);
  882. #endif
  883. }
  884. #ifndef HOST_WIN32
  885. gboolean
  886. mono_pe_file_time_date_stamp (const gunichar2 *filename, guint32 *out)
  887. {
  888. void *map_handle;
  889. guint32 map_size;
  890. gpointer file_map = mono_pe_file_map (filename, &map_size, &map_handle);
  891. if (!file_map)
  892. return FALSE;
  893. /* Figure this out when we support 64bit PE files */
  894. if (1) {
  895. IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER *)file_map;
  896. if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
  897. mono_pe_file_unmap (file_map, map_handle);
  898. return FALSE;
  899. }
  900. IMAGE_NT_HEADERS32 *nt_headers = (IMAGE_NT_HEADERS32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
  901. if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
  902. mono_pe_file_unmap (file_map, map_handle);
  903. return FALSE;
  904. }
  905. *out = nt_headers->FileHeader.TimeDateStamp;
  906. } else {
  907. g_assert_not_reached ();
  908. }
  909. mono_pe_file_unmap (file_map, map_handle);
  910. return TRUE;
  911. }
  912. gpointer
  913. mono_pe_file_map (const gunichar2 *filename, guint32 *map_size, void **handle)
  914. {
  915. gchar *filename_ext = NULL;
  916. gchar *located_filename = NULL;
  917. guint64 fsize = 0;
  918. gpointer file_map = NULL;
  919. MonoFileMap *filed = NULL;
  920. ERROR_DECL (error);
  921. /* According to the MSDN docs, a search path is applied to
  922. * filename. FIXME: implement this, for now just pass it
  923. * straight to open
  924. */
  925. filename_ext = mono_unicode_to_external_checked (filename, error);
  926. // This block was added to diagnose https://github.com/mono/mono/issues/14730, remove after resolved
  927. if (G_UNLIKELY (filename_ext == NULL)) {
  928. GString *raw_bytes = g_string_new (NULL);
  929. const gunichar2 *p = filename;
  930. while (*p)
  931. g_string_append_printf (raw_bytes, "%04X ", *p++);
  932. g_assertf (filename_ext != NULL, "%s: unicode conversion returned NULL; %s; input was: %s", __func__, mono_error_get_message (error), raw_bytes->str);
  933. g_string_free (raw_bytes, TRUE);
  934. }
  935. if (filename_ext == NULL) {
  936. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
  937. mono_error_cleanup (error);
  938. goto exit;
  939. }
  940. if ((filed = mono_file_map_open (filename_ext)) == NULL && IS_PORTABILITY_SET) {
  941. gint saved_errno = errno;
  942. located_filename = mono_portability_find_file (filename_ext, TRUE);
  943. if (!located_filename) {
  944. mono_set_errno (saved_errno);
  945. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error opening file %s (3): %s", __func__, filename_ext, strerror (errno));
  946. goto exit;
  947. }
  948. if ((filed = mono_file_map_open (located_filename)) == NULL) {
  949. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error opening file %s (3): %s", __func__, located_filename, strerror (errno));
  950. goto exit;
  951. }
  952. }
  953. else if (filed == NULL) {
  954. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error opening file %s (3): %s", __func__, filename_ext, strerror (errno));
  955. goto exit;
  956. }
  957. fsize = mono_file_map_size (filed);
  958. if (fsize == 0) {
  959. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error stat()ing file %s: %s", __func__, filename_ext, strerror (errno));
  960. goto exit;
  961. }
  962. g_assert (fsize <= G_MAXUINT32);
  963. *map_size = fsize;
  964. /* Check basic file size */
  965. if (fsize < sizeof(IMAGE_DOS_HEADER)) {
  966. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: File %s is too small: %" PRId64, __func__, filename_ext, fsize);
  967. goto exit;
  968. }
  969. file_map = mono_file_map (fsize, MONO_MMAP_READ | MONO_MMAP_PRIVATE, mono_file_map_fd (filed), 0, handle);
  970. if (file_map == NULL) {
  971. mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Error mmap()int file %s: %s", __func__, filename_ext, strerror (errno));
  972. goto exit;
  973. }
  974. exit:
  975. if (filed)
  976. mono_file_map_close (filed);
  977. g_free (located_filename);
  978. g_free (filename_ext);
  979. return file_map;
  980. }
  981. void
  982. mono_pe_file_unmap (gpointer file_map, void *handle)
  983. {
  984. gint res;
  985. res = mono_file_unmap (file_map, handle);
  986. if (G_UNLIKELY (res != 0))
  987. g_error ("%s: mono_file_unmap failed, error: \"%s\" (%d)", __func__, g_strerror (errno), errno);
  988. }
  989. #endif /* HOST_WIN32 */
  990. /*
  991. * This function returns the cpu usage in percentage,
  992. * normalized on the number of cores.
  993. *
  994. * Warning : the percentage returned can be > 100%. This
  995. * might happens on systems like Android which, for
  996. * battery and performance reasons, shut down cores and
  997. * lie about the number of active cores.
  998. */
  999. #ifndef HOST_WIN32
  1000. gint32
  1001. mono_cpu_usage (MonoCpuUsageState *prev)
  1002. {
  1003. gint32 cpu_usage = 0;
  1004. #ifdef HAVE_GETRUSAGE
  1005. gint64 cpu_total_time;
  1006. gint64 cpu_busy_time;
  1007. struct rusage resource_usage;
  1008. gint64 current_time;
  1009. gint64 kernel_time;
  1010. gint64 user_time;
  1011. if (getrusage (RUSAGE_SELF, &resource_usage) == -1) {
  1012. g_error ("getrusage() failed, errno is %d (%s)\n", errno, strerror (errno));
  1013. return -1;
  1014. }
  1015. current_time = mono_100ns_ticks ();
  1016. kernel_time = resource_usage.ru_stime.tv_sec * 1000 * 1000 * 10 + resource_usage.ru_stime.tv_usec * 10;
  1017. user_time = resource_usage.ru_utime.tv_sec * 1000 * 1000 * 10 + resource_usage.ru_utime.tv_usec * 10;
  1018. cpu_busy_time = (user_time - (prev ? prev->user_time : 0)) + (kernel_time - (prev ? prev->kernel_time : 0));
  1019. cpu_total_time = (current_time - (prev ? prev->current_time : 0)) * mono_cpu_count ();
  1020. if (prev) {
  1021. prev->kernel_time = kernel_time;
  1022. prev->user_time = user_time;
  1023. prev->current_time = current_time;
  1024. }
  1025. if (cpu_total_time > 0 && cpu_busy_time > 0)
  1026. cpu_usage = (gint32)(cpu_busy_time * 100 / cpu_total_time);
  1027. #endif
  1028. return cpu_usage;
  1029. }
  1030. #endif /* !HOST_WIN32 */