PageRenderTime 60ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/processes.c

http://github.com/octo/collectd
C | 2644 lines | 2002 code | 436 blank | 206 comment | 527 complexity | 91e6d51c5e945329e2148f4bd43b2aa2 MD5 | raw file
Possible License(s): GPL-2.0
  1. /**
  2. * collectd - src/processes.c
  3. * Copyright (C) 2005 Lyonel Vincent
  4. * Copyright (C) 2006-2017 Florian octo Forster
  5. * Copyright (C) 2008 Oleg King
  6. * Copyright (C) 2009 Sebastian Harl
  7. * Copyright (C) 2009 Andrés J. Díaz
  8. * Copyright (C) 2009 Manuel Sanmartin
  9. * Copyright (C) 2010 Clément Stenac
  10. * Copyright (C) 2012 Cosmin Ioiart
  11. *
  12. * This program is free software; you can redistribute it and/or modify it
  13. * under the terms of the GNU General Public License as published by the
  14. * Free Software Foundation; either version 2 of the License, or (at your
  15. * option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful, but
  18. * WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License along
  23. * with this program; if not, write to the Free Software Foundation, Inc.,
  24. * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  25. *
  26. * Authors:
  27. * Lyonel Vincent <lyonel at ezix.org>
  28. * Florian octo Forster <octo at collectd.org>
  29. * Oleg King <king2 at kaluga.ru>
  30. * Sebastian Harl <sh at tokkee.org>
  31. * Andrés J. Díaz <ajdiaz at connectical.com>
  32. * Manuel Sanmartin
  33. * Clément Stenac <clement.stenac at diwi.org>
  34. * Cosmin Ioiart <cioiart at gmail.com>
  35. * Pavel Rochnyack <pavel2000 at ngs.ru>
  36. * Wilfried Goesgens <dothebart at citadel.org>
  37. **/
  38. #include "collectd.h"
  39. #include "plugin.h"
  40. #include "utils/common/common.h"
  41. #if HAVE_LIBTASKSTATS
  42. #include "utils/taskstats/taskstats.h"
  43. #include "utils_complain.h"
  44. #endif
  45. /* Include header files for the mach system, if they exist.. */
  46. #if HAVE_THREAD_INFO
  47. #if HAVE_MACH_MACH_INIT_H
  48. #include <mach/mach_init.h>
  49. #endif
  50. #if HAVE_MACH_HOST_PRIV_H
  51. #include <mach/host_priv.h>
  52. #endif
  53. #if HAVE_MACH_MACH_ERROR_H
  54. #include <mach/mach_error.h>
  55. #endif
  56. #if HAVE_MACH_MACH_HOST_H
  57. #include <mach/mach_host.h>
  58. #endif
  59. #if HAVE_MACH_MACH_PORT_H
  60. #include <mach/mach_port.h>
  61. #endif
  62. #if HAVE_MACH_MACH_TYPES_H
  63. #include <mach/mach_types.h>
  64. #endif
  65. #if HAVE_MACH_MESSAGE_H
  66. #include <mach/message.h>
  67. #endif
  68. #if HAVE_MACH_PROCESSOR_SET_H
  69. #include <mach/processor_set.h>
  70. #endif
  71. #if HAVE_MACH_TASK_H
  72. #include <mach/task.h>
  73. #endif
  74. #if HAVE_MACH_THREAD_ACT_H
  75. #include <mach/thread_act.h>
  76. #endif
  77. #if HAVE_MACH_VM_REGION_H
  78. #include <mach/vm_region.h>
  79. #endif
  80. #if HAVE_MACH_VM_MAP_H
  81. #include <mach/vm_map.h>
  82. #endif
  83. #if HAVE_MACH_VM_PROT_H
  84. #include <mach/vm_prot.h>
  85. #endif
  86. #if HAVE_SYS_SYSCTL_H
  87. #include <sys/sysctl.h>
  88. #endif
  89. /* #endif HAVE_THREAD_INFO */
  90. #elif KERNEL_LINUX
  91. #if HAVE_LINUX_CONFIG_H
  92. #include <linux/config.h>
  93. #endif
  94. #ifndef CONFIG_HZ
  95. #define CONFIG_HZ 100
  96. #endif
  97. /* #endif KERNEL_LINUX */
  98. #elif HAVE_LIBKVM_GETPROCS && \
  99. (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
  100. #include <kvm.h>
  101. #include <sys/param.h>
  102. #include <sys/proc.h>
  103. #include <sys/sysctl.h>
  104. #include <sys/user.h>
  105. /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
  106. * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
  107. #elif HAVE_PROCINFO_H
  108. #include <procinfo.h>
  109. #include <sys/types.h>
  110. #define MAXPROCENTRY 32
  111. #define MAXTHRDENTRY 16
  112. #define MAXARGLN 1024
  113. /* #endif HAVE_PROCINFO_H */
  114. #elif KERNEL_SOLARIS
  115. /* Hack: Avoid #error when building a 32-bit binary with
  116. * _FILE_OFFSET_BITS=64. There is a reason for this #error, as one
  117. * of the structures in <sys/procfs.h> uses an off_t, but that
  118. * isn't relevant to our usage of procfs. */
  119. #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
  120. #define SAVE_FOB_64
  121. #undef _FILE_OFFSET_BITS
  122. #endif
  123. #include <procfs.h>
  124. #ifdef SAVE_FOB_64
  125. #define _FILE_OFFSET_BITS 64
  126. #undef SAVE_FOB_64
  127. #endif
  128. #include <dirent.h>
  129. #include <sys/user.h>
  130. #ifndef MAXCOMLEN
  131. #define MAXCOMLEN 16
  132. #endif
  133. /* #endif KERNEL_SOLARIS */
  134. #else
  135. #error "No applicable input method."
  136. #endif
  137. #if HAVE_REGEX_H
  138. #include <regex.h>
  139. #endif
  140. #if HAVE_KSTAT_H
  141. #include <kstat.h>
  142. #endif
  143. #ifdef HAVE_SYS_CAPABILITY_H
  144. #include <sys/capability.h>
  145. #endif
  146. #ifndef CMDLINE_BUFFER_SIZE
  147. #if defined(ARG_MAX) && (ARG_MAX < 4096)
  148. #define CMDLINE_BUFFER_SIZE ARG_MAX
  149. #else
  150. #define CMDLINE_BUFFER_SIZE 4096
  151. #endif
  152. #endif
  153. #define PROCSTAT_NAME_LEN 256
  154. typedef struct process_entry_s {
  155. unsigned long id;
  156. char name[PROCSTAT_NAME_LEN];
  157. unsigned long num_proc;
  158. unsigned long num_lwp;
  159. unsigned long num_fd;
  160. unsigned long num_maps;
  161. unsigned long vmem_size;
  162. unsigned long vmem_rss;
  163. unsigned long vmem_data;
  164. unsigned long vmem_code;
  165. unsigned long stack_size;
  166. derive_t vmem_minflt_counter;
  167. derive_t vmem_majflt_counter;
  168. derive_t cpu_user_counter;
  169. derive_t cpu_system_counter;
  170. /* io data */
  171. derive_t io_rchar;
  172. derive_t io_wchar;
  173. derive_t io_syscr;
  174. derive_t io_syscw;
  175. derive_t io_diskr;
  176. derive_t io_diskw;
  177. bool has_io;
  178. derive_t cswitch_vol;
  179. derive_t cswitch_invol;
  180. bool has_cswitch;
  181. #if HAVE_LIBTASKSTATS
  182. ts_delay_t delay;
  183. #endif
  184. bool has_delay;
  185. bool has_fd;
  186. bool has_maps;
  187. } process_entry_t;
  188. typedef struct procstat_entry_s {
  189. unsigned long id;
  190. unsigned char age;
  191. derive_t vmem_minflt_counter;
  192. derive_t vmem_majflt_counter;
  193. derive_t cpu_user_counter;
  194. derive_t cpu_system_counter;
  195. /* io data */
  196. derive_t io_rchar;
  197. derive_t io_wchar;
  198. derive_t io_syscr;
  199. derive_t io_syscw;
  200. derive_t io_diskr;
  201. derive_t io_diskw;
  202. derive_t cswitch_vol;
  203. derive_t cswitch_invol;
  204. #if HAVE_LIBTASKSTATS
  205. value_to_rate_state_t delay_cpu;
  206. value_to_rate_state_t delay_blkio;
  207. value_to_rate_state_t delay_swapin;
  208. value_to_rate_state_t delay_freepages;
  209. #endif
  210. struct procstat_entry_s *next;
  211. } procstat_entry_t;
  212. typedef struct procstat {
  213. char name[PROCSTAT_NAME_LEN];
  214. #if HAVE_REGEX_H
  215. regex_t *re;
  216. #endif
  217. unsigned long num_proc;
  218. unsigned long num_lwp;
  219. unsigned long num_fd;
  220. unsigned long num_maps;
  221. unsigned long vmem_size;
  222. unsigned long vmem_rss;
  223. unsigned long vmem_data;
  224. unsigned long vmem_code;
  225. unsigned long stack_size;
  226. derive_t vmem_minflt_counter;
  227. derive_t vmem_majflt_counter;
  228. derive_t cpu_user_counter;
  229. derive_t cpu_system_counter;
  230. /* io data */
  231. derive_t io_rchar;
  232. derive_t io_wchar;
  233. derive_t io_syscr;
  234. derive_t io_syscw;
  235. derive_t io_diskr;
  236. derive_t io_diskw;
  237. derive_t cswitch_vol;
  238. derive_t cswitch_invol;
  239. /* Linux Delay Accounting. Unit is ns/s. */
  240. gauge_t delay_cpu;
  241. gauge_t delay_blkio;
  242. gauge_t delay_swapin;
  243. gauge_t delay_freepages;
  244. bool report_fd_num;
  245. bool report_maps_num;
  246. bool report_ctx_switch;
  247. bool report_delay;
  248. struct procstat *next;
  249. struct procstat_entry_s *instances;
  250. } procstat_t;
  251. static procstat_t *list_head_g;
  252. static bool want_init = true;
  253. static bool report_ctx_switch;
  254. static bool report_fd_num;
  255. static bool report_maps_num;
  256. static bool report_delay;
  257. #if HAVE_THREAD_INFO
  258. static mach_port_t port_host_self;
  259. static mach_port_t port_task_self;
  260. static processor_set_name_array_t pset_list;
  261. static mach_msg_type_number_t pset_list_len;
  262. /* #endif HAVE_THREAD_INFO */
  263. #elif KERNEL_LINUX
  264. static long pagesize_g;
  265. static void ps_fill_details(const procstat_t *ps, process_entry_t *entry);
  266. /* #endif KERNEL_LINUX */
  267. #elif HAVE_LIBKVM_GETPROCS && \
  268. (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
  269. static int pagesize;
  270. /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
  271. * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
  272. #elif HAVE_PROCINFO_H
  273. static struct procentry64 procentry[MAXPROCENTRY];
  274. static struct thrdentry64 thrdentry[MAXTHRDENTRY];
  275. static int pagesize;
  276. #ifndef _AIXVERSION_610
  277. int getprocs64(void *procsinfo, int sizproc, void *fdsinfo, int sizfd,
  278. pid_t *index, int count);
  279. int getthrds64(pid_t, void *, int, tid64_t *, int);
  280. #endif
  281. int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
  282. #endif /* HAVE_PROCINFO_H */
  283. #if HAVE_LIBTASKSTATS
  284. static ts_t *taskstats_handle;
  285. #endif
  286. /* put name of process from config to list_head_g tree
  287. * list_head_g is a list of 'procstat_t' structs with
  288. * processes names we want to watch */
  289. static procstat_t *ps_list_register(const char *name, const char *regexp) {
  290. procstat_t *new;
  291. procstat_t *ptr;
  292. int status;
  293. new = calloc(1, sizeof(*new));
  294. if (new == NULL) {
  295. ERROR("processes plugin: ps_list_register: calloc failed.");
  296. return NULL;
  297. }
  298. sstrncpy(new->name, name, sizeof(new->name));
  299. new->io_rchar = -1;
  300. new->io_wchar = -1;
  301. new->io_syscr = -1;
  302. new->io_syscw = -1;
  303. new->io_diskr = -1;
  304. new->io_diskw = -1;
  305. new->cswitch_vol = -1;
  306. new->cswitch_invol = -1;
  307. new->report_fd_num = report_fd_num;
  308. new->report_maps_num = report_maps_num;
  309. new->report_ctx_switch = report_ctx_switch;
  310. new->report_delay = report_delay;
  311. #if HAVE_REGEX_H
  312. if (regexp != NULL) {
  313. DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
  314. name);
  315. new->re = malloc(sizeof(*new->re));
  316. if (new->re == NULL) {
  317. ERROR("processes plugin: ps_list_register: malloc failed.");
  318. sfree(new);
  319. return NULL;
  320. }
  321. status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
  322. if (status != 0) {
  323. DEBUG("ProcessMatch: compiling the regular expression \"%s\" failed.",
  324. regexp);
  325. sfree(new->re);
  326. sfree(new);
  327. return NULL;
  328. }
  329. }
  330. #else
  331. if (regexp != NULL) {
  332. ERROR("processes plugin: ps_list_register: "
  333. "Regular expression \"%s\" found in config "
  334. "file, but support for regular expressions "
  335. "has been disabled at compile time.",
  336. regexp);
  337. sfree(new);
  338. return NULL;
  339. }
  340. #endif
  341. for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) {
  342. if (strcmp(ptr->name, name) == 0) {
  343. WARNING("processes plugin: You have configured more "
  344. "than one `Process' or "
  345. "`ProcessMatch' with the same name. "
  346. "All but the first setting will be "
  347. "ignored.");
  348. #if HAVE_REGEX_H
  349. sfree(new->re);
  350. #endif
  351. sfree(new);
  352. return NULL;
  353. }
  354. if (ptr->next == NULL)
  355. break;
  356. }
  357. if (ptr == NULL)
  358. list_head_g = new;
  359. else
  360. ptr->next = new;
  361. return new;
  362. } /* void ps_list_register */
  363. /* try to match name against entry, returns 1 if success */
  364. static int ps_list_match(const char *name, const char *cmdline,
  365. procstat_t *ps) {
  366. #if HAVE_REGEX_H
  367. if (ps->re != NULL) {
  368. int status;
  369. const char *str;
  370. str = cmdline;
  371. if ((str == NULL) || (str[0] == 0))
  372. str = name;
  373. assert(str != NULL);
  374. status = regexec(ps->re, str,
  375. /* nmatch = */ 0,
  376. /* pmatch = */ NULL,
  377. /* eflags = */ 0);
  378. if (status == 0)
  379. return 1;
  380. } else
  381. #endif
  382. if (strcmp(ps->name, name) == 0)
  383. return 1;
  384. return 0;
  385. } /* int ps_list_match */
  386. static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
  387. derive_t new_counter) {
  388. unsigned long curr_value;
  389. if (want_init) {
  390. *curr_counter = new_counter;
  391. return;
  392. }
  393. if (new_counter < *curr_counter)
  394. curr_value = new_counter + (ULONG_MAX - *curr_counter);
  395. else
  396. curr_value = new_counter - *curr_counter;
  397. if (*group_counter == -1)
  398. *group_counter = 0;
  399. *curr_counter = new_counter;
  400. *group_counter += curr_value;
  401. }
  402. #if HAVE_LIBTASKSTATS
  403. static void ps_update_delay_one(gauge_t *out_rate_sum,
  404. value_to_rate_state_t *state, uint64_t cnt,
  405. cdtime_t t) {
  406. gauge_t rate = NAN;
  407. int status = value_to_rate(&rate, (value_t){.counter = (counter_t)cnt},
  408. DS_TYPE_COUNTER, t, state);
  409. if ((status != 0) || isnan(rate)) {
  410. return;
  411. }
  412. if (isnan(*out_rate_sum)) {
  413. *out_rate_sum = rate;
  414. } else {
  415. *out_rate_sum += rate;
  416. }
  417. }
  418. static void ps_update_delay(procstat_t *out, procstat_entry_t *prev,
  419. process_entry_t *curr) {
  420. cdtime_t now = cdtime();
  421. ps_update_delay_one(&out->delay_cpu, &prev->delay_cpu, curr->delay.cpu_ns,
  422. now);
  423. ps_update_delay_one(&out->delay_blkio, &prev->delay_blkio,
  424. curr->delay.blkio_ns, now);
  425. ps_update_delay_one(&out->delay_swapin, &prev->delay_swapin,
  426. curr->delay.swapin_ns, now);
  427. ps_update_delay_one(&out->delay_freepages, &prev->delay_freepages,
  428. curr->delay.freepages_ns, now);
  429. }
  430. #endif
  431. /* add process entry to 'instances' of process 'name' (or refresh it) */
  432. static void ps_list_add(const char *name, const char *cmdline,
  433. process_entry_t *entry) {
  434. procstat_entry_t *pse;
  435. if (entry->id == 0)
  436. return;
  437. for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
  438. if ((ps_list_match(name, cmdline, ps)) == 0)
  439. continue;
  440. #if KERNEL_LINUX
  441. ps_fill_details(ps, entry);
  442. #endif
  443. for (pse = ps->instances; pse != NULL; pse = pse->next)
  444. if ((pse->id == entry->id) || (pse->next == NULL))
  445. break;
  446. if ((pse == NULL) || (pse->id != entry->id)) {
  447. procstat_entry_t *new;
  448. new = calloc(1, sizeof(*new));
  449. if (new == NULL)
  450. return;
  451. new->id = entry->id;
  452. if (pse == NULL)
  453. ps->instances = new;
  454. else
  455. pse->next = new;
  456. pse = new;
  457. }
  458. pse->age = 0;
  459. ps->num_proc += entry->num_proc;
  460. ps->num_lwp += entry->num_lwp;
  461. ps->num_fd += entry->num_fd;
  462. ps->num_maps += entry->num_maps;
  463. ps->vmem_size += entry->vmem_size;
  464. ps->vmem_rss += entry->vmem_rss;
  465. ps->vmem_data += entry->vmem_data;
  466. ps->vmem_code += entry->vmem_code;
  467. ps->stack_size += entry->stack_size;
  468. if ((entry->io_rchar != -1) && (entry->io_wchar != -1)) {
  469. ps_update_counter(&ps->io_rchar, &pse->io_rchar, entry->io_rchar);
  470. ps_update_counter(&ps->io_wchar, &pse->io_wchar, entry->io_wchar);
  471. }
  472. if ((entry->io_syscr != -1) && (entry->io_syscw != -1)) {
  473. ps_update_counter(&ps->io_syscr, &pse->io_syscr, entry->io_syscr);
  474. ps_update_counter(&ps->io_syscw, &pse->io_syscw, entry->io_syscw);
  475. }
  476. if ((entry->io_diskr != -1) && (entry->io_diskw != -1)) {
  477. ps_update_counter(&ps->io_diskr, &pse->io_diskr, entry->io_diskr);
  478. ps_update_counter(&ps->io_diskw, &pse->io_diskw, entry->io_diskw);
  479. }
  480. if ((entry->cswitch_vol != -1) && (entry->cswitch_invol != -1)) {
  481. ps_update_counter(&ps->cswitch_vol, &pse->cswitch_vol,
  482. entry->cswitch_vol);
  483. ps_update_counter(&ps->cswitch_invol, &pse->cswitch_invol,
  484. entry->cswitch_invol);
  485. }
  486. ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
  487. entry->vmem_minflt_counter);
  488. ps_update_counter(&ps->vmem_majflt_counter, &pse->vmem_majflt_counter,
  489. entry->vmem_majflt_counter);
  490. ps_update_counter(&ps->cpu_user_counter, &pse->cpu_user_counter,
  491. entry->cpu_user_counter);
  492. ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
  493. entry->cpu_system_counter);
  494. #if HAVE_LIBTASKSTATS
  495. if (entry->has_delay)
  496. ps_update_delay(ps, pse, entry);
  497. #endif
  498. }
  499. }
  500. /* remove old entries from instances of processes in list_head_g */
  501. static void ps_list_reset(void) {
  502. procstat_entry_t *pse;
  503. procstat_entry_t *pse_prev;
  504. for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
  505. ps->num_proc = 0;
  506. ps->num_lwp = 0;
  507. ps->num_fd = 0;
  508. ps->num_maps = 0;
  509. ps->vmem_size = 0;
  510. ps->vmem_rss = 0;
  511. ps->vmem_data = 0;
  512. ps->vmem_code = 0;
  513. ps->stack_size = 0;
  514. ps->delay_cpu = NAN;
  515. ps->delay_blkio = NAN;
  516. ps->delay_swapin = NAN;
  517. ps->delay_freepages = NAN;
  518. pse_prev = NULL;
  519. pse = ps->instances;
  520. while (pse != NULL) {
  521. if (pse->age > 0) {
  522. DEBUG("Removing this procstat entry cause it's too old: "
  523. "id = %lu; name = %s;",
  524. pse->id, ps->name);
  525. if (pse_prev == NULL) {
  526. ps->instances = pse->next;
  527. free(pse);
  528. pse = ps->instances;
  529. } else {
  530. pse_prev->next = pse->next;
  531. free(pse);
  532. pse = pse_prev->next;
  533. }
  534. } else {
  535. pse->age = 1;
  536. pse_prev = pse;
  537. pse = pse->next;
  538. }
  539. } /* while (pse != NULL) */
  540. } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
  541. }
  542. static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) {
  543. for (int i = 0; i < ci->children_num; i++) {
  544. oconfig_item_t *c = ci->children + i;
  545. if (strcasecmp(c->key, "CollectContextSwitch") == 0)
  546. cf_util_get_boolean(c, &ps->report_ctx_switch);
  547. else if (strcasecmp(c->key, "CollectFileDescriptor") == 0)
  548. cf_util_get_boolean(c, &ps->report_fd_num);
  549. else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
  550. cf_util_get_boolean(c, &ps->report_maps_num);
  551. else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
  552. #if HAVE_LIBTASKSTATS
  553. cf_util_get_boolean(c, &ps->report_delay);
  554. #else
  555. WARNING("processes plugin: The plugin has been compiled without support "
  556. "for the \"CollectDelayAccounting\" option.");
  557. #endif
  558. } else {
  559. ERROR("processes plugin: Option \"%s\" not allowed here.", c->key);
  560. }
  561. } /* for (ci->children) */
  562. } /* void ps_tune_instance */
  563. /* put all pre-defined 'Process' names from config to list_head_g tree */
  564. static int ps_config(oconfig_item_t *ci) {
  565. #if KERNEL_LINUX
  566. const size_t max_procname_len = 15;
  567. #elif KERNEL_SOLARIS || KERNEL_FREEBSD
  568. const size_t max_procname_len = MAXCOMLEN - 1;
  569. #endif
  570. procstat_t *ps;
  571. for (int i = 0; i < ci->children_num; ++i) {
  572. oconfig_item_t *c = ci->children + i;
  573. if (strcasecmp(c->key, "Process") == 0) {
  574. if ((c->values_num != 1) || (OCONFIG_TYPE_STRING != c->values[0].type)) {
  575. ERROR("processes plugin: `Process' expects exactly "
  576. "one string argument (got %i).",
  577. c->values_num);
  578. continue;
  579. }
  580. #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD
  581. if (strlen(c->values[0].value.string) > max_procname_len) {
  582. WARNING("processes plugin: this platform has a %" PRIsz
  583. " character limit "
  584. "to process names. The `Process \"%s\"' option will "
  585. "not work as expected.",
  586. max_procname_len, c->values[0].value.string);
  587. }
  588. #endif
  589. ps = ps_list_register(c->values[0].value.string, NULL);
  590. if (c->children_num != 0 && ps != NULL)
  591. ps_tune_instance(c, ps);
  592. } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
  593. if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
  594. (OCONFIG_TYPE_STRING != c->values[1].type)) {
  595. ERROR("processes plugin: `ProcessMatch' needs exactly "
  596. "two string arguments (got %i).",
  597. c->values_num);
  598. continue;
  599. }
  600. ps = ps_list_register(c->values[0].value.string,
  601. c->values[1].value.string);
  602. if (c->children_num != 0 && ps != NULL)
  603. ps_tune_instance(c, ps);
  604. } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
  605. cf_util_get_boolean(c, &report_ctx_switch);
  606. } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) {
  607. cf_util_get_boolean(c, &report_fd_num);
  608. } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
  609. cf_util_get_boolean(c, &report_maps_num);
  610. } else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
  611. #if HAVE_LIBTASKSTATS
  612. cf_util_get_boolean(c, &report_delay);
  613. #else
  614. WARNING("processes plugin: The plugin has been compiled without support "
  615. "for the \"CollectDelayAccounting\" option.");
  616. #endif
  617. } else {
  618. ERROR("processes plugin: The `%s' configuration option is not "
  619. "understood and will be ignored.",
  620. c->key);
  621. continue;
  622. }
  623. }
  624. return 0;
  625. }
  626. static int ps_init(void) {
  627. #if HAVE_THREAD_INFO
  628. kern_return_t status;
  629. port_host_self = mach_host_self();
  630. port_task_self = mach_task_self();
  631. if (pset_list != NULL) {
  632. vm_deallocate(port_task_self, (vm_address_t)pset_list,
  633. pset_list_len * sizeof(processor_set_t));
  634. pset_list = NULL;
  635. pset_list_len = 0;
  636. }
  637. if ((status = host_processor_sets(port_host_self, &pset_list,
  638. &pset_list_len)) != KERN_SUCCESS) {
  639. ERROR("host_processor_sets failed: %s\n", mach_error_string(status));
  640. pset_list = NULL;
  641. pset_list_len = 0;
  642. return -1;
  643. }
  644. /* #endif HAVE_THREAD_INFO */
  645. #elif KERNEL_LINUX
  646. pagesize_g = sysconf(_SC_PAGESIZE);
  647. DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
  648. #if HAVE_LIBTASKSTATS
  649. if (taskstats_handle == NULL) {
  650. taskstats_handle = ts_create();
  651. if (taskstats_handle == NULL) {
  652. WARNING("processes plugin: Creating taskstats handle failed.");
  653. }
  654. }
  655. #endif
  656. /* #endif KERNEL_LINUX */
  657. #elif HAVE_LIBKVM_GETPROCS && \
  658. (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
  659. pagesize = getpagesize();
  660. /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
  661. * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
  662. #elif HAVE_PROCINFO_H
  663. pagesize = getpagesize();
  664. #endif /* HAVE_PROCINFO_H */
  665. return 0;
  666. } /* int ps_init */
  667. /* submit global state (e.g.: qty of zombies, running, etc..) */
  668. static void ps_submit_state(const char *state, double value) {
  669. value_list_t vl = VALUE_LIST_INIT;
  670. vl.values = &(value_t){.gauge = value};
  671. vl.values_len = 1;
  672. sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
  673. sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
  674. sstrncpy(vl.type, "ps_state", sizeof(vl.type));
  675. sstrncpy(vl.type_instance, state, sizeof(vl.type_instance));
  676. plugin_dispatch_values(&vl);
  677. }
  678. /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
  679. static void ps_submit_proc_list(procstat_t *ps) {
  680. value_list_t vl = VALUE_LIST_INIT;
  681. value_t values[2];
  682. vl.values = values;
  683. sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
  684. sstrncpy(vl.plugin_instance, ps->name, sizeof(vl.plugin_instance));
  685. sstrncpy(vl.type, "ps_vm", sizeof(vl.type));
  686. vl.values[0].gauge = ps->vmem_size;
  687. vl.values_len = 1;
  688. plugin_dispatch_values(&vl);
  689. sstrncpy(vl.type, "ps_rss", sizeof(vl.type));
  690. vl.values[0].gauge = ps->vmem_rss;
  691. vl.values_len = 1;
  692. plugin_dispatch_values(&vl);
  693. sstrncpy(vl.type, "ps_data", sizeof(vl.type));
  694. vl.values[0].gauge = ps->vmem_data;
  695. vl.values_len = 1;
  696. plugin_dispatch_values(&vl);
  697. sstrncpy(vl.type, "ps_code", sizeof(vl.type));
  698. vl.values[0].gauge = ps->vmem_code;
  699. vl.values_len = 1;
  700. plugin_dispatch_values(&vl);
  701. sstrncpy(vl.type, "ps_stacksize", sizeof(vl.type));
  702. vl.values[0].gauge = ps->stack_size;
  703. vl.values_len = 1;
  704. plugin_dispatch_values(&vl);
  705. sstrncpy(vl.type, "ps_cputime", sizeof(vl.type));
  706. vl.values[0].derive = ps->cpu_user_counter;
  707. vl.values[1].derive = ps->cpu_system_counter;
  708. vl.values_len = 2;
  709. plugin_dispatch_values(&vl);
  710. sstrncpy(vl.type, "ps_count", sizeof(vl.type));
  711. vl.values[0].gauge = ps->num_proc;
  712. vl.values[1].gauge = ps->num_lwp;
  713. vl.values_len = 2;
  714. plugin_dispatch_values(&vl);
  715. sstrncpy(vl.type, "ps_pagefaults", sizeof(vl.type));
  716. vl.values[0].derive = ps->vmem_minflt_counter;
  717. vl.values[1].derive = ps->vmem_majflt_counter;
  718. vl.values_len = 2;
  719. plugin_dispatch_values(&vl);
  720. if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) {
  721. sstrncpy(vl.type, "io_octets", sizeof(vl.type));
  722. vl.values[0].derive = ps->io_rchar;
  723. vl.values[1].derive = ps->io_wchar;
  724. vl.values_len = 2;
  725. plugin_dispatch_values(&vl);
  726. }
  727. if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
  728. sstrncpy(vl.type, "io_ops", sizeof(vl.type));
  729. vl.values[0].derive = ps->io_syscr;
  730. vl.values[1].derive = ps->io_syscw;
  731. vl.values_len = 2;
  732. plugin_dispatch_values(&vl);
  733. }
  734. if ((ps->io_diskr != -1) && (ps->io_diskw != -1)) {
  735. sstrncpy(vl.type, "disk_octets", sizeof(vl.type));
  736. vl.values[0].derive = ps->io_diskr;
  737. vl.values[1].derive = ps->io_diskw;
  738. vl.values_len = 2;
  739. plugin_dispatch_values(&vl);
  740. }
  741. if (ps->num_fd > 0) {
  742. sstrncpy(vl.type, "file_handles", sizeof(vl.type));
  743. vl.values[0].gauge = ps->num_fd;
  744. vl.values_len = 1;
  745. plugin_dispatch_values(&vl);
  746. }
  747. if (ps->num_maps > 0) {
  748. sstrncpy(vl.type, "file_handles", sizeof(vl.type));
  749. sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance));
  750. vl.values[0].gauge = ps->num_maps;
  751. vl.values_len = 1;
  752. plugin_dispatch_values(&vl);
  753. }
  754. if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) {
  755. sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
  756. sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
  757. vl.values[0].derive = ps->cswitch_vol;
  758. vl.values_len = 1;
  759. plugin_dispatch_values(&vl);
  760. sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
  761. sstrncpy(vl.type_instance, "involuntary", sizeof(vl.type_instance));
  762. vl.values[0].derive = ps->cswitch_invol;
  763. vl.values_len = 1;
  764. plugin_dispatch_values(&vl);
  765. }
  766. /* The ps->delay_* metrics are in nanoseconds per second. Convert to seconds
  767. * per second. */
  768. gauge_t const delay_factor = 1000000000.0;
  769. struct {
  770. const char *type_instance;
  771. gauge_t rate_ns;
  772. } delay_metrics[] = {
  773. {"delay-cpu", ps->delay_cpu},
  774. {"delay-blkio", ps->delay_blkio},
  775. {"delay-swapin", ps->delay_swapin},
  776. {"delay-freepages", ps->delay_freepages},
  777. };
  778. for (size_t i = 0; i < STATIC_ARRAY_SIZE(delay_metrics); i++) {
  779. if (isnan(delay_metrics[i].rate_ns)) {
  780. continue;
  781. }
  782. sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
  783. sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
  784. sizeof(vl.type_instance));
  785. vl.values[0].gauge = delay_metrics[i].rate_ns / delay_factor;
  786. vl.values_len = 1;
  787. plugin_dispatch_values(&vl);
  788. }
  789. DEBUG(
  790. "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
  791. "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
  792. "vmem_code = %lu; "
  793. "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
  794. "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
  795. "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
  796. "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
  797. "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
  798. "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 "; "
  799. "delay_cpu = %g; delay_blkio = %g; "
  800. "delay_swapin = %g; delay_freepages = %g;",
  801. ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
  802. ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
  803. ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
  804. ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
  805. ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
  806. ps->cswitch_invol, ps->delay_cpu, ps->delay_blkio, ps->delay_swapin,
  807. ps->delay_freepages);
  808. } /* void ps_submit_proc_list */
  809. #if KERNEL_LINUX || KERNEL_SOLARIS
  810. static void ps_submit_fork_rate(derive_t value) {
  811. value_list_t vl = VALUE_LIST_INIT;
  812. vl.values = &(value_t){.derive = value};
  813. vl.values_len = 1;
  814. sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
  815. sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
  816. sstrncpy(vl.type, "fork_rate", sizeof(vl.type));
  817. sstrncpy(vl.type_instance, "", sizeof(vl.type_instance));
  818. plugin_dispatch_values(&vl);
  819. }
  820. #endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
  821. /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
  822. #if KERNEL_LINUX
  823. static int ps_read_tasks_status(process_entry_t *ps) {
  824. char dirname[64];
  825. DIR *dh;
  826. char filename[64];
  827. FILE *fh;
  828. struct dirent *ent;
  829. derive_t cswitch_vol = 0;
  830. derive_t cswitch_invol = 0;
  831. char buffer[1024];
  832. char *fields[8];
  833. int numfields;
  834. snprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
  835. if ((dh = opendir(dirname)) == NULL) {
  836. DEBUG("Failed to open directory `%s'", dirname);
  837. return -1;
  838. }
  839. while ((ent = readdir(dh)) != NULL) {
  840. char *tpid;
  841. if (!isdigit((int)ent->d_name[0]))
  842. continue;
  843. tpid = ent->d_name;
  844. int r = snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status",
  845. ps->id, tpid);
  846. if ((size_t)r >= sizeof(filename)) {
  847. DEBUG("Filename too long: `%s'", filename);
  848. continue;
  849. }
  850. if ((fh = fopen(filename, "r")) == NULL) {
  851. DEBUG("Failed to open file `%s'", filename);
  852. continue;
  853. }
  854. while (fgets(buffer, sizeof(buffer), fh) != NULL) {
  855. derive_t tmp;
  856. char *endptr;
  857. if (strncmp(buffer, "voluntary_ctxt_switches", 23) != 0 &&
  858. strncmp(buffer, "nonvoluntary_ctxt_switches", 26) != 0)
  859. continue;
  860. numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
  861. if (numfields < 2)
  862. continue;
  863. errno = 0;
  864. endptr = NULL;
  865. tmp = (derive_t)strtoll(fields[1], &endptr, /* base = */ 10);
  866. if ((errno == 0) && (endptr != fields[1])) {
  867. if (strncmp(buffer, "voluntary_ctxt_switches", 23) == 0) {
  868. cswitch_vol += tmp;
  869. } else if (strncmp(buffer, "nonvoluntary_ctxt_switches", 26) == 0) {
  870. cswitch_invol += tmp;
  871. }
  872. }
  873. } /* while (fgets) */
  874. if (fclose(fh)) {
  875. WARNING("processes: fclose: %s", STRERRNO);
  876. }
  877. }
  878. closedir(dh);
  879. ps->cswitch_vol = cswitch_vol;
  880. ps->cswitch_invol = cswitch_invol;
  881. return 0;
  882. } /* int *ps_read_tasks_status */
  883. /* Read data from /proc/pid/status */
  884. static int ps_read_status(long pid, process_entry_t *ps) {
  885. FILE *fh;
  886. char buffer[1024];
  887. char filename[64];
  888. unsigned long lib = 0;
  889. unsigned long exe = 0;
  890. unsigned long data = 0;
  891. unsigned long threads = 0;
  892. char *fields[8];
  893. int numfields;
  894. snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
  895. if ((fh = fopen(filename, "r")) == NULL)
  896. return -1;
  897. while (fgets(buffer, sizeof(buffer), fh) != NULL) {
  898. unsigned long tmp;
  899. char *endptr;
  900. if (strncmp(buffer, "Vm", 2) != 0 && strncmp(buffer, "Threads", 7) != 0)
  901. continue;
  902. numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
  903. if (numfields < 2)
  904. continue;
  905. errno = 0;
  906. endptr = NULL;
  907. tmp = strtoul(fields[1], &endptr, /* base = */ 10);
  908. if ((errno == 0) && (endptr != fields[1])) {
  909. if (strncmp(buffer, "VmData", 6) == 0) {
  910. data = tmp;
  911. } else if (strncmp(buffer, "VmLib", 5) == 0) {
  912. lib = tmp;
  913. } else if (strncmp(buffer, "VmExe", 5) == 0) {
  914. exe = tmp;
  915. } else if (strncmp(buffer, "Threads", 7) == 0) {
  916. threads = tmp;
  917. }
  918. }
  919. } /* while (fgets) */
  920. if (fclose(fh)) {
  921. WARNING("processes: fclose: %s", STRERRNO);
  922. }
  923. ps->vmem_data = data * 1024;
  924. ps->vmem_code = (exe + lib) * 1024;
  925. if (threads != 0)
  926. ps->num_lwp = threads;
  927. return 0;
  928. } /* int *ps_read_status */
  929. static int ps_read_io(process_entry_t *ps) {
  930. FILE *fh;
  931. char buffer[1024];
  932. char filename[64];
  933. char *fields[8];
  934. int numfields;
  935. snprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
  936. if ((fh = fopen(filename, "r")) == NULL) {
  937. DEBUG("ps_read_io: Failed to open file `%s'", filename);
  938. return -1;
  939. }
  940. while (fgets(buffer, sizeof(buffer), fh) != NULL) {
  941. derive_t *val = NULL;
  942. long long tmp;
  943. char *endptr;
  944. if (strncasecmp(buffer, "rchar:", 6) == 0)
  945. val = &(ps->io_rchar);
  946. else if (strncasecmp(buffer, "wchar:", 6) == 0)
  947. val = &(ps->io_wchar);
  948. else if (strncasecmp(buffer, "syscr:", 6) == 0)
  949. val = &(ps->io_syscr);
  950. else if (strncasecmp(buffer, "syscw:", 6) == 0)
  951. val = &(ps->io_syscw);
  952. else if (strncasecmp(buffer, "read_bytes:", 11) == 0)
  953. val = &(ps->io_diskr);
  954. else if (strncasecmp(buffer, "write_bytes:", 12) == 0)
  955. val = &(ps->io_diskw);
  956. else
  957. continue;
  958. numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
  959. if (numfields < 2)
  960. continue;
  961. errno = 0;
  962. endptr = NULL;
  963. tmp = strtoll(fields[1], &endptr, /* base = */ 10);
  964. if ((errno != 0) || (endptr == fields[1]))
  965. *val = -1;
  966. else
  967. *val = (derive_t)tmp;
  968. } /* while (fgets) */
  969. if (fclose(fh)) {
  970. WARNING("processes: fclose: %s", STRERRNO);
  971. }
  972. return 0;
  973. } /* int ps_read_io (...) */
  974. static int ps_count_maps(pid_t pid) {
  975. FILE *fh;
  976. char buffer[1024];
  977. char filename[64];
  978. int count = 0;
  979. snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
  980. if ((fh = fopen(filename, "r")) == NULL) {
  981. DEBUG("ps_count_maps: Failed to open file `%s'", filename);
  982. return -1;
  983. }
  984. while (fgets(buffer, sizeof(buffer), fh) != NULL) {
  985. if (strchr(buffer, '\n')) {
  986. count++;
  987. }
  988. } /* while (fgets) */
  989. if (fclose(fh)) {
  990. WARNING("processes: fclose: %s", STRERRNO);
  991. }
  992. return count;
  993. } /* int ps_count_maps (...) */
  994. static int ps_count_fd(int pid) {
  995. char dirname[64];
  996. DIR *dh;
  997. struct dirent *ent;
  998. int count = 0;
  999. snprintf(dirname, sizeof(dirname), "/proc/%i/fd", pid);
  1000. if ((dh = opendir(dirname)) == NULL) {
  1001. DEBUG("Failed to open directory `%s'", dirname);
  1002. return -1;
  1003. }
  1004. while ((ent = readdir(dh)) != NULL) {
  1005. if (!isdigit((int)ent->d_name[0]))
  1006. continue;
  1007. else
  1008. count++;
  1009. }
  1010. closedir(dh);
  1011. return (count >= 1) ? count : 1;
  1012. } /* int ps_count_fd (pid) */
  1013. #if HAVE_LIBTASKSTATS
  1014. static int ps_delay(process_entry_t *ps) {
  1015. if (taskstats_handle == NULL) {
  1016. return ENOTCONN;
  1017. }
  1018. int status = ts_delay_by_tgid(taskstats_handle, (uint32_t)ps->id, &ps->delay);
  1019. if (status == EPERM) {
  1020. static c_complain_t c;
  1021. #if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_NET_ADMIN)
  1022. if (check_capability(CAP_NET_ADMIN) != 0) {
  1023. if (getuid() == 0) {
  1024. c_complain(
  1025. LOG_ERR, &c,
  1026. "processes plugin: Reading Delay Accounting metric failed: %s. "
  1027. "collectd is running as root, but missing the CAP_NET_ADMIN "
  1028. "capability. The most common cause for this is that the init "
  1029. "system is dropping capabilities.",
  1030. STRERROR(status));
  1031. } else {
  1032. c_complain(
  1033. LOG_ERR, &c,
  1034. "processes plugin: Reading Delay Accounting metric failed: %s. "
  1035. "collectd is not running as root and missing the CAP_NET_ADMIN "
  1036. "capability. Either run collectd as root or grant it the "
  1037. "CAP_NET_ADMIN capability using \"setcap cap_net_admin=ep " PREFIX
  1038. "/sbin/collectd\".",
  1039. STRERROR(status));
  1040. }
  1041. } else {
  1042. ERROR("processes plugin: ts_delay_by_tgid failed: %s. The CAP_NET_ADMIN "
  1043. "capability is available (I checked), so this error is utterly "
  1044. "unexpected.",
  1045. STRERROR(status));
  1046. }
  1047. #else
  1048. c_complain(LOG_ERR, &c,
  1049. "processes plugin: Reading Delay Accounting metric failed: %s. "
  1050. "Reading Delay Accounting metrics requires root privileges.",
  1051. STRERROR(status));
  1052. #endif
  1053. return status;
  1054. } else if (status != 0) {
  1055. ERROR("processes plugin: ts_delay_by_tgid failed: %s", STRERROR(status));
  1056. return status;
  1057. }
  1058. return 0;
  1059. }
  1060. #endif
  1061. static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
  1062. if (entry->has_io == false) {
  1063. ps_read_io(entry);
  1064. entry->has_io = true;
  1065. }
  1066. if (ps->report_ctx_switch) {
  1067. if (entry->has_cswitch == false) {
  1068. ps_read_tasks_status(entry);
  1069. entry->has_cswitch = true;
  1070. }
  1071. }
  1072. if (ps->report_maps_num) {
  1073. int num_maps;
  1074. if (entry->has_maps == false && (num_maps = ps_count_maps(entry->id)) > 0) {
  1075. entry->num_maps = num_maps;
  1076. }
  1077. entry->has_maps = true;
  1078. }
  1079. if (ps->report_fd_num) {
  1080. int num_fd;
  1081. if (entry->has_fd == false && (num_fd = ps_count_fd(entry->id)) > 0) {
  1082. entry->num_fd = num_fd;
  1083. }
  1084. entry->has_fd = true;
  1085. }
  1086. #if HAVE_LIBTASKSTATS
  1087. if (ps->report_delay && !entry->has_delay) {
  1088. if (ps_delay(entry) == 0) {
  1089. entry->has_delay = true;
  1090. }
  1091. }
  1092. #endif
  1093. } /* void ps_fill_details (...) */
  1094. /* ps_read_process reads process counters on Linux. */
  1095. static int ps_read_process(long pid, process_entry_t *ps, char *state) {
  1096. char filename[64];
  1097. char buffer[1024];
  1098. char *fields[64];
  1099. char fields_len;
  1100. size_t buffer_len;
  1101. char *buffer_ptr;
  1102. size_t name_start_pos;
  1103. size_t name_end_pos;
  1104. size_t name_len;
  1105. derive_t cpu_user_counter;
  1106. derive_t cpu_system_counter;
  1107. long long unsigned vmem_size;
  1108. long long unsigned vmem_rss;
  1109. long long unsigned stack_size;
  1110. ssize_t status;
  1111. snprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
  1112. status = read_text_file_contents(filename, buffer, sizeof(buffer));
  1113. if (status <= 0)
  1114. return -1;
  1115. buffer_len = (size_t)status;
  1116. /* The name of the process is enclosed in parens. Since the name can
  1117. * contain parens itself, spaces, numbers and pretty much everything
  1118. * else, use these to determine the process name. We don't use
  1119. * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
  1120. * otherwise be required to determine name_len. */
  1121. name_start_pos = 0;
  1122. while (name_start_pos < buffer_len && buffer[name_start_pos] != '(')
  1123. name_start_pos++;
  1124. name_end_pos = buffer_len;
  1125. while (name_end_pos > 0 && buffer[name_end_pos] != ')')
  1126. name_end_pos--;
  1127. /* Either '(' or ')' is not found or they are in the wrong order.
  1128. * Anyway, something weird that shouldn't happen ever. */
  1129. if (name_start_pos >= name_end_pos) {
  1130. ERROR("processes plugin: name_start_pos = %" PRIsz
  1131. " >= name_end_pos = %" PRIsz,
  1132. name_start_pos, name_end_pos);
  1133. return -1;
  1134. }
  1135. name_len = (name_end_pos - name_start_pos) - 1;
  1136. if (name_len >= sizeof(ps->name))
  1137. name_len = sizeof(ps->name) - 1;
  1138. sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
  1139. if ((buffer_len - name_end_pos) < 2)
  1140. return -1;
  1141. buffer_ptr = &buffer[name_end_pos + 2];
  1142. fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
  1143. if (fields_len < 22) {
  1144. DEBUG("processes plugin: ps_read_process (pid = %li):"
  1145. " `%s' has only %i fields..",
  1146. pid, filename, fields_len);
  1147. return -1;
  1148. }
  1149. *state = fields[0][0];
  1150. if (*state == 'Z') {
  1151. ps->num_lwp = 0;
  1152. ps->num_proc = 0;
  1153. } else {
  1154. ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
  1155. if ((ps_read_status(pid, ps)) != 0) {
  1156. /* No VMem data */
  1157. ps->vmem_data = -1;
  1158. ps->vmem_code = -1;
  1159. DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
  1160. }
  1161. if (ps->num_lwp == 0)
  1162. ps->num_lwp = 1;
  1163. ps->num_proc = 1;
  1164. }
  1165. /* Leave the rest at zero if this is only a zombi */
  1166. if (ps->num_proc == 0) {
  1167. DEBUG("processes plugin: This is only a zombie: pid = %li; "
  1168. "name = %s;",
  1169. pid, ps->name);
  1170. return 0;
  1171. }
  1172. cpu_user_counter = atoll(fields[11]);
  1173. cpu_system_counter = atoll(fields[12]);
  1174. vmem_size = atoll(fields[20]);
  1175. vmem_rss = atoll(fields[21]);
  1176. ps->vmem_minflt_counter = atol(fields[7]);
  1177. ps->vmem_majflt_counter = atol(fields[9]);
  1178. {
  1179. unsigned long long stack_start = atoll(fields[25]);
  1180. unsigned long long stack_ptr = atoll(fields[26]);
  1181. stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr
  1182. : stack_ptr - stack_start;
  1183. }
  1184. /* Convert jiffies to useconds */
  1185. cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
  1186. cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
  1187. vmem_rss = vmem_rss * pagesize_g;
  1188. ps->cpu_user_counter = cpu_user_counter;
  1189. ps->cpu_system_counter = cpu_system_counter;
  1190. ps->vmem_size = (unsigned long)vmem_size;
  1191. ps->vmem_rss = (unsigned long)vmem_rss;
  1192. ps->stack_size = (unsigned long)stack_size;
  1193. /* no data by default. May be filled by ps_fill_details () */
  1194. ps->io_rchar = -1;
  1195. ps->io_wchar = -1;
  1196. ps->io_syscr = -1;
  1197. ps->io_syscw = -1;
  1198. ps->io_diskr = -1;
  1199. ps->io_diskw = -1;
  1200. ps->cswitch_vol = -1;
  1201. ps->cswitch_invol = -1;
  1202. /* success */
  1203. return 0;
  1204. } /* int ps_read_process (...) */
  1205. static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
  1206. char *buf_ptr;
  1207. size_t len;
  1208. char file[PATH_MAX];
  1209. int fd;
  1210. size_t n;
  1211. if ((pid < 1) || (NULL == buf) || (buf_len < 2))
  1212. return NULL;
  1213. snprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
  1214. errno = 0;
  1215. fd = open(file, O_RDONLY);
  1216. if (fd < 0) {
  1217. /* ENOENT means the process exited while we were handling it.
  1218. * Don't complain about this, it only fills the logs. */
  1219. if (errno != ENOENT)
  1220. WARNING("processes plugin: Failed to open `%s': %s.", file, STRERRNO);
  1221. return NULL;
  1222. }
  1223. buf_ptr = buf;
  1224. len = buf_len;
  1225. n = 0;
  1226. while (42) {
  1227. ssize_t status;
  1228. status = read(fd, (void *)buf_ptr, len);
  1229. if (status < 0) {
  1230. if ((EAGAIN == errno) || (EINTR == errno))
  1231. continue;
  1232. WARNING("processes plugin: Failed to read from `%s': %s.", file,
  1233. STRERRNO);
  1234. close(fd);
  1235. return NULL;
  1236. }
  1237. n += status;
  1238. if (status == 0)
  1239. break;
  1240. buf_ptr += status;
  1241. len -= status;
  1242. if (len == 0)
  1243. break;
  1244. }
  1245. close(fd);
  1246. if (0 == n) {
  1247. /* cmdline not available; e.g. kernel thread, zombie */
  1248. if (NULL == name)
  1249. return NULL;
  1250. snprintf(buf, buf_len, "[%s]", name);
  1251. return buf;
  1252. }
  1253. assert(n <= buf_len);
  1254. if (n == buf_len)
  1255. --n;
  1256. buf[n] = '\0';
  1257. --n;
  1258. /* remove trailing whitespace */
  1259. while ((n > 0) && (isspace(buf[n]) || ('\0' == buf[n]))) {
  1260. buf[n] = '\0';
  1261. --n;
  1262. }
  1263. /* arguments are separated by '\0' in /proc/<pid>/cmdline */
  1264. while (n > 0) {
  1265. if ('\0' == buf[n])
  1266. buf[n] = ' ';
  1267. --n;
  1268. }
  1269. return buf;
  1270. } /* char *ps_get_cmdline (...) */
  1271. static int read_fork_rate(void) {
  1272. FILE *proc_stat;
  1273. char buffer[1024];
  1274. value_t value;
  1275. bool value_valid = 0;
  1276. proc_stat = fopen("/proc/stat", "r");
  1277. if (proc_stat == NULL) {
  1278. ERROR("processes plugin: fopen (/proc/stat) failed: %s", STRERRNO);
  1279. return -1;
  1280. }
  1281. while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
  1282. int status;
  1283. char *fields[3];
  1284. int fields_num;
  1285. fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
  1286. if (fields_num != 2)
  1287. continue;
  1288. if (strcmp("processes", fields[0]) != 0)
  1289. continue;
  1290. status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
  1291. if (status == 0)
  1292. value_valid = 1;
  1293. break;
  1294. }
  1295. fclose(proc_stat);
  1296. if (!value_valid)
  1297. return -1;
  1298. ps_submit_fork_rate(value.derive);
  1299. return 0;
  1300. }
  1301. #endif /*KERNEL_LINUX */
  1302. #if KERNEL_SOLARIS
  1303. static char *ps_get_cmdline(long pid,
  1304. char *name __attribute__((unused)), /* {{{ */
  1305. char *buffer, size_t buffer_size) {
  1306. char path[PATH_MAX];
  1307. psinfo_t info;
  1308. ssize_t status;
  1309. snprintf(path, sizeof(path), "/proc/%li/psinfo", pid);
  1310. status = read_file_contents(path, &info, sizeof(info));
  1311. if ((status < 0) || (((size_t)status) != sizeof(info))) {
  1312. ERROR("processes plugin: Unexpected return value "
  1313. "while reading \"%s\": "
  1314. "Returned %zd but expected %" PRIsz ".",
  1315. path, status, buffer_size);
  1316. return NULL;
  1317. }
  1318. sstrncpy(buffer, info.pr_psargs, buffer_size);
  1319. return buffer;
  1320. } /* }}} int ps_get_cmdline */
  1321. /*
  1322. * Reads process information on the Solaris OS. The information comes mainly
  1323. * from
  1324. * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
  1325. * The values for input and ouput chars are calculated "by hand"
  1326. * Added a few "solaris" specific process states as well
  1327. */
  1328. static int ps_read_process(long pid, process_entry_t *ps, char *state) {
  1329. char filename[64];
  1330. char f_psinfo[64], f_usage[64];
  1331. char *buffer;
  1332. pstatus_t *myStatus;
  1333. psinfo_t *myInfo;
  1334. prusage_t *myUsage;
  1335. snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
  1336. snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid);
  1337. snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid);
  1338. buffer = calloc(1, sizeof(pstatus_t));
  1339. read_file_contents(filename, buffer, sizeof(pstatus_t));
  1340. myStatus = (pstatus_t *)buffer;
  1341. buffer = calloc(1, sizeof(psinfo_t));
  1342. read_file_contents(f_psinfo, buffer, sizeof(psinfo_t));
  1343. myInfo = (psinfo_t *)buffer;
  1344. buffer = calloc(1, sizeof(prusage_t));
  1345. read_file_contents(f_usage, buffer, sizeof(prusage_t));
  1346. myUsage = (prusage_t *)buffer;
  1347. sstrncpy(ps->name, myInfo->pr_fname, sizeof(myInfo->pr_fname));
  1348. ps->num_lwp = myStatus->pr_nlwp;
  1349. if (myInfo->pr_wstat != 0) {
  1350. ps->num_proc = 0;
  1351. ps->num_lwp = 0;
  1352. *state = (char)'Z';
  1353. sfree(myStatus);
  1354. sfree(myInfo);
  1355. sfree(myUsage);
  1356. return 0;
  1357. } else {
  1358. ps->num_proc = 1;
  1359. ps->num_lwp = myInfo->pr_nlwp;
  1360. }
  1361. /*
  1362. * Convert system time and user time from nanoseconds to microseconds
  1363. * for compatibility with the linux module
  1364. */
  1365. ps->cpu_system_counter = myStatus->pr_stime.tv_nsec / 1000;
  1366. ps->cpu_user_counter = myStatus->pr_utime.tv_nsec / 1000;
  1367. /*
  1368. * Convert rssize from KB to bytes to be consistent w/ the linux module
  1369. */
  1370. ps->vmem_rss = myInfo->pr_rssize * 1024;
  1371. ps->vmem_size = myInfo->pr_size * 1024;
  1372. ps->vmem_minflt_counter = myUsage->pr_minf;
  1373. ps->vmem_majflt_counter = myUsage->pr_majf;
  1374. /*
  1375. * TODO: Data and code segment calculations for Solaris
  1376. */
  1377. ps->vmem_data = -1;
  1378. ps->vmem_code = -1;
  1379. ps->stack_size = myStatus->pr_stksize;
  1380. /*
  1381. * TODO: File descriptor count for Solaris
  1382. */
  1383. ps->num_fd = 0;
  1384. /* Number of memory mappings */
  1385. ps->num_maps = 0;
  1386. /*
  1387. * Calculating input/ouput chars
  1388. * Formula used is total chars / total blocks => chars/block
  1389. * then convert input/output blocks to chars
  1390. */
  1391. ulong_t tot_chars = myUsage->pr_ioch;
  1392. ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
  1393. ulong_t chars_per_block = 1;
  1394. if (tot_blocks != 0)
  1395. chars_per_block = tot_chars / tot_blocks;
  1396. ps->io_rchar = myUsage->pr_inblk * chars_per_block;
  1397. ps->io_wchar = myUsage->pr_oublk * chars_per_block;
  1398. ps->io_syscr = myUsage->pr_sysc;
  1399. ps->io_syscw = myUsage->pr_sysc;
  1400. ps->io_diskr = -1;
  1401. ps->io_diskw = -1;
  1402. /*
  1403. * TODO: context switch counters for Solaris
  1404. */
  1405. ps->cswitch_vol = -1;
  1406. ps->cswitch_invol = -1;
  1407. /*
  1408. * TODO: Find way of setting BLOCKED and PAGING status
  1409. */
  1410. *state = (char)'R';
  1411. if (myStatus->pr_flags & PR_ASLEEP)
  1412. *state = (char)'S';
  1413. else if (myStatus->pr_flags & PR_STOPPED)
  1414. *state = (char)'T';
  1415. else if (myStatus->pr_flags & PR_DETACH)
  1416. *state = (char)'E';
  1417. else if (myStatus->pr_flags & PR_DAEMON)
  1418. *state = (char)'A';
  1419. else if (myStatus->pr_flags & PR_ISSYS)
  1420. *state = (char)'Y';
  1421. else if (myStatus->pr_flags & PR_ORPHAN)
  1422. *state = (char)'O';
  1423. sfree(myStatus);
  1424. sfree(myInfo);
  1425. sfree(myUsage);
  1426. return 0;
  1427. }
  1428. /*
  1429. * Reads the number of threads created since the last reboot. On Solaris these
  1430. * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
  1431. * The result is the sum for all the threads created on each cpu
  1432. */
  1433. static int read_fork_rate(void) {
  1434. extern kstat_ctl_t *kc;
  1435. derive_t result = 0;
  1436. if (kc == NULL)
  1437. return -1;
  1438. for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
  1439. ksp_chain = ksp_chain->ks_next) {
  1440. if ((strcmp(ksp_chain->ks_module, "cpu") == 0) &&
  1441. (strcmp(ksp_chain->ks_name, "sys") == 0) &&
  1442. (strcmp(ksp_chain->ks_class, "misc") == 0)) {
  1443. long long tmp;
  1444. kstat_read(kc, ksp_chain, NULL);
  1445. tmp = get_kstat_value(ksp_chain, "nthreads");
  1446. if (tmp != -1LL)
  1447. result += tmp;
  1448. }
  1449. }
  1450. ps_submit_fork_rate(result);
  1451. return 0;
  1452. }
  1453. #endif /* KERNEL_SOLARIS */
  1454. #if HAVE_THREAD_INFO
  1455. static int mach_get_task_name(task_t t, int *pid, char *name,
  1456. size_t name_max_len) {
  1457. int mib[4];
  1458. struct kinfo_proc kp;
  1459. size_t kp_size;
  1460. mib[0] = CTL_KERN;
  1461. mib[1] = KERN_PROC;
  1462. mib[2] = KERN_PROC_PID;
  1463. if (pid_for_task(t, pid) != KERN_SUCCESS)
  1464. return -1;
  1465. mib[3] = *pid;
  1466. kp_size = sizeof(kp);
  1467. if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
  1468. return -1;
  1469. if (name_max_len > (MAXCOMLEN + 1))
  1470. name_max_len = MAXCOMLEN + 1;
  1471. strncpy(name, kp.kp_proc.p_comm, name_max_len - 1);
  1472. name[name_max_len - 1] = '\0';
  1473. DEBUG("pid = %i; name = %s;", *pid, name);
  1474. /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
  1475. * `top' does it, because it is a lot of work and only used when
  1476. * debugging. -octo */
  1477. return 0;
  1478. }
  1479. #endif /* HAVE_THREAD_INFO */
  1480. /* end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO */
  1481. /* do actual readings from kernel */
  1482. static int ps_read(void) {
  1483. #if HAVE_THREAD_INFO
  1484. kern_return_t status;
  1485. processor_set_t port_pset_priv;
  1486. task_array_t task_list;
  1487. mach_msg_type_number_t task_list_len;
  1488. int task_pid;
  1489. char task_name[MAXCOMLEN + 1];
  1490. thread_act_array_t thread_list;
  1491. mach_msg_type_number_t thread_list_len;
  1492. thread_basic_info_data_t thread_data;
  1493. mach_msg_type_number_t thread_data_len;
  1494. int running = 0;
  1495. int sleeping = 0;
  1496. int zombies = 0;
  1497. int stopped = 0;
  1498. int blocked = 0;
  1499. procstat_t *ps;
  1500. process_entry_t pse;
  1501. ps_list_reset();
  1502. /*
  1503. * The Mach-concept is a little different from the traditional UNIX
  1504. * concept: All the work is done in threads. Threads are contained in
  1505. * `tasks'. Therefore, `task status' doesn't make much sense, since
  1506. * it's actually a `thread status'.
  1507. * Tasks are assigned to sets of processors, so that's where you go to
  1508. * get a list.
  1509. */
  1510. for (mach_msg_type_number_t pset = 0; pset < pset_list_len; pset++) {
  1511. if ((status = host_processor_set_priv(port_host_self, pset_list[pset],
  1512. &port_pset_priv)) != KERN_SUCCESS) {
  1513. ERROR("host_processor_set_priv failed: %s\n", mach_error_string(status));
  1514. continue;
  1515. }
  1516. if ((status = processor_set_tasks(port_pset_priv, &task_list,
  1517. &task_list_len)) != KERN_SUCCESS) {
  1518. ERROR("processor_set_tasks failed: %s\n", mach_error_string(status));
  1519. mach_port_deallocate(port_task_self, port_pset_priv);
  1520. continue;
  1521. }
  1522. for (mach_msg_type_number_t task = 0; task < task_list_len; task++) {
  1523. ps = NULL;
  1524. if (mach_get_task_name(task_list[task], &task_pid, task_name,
  1525. PROCSTAT_NAME_LEN) == 0) {
  1526. /* search for at least one match */
  1527. for (ps = list_head_g; ps != NULL; ps = ps->next)
  1528. /* FIXME: cmdline should be here instead of NULL */
  1529. if (ps_list_match(task_name, NULL, ps) == 1)
  1530. break;
  1531. }
  1532. /* Collect more detailed statistics for this process */
  1533. if (ps != NULL) {
  1534. task_basic_info_data_t task_basic_info;
  1535. mach_msg_type_number_t task_basic_info_len;
  1536. task_events_info_data_t task_events_info;
  1537. mach_msg_type_number_t task_events_info_len;
  1538. task_absolutetime_info_data_t task_absolutetime_info;
  1539. mach_msg_type_number_t task_absolutetime_info_len;
  1540. memset(&pse, '\0', sizeof(pse));
  1541. pse.id = task_pid;
  1542. task_basic_info_len = TASK_BASIC_INFO_COUNT;
  1543. status = task_info(task_list[task], TASK_BASIC_INFO,
  1544. (task_info_t)&task_basic_info, &task_basic_info_len);
  1545. if (status != KERN_SUCCESS) {
  1546. ERROR("task_info failed: %s", mach_error_string(status));
  1547. continue; /* with next thread_list */
  1548. }
  1549. task_events_info_len = TASK_EVENTS_INFO_COUNT;
  1550. status =
  1551. task_info(task_list[task], TASK_EVENTS_INFO,
  1552. (task_info_t)&task_events_info, &task_events_info_len);
  1553. if (status != KERN_SUCCESS) {
  1554. ERROR("task_info failed: %s", mach_error_string(status));
  1555. continue; /* with next thread_list */
  1556. }
  1557. task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
  1558. status = task_info(task_list[task], TASK_ABSOLUTETIME_INFO,
  1559. (task_info_t)&task_absolutetime_info,
  1560. &task_absolutetime_info_len);
  1561. if (status != KERN_SUCCESS) {
  1562. ERROR("task_info failed: %s", mach_error_string(status));
  1563. continue; /* with next thread_list */
  1564. }
  1565. pse.num_proc++;
  1566. pse.vmem_size = task_basic_info.virtual_size;
  1567. pse.vmem_rss = task_basic_info.resident_size;
  1568. /* Does not seem to be easily exposed */
  1569. pse.vmem_data = 0;
  1570. pse.vmem_code = 0;
  1571. pse.io_rchar = -1;
  1572. pse.io_wchar = -1;
  1573. pse.io_syscr = -1;
  1574. pse.io_syscw = -1;
  1575. pse.io_diskr = -1;
  1576. pse.io_diskw = -1;
  1577. /* File descriptor count not implemented */
  1578. pse.num_fd = 0;
  1579. /* Number of memory mappings */
  1580. pse.num_maps = 0;
  1581. pse.vmem_minflt_counter = task_events_info.cow_faults;
  1582. pse.vmem_majflt_counter = task_events_info.faults;
  1583. pse.cpu_user_counter = task_absolutetime_info.total_user;
  1584. pse.cpu_system_counter = task_absolutetime_info.total_system;
  1585. /* context switch counters not implemented */
  1586. pse.cswitch_vol = -1;
  1587. pse.cswitch_invol = -1;
  1588. }
  1589. status = task_threads(task_list[task], &thread_list, &thread_list_len);
  1590. if (status != KERN_SUCCESS) {
  1591. /* Apple's `top' treats this case a zombie. It
  1592. * makes sense to some extend: A `zombie'
  1593. * thread is nonsense, since the task/process
  1594. * is dead. */
  1595. zombies++;
  1596. DEBUG("task_threads failed: %s", mach_error_string(status));
  1597. if (task_list[task] != port_task_self)
  1598. mach_port_deallocate(port_task_self, task_list[task]);
  1599. continue; /* with next task_list */
  1600. }
  1601. for (mach_msg_type_number_t thread = 0; thread < thread_list_len;
  1602. thread++) {
  1603. thread_data_len = THREAD_BASIC_INFO_COUNT;
  1604. status = thread_info(thread_list[thread], THREAD_BASIC_INFO,
  1605. (thread_info_t)&thread_data, &thread_data_len);
  1606. if (status != KERN_SUCCESS) {
  1607. ERROR("thread_info failed: %s", mach_error_string(status));
  1608. if (task_list[task] != port_task_self)
  1609. mach_port_deallocate(port_task_self, thread_list[thread]);
  1610. continue; /* with next thread_list */
  1611. }
  1612. if (ps != NULL)
  1613. pse.num_lwp++;
  1614. switch (thread_data.run_state) {
  1615. case TH_STATE_RUNNING:
  1616. running++;
  1617. break;
  1618. case TH_STATE_STOPPED:
  1619. /* What exactly is `halted'? */
  1620. case TH_STATE_HALTED:
  1621. stopped++;
  1622. break;
  1623. case TH_STATE_WAITING:
  1624. sleeping++;
  1625. break;
  1626. case TH_STATE_UNINTERRUPTIBLE:
  1627. blocked++;
  1628. break;
  1629. /* There is no `zombie' case here,
  1630. * since there are no zombie-threads.
  1631. * There's only zombie tasks, which are
  1632. * handled above. */
  1633. default:
  1634. WARNING("Unknown thread status: %i", thread_data.run_state);
  1635. break;
  1636. } /* switch (thread_data.run_state) */
  1637. if (task_list[task] != port_task_self) {
  1638. status = mach_port_deallocate(port_task_self, thread_list[thread]);
  1639. if (status != KERN_SUCCESS)
  1640. ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
  1641. }
  1642. } /* for (thread_list) */
  1643. if ((status = vm_deallocate(port_task_self, (vm_address_t)thread_list,
  1644. thread_list_len * sizeof(thread_act_t))) !=
  1645. KERN_SUCCESS) {
  1646. ERROR("vm_deallocate failed: %s", mach_error_string(status));
  1647. }
  1648. thread_list = NULL;
  1649. thread_list_len = 0;
  1650. /* Only deallocate the task port, if it isn't our own.
  1651. * Don't know what would happen in that case, but this
  1652. * is what Apple's top does.. ;) */
  1653. if (task_list[task] != port_task_self) {
  1654. status = mach_port_deallocate(port_task_self, task_list[task]);
  1655. if (status != KERN_SUCCESS)
  1656. ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
  1657. }
  1658. if (ps != NULL)
  1659. /* FIXME: cmdline should be here instead of NULL */
  1660. ps_list_add(task_name, NULL, &pse);
  1661. } /* for (task_list) */
  1662. if ((status = vm_deallocate(port_task_self, (vm_address_t)task_list,
  1663. task_list_len * sizeof(task_t))) !=
  1664. KERN_SUCCESS) {
  1665. ERROR("vm_deallocate failed: %s", mach_error_string(status));
  1666. }
  1667. task_list = NULL;
  1668. task_list_len = 0;
  1669. if ((status = mach_port_deallocate(port_task_self, port_pset_priv)) !=
  1670. KERN_SUCCESS) {
  1671. ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
  1672. }
  1673. } /* for (pset_list) */
  1674. ps_submit_state("running", running);
  1675. ps_submit_state("sleeping", sleeping);
  1676. ps_submit_state("zombies", zombies);
  1677. ps_submit_state("stopped", stopped);
  1678. ps_submit_state("blocked", blocked);
  1679. for (ps = list_head_g; ps != NULL; ps = ps->next)
  1680. ps_submit_proc_list(ps);
  1681. /* #endif HAVE_THREAD_INFO */
  1682. #elif KERNEL_LINUX
  1683. int running = 0;
  1684. int sleeping = 0;
  1685. int zombies = 0;
  1686. int stopped = 0;
  1687. int paging = 0;
  1688. int blocked = 0;
  1689. struct dirent *ent;
  1690. DIR *proc;
  1691. long pid;
  1692. char cmdline[CMDLINE_BUFFER_SIZE];
  1693. int status;
  1694. process_entry_t pse;
  1695. char state;
  1696. running = sleeping = zombies = stopped = paging = blocked = 0;
  1697. ps_list_reset();
  1698. if ((proc = opendir("/proc")) == NULL) {
  1699. ERROR("Cannot open `/proc': %s", STRERRNO);
  1700. return -1;
  1701. }
  1702. while ((ent = readdir(proc)) != NULL) {
  1703. if (!isdigit(ent->d_name[0]))
  1704. continue;
  1705. if ((pid = atol(ent->d_name)) < 1)
  1706. continue;
  1707. memset(&pse, 0, sizeof(pse));
  1708. pse.id = pid;
  1709. status = ps_read_process(pid, &pse, &state);
  1710. if (status != 0) {
  1711. DEBUG("ps_read_process failed: %i", status);
  1712. continue;
  1713. }
  1714. switch (state) {
  1715. case 'R':
  1716. running++;
  1717. break;
  1718. case 'S':
  1719. sleeping++;
  1720. break;
  1721. case 'D':
  1722. blocked++;
  1723. break;
  1724. case 'Z':
  1725. zombies++;
  1726. break;
  1727. case 'T':
  1728. stopped++;
  1729. break;
  1730. case 'W':
  1731. paging++;
  1732. break;
  1733. }
  1734. ps_list_add(pse.name,
  1735. ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
  1736. }
  1737. closedir(proc);
  1738. ps_submit_state("running", running);
  1739. ps_submit_state("sleeping", sleeping);
  1740. ps_submit_state("zombies", zombies);
  1741. ps_submit_state("stopped", stopped);
  1742. ps_submit_state("paging", paging);
  1743. ps_submit_state("blocked", blocked);
  1744. for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
  1745. ps_submit_proc_list(ps_ptr);
  1746. read_fork_rate();
  1747. /* #endif KERNEL_LINUX */
  1748. #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
  1749. int running = 0;
  1750. int sleeping = 0;
  1751. int zombies = 0;
  1752. int stopped = 0;
  1753. int blocked = 0;
  1754. int idle = 0;
  1755. int wait = 0;
  1756. kvm_t *kd;
  1757. char errbuf[_POSIX2_LINE_MAX];
  1758. struct kinfo_proc *procs; /* array of processes */
  1759. struct kinfo_proc *proc_ptr = NULL;
  1760. int count; /* returns number of processes */
  1761. process_entry_t pse;
  1762. ps_list_reset();
  1763. /* Open the kvm interface, get a descriptor */
  1764. kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
  1765. if (kd == NULL) {
  1766. ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
  1767. return 0;
  1768. }
  1769. /* Get the list of processes. */
  1770. procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
  1771. if (procs == NULL) {
  1772. ERROR("processes plugin: Cannot get kvm processes list: %s",
  1773. kvm_geterr(kd));
  1774. kvm_close(kd);
  1775. return 0;
  1776. }
  1777. /* Iterate through the processes in kinfo_proc */
  1778. for (int i = 0; i < count; i++) {
  1779. /* Create only one process list entry per _process_, i.e.
  1780. * filter out threads (duplicate PID entries). */
  1781. if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
  1782. char cmdline[CMDLINE_BUFFER_SIZE] = "";
  1783. bool have_cmdline = 0;
  1784. proc_ptr = &(procs[i]);
  1785. /* Don't probe system processes and processes without arguments */
  1786. if (((procs[i].ki_flag & P_SYSTEM) == 0) && (procs[i].ki_args != NULL)) {
  1787. char **argv;
  1788. int argc;
  1789. int status;
  1790. /* retrieve the arguments */
  1791. argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
  1792. argc = 0;
  1793. if ((argv != NULL) && (argv[0] != NULL)) {
  1794. while (argv[argc] != NULL)
  1795. argc++;
  1796. status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
  1797. if (status < 0)
  1798. WARNING("processes plugin: Command line did not fit into buffer.");
  1799. else
  1800. have_cmdline = 1;
  1801. }
  1802. } /* if (process has argument list) */
  1803. memset(&pse, 0, sizeof(pse));
  1804. pse.id = procs[i].ki_pid;
  1805. pse.num_proc = 1;
  1806. pse.num_lwp = procs[i].ki_numthreads;
  1807. pse.vmem_size = procs[i].ki_size;
  1808. pse.vmem_rss = procs[i].ki_rssize * pagesize;
  1809. pse.vmem_data = procs[i].ki_dsize * pagesize;
  1810. pse.vmem_code = procs[i].ki_tsize * pagesize;
  1811. pse.stack_size = procs[i].ki_ssize * pagesize;
  1812. pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
  1813. pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
  1814. pse.cpu_user_counter = 0;
  1815. pse.cpu_system_counter = 0;
  1816. /*
  1817. * The u-area might be swapped out, and we can't get
  1818. * at it because we have a crashdump and no swap.
  1819. * If it's here fill in these fields, otherwise, just
  1820. * leave them 0.
  1821. */
  1822. if (procs[i].ki_flag & P_INMEM) {
  1823. pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec +
  1824. (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
  1825. pse.cpu_system_counter =
  1826. procs[i].ki_rusage.ru_stime.tv_usec +
  1827. (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
  1828. }
  1829. /* no I/O data */
  1830. pse.io_rchar = -1;
  1831. pse.io_wchar = -1;
  1832. pse.io_syscr = -1;
  1833. pse.io_syscw = -1;
  1834. pse.io_diskr = -1;
  1835. pse.io_diskw = -1;
  1836. /* file descriptor count not implemented */
  1837. pse.num_fd = 0;
  1838. /* Number of memory mappings */
  1839. pse.num_maps = 0;
  1840. /* context switch counters not implemented */
  1841. pse.cswitch_vol = -1;
  1842. pse.cswitch_invol = -1;
  1843. ps_list_add(procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
  1844. switch (procs[i].ki_stat) {
  1845. case SSTOP:
  1846. stopped++;
  1847. break;
  1848. case SSLEEP:
  1849. sleeping++;
  1850. break;
  1851. case SRUN:
  1852. running++;
  1853. break;
  1854. case SIDL:
  1855. idle++;
  1856. break;
  1857. case SWAIT:
  1858. wait++;
  1859. break;
  1860. case SLOCK:
  1861. blocked++;
  1862. break;
  1863. case SZOMB:
  1864. zombies++;
  1865. break;
  1866. }
  1867. } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
  1868. }
  1869. kvm_close(kd);
  1870. ps_submit_state("running", running);
  1871. ps_submit_state("sleeping", sleeping);
  1872. ps_submit_state("zombies", zombies);
  1873. ps_submit_state("stopped", stopped);
  1874. ps_submit_state("blocked", blocked);
  1875. ps_submit_state("idle", idle);
  1876. ps_submit_state("wait", wait);
  1877. for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
  1878. ps_submit_proc_list(ps_ptr);
  1879. /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
  1880. #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
  1881. int running = 0;
  1882. int sleeping = 0;
  1883. int zombies = 0;
  1884. int stopped = 0;
  1885. int onproc = 0;
  1886. int idle = 0;
  1887. int dead = 0;
  1888. kvm_t *kd;
  1889. char errbuf[1024];
  1890. struct kinfo_proc *procs; /* array of processes */
  1891. struct kinfo_proc *proc_ptr = NULL;
  1892. int count; /* returns number of processes */
  1893. process_entry_t pse;
  1894. ps_list_reset();
  1895. /* Open the kvm interface, get a descriptor */
  1896. kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
  1897. if (kd == NULL) {
  1898. ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
  1899. return 0;
  1900. }
  1901. /* Get the list of processes. */
  1902. procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
  1903. if (procs == NULL) {
  1904. ERROR("processes plugin: Cannot get kvm processes list: %s",
  1905. kvm_geterr(kd));
  1906. kvm_close(kd);
  1907. return 0;
  1908. }
  1909. /* Iterate through the processes in kinfo_proc */
  1910. for (int i = 0; i < count; i++) {
  1911. /* Create only one process list entry per _process_, i.e.
  1912. * filter out threads (duplicate PID entries). */
  1913. if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
  1914. char cmdline[CMDLINE_BUFFER_SIZE] = "";
  1915. bool have_cmdline = 0;
  1916. proc_ptr = &(procs[i]);
  1917. /* Don't probe zombie processes */
  1918. if (!P_ZOMBIE(proc_ptr)) {
  1919. char **argv;
  1920. int argc;
  1921. int status;
  1922. /* retrieve the arguments */
  1923. argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
  1924. argc = 0;
  1925. if ((argv != NULL) && (argv[0] != NULL)) {
  1926. while (argv[argc] != NULL)
  1927. argc++;
  1928. status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
  1929. if (status < 0)
  1930. WARNING("processes plugin: Command line did not fit into buffer.");
  1931. else
  1932. have_cmdline = 1;
  1933. }
  1934. } /* if (process has argument list) */
  1935. memset(&pse, 0, sizeof(pse));
  1936. pse.id = procs[i].p_pid;
  1937. pse.num_proc = 1;
  1938. pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
  1939. pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
  1940. pse.vmem_data = procs[i].p_vm_dsize * pagesize;
  1941. pse.vmem_code = procs[i].p_vm_tsize * pagesize;
  1942. pse.stack_size = procs[i].p_vm_ssize * pagesize;
  1943. pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
  1944. pse.vmem_minflt_counter = procs[i].p_uru_minflt;
  1945. pse.vmem_majflt_counter = procs[i].p_uru_majflt;
  1946. pse.cpu_user_counter =
  1947. procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
  1948. pse.cpu_system_counter =
  1949. procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
  1950. /* no I/O data */
  1951. pse.io_rchar = -1;
  1952. pse.io_wchar = -1;
  1953. pse.io_syscr = -1;
  1954. pse.io_syscw = -1;
  1955. pse.io_diskr = -1;
  1956. pse.io_diskw = -1;
  1957. /* file descriptor count not implemented */
  1958. pse.num_fd = 0;
  1959. /* Number of memory mappings */
  1960. pse.num_maps = 0;
  1961. /* context switch counters not implemented */
  1962. pse.cswitch_vol = -1;
  1963. pse.cswitch_invol = -1;
  1964. ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
  1965. switch (procs[i].p_stat) {
  1966. case SSTOP:
  1967. stopped++;
  1968. break;
  1969. case SSLEEP:
  1970. sleeping++;
  1971. break;
  1972. case SRUN:
  1973. running++;
  1974. break;
  1975. case SIDL:
  1976. idle++;
  1977. break;
  1978. case SONPROC:
  1979. onproc++;
  1980. break;
  1981. case SDEAD:
  1982. dead++;
  1983. break;
  1984. case SZOMB:
  1985. zombies++;
  1986. break;
  1987. }
  1988. } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
  1989. }
  1990. kvm_close(kd);
  1991. ps_submit_state("running", running);
  1992. ps_submit_state("sleeping", sleeping);
  1993. ps_submit_state("zombies", zombies);
  1994. ps_submit_state("stopped", stopped);
  1995. ps_submit_state("onproc", onproc);
  1996. ps_submit_state("idle", idle);
  1997. ps_submit_state("dead", dead);
  1998. for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
  1999. ps_submit_proc_list(ps_ptr);
  2000. /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
  2001. #elif HAVE_PROCINFO_H
  2002. /* AIX */
  2003. int running = 0;
  2004. int sleeping = 0;
  2005. int zombies = 0;
  2006. int stopped = 0;
  2007. int paging = 0;
  2008. int blocked = 0;
  2009. pid_t pindex = 0;
  2010. int nprocs;
  2011. process_entry_t pse;
  2012. ps_list_reset();
  2013. while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
  2014. /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
  2015. &pindex, MAXPROCENTRY)) > 0) {
  2016. for (int i = 0; i < nprocs; i++) {
  2017. tid64_t thindex;
  2018. int nthreads;
  2019. char arglist[MAXARGLN + 1];
  2020. char *cargs;
  2021. char *cmdline;
  2022. if (procentry[i].pi_state == SNONE)
  2023. continue;
  2024. /* if (procentry[i].pi_state == SZOMB) FIXME */
  2025. cmdline = procentry[i].pi_comm;
  2026. cargs = procentry[i].pi_comm;
  2027. if (procentry[i].pi_flags & SKPROC) {
  2028. if (procentry[i].pi_pid == 0)
  2029. cmdline = "swapper";
  2030. cargs = cmdline;
  2031. } else {
  2032. if (getargs(&procentry[i], sizeof(struct procentry64), arglist,
  2033. MAXARGLN) >= 0) {
  2034. int n;
  2035. n = -1;
  2036. while (++n < MAXARGLN) {
  2037. if (arglist[n] == '\0') {
  2038. if (arglist[n + 1] == '\0')
  2039. break;
  2040. arglist[n] = ' ';
  2041. }
  2042. }
  2043. cargs = arglist;
  2044. }
  2045. }
  2046. memset(&pse, 0, sizeof(pse));
  2047. pse.id = procentry[i].pi_pid;
  2048. pse.num_lwp = procentry[i].pi_thcount;
  2049. pse.num_proc = 1;
  2050. thindex = 0;
  2051. while ((nthreads = getthrds64(procentry[i].pi_pid, thrdentry,
  2052. sizeof(struct thrdentry64), &thindex,
  2053. MAXTHRDENTRY)) > 0) {
  2054. int j;
  2055. for (j = 0; j < nthreads; j++) {
  2056. switch (thrdentry[j].ti_state) {
  2057. /* case TSNONE: break; */
  2058. case TSIDL:
  2059. blocked++;
  2060. break; /* FIXME is really blocked */
  2061. case TSRUN:
  2062. running++;
  2063. break;
  2064. case TSSLEEP:
  2065. sleeping++;
  2066. break;
  2067. case TSSWAP:
  2068. paging++;
  2069. break;
  2070. case TSSTOP:
  2071. stopped++;
  2072. break;
  2073. case TSZOMB:
  2074. zombies++;
  2075. break;
  2076. }
  2077. }
  2078. if (nthreads < MAXTHRDENTRY)
  2079. break;
  2080. }
  2081. /* tv_usec is nanosec ??? */
  2082. pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
  2083. procentry[i].pi_ru.ru_utime.tv_usec / 1000;
  2084. /* tv_usec is nanosec ??? */
  2085. pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
  2086. procentry[i].pi_ru.ru_stime.tv_usec / 1000;
  2087. pse.vmem_minflt_counter = procentry[i].pi_minflt;
  2088. pse.vmem_majflt_counter = procentry[i].pi_majflt;
  2089. pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
  2090. pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
  2091. /* Not supported/implemented */
  2092. pse.vmem_data = 0;
  2093. pse.vmem_code = 0;
  2094. pse.stack_size = 0;
  2095. pse.io_rchar = -1;
  2096. pse.io_wchar = -1;
  2097. pse.io_syscr = -1;
  2098. pse.io_syscw = -1;
  2099. pse.io_diskr = -1;
  2100. pse.io_diskw = -1;
  2101. pse.num_fd = 0;
  2102. pse.num_maps = 0;
  2103. pse.cswitch_vol = -1;
  2104. pse.cswitch_invol = -1;
  2105. ps_list_add(cmdline, cargs, &pse);
  2106. } /* for (i = 0 .. nprocs) */
  2107. if (nprocs < MAXPROCENTRY)
  2108. break;
  2109. } /* while (getprocs64() > 0) */
  2110. ps_submit_state("running", running);
  2111. ps_submit_state("sleeping", sleeping);
  2112. ps_submit_state("zombies", zombies);
  2113. ps_submit_state("stopped", stopped);
  2114. ps_submit_state("paging", paging);
  2115. ps_submit_state("blocked", blocked);
  2116. for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next)
  2117. ps_submit_proc_list(ps);
  2118. /* #endif HAVE_PROCINFO_H */
  2119. #elif KERNEL_SOLARIS
  2120. /*
  2121. * The Solaris section adds a few more process states and removes some
  2122. * process states compared to linux. Most notably there is no "PAGING"
  2123. * and "BLOCKED" state for a process. The rest is similar to the linux
  2124. * code.
  2125. */
  2126. int running = 0;
  2127. int sleeping = 0;
  2128. int zombies = 0;
  2129. int stopped = 0;
  2130. int detached = 0;
  2131. int daemon = 0;
  2132. int system = 0;
  2133. int orphan = 0;
  2134. struct dirent *ent;
  2135. DIR *proc;
  2136. int status;
  2137. char state;
  2138. char cmdline[PRARGSZ];
  2139. ps_list_reset();
  2140. proc = opendir("/proc");
  2141. if (proc == NULL)
  2142. return -1;
  2143. while ((ent = readdir(proc)) != NULL) {
  2144. long pid;
  2145. process_entry_t pse;
  2146. char *endptr;
  2147. if (!isdigit((int)ent->d_name[0]))
  2148. continue;
  2149. pid = strtol(ent->d_name, &endptr, 10);
  2150. if (*endptr != 0) /* value didn't completely parse as a number */
  2151. continue;
  2152. memset(&pse, 0, sizeof(pse));
  2153. pse.id = pid;
  2154. status = ps_read_process(pid, &pse, &state);
  2155. if (status != 0) {
  2156. DEBUG("ps_read_process failed: %i", status);
  2157. continue;
  2158. }
  2159. switch (state) {
  2160. case 'R':
  2161. running++;
  2162. break;
  2163. case 'S':
  2164. sleeping++;
  2165. break;
  2166. case 'E':
  2167. detached++;
  2168. break;
  2169. case 'Z':
  2170. zombies++;
  2171. break;
  2172. case 'T':
  2173. stopped++;
  2174. break;
  2175. case 'A':
  2176. daemon++;
  2177. break;
  2178. case 'Y':
  2179. system++;
  2180. break;
  2181. case 'O':
  2182. orphan++;
  2183. break;
  2184. }
  2185. ps_list_add(pse.name,
  2186. ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
  2187. } /* while(readdir) */
  2188. closedir(proc);
  2189. ps_submit_state("running", running);
  2190. ps_submit_state("sleeping", sleeping);
  2191. ps_submit_state("zombies", zombies);
  2192. ps_submit_state("stopped", stopped);
  2193. ps_submit_state("detached", detached);
  2194. ps_submit_state("daemon", daemon);
  2195. ps_submit_state("system", system);
  2196. ps_submit_state("orphan", orphan);
  2197. for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
  2198. ps_submit_proc_list(ps_ptr);
  2199. read_fork_rate();
  2200. #endif /* KERNEL_SOLARIS */
  2201. want_init = false;
  2202. return 0;
  2203. } /* int ps_read */
  2204. void module_register(void) {
  2205. plugin_register_complex_config("processes", ps_config);
  2206. plugin_register_init("processes", ps_init);
  2207. plugin_register_read("processes", ps_read);
  2208. } /* void module_register */