PageRenderTime 64ms CodeModel.GetById 24ms RepoModel.GetById 0ms 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

Large files files are truncated, but you can click here to view the full file

  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 …

Large files files are truncated, but you can click here to view the full file