diff options
Diffstat (limited to 'src/collectl2pcp/proc.c')
-rw-r--r-- | src/collectl2pcp/proc.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/collectl2pcp/proc.c b/src/collectl2pcp/proc.c new file mode 100644 index 0000000..516087c --- /dev/null +++ b/src/collectl2pcp/proc.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2013 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * Handler for per-process metrics + * + * proc:28896 stat 28896 (bash) S 3574 28896 28896 34844 28896 4202496 2286 23473 1 21 4 2 486 129 20 0 1 0 125421198 119214080 85 18446744073709551615 4194304 5080360 140737040162832 140737040157848 212270400064 0 0 3686404 1266761467 18446744071582594345 0 0 17 2 0 0 0 0 0 9310744 9344396 14004224 + * + */ + +#include "metrics.h" + +#define ticks_to_msec(ticks) (1000ULL * strtoull(ticks, NULL, 0) / kernel_all_hz) + +/* /proc/PID/stat fields (starting at fields[2]) */ +#define PROC_PID_STAT_PID 0 +#define PROC_PID_STAT_CMD 1 +#define PROC_PID_STAT_STATE 2 +#define PROC_PID_STAT_PPID 3 +#define PROC_PID_STAT_PGRP 4 +#define PROC_PID_STAT_SESSION 5 +#define PROC_PID_STAT_TTY 6 +#define PROC_PID_STAT_TTY_PGRP 7 +#define PROC_PID_STAT_FLAGS 8 +#define PROC_PID_STAT_MINFLT 9 +#define PROC_PID_STAT_CMIN_FLT 10 +#define PROC_PID_STAT_MAJ_FLT 11 +#define PROC_PID_STAT_CMAJ_FLT 12 +#define PROC_PID_STAT_UTIME 13 +#define PROC_PID_STAT_STIME 14 +#define PROC_PID_STAT_CUTIME 15 +#define PROC_PID_STAT_CSTIME 16 +#define PROC_PID_STAT_PRIORITY 17 +#define PROC_PID_STAT_NICE 18 +#define PROC_PID_STAT_REMOVED 19 +#define PROC_PID_STAT_IT_REAL_VALUE 20 +#define PROC_PID_STAT_START_TIME 21 +#define PROC_PID_STAT_VSIZE 22 +#define PROC_PID_STAT_RSS 23 +#define PROC_PID_STAT_RSS_RLIM 24 +#define PROC_PID_STAT_START_CODE 25 +#define PROC_PID_STAT_END_CODE 26 +#define PROC_PID_STAT_START_STACK 27 +#define PROC_PID_STAT_ESP 28 +#define PROC_PID_STAT_EIP 29 +#define PROC_PID_STAT_SIGNAL 30 +#define PROC_PID_STAT_BLOCKED 31 +#define PROC_PID_STAT_SIGIGNORE 32 +#define PROC_PID_STAT_SIGCATCH 33 +#define PROC_PID_STAT_WCHAN 34 +#define PROC_PID_STAT_NSWAP 35 +#define PROC_PID_STAT_CNSWAP 36 +#define PROC_PID_STAT_EXIT_SIGNAL 37 +#define PROC_PID_STAT_PROCESSOR 38 +#define PROC_PID_STAT_TTYNAME 39 +#define PROC_PID_STAT_WCHAN_SYMBOL 40 +#define PROC_PID_STAT_PSARGS 41 + +static char *inst; +static fields_t *proc_stat; + +static int +find_command_start(const char *buf, size_t len) +{ + int i; + + /* skip over (minimal) leading "proc:N cmd " */ + for (i = 7; i < len - 4; i++) + if (strncmp(&buf[i], "cmd", 4) == 0) + return i + 4; + return -1; /* wha? cannot find the "cmd" component */ +} + +static void +inst_command_clean(char *command, size_t size) +{ + int i; + + /* command contains nulls - replace 'em */ + for (i = 0; i < size; i++) { + if (!isprint(command[i])) + command[i] = ' '; + } + /* and trailing whitespace - clean that */ + while (--size) { + if (isspace(command[size])) + command[size] = '\0'; + else + break; + } +} + +void +base_command_name(const char *command, char *base, size_t size) +{ + char *p, *start, *end; + int kernel = (command[0] == '('); /* kernel daemons heuristic */ + + /* moral equivalent of basename, dealing with args stripping too */ + for (p = end = start = (char *)command; *p; end = ++p) { + if (kernel) + continue; + else if (*p == '/') + start = end = p+1; + else if (isspace(*p)) + break; + } + size--; /* allow for a null */ + if (size > (end - start)) + size = (end - start); + memcpy(base, start, size); + base[size] = '\0'; +} + +int +proc_handler(handler_t *h, fields_t *f) +{ + int pid, off, bytes; + char *command; + size_t size; + + if (f->nfields < 2 || f->fieldlen[0] < 6) + return 0; + + if (strcmp(f->fields[1], "cmd") == 0) { + /* + * e.g. : + * proc:27041 cmd /bin/sh /usr/prod/mts/common/bin/dblogin_gateway_reader + */ + if ((off = find_command_start(f->buf, f->len)) < 0) + return 0; + size = f->len - off + 16; /* +16 for the "%06d " pid */ + if ((inst = (char *)malloc(size)) == NULL) + return 0; + sscanf(f->buf, "proc:%d", &pid); + bytes = snprintf(inst, size, "%06d ", pid); + + /* f->buf contains nulls - so memcpy it then replace 'em */ + size = f->len - off - 1; + command = inst + bytes; + memcpy(command, f->buf + off, size); + command[size] = '\0'; + inst_command_clean(command, size); + } + + if (inst == NULL && strcmp(f->fields[1], "stat") == 0) { + /* no instance yet, so stash it for later */ + proc_stat = fields_dup(f); + return 0; + } + + if (inst) { + pmInDom indom = pmInDom_build(PROC_DOMAIN, PROC_PROC_INDOM); + + if ((command = strchr(inst, ' ')) != NULL) { + char cmdname[MAXPATHLEN]; + + command++; + base_command_name(command, &cmdname[0], sizeof(cmdname)); + put_str_value("proc.psinfo.cmd", indom, inst, cmdname); + put_str_value("proc.psinfo.psargs", indom, inst, command); + } + + /* emit the stashed proc_stat fields */ + put_ull_value("proc.psinfo.utime", indom, inst, ticks_to_msec(proc_stat->fields[PROC_PID_STAT_UTIME+2])); + put_ull_value("proc.psinfo.stime", indom, inst, ticks_to_msec(proc_stat->fields[PROC_PID_STAT_STIME+2])); + put_str_value("proc.psinfo.processor", indom, inst, proc_stat->fields[PROC_PID_STAT_PROCESSOR+2]); + put_str_value("proc.psinfo.rss", indom, inst, proc_stat->fields[PROC_PID_STAT_RSS+2]); + put_str_value("proc.psinfo.vsize", indom, inst, proc_stat->fields[PROC_PID_STAT_VSIZE+2]); + put_str_value("proc.psinfo.sname", indom, inst, proc_stat->fields[PROC_PID_STAT_STATE+2]); + /* and the rest .. */ + fields_free(proc_stat); + proc_stat = NULL; + + /* TODO emit other stashed stuff .. */ + + free(inst); + inst = NULL; + } + + return 0; +} |