diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/linux/proc_stat.c | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/linux/proc_stat.c')
-rw-r--r-- | src/pmdas/linux/proc_stat.c | 304 |
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; +} |