summaryrefslogtreecommitdiff
path: root/src/pmdas/linux/proc_stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/linux/proc_stat.c')
-rw-r--r--src/pmdas/linux/proc_stat.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/pmdas/linux/proc_stat.c b/src/pmdas/linux/proc_stat.c
new file mode 100644
index 0000000..6a8a798
--- /dev/null
+++ b/src/pmdas/linux/proc_stat.c
@@ -0,0 +1,304 @@
+/*
+ * Linux /proc/stat metrics cluster
+ *
+ * Copyright (c) 2012-2014 Red Hat.
+ * Copyright (c) 2008-2009 Aconex. All Rights Reserved.
+ * Copyright (c) 2000,2004-2008 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * 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.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "indom.h"
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include "proc_cpuinfo.h"
+#include "proc_stat.h"
+
+int
+refresh_proc_stat(proc_cpuinfo_t *proc_cpuinfo, proc_stat_t *proc_stat)
+{
+ pmdaIndom *idp = PMDAINDOM(CPU_INDOM);
+ char buf[MAXPATHLEN];
+ char fmt[64];
+ static int fd = -1; /* kept open until exit() */
+ static int started;
+ static char *statbuf;
+ static int maxstatbuf;
+ static char **bufindex;
+ static int nbufindex;
+ static int maxbufindex;
+ int size;
+ int n;
+ int i;
+ int j;
+
+ if (fd >= 0) {
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return -oserror();
+ } else {
+ snprintf(buf, sizeof(buf), "%s/proc/stat", linux_statspath);
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ return -oserror();
+ }
+
+ for (n=0;;) {
+ while (n >= maxstatbuf) {
+ size = maxstatbuf + 512;
+ if ((statbuf = (char *)realloc(statbuf, size)) == NULL)
+ return -ENOMEM;
+ maxstatbuf = size;
+ }
+ size = (statbuf + maxstatbuf) - (statbuf + n);
+ if ((i = read(fd, statbuf + n, size)) > 0)
+ n += i;
+ else
+ break;
+ }
+ statbuf[n] = '\0';
+
+ if (bufindex == NULL) {
+ size = 4 * sizeof(char *);
+ if ((bufindex = (char **)malloc(size)) == NULL)
+ return -ENOMEM;
+ maxbufindex = 4;
+ }
+
+ nbufindex = 0;
+ bufindex[nbufindex] = statbuf;
+ for (i=0; i < n; i++) {
+ if (statbuf[i] == '\n' || statbuf[i] == '\0') {
+ statbuf[i] = '\0';
+ if (nbufindex + 1 >= maxbufindex) {
+ size = (maxbufindex + 4) * sizeof(char *);
+ if ((bufindex = (char **)realloc(bufindex, size)) == NULL)
+ return -ENOMEM;
+ maxbufindex += 4;
+ }
+ bufindex[++nbufindex] = statbuf + i + 1;
+ }
+ }
+
+ if (!started) {
+ started = 1;
+ memset(proc_stat, 0, sizeof(*proc_stat));
+
+ /* hz of running kernel */
+ proc_stat->hz = sysconf(_SC_CLK_TCK);
+
+ /* scan ncpus */
+ for (i=0; i < nbufindex; i++) {
+ if (strncmp("cpu", bufindex[i], 3) == 0 && isdigit((int)bufindex[i][3]))
+ proc_stat->ncpu++;
+ }
+ if (proc_stat->ncpu == 0)
+ proc_stat->ncpu = 1; /* non-SMP kernel? */
+ proc_stat->cpu_indom = idp;
+ proc_stat->cpu_indom->it_numinst = proc_stat->ncpu;
+ proc_stat->cpu_indom->it_set = (pmdaInstid *)malloc(
+ proc_stat->ncpu * sizeof(pmdaInstid));
+ /*
+ * Map out the CPU instance domain.
+ *
+ * The first call to cpu_name() does initialization on the
+ * proc_cpuinfo structure.
+ */
+ for (i=0; i < proc_stat->ncpu; i++) {
+ proc_stat->cpu_indom->it_set[i].i_inst = i;
+ proc_stat->cpu_indom->it_set[i].i_name = cpu_name(proc_cpuinfo, i);
+ }
+
+ n = proc_stat->ncpu * sizeof(unsigned long long);
+ proc_stat->p_user = (unsigned long long *)calloc(1, n);
+ proc_stat->p_nice = (unsigned long long *)calloc(1, n);
+ proc_stat->p_sys = (unsigned long long *)calloc(1, n);
+ proc_stat->p_idle = (unsigned long long *)calloc(1, n);
+ proc_stat->p_wait = (unsigned long long *)calloc(1, n);
+ proc_stat->p_irq = (unsigned long long *)calloc(1, n);
+ proc_stat->p_sirq = (unsigned long long *)calloc(1, n);
+ proc_stat->p_steal = (unsigned long long *)calloc(1, n);
+ proc_stat->p_guest = (unsigned long long *)calloc(1, n);
+
+ n = proc_cpuinfo->node_indom->it_numinst * sizeof(unsigned long long);
+ proc_stat->n_user = calloc(1, n);
+ proc_stat->n_nice = calloc(1, n);
+ proc_stat->n_sys = calloc(1, n);
+ proc_stat->n_idle = calloc(1, n);
+ proc_stat->n_wait = calloc(1, n);
+ proc_stat->n_irq = calloc(1, n);
+ proc_stat->n_sirq = calloc(1, n);
+ proc_stat->n_steal = calloc(1, n);
+ proc_stat->n_guest = calloc(1, n);
+ }
+ else {
+ /* reset per-node stats */
+ n = proc_cpuinfo->node_indom->it_numinst * sizeof(unsigned long long);
+ memset(proc_stat->n_user, 0, n);
+ memset(proc_stat->n_nice, 0, n);
+ memset(proc_stat->n_sys, 0, n);
+ memset(proc_stat->n_idle, 0, n);
+ memset(proc_stat->n_wait, 0, n);
+ memset(proc_stat->n_irq, 0, n);
+ memset(proc_stat->n_sirq, 0, n);
+ memset(proc_stat->n_steal, 0, n);
+ memset(proc_stat->n_guest, 0, n);
+ }
+ /*
+ * cpu 95379 4 20053 6502503
+ * 2.6 kernels have 3 additional fields
+ * for wait, irq and soft_irq.
+ */
+ strcpy(fmt, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu");
+ n = sscanf((const char *)bufindex[0], fmt,
+ &proc_stat->user, &proc_stat->nice,
+ &proc_stat->sys, &proc_stat->idle,
+ &proc_stat->wait, &proc_stat->irq,
+ &proc_stat->sirq, &proc_stat->steal,
+ &proc_stat->guest);
+
+ /*
+ * per-cpu stats
+ * e.g. cpu0 95379 4 20053 6502503
+ * 2.6 kernels have 3 additional fields for wait, irq and soft_irq.
+ * More recent (2008) 2.6 kernels have an extra field for guest.
+ */
+ if (proc_stat->ncpu == 1) {
+ /*
+ * Don't bother scanning - the counters are the same
+ * as for "all" cpus, as already scanned above.
+ * This also handles the non-SMP code where
+ * there is no line starting with "cpu0".
+ */
+ proc_stat->p_user[0] = proc_stat->user;
+ proc_stat->p_nice[0] = proc_stat->nice;
+ proc_stat->p_sys[0] = proc_stat->sys;
+ proc_stat->p_idle[0] = proc_stat->idle;
+ proc_stat->p_wait[0] = proc_stat->wait;
+ proc_stat->p_irq[0] = proc_stat->irq;
+ proc_stat->p_sirq[0] = proc_stat->sirq;
+ proc_stat->p_steal[0] = proc_stat->steal;
+ proc_stat->p_guest[0] = proc_stat->guest;
+ }
+ else {
+ strcpy(fmt, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu");
+ for (i=0; i < proc_stat->ncpu; i++) {
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp("cpu", bufindex[j], 3) == 0 && isdigit((int)bufindex[j][3])) {
+ int c;
+ int cpunum = atoi(&bufindex[j][3]);
+ int node;
+ if (cpunum >= 0 && cpunum < proc_stat->ncpu) {
+ n = sscanf(bufindex[j], fmt, &c,
+ &proc_stat->p_user[cpunum],
+ &proc_stat->p_nice[cpunum],
+ &proc_stat->p_sys[cpunum],
+ &proc_stat->p_idle[cpunum],
+ &proc_stat->p_wait[cpunum],
+ &proc_stat->p_irq[cpunum],
+ &proc_stat->p_sirq[cpunum],
+ &proc_stat->p_steal[cpunum],
+ &proc_stat->p_guest[cpunum]);
+ if ((node = proc_cpuinfo->cpuinfo[cpunum].node) != -1) {
+ proc_stat->n_user[node] += proc_stat->p_user[cpunum];
+ proc_stat->n_nice[node] += proc_stat->p_nice[cpunum];
+ proc_stat->n_sys[node] += proc_stat->p_sys[cpunum];
+ proc_stat->n_idle[node] += proc_stat->p_idle[cpunum];
+ proc_stat->n_wait[node] += proc_stat->p_wait[cpunum];
+ proc_stat->n_irq[node] += proc_stat->p_irq[cpunum];
+ proc_stat->n_sirq[node] += proc_stat->p_sirq[cpunum];
+ proc_stat->n_steal[node] += proc_stat->p_steal[cpunum];
+ proc_stat->n_guest[node] += proc_stat->p_guest[cpunum];
+ }
+ }
+ }
+ }
+ if (j == nbufindex)
+ break;
+ }
+ }
+
+ /*
+ * page 59739 34786
+ * Note: this has moved to /proc/vmstat in 2.6 kernels
+ */
+ strcpy(fmt, "page %u %u");
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp(fmt, bufindex[j], 5) == 0) {
+ sscanf((const char *)bufindex[j], fmt,
+ &proc_stat->page[0], &proc_stat->page[1]);
+ break;
+ }
+ }
+
+ /*
+ * swap 0 1
+ * Note: this has moved to /proc/vmstat in 2.6 kernels
+ */
+ strcpy(fmt, "swap %u %u");
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp(fmt, bufindex[j], 5) == 0) {
+ sscanf((const char *)bufindex[j], fmt,
+ &proc_stat->swap[0], &proc_stat->swap[1]);
+ break;
+ }
+ }
+
+ /*
+ * intr 32845463 24099228 2049 0 2 ....
+ * (just export the first number, which is total interrupts)
+ */
+ strcpy(fmt, "intr %llu");
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp(fmt, bufindex[j], 5) == 0) {
+ sscanf((const char *)bufindex[j], fmt, &proc_stat->intr);
+ break;
+ }
+ }
+
+ /*
+ * ctxt 1733480
+ */
+ strcpy(fmt, "ctxt %llu");
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp(fmt, bufindex[j], 5) == 0) {
+ sscanf((const char *)bufindex[j], fmt, &proc_stat->ctxt);
+ break;
+ }
+ }
+
+ /*
+ * btime 1733480
+ */
+ strcpy(fmt, "btime %lu");
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp(fmt, bufindex[j], 6) == 0) {
+ sscanf((const char *)bufindex[j], fmt, &proc_stat->btime);
+ break;
+ }
+ }
+
+ /*
+ * processes 2213
+ */
+ strcpy(fmt, "processes %lu");
+ for (j=0; j < nbufindex; j++) {
+ if (strncmp(fmt, bufindex[j], 10) == 0) {
+ sscanf((const char *)bufindex[j], fmt, &proc_stat->processes);
+ break;
+ }
+ }
+
+ /* success */
+ return 0;
+}