PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/mono/utils/mono-proclib.c

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