summaryrefslogtreecommitdiff
path: root/src/pmdas/freebsd
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/freebsd
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/freebsd')
-rw-r--r--src/pmdas/freebsd/GNUmakefile69
-rw-r--r--src/pmdas/freebsd/disk.c208
-rw-r--r--src/pmdas/freebsd/freebsd.c991
-rw-r--r--src/pmdas/freebsd/freebsd.h44
-rw-r--r--src/pmdas/freebsd/help95
-rw-r--r--src/pmdas/freebsd/netif.c225
-rw-r--r--src/pmdas/freebsd/root_freebsd172
7 files changed, 1804 insertions, 0 deletions
diff --git a/src/pmdas/freebsd/GNUmakefile b/src/pmdas/freebsd/GNUmakefile
new file mode 100644
index 0000000..5aa647d
--- /dev/null
+++ b/src/pmdas/freebsd/GNUmakefile
@@ -0,0 +1,69 @@
+#
+# Copyright (c) 2000,2003,2004,2008 Silicon Graphics, Inc. All Rights Reserved.
+# Copyright (c) 2007-2010 Aconex. All Rights Reserved.
+# Copyright (c) 2012 Ken McDonell. 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.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+IAM = freebsd
+DOMAIN = FREEBSD
+CMDTARGET = pmdafreebsd
+LIBTARGET = pmda_freebsd.so
+PMDADIR = $(PCP_PMDAS_DIR)/$(IAM)
+CONF_LINE = "freebsd 85 dso freebsd_init $(PMDADIR)/$(LIBTARGET)"
+
+CFILES = freebsd.c disk.c netif.c
+
+HFILES = freebsd.h
+
+LSRCFILES = help root_freebsd
+LDIRT = help.dir help.pag help.sed help.tmp domain.h $(IAM).log
+
+LLDLIBS = $(PCP_PMDALIB) -ldevstat
+
+default: build-me
+
+include $(BUILDRULES)
+
+ifeq "$(TARGET_OS)" "freebsd"
+build-me: domain.h $(LIBTARGET) $(CMDTARGET) help.dir help.pag
+ @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \
+ echo $(CONF_LINE) >> ../pmcd.conf ; \
+ fi
+
+install: default
+ $(INSTALL) -m 755 -d $(PMDADIR)
+ $(INSTALL) -m 644 domain.h help help.dir help.pag $(PMDADIR)
+ $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR)
+ $(INSTALL) -m 644 root_freebsd $(PCP_VAR_DIR)/pmns/root_freebsd
+else
+build-me:
+install:
+endif
+
+help.dir help.pag : domain.h help
+ $(SED) <domain.h >help.sed -n -e '/#define/s/#define[ ]*\([A-Z][A-Z]*\)[ ]*\([0-9][0-9]*\)/s@\1@\2@/p'
+ $(SED) -f help.sed <help >help.tmp
+ $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_freebsd -v 2 -o help <help.tmp
+ rm -f help.tmp help.tmp
+
+default_pcp : default
+
+install_pcp : install
+
+domain.h: ../../pmns/stdpmid
+ $(DOMAIN_MAKERULE)
+
+$(OBJECTS): domain.h freebsd.h
diff --git a/src/pmdas/freebsd/disk.c b/src/pmdas/freebsd/disk.c
new file mode 100644
index 0000000..c54de4d
--- /dev/null
+++ b/src/pmdas/freebsd/disk.c
@@ -0,0 +1,208 @@
+/*
+ * FreeBSD Kernel PMDA - disk metrics
+ *
+ * Copyright (c) 2012 Ken McDonell. 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include <devstat.h>
+#include "freebsd.h"
+
+struct devinfo devinfo = { 0 };
+struct statinfo statinfo;
+
+void
+refresh_disk_metrics(void)
+{
+ static int init_done = 0;
+ int i;
+ int sts;
+
+ if (!init_done) {
+ sts = devstat_checkversion(NULL);
+ if (sts != 0) {
+ fprintf(stderr, "refresh_disk_metrics: devstat_checkversion: failed! %s\n", devstat_errbuf);
+ exit(1);
+ }
+ statinfo.dinfo = &devinfo;
+ init_done = 1;
+ }
+
+ sts = devstat_getdevs(NULL, &statinfo);
+ if (sts < 0) {
+ fprintf(stderr, "refresh_disk_metrics: devstat_getdevs: %s\n", strerror(errno));
+ exit(1);
+ }
+ else if (sts == 1) {
+ /*
+ * First call, else devstat[] list has changed
+ */
+ struct devstat *dsp;
+ char iname[DEVSTAT_NAME_LEN+6];
+ pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_INACTIVE);
+ for (i = 0; i < devinfo.numdevs; i++) {
+ dsp = &devinfo.devices[i];
+ /*
+ * Skip entries that are not interesting ... only include
+ * "da" (direct access) disks at this stage
+ */
+ if (strcmp(dsp->device_name, "da") != 0)
+ continue;
+ snprintf(iname, sizeof(iname), "%s%d", dsp->device_name, dsp->unit_number);
+ sts = pmdaCacheLookupName(indomtab[DISK_INDOM].it_indom, iname, NULL, NULL);
+ if (sts == PMDA_CACHE_ACTIVE) {
+ int j;
+ fprintf(stderr, "refresh_disk_metrics: Warning: duplicate name (%s) in disk indom\n", iname);
+ for (j = 0; j < devinfo.numdevs; j++) {
+ dsp = &devinfo.devices[j];
+ fprintf(stderr, " devinfo[%d]: %s%d\n", j, dsp->device_name, dsp->unit_number);
+ }
+ continue;
+ }
+ else {
+ /* new entry or reactivate an existing one */
+ pmdaCacheStore(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_ADD, iname, (void *)dsp);
+ }
+ }
+ }
+
+}
+
+int
+do_disk_metrics(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+ struct devstat *dsp;
+ int sts;
+
+ if (inst != PM_IN_NULL) {
+ /*
+ * per-disk metrics
+ */
+ sts = pmdaCacheLookup(indomtab[DISK_INDOM].it_indom, inst, NULL, (void **)&dsp);
+ if (sts == PMDA_CACHE_ACTIVE) {
+ sts = 1;
+ /* cluster and domain already checked, just need item ... */
+ switch (pmid_item(mdesc->m_desc.pmid)) {
+ case 0: /* disk.dev.read */
+ atom->ull = dsp->operations[DEVSTAT_READ];
+ break;
+
+ case 1: /* disk.dev.write */
+ atom->ull = dsp->operations[DEVSTAT_WRITE];
+ break;
+
+ case 2: /* disk.dev.total */
+ atom->ull = dsp->operations[DEVSTAT_READ] + dsp->operations[DEVSTAT_WRITE];
+ break;
+
+ case 3: /* disk.dev.read_bytes */
+ atom->ull = dsp->bytes[DEVSTAT_READ];
+ break;
+
+ case 4: /* disk.dev.write_bytes */
+ atom->ull = dsp->bytes[DEVSTAT_WRITE];
+ break;
+
+ case 5: /* disk.dev.total_bytes */
+ atom->ull = dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE];
+ break;
+
+ case 12: /* disk.dev.blkread */
+ atom->ull = dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_READ] / dsp->block_size;
+ break;
+
+ case 13: /* disk.dev.blkwrite */
+ atom->ull = dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_WRITE] / dsp->block_size;
+ break;
+
+ case 14: /* disk.dev.blktotal */
+ atom->ull = dsp->block_size == 0 ? 0 : (dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]) / dsp->block_size;
+ break;
+
+ default:
+ sts = PM_ERR_PMID;
+ break;
+ }
+ }
+ else
+ sts = 0;
+ }
+ else {
+ /*
+ * all-disk summary metrics
+ */
+ int i;
+ atom->ull = 0;
+ sts = 1;
+ pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_WALK_REWIND);
+ while (sts == 1 && (i = pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_WALK_NEXT)) >= 0) {
+ int lsts;
+ lsts = pmdaCacheLookup(indomtab[DISK_INDOM].it_indom, i, NULL, (void **)&dsp);
+ if (lsts == PMDA_CACHE_ACTIVE) {
+ /* cluster and domain already checked, just need item ... */
+ switch (pmid_item(mdesc->m_desc.pmid)) {
+ case 6: /* disk.all.read */
+ atom->ull += dsp->operations[DEVSTAT_READ];
+ break;
+
+ case 7: /* disk.all.write */
+ atom->ull += dsp->operations[DEVSTAT_WRITE];
+ break;
+
+ case 8: /* disk.all.total */
+ atom->ull += dsp->operations[DEVSTAT_READ] + dsp->operations[DEVSTAT_WRITE];
+ break;
+
+ case 9: /* disk.all.read_bytes */
+ atom->ull += dsp->bytes[DEVSTAT_READ];
+ break;
+
+ case 10: /* disk.all.write_bytes */
+ atom->ull += dsp->bytes[DEVSTAT_WRITE];
+ break;
+
+ case 11: /* disk.all.total_bytes */
+ atom->ull += dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE];
+ break;
+
+ case 15: /* disk.all.blkread */
+ atom->ull += dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_READ] / dsp->block_size;
+ break;
+
+ case 16: /* disk.all.blkwrite */
+ atom->ull += dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_WRITE] / dsp->block_size;
+ break;
+
+ case 17: /* disk.all.blktotal */
+ atom->ull += dsp->block_size == 0 ? 0 : (dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]) / dsp->block_size;
+ break;
+
+ default:
+ sts = PM_ERR_PMID;
+ break;
+ }
+ }
+ }
+ if (i < 0 && i != -1)
+ /* not end of indom from cache walk, some other error */
+ sts = i;
+ }
+
+ return sts;
+}
diff --git a/src/pmdas/freebsd/freebsd.c b/src/pmdas/freebsd/freebsd.c
new file mode 100644
index 0000000..3170b20
--- /dev/null
+++ b/src/pmdas/freebsd/freebsd.c
@@ -0,0 +1,991 @@
+/*
+ * FreeBSD Kernel PMDA
+ *
+ * Copyright (c) 2012 Red Hat.
+ * Copyright (c) 2012 Ken McDonell. 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 <limits.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <vm/vm_param.h>
+
+#include "domain.h"
+#include "freebsd.h"
+
+/* static instances */
+static pmdaInstid loadav_indom[] = {
+ { 1, "1 minute" }, { 5, "5 minute" }, { 15, "15 minute" }
+};
+
+/* instance domains */
+pmdaIndom indomtab[] = {
+ { LOADAV_INDOM, sizeof(loadav_indom)/sizeof(loadav_indom[0]), loadav_indom },
+ { CPU_INDOM, 0, NULL },
+ { DISK_INDOM, 0, NULL },
+ { NETIF_INDOM, 0, NULL },
+};
+static int indomtablen = sizeof(indomtab) / sizeof(indomtab[0]);
+
+#define CL_SYSCTL 0
+#define CL_SPECIAL 1
+#define CL_DISK 2
+#define CL_NETIF 3
+
+/*
+ * All the PCP metrics.
+ *
+ * For sysctl metrics, m_user (the first field) is set the the PCP
+ * name of the metric, and during initialization this is replaced by
+ * a pointer to the corresponding entry in mib[] (based on matching,
+ * or prefix matching (see matchname()) the PCP name here with
+ * m_pcpname[] in the mib[] entries.
+ *
+ * cluster map
+ * CL_SYSCTL simple sysctl() metrics, either one metric per mib, or
+ * one struct per mib
+ * CL_SPECIAL trickier sysctl() metrics involving synthesis or arithmetic
+ * or other methods
+ * CL_DISK disk metrics
+ * CL_NETIF network interface metrics
+ */
+
+static pmdaMetric metrictab[] = {
+ { (void *)"hinv.ncpu",
+ { PMDA_PMID(CL_SYSCTL,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { (void *)"hinv.physmem",
+ { PMDA_PMID(CL_SYSCTL,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { (void *)"kernel.all.load",
+ { PMDA_PMID(CL_SYSCTL,2), PM_TYPE_FLOAT, LOADAV_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { (void *)"kernel.all.cpu.user",
+ { PMDA_PMID(CL_SYSCTL,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.all.cpu.nice",
+ { PMDA_PMID(CL_SYSCTL,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.all.cpu.sys",
+ { PMDA_PMID(CL_SYSCTL,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.all.cpu.intr",
+ { PMDA_PMID(CL_SYSCTL,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.all.cpu.idle",
+ { PMDA_PMID(CL_SYSCTL,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.percpu.cpu.user",
+ { PMDA_PMID(CL_SYSCTL,8), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.percpu.cpu.nice",
+ { PMDA_PMID(CL_SYSCTL,9), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.percpu.cpu.sys",
+ { PMDA_PMID(CL_SYSCTL,10), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.percpu.cpu.intr",
+ { PMDA_PMID(CL_SYSCTL,11), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.percpu.cpu.idle",
+ { PMDA_PMID(CL_SYSCTL,12), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } },
+ { (void *)"kernel.all.hz",
+ { PMDA_PMID(CL_SYSCTL,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) } },
+ { (void *)"hinv.cpu.vendor",
+ { PMDA_PMID(CL_SYSCTL,15), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { (void *)"hinv.cpu.model",
+ { PMDA_PMID(CL_SYSCTL,16), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { (void *)"hinv.cpu.arch",
+ { PMDA_PMID(CL_SYSCTL,17), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { (void *)"swap.pagesin",
+ { PMDA_PMID(CL_SYSCTL,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"swap.pagesout",
+ { PMDA_PMID(CL_SYSCTL,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"swap.in",
+ { PMDA_PMID(CL_SYSCTL,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"swap.in",
+ { PMDA_PMID(CL_SYSCTL,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"kernel.all.pswitch",
+ { PMDA_PMID(CL_SYSCTL,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"kernel.all.syscall",
+ { PMDA_PMID(CL_SYSCTL,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"kernel.all.intr",
+ { PMDA_PMID(CL_SYSCTL,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { (void *)"swap.length",
+ { PMDA_PMID(CL_SYSCTL,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { (void *)"swap.used",
+ { PMDA_PMID(CL_SYSCTL,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+
+ { NULL, /* hinv.ndisk */
+ { PMDA_PMID(CL_SPECIAL,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ /*
+ * swap.free is the difference between sysctl variables vm.swap_total
+ * and vm.swap_reserved, so it is important they are kept together
+ * in mib[] below
+ */
+ { (void *)"swap.length", /* swap.free */
+ { PMDA_PMID(CL_SPECIAL,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* hinv.pagesize */
+ { PMDA_PMID(CL_SPECIAL,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { (void *)"mem.util.all",
+ { PMDA_PMID(CL_SPECIAL,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ /*
+ * mem.util.used is computed from several of the vm.stats.vm.v_*_count
+ * sysctl metrics, so it is important they are kept together in mib[]
+ * below
+ */
+ { (void *)"mem.util.all", /* mem.util.used */
+ { PMDA_PMID(CL_SPECIAL,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ { (void *)"mem.util.free",
+ { PMDA_PMID(CL_SPECIAL,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ { (void *)"mem.util.bufmem",
+ { PMDA_PMID(CL_SPECIAL,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ { (void *)"mem.util.cached",
+ { PMDA_PMID(CL_SPECIAL,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ { (void *)"mem.util.wired",
+ { PMDA_PMID(CL_SPECIAL,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ { (void *)"mem.util.active",
+ { PMDA_PMID(CL_SPECIAL,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ { (void *)"mem.util.inactive",
+ { PMDA_PMID(CL_SPECIAL,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+ /*
+ * mem.util.avail is computed from several of the vm.stats.vm.v_*_count
+ * sysctl metrics, so it is important they are kept together in mib[]
+ * below
+ */
+ { (void *)"mem.util.all", /* mem.util.avail */
+ { PMDA_PMID(CL_SPECIAL,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } },
+
+ { NULL, /* disk.dev.read */
+ { PMDA_PMID(CL_DISK,0), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.dev.write */
+ { PMDA_PMID(CL_DISK,1), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.dev.total */
+ { PMDA_PMID(CL_DISK,2), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.dev.read_bytes */
+ { PMDA_PMID(CL_DISK,3), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* disk.dev.write_bytes */
+ { PMDA_PMID(CL_DISK,4), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* disk.dev.total_bytes */
+ { PMDA_PMID(CL_DISK,5), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* disk.all.read */
+ { PMDA_PMID(CL_DISK,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.all.write */
+ { PMDA_PMID(CL_DISK,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.all.total */
+ { PMDA_PMID(CL_DISK,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.all.read_bytes */
+ { PMDA_PMID(CL_DISK,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* disk.all.write_bytes */
+ { PMDA_PMID(CL_DISK,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* disk.all.total_bytes */
+ { PMDA_PMID(CL_DISK,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* disk.dev.blkread */
+ { PMDA_PMID(CL_DISK,12), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.dev.blkwrite */
+ { PMDA_PMID(CL_DISK,13), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.dev.blktotal */
+ { PMDA_PMID(CL_DISK,14), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.all.blkread */
+ { PMDA_PMID(CL_DISK,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.all.blkwrite */
+ { PMDA_PMID(CL_DISK,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* disk.all.blktotal */
+ { PMDA_PMID(CL_DISK,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+
+ { NULL, /* network.interface.mtu */
+ { PMDA_PMID(CL_NETIF,0), PM_TYPE_U32, NETIF_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { NULL, /* network.interface.up */
+ { PMDA_PMID(CL_NETIF,1), PM_TYPE_U32, NETIF_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { NULL, /* network.interface.baudrate */
+ { PMDA_PMID(CL_NETIF,2), PM_TYPE_U64, NETIF_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+ { NULL, /* network.interface.in.bytes */
+ { PMDA_PMID(CL_NETIF,3), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* network.interface.in.packets */
+ { PMDA_PMID(CL_NETIF,4), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.in.mcasts */
+ { PMDA_PMID(CL_NETIF,5), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.in.errors */
+ { PMDA_PMID(CL_NETIF,6), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.in.drops */
+ { PMDA_PMID(CL_NETIF,7), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.out.bytes */
+ { PMDA_PMID(CL_NETIF,8), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* network.interface.out.packets */
+ { PMDA_PMID(CL_NETIF,9), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.out.mcasts */
+ { PMDA_PMID(CL_NETIF,10), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.out.errors */
+ { PMDA_PMID(CL_NETIF,11), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.out.collisions */
+ { PMDA_PMID(CL_NETIF,12), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.total.bytes */
+ { PMDA_PMID(CL_NETIF,13), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } },
+ { NULL, /* network.interface.total.packets */
+ { PMDA_PMID(CL_NETIF,14), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.total.mcasts */
+ { PMDA_PMID(CL_NETIF,15), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+ { NULL, /* network.interface.total.errors */
+ { PMDA_PMID(CL_NETIF,16), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+};
+static int metrictablen = sizeof(metrictab) / sizeof(metrictab[0]);
+
+/*
+ * mapping between PCP metrics and sysctl metrics
+ *
+ * initialization note ... all elments after m_pcpname and m_name are OK
+ * to be 0 or NULL
+ */
+typedef struct {
+ const char *m_pcpname; /* PCP metric name or prefix (see matchname() */
+ const char *m_name; /* sysctl metric name */
+ size_t m_miblen; /* number of elements in m_mib[] */
+ int *m_mib;
+ int m_fetched; /* 1 if m_data is current and valid */
+ size_t m_datalen; /* number of bytes in m_data[] */
+ void *m_data; /* value from sysctl */
+} mib_t;
+static mib_t map[] = {
+ { "hinv.ncpu", "hw.ncpu" },
+ { "hinv.physmem", "hw.physmem" },
+ { "hinv.cpu.vendor", "hw.machine" },
+ { "hinv.cpu.model", "hw.model" },
+ { "hinv.cpu.arch", "hw.machine_arch" },
+ { "kernel.all.load", "vm.loadavg" },
+ { "kernel.all.hz", "kern.clockrate" },
+ { "kernel.all.cpu.*", "kern.cp_time" },
+ { "kernel.percpu.cpu.*", "kern.cp_times" },
+ { "swap.pagesin", "vm.stats.vm.v_swappgsin" },
+ { "swap.pagesout", "vm.stats.vm.v_swappgsout" },
+ { "swap.in", "vm.stats.vm.v_swapin" },
+ { "swap.out", "vm.stats.vm.v_swapout" },
+ { "kernel.all.pswitch", "vm.stats.sys.v_swtch" },
+ { "kernel.all.syscall", "vm.stats.sys.v_syscall" },
+ { "kernel.all.intr", "vm.stats.sys.v_intr" },
+ { "mem.util.bufmem", "vfs.bufspace" },
+/*
+ * DO NOT MOVE next 2 entries ... see note above for swap.free
+ */
+ { "swap.length", "vm.swap_total" },
+ { "swap.used", "vm.swap_reserved" },
+/*
+ * DO NOT MOVE next 6 entries ... see note above for mem.util.avail
+ * and mem.util.used
+ */
+ { "mem.util.all", "vm.stats.vm.v_page_count" },
+ { "mem.util.free", "vm.stats.vm.v_free_count" },
+ { "mem.util.cached", "vm.stats.vm.v_cache_count" },
+ { "mem.util.wired", "vm.stats.vm.v_wire_count" },
+ { "mem.util.active", "vm.stats.vm.v_active_count" },
+ { "mem.util.inactive", "vm.stats.vm.v_inactive_count" },
+
+};
+static int maplen = sizeof(map) / sizeof(map[0]);
+static mib_t bad_mib = { "bad.mib", "bad.mib", 0, NULL, 0, 0, NULL };
+
+static char *username;
+static int isDSO = 1; /* =0 I am a daemon */
+static int cpuhz; /* frequency for CPU time metrics */
+static int ncpu; /* number of cpus in kern.cp_times data */
+static int pagesize; /* vm page size */
+
+/*
+ * Fetch values from sysctl()
+ *
+ * Expect the result to be xpect bytes to match the PCP data size or
+ * anticipated structure size, unless xpect is ==0 in which case the
+ * size test is skipped.
+ */
+static int
+do_sysctl(mib_t *mp, size_t xpect)
+{
+ /*
+ * Note zero trip if mp->m_data and mp->datalen are already valid
+ * and current
+ */
+ for ( ; mp->m_fetched == 0; ) {
+ int sts;
+ sts = sysctl(mp->m_mib, (u_int)mp->m_miblen, mp->m_data, &mp->m_datalen, NULL, 0);
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "sysctl(%s%s) -> %d (datalen=%d)\n", mp->m_name, mp->m_data == NULL ? " firstcall" : "", sts, (int)mp->m_datalen);
+ }
+#endif
+ if (sts == 0 && mp->m_data != NULL) {
+ mp->m_fetched = 1;
+ break;
+ }
+ if ((sts == -1 && errno == ENOMEM) || (sts == 0 && mp->m_data == NULL)) {
+ /* first call for this one, or data changed size */
+ mp->m_data = realloc(mp->m_data, mp->m_datalen);
+ if (mp->m_data == NULL) {
+ fprintf(stderr, "Error: %s: buffer alloc failed for sysctl metric \"%s\"\n", mp->m_pcpname, mp->m_name);
+ __pmNoMem("do_sysctl", mp->m_datalen, PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ }
+ else
+ return -errno;
+ }
+ if (xpect > 0 && mp->m_datalen != xpect) {
+ fprintf(stderr, "Error: %s: sysctl(%s) datalen=%d not %d!\n", mp->m_pcpname, mp->m_name, (int)mp->m_datalen, (int)xpect);
+ return 0;
+ }
+ return mp->m_datalen;
+}
+
+/*
+ * kernel memory reader setup
+ */
+struct nlist symbols[] = {
+ { .n_name = "_ifnet" },
+ { .n_name = NULL }
+};
+kvm_t *kvmp;
+
+static void
+kmemread_init(void)
+{
+ int sts;
+ int i;
+ char errmsg[_POSIX2_LINE_MAX];
+
+ /*
+ * If we're running as a daemon PMDA, assume we're setgid kmem,
+ * so we can open /dev/kmem, and downgrade privileges after the
+ * kvm_open().
+ * For a DSO PMDA, we have to assume pmcd has the required
+ * privileges and don't dink with them.
+ */
+ kvmp = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errmsg);
+ if (!isDSO)
+ setgid(getgid());
+ if (kvmp == NULL) {
+ fprintf(stderr, "kmemread_init: kvm_openfiles failed: %s\n", errmsg);
+ return;
+ }
+
+ sts = kvm_nlist(kvmp, symbols);
+ if (sts < 0) {
+ fprintf(stderr, "kmemread_init: kvm_nlist failed: %s\n", pmErrStr(-errno));
+ for (i = 0; i < sizeof(symbols)/sizeof(symbols[0])-1; i++)
+ symbols[i].n_value = 0;
+ return;
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ for (i = 0; i < sizeof(symbols)/sizeof(symbols[0])-1; i++) {
+ fprintf(stderr, "Info: kernel symbol %s found at 0x%08lx\n", symbols[i].n_name, symbols[i].n_value);
+ }
+ }
+#endif
+
+}
+
+/*
+ * Callback provided to pmdaFetch ... come here once per metric-instance
+ * pair in each pmFetch().
+ */
+static int
+freebsd_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+ int sts = PM_ERR_PMID;
+ __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid);
+ mib_t *mp;
+
+ mp = (mib_t *)mdesc->m_user;
+ if (idp->cluster == CL_SYSCTL) {
+ /* sysctl() simple cases */
+ switch (idp->item) {
+ /* 32-bit integer values */
+ case 0: /* hinv.ncpu */
+ case 18: /* swap.pagesin */
+ case 19: /* swap.pagesout */
+ case 20: /* swap.in */
+ case 21: /* swap.out */
+ case 22: /* kernel.all.pswitch */
+ case 23: /* kernel.all.syscall */
+ case 24: /* kernel.all.intr */
+ sts = do_sysctl(mp, sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul = *((__uint32_t *)mp->m_data);
+ sts = 1;
+ }
+ break;
+
+ /* 64-bit integer values */
+ case 1: /* hinv.physmem */
+ case 25: /* swap.length */
+ case 26: /* swap.used */
+ sts = do_sysctl(mp, sizeof(atom->ull));
+ if (sts > 0) {
+ atom->ull = *((__uint64_t *)mp->m_data);
+ sts = 1;
+ }
+ break;
+
+ /* string values */
+ case 15: /* hinv.cpu.vendor */
+ case 16: /* hinv.cpu.model */
+ case 17: /* hinv.cpu.arch */
+ sts = do_sysctl(mp, (size_t)0);
+ if (sts > 0) {
+ atom->cp = (char *)mp->m_data;
+ sts = 1;
+ }
+ break;
+
+ /* structs and aggregates */
+ case 2: /* kernel.all.load */
+ sts = do_sysctl(mp, 3*sizeof(atom->d));
+ if (sts > 0) {
+ int i;
+ struct loadavg *lp = (struct loadavg *)mp->m_data;
+ if (inst == 1)
+ i = 0;
+ else if (inst == 5)
+ i = 1;
+ else if (inst == 15)
+ i = 2;
+ else
+ return PM_ERR_INST;
+ atom->f = (float)((double)lp->ldavg[i] / lp->fscale);
+ sts = 1;
+ }
+ break;
+
+ case 3: /* kernel.all.cpu.user */
+ case 4: /* kernel.all.cpu.nice */
+ case 5: /* kernel.all.cpu.sys */
+ case 6: /* kernel.all.cpu.intr */
+ case 7: /* kernel.all.cpu.idle */
+ sts = do_sysctl(mp, CPUSTATES*sizeof(atom->ull));
+ if (sts > 0) {
+ /*
+ * PMID assignment is important in the "-3" below so
+ * that metrics map to consecutive elements of the
+ * returned value in the order defined for CPUSTATES,
+ * i.e. CP_USER, CP_NICE, CP_SYS, CP_INTR and
+ * CP_IDLE
+ */
+ atom->ull = 1000*((__uint64_t *)mp->m_data)[idp->item-3]/cpuhz;
+ sts = 1;
+ }
+ break;
+
+ case 8: /* kernel.percpu.cpu.user */
+ case 9: /* kernel.percpu.cpu.nice */
+ case 10: /* kernel.percpu.cpu.sys */
+ case 11: /* kernel.percpu.cpu.intr */
+ case 12: /* kernel.percpu.cpu.idle */
+ sts = do_sysctl(mp, ncpu*CPUSTATES*sizeof(atom->ull));
+ if (sts > 0) {
+ /*
+ * PMID assignment is important in the "-8" below so
+ * that metrics map to consecutive elements of the
+ * returned value in the order defined for CPUSTATES,
+ * i.e. CP_USER, CP_NICE, CP_SYS, CP_INTR and
+ * CP_IDLE, and then there is one such set for each
+ * CPU up to the maximum number of CPUs installed in
+ * the system.
+ */
+ atom->ull = 1000*((__uint64_t *)mp->m_data)[inst * CPUSTATES + idp->item-8]/cpuhz;
+ sts = 1;
+ }
+ break;
+
+ case 13: /* kernel.all.hz */
+ sts = do_sysctl(mp, sizeof(struct clockinfo));
+ if (sts > 0) {
+ struct clockinfo *cp = (struct clockinfo *)mp->m_data;
+ atom->ul = cp->hz;
+ sts = 1;
+ }
+ break;
+
+ }
+ }
+ else if (idp->cluster == CL_SPECIAL) {
+ /* special cases */
+ switch (idp->item) {
+ case 0: /* hinv.ndisk */
+ refresh_disk_metrics();
+ atom->ul = pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_SIZE_ACTIVE);
+ sts = 1;
+ break;
+
+ case 1: /* swap.free */
+ /* first vm.swap_total */
+ sts = do_sysctl(mp, sizeof(atom->ull));
+ if (sts > 0) {
+ atom->ull = *((__uint64_t *)mp->m_data);
+ /*
+ * now subtract vm.swap_reserved ... assumes consecutive
+ * mib[] entries
+ */
+ mp++;
+ sts = do_sysctl(mp, sizeof(atom->ull));
+ if (sts > 0) {
+ atom->ull -= *((__uint64_t *)mp->m_data);
+ sts = 1;
+ }
+ }
+ break;
+
+ case 3: /* hinv.pagesize */
+ atom->ul = pagesize;
+ sts = 1;
+ break;
+
+ case 4: /* mem.util.all */
+ case 6: /* mem.util.free */
+ case 8: /* mem.util.cached */
+ case 9: /* mem.util.wired */
+ case 10: /* mem.util.active */
+ case 11: /* mem.util.inactive */
+ sts = do_sysctl(mp, sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul = *((__uint32_t *)mp->m_data) * (pagesize / 1024);
+ sts = 1;
+ }
+ break;
+
+ case 7: /* mem.util.bufmem */
+ sts = do_sysctl(mp, sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul = *((__uint32_t *)mp->m_data) / 1024;
+ sts = 1;
+ }
+ break;
+
+ case 5: /* mem.util.used */
+ /*
+ * mp-> v_page_count entry in mib[]
+ * assuming consecutive mib[] entries, we want
+ * v_page_count mp[0] - v_free_count mp[1] -
+ * v_cache_count mp[2] - v_inactive_count mp[5]
+ */
+ sts = do_sysctl(mp, sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul = *((__uint32_t *)mp->m_data);
+ sts = do_sysctl(&mp[1], sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul -= *((__uint32_t *)mp[1].m_data);
+ sts = do_sysctl(&mp[2], sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul -= *((__uint32_t *)mp[2].m_data);
+ sts = do_sysctl(&mp[5], sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul -= *((__uint32_t *)mp[5].m_data);
+ atom->ul *= (pagesize / 1024);
+ sts = 1;
+ }
+ }
+ }
+ }
+ break;
+
+ case 12: /* mem.util.avail */
+ /*
+ * mp-> v_page_count entry in mib[]
+ * assuming consecutive mib[] entries, we want
+ * v_free_count mp[1] + v_cache_count mp[2] +
+ * v_inactive_count mp[5]
+ */
+ sts = do_sysctl(&mp[1], sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul = *((__uint32_t *)mp[1].m_data);
+ sts = do_sysctl(&mp[2], sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul += *((__uint32_t *)mp[2].m_data);
+ sts = do_sysctl(&mp[5], sizeof(atom->ul));
+ if (sts > 0) {
+ atom->ul += *((__uint32_t *)mp[5].m_data);
+ atom->ul *= (pagesize / 1024);
+ sts = 1;
+ }
+ }
+ }
+ break;
+
+ }
+ }
+ else if (idp->cluster == CL_DISK) {
+ /* disk metrics */
+ sts = do_disk_metrics(mdesc, inst, atom);
+ }
+ else if (idp->cluster == CL_NETIF) {
+ /* network interface metrics */
+ sts = do_netif_metrics(mdesc, inst, atom);
+ }
+
+ return sts;
+}
+
+/*
+ * wrapper for pmdaFetch ... force value caches to be reloaded if needed,
+ * then do the fetch
+ */
+static int
+freebsd_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda)
+{
+ int i;
+ int done_disk = 0;
+ int done_netif = 0;
+
+ for (i = 0; i < maplen; i++) {
+ map[i].m_fetched = 0;
+ }
+
+ /*
+ * pre-fetch all metrics if needed, and update instance domains if
+ * they have changed
+ */
+ for (i = 0; !done_disk && !done_netif && i < numpmid; i++) {
+ if (pmid_cluster(pmidlist[i]) == CL_DISK) {
+ refresh_disk_metrics();
+ done_disk = 1;
+ }
+ else if (pmid_cluster(pmidlist[i]) == CL_NETIF) {
+ refresh_netif_metrics();
+ done_netif = 1;
+ }
+ }
+
+ return pmdaFetch(numpmid, pmidlist, resp, pmda);
+}
+
+/*
+ * wrapper for pmdaInstance ... refresh required instance domain first
+ */
+static int
+freebsd_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda)
+{
+ /*
+ * indomtab[] instance names and ids are not used for some indoms,
+ * ensure pmdaCache is current
+ */
+ if (indom == indomtab[DISK_INDOM].it_indom)
+ refresh_disk_metrics();
+ if (indom == indomtab[NETIF_INDOM].it_indom)
+ refresh_netif_metrics();
+
+ return pmdaInstance(indom, inst, name, result, pmda);
+}
+
+/*
+ * PCP metric name matching for linking metrictab[] entries to mib[]
+ * entries.
+ *
+ * Return 1 if prefix[] is equal to, or a prefix of name[]
+ *
+ * prefix[] of the form "a.bc" or "a.bc*" matches a name[] like "a.bc"
+ * or "a.bcanything", to improve readability of the initializers in
+ * mib[], and asterisk is a "match all" special case, so "a.b.*" matches
+ * "a.b.anything"
+ */
+static int
+matchname(const char *prefix, const char *name)
+{
+ while (*prefix != '\0' && *name != '\0' && *prefix == *name) {
+ prefix++;
+ name++;
+ }
+ if (*prefix == '\0' || *prefix == '*')
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Initialise the agent (both daemon and DSO).
+ *
+ * Do mapping from sysclt(3) names to mibs.
+ * Collect some global constants.
+ * Build the system-specific, but not dynamic, instance domains,
+ * e.g. CPU_INDOM.
+ * Initialize the kernel memory reader.
+ */
+void
+freebsd_init(pmdaInterface *dp)
+{
+ int i;
+ int m;
+ int sts;
+ struct clockinfo clockrates;
+ size_t sz;
+ int mib[CTL_MAXNAME]; /* enough for longest mib key */
+ char iname[16]; /* enough for cpuNN.. */
+
+ if (isDSO) {
+ char mypath[MAXPATHLEN];
+ int sep = __pmPathSeparator();
+ snprintf(mypath, sizeof(mypath), "%s%c" "freebsd" "%c" "help",
+ pmGetConfig("PCP_PMDAS_DIR"), sep, sep);
+ pmdaDSO(dp, PMDA_INTERFACE_5, "freebsd DSO", mypath);
+ } else {
+ __pmSetProcessIdentity(username);
+ }
+
+ if (dp->status != 0)
+ return;
+
+ dp->version.four.fetch = freebsd_fetch;
+ dp->version.four.instance = freebsd_instance;
+
+ pmdaSetFetchCallBack(dp, freebsd_fetchCallBack);
+
+ pmdaInit(dp, indomtab, indomtablen, metrictab, metrictablen);
+
+ /*
+ * Link metrictab[] entries via m_user to map[] entries based on
+ * matching sysctl(3) name
+ *
+ * also translate the sysctl(3) name to a mib
+ */
+ for (m = 0; m < metrictablen; m++) {
+ if (metrictab[m].m_user == NULL) {
+ /* not using sysctl(3) */
+ continue;
+ }
+ for (i = 0; i < maplen; i++) {
+ if (matchname(map[i].m_pcpname, (char *)metrictab[m].m_user)) {
+ if (map[i].m_mib == NULL) {
+ /*
+ * multiple metrictab[] entries may point to the same
+ * mib[] entry, but this is the first time for this
+ * mib[] entry ...
+ */
+ map[i].m_miblen = sizeof(mib);
+ sts = sysctlnametomib(map[i].m_name, mib, &map[i].m_miblen);
+ if (sts == 0) {
+ map[i].m_mib = (int *)malloc(map[i].m_miblen*sizeof(map[i].m_mib[0]));
+ if (map[i].m_mib == NULL) {
+ fprintf(stderr, "Error: %s (%s): failed mib alloc for sysctl metric \"%s\"\n", map[i].m_pcpname, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name);
+ __pmNoMem("freebsd_init: mib", map[i].m_miblen*sizeof(map[i].m_mib[0]), PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ memcpy(map[i].m_mib, mib, map[i].m_miblen*sizeof(map[i].m_mib[0]));
+ }
+ else {
+ fprintf(stderr, "Error: %s (%s): failed sysctlnametomib(\"%s\", ...): %s\n", map[i].m_pcpname, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name, pmErrStr(-errno));
+ metrictab[m].m_user = (void *)&bad_mib;
+ }
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ int p;
+ fprintf(stderr, "Info: %s (%s): sysctl metric \"%s\" -> ", (char *)metrictab[m].m_user, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name);
+ for (p = 0; p < map[i].m_miblen; p++) {
+ if (p > 0) fputc('.', stderr);
+ fprintf(stderr, "%d", map[i].m_mib[p]);
+ }
+ fputc('\n', stderr);
+ }
+#endif
+ metrictab[m].m_user = (void *)&map[i];
+ break;
+ }
+ }
+ if (i == maplen) {
+ fprintf(stderr, "Error: %s (%s): cannot match name in sysctl map[]\n", (char *)metrictab[m].m_user, pmIDStr(metrictab[m].m_desc.pmid));
+ metrictab[m].m_user = (void *)&bad_mib;
+ }
+ }
+
+ /*
+ * Collect some global constants needed later ...
+ */
+ sz = sizeof(clockrates);
+ sts = sysctlbyname("kern.clockrate", &clockrates, &sz, NULL, 0);
+ if (sts < 0) {
+ fprintf(stderr, "Fatal Error: sysctlbyname(\"kern.clockrate\", ...) failed: %s\n", pmErrStr(-errno));
+ exit(1);
+ }
+ cpuhz = clockrates.stathz;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Info: CPU time \"hz\" = %d\n", cpuhz);
+#endif
+
+ sts = sysctlbyname("kern.cp_times", NULL, &sz, NULL, 0);
+ if (sts < 0) {
+ fprintf(stderr, "Fatal Error: sysctlbyname(\"kern.cp_times\", ...) failed: %s\n", pmErrStr(-errno));
+ exit(1);
+ }
+ /*
+ * see note below when fetching kernel.percpu.cpu.* metrics to
+ * explain this
+ */
+ ncpu = sz / (CPUSTATES * sizeof(__uint64_t));
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Info: ncpu = %d\n", ncpu);
+#endif
+
+ sz = sizeof(pagesize);
+ sts = sysctlbyname("hw.pagesize", &pagesize, &sz, NULL, 0);
+ if (sts < 0) {
+ fprintf(stderr, "Fatal Error: sysctlbyname(\"hw.pagesize\", ...) failed: %s\n", pmErrStr(-errno));
+ exit(1);
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Info: VM pagesize = %d\n", pagesize);
+#endif
+
+ /*
+ * Build some instance domains ...
+ */
+ indomtab[CPU_INDOM].it_numinst = ncpu;
+ indomtab[CPU_INDOM].it_set = (pmdaInstid *)malloc(ncpu * sizeof(pmdaInstid));
+ if (indomtab[CPU_INDOM].it_set == NULL) {
+ __pmNoMem("freebsd_init: CPU_INDOM it_set", ncpu * sizeof(pmdaInstid), PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ for (i = 0; i < ncpu; i++) {
+ indomtab[CPU_INDOM].it_set[i].i_inst = i;
+ snprintf(iname, sizeof(iname), "cpu%d", i);
+ indomtab[CPU_INDOM].it_set[i].i_name = strdup(iname);
+ if (indomtab[CPU_INDOM].it_set[i].i_name == NULL) {
+ __pmNoMem("freebsd_init: CPU_INDOM strdup iname", strlen(iname), PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ }
+
+ kmemread_init();
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [options]\n\n", pmProgname);
+ fputs("Options:\n"
+ " -d domain use domain (numeric) for metrics domain of PMDA\n"
+ " -l logfile write log into logfile rather than using default log name\n"
+ " -U username user account to run under (default \"pcp\")\n"
+ "\nExactly one of the following options may appear:\n"
+ " -i port expect PMCD to connect on given inet port (number or name)\n"
+ " -p expect PMCD to supply stdin/stdout (pipe)\n"
+ " -u socket expect PMCD to connect on given unix domain socket\n"
+ " -6 port expect PMCD to connect on given ipv6 port (number or name)\n",
+ stderr);
+ exit(1);
+}
+
+/*
+ * Set up the agent if running as a daemon.
+ */
+int
+main(int argc, char **argv)
+{
+ int c, err = 0;
+ int sep = __pmPathSeparator();
+ pmdaInterface dispatch;
+ char mypath[MAXPATHLEN];
+
+ isDSO = 0;
+ __pmSetProgname(argv[0]);
+ __pmGetUsername(&username);
+
+ snprintf(mypath, sizeof(mypath), "%s%c" "freebsd" "%c" "help",
+ pmGetConfig("PCP_PMDAS_DIR"), sep, sep);
+ pmdaDaemon(&dispatch, PMDA_INTERFACE_5, pmProgname, FREEBSD,
+ "freebsd.log", mypath);
+
+ while ((c = pmdaGetOpt(argc, argv, "D:d:i:l:pu:U:6:?", &dispatch, &err)) != EOF) {
+ switch(c) {
+ case 'U':
+ username = optarg;
+ break;
+ default:
+ err++;
+ }
+ }
+ if (err)
+ usage();
+
+ pmdaOpenLog(&dispatch);
+ freebsd_init(&dispatch);
+ pmdaConnect(&dispatch);
+ pmdaMain(&dispatch);
+ exit(0);
+}
diff --git a/src/pmdas/freebsd/freebsd.h b/src/pmdas/freebsd/freebsd.h
new file mode 100644
index 0000000..c6b9b72
--- /dev/null
+++ b/src/pmdas/freebsd/freebsd.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright (c) 2012 Ken McDonell 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * instance domains control
+ */
+#define LOADAV_INDOM 0
+#define CPU_INDOM 1
+#define DISK_INDOM 2
+#define NETIF_INDOM 3
+extern pmdaIndom indomtab[];
+
+extern void refresh_disk_metrics(void);
+extern int do_disk_metrics(pmdaMetric *, unsigned int, pmAtomValue *);
+
+extern void refresh_netif_metrics(void);
+extern int do_netif_metrics(pmdaMetric *, unsigned int, pmAtomValue *);
+
+/*
+ * kernel memory reader pieces
+ */
+#include <kvm.h>
+#include <nlist.h>
+
+extern kvm_t *kvmp;
+
+#define KERN_IFNET 0
+extern struct nlist symbols[];
diff --git a/src/pmdas/freebsd/help b/src/pmdas/freebsd/help
new file mode 100644
index 0000000..5bb566a
--- /dev/null
+++ b/src/pmdas/freebsd/help
@@ -0,0 +1,95 @@
+@ FREEBSD.0 Instance domain for load average
+Universally 3 instances, "1 minute" (1), "5 minute" (5) and
+"15 minute" (15).
+
+@ FREEBSD.1 CPU Instance domain for kernel.percpu metrics
+One instance for each physical CPU.
+
+@ FREEBSD.2 DISK Instance domain for disk.dev metrics
+One instance for each physical "direct access" device.
+
+@ kernel.all.hz system hz rate
+Microseconds per "hz" tick.
+@ kernel.all.load 1, 5 and 15 minute load average
+@ kernel.all.pswitch count of context switches
+@ kernel.all.syscall count of system calls
+@ kernel.all.intr count of interrupts serviced
+@ kernel.all.cpu.user total user CPU time for all CPUs
+@ kernel.all.cpu.nice total nice user CPU time for all CPUs
+@ kernel.all.cpu.sys total sys CPU time for all CPUs
+@ kernel.all.cpu.intr total interrupt CPU time for all CPUs
+@ kernel.all.cpu.idle total idle CPU time for all CPUs
+@ kernel.percpu.cpu.user user CPU time for each CPU
+@ kernel.percpu.cpu.nice nice user CPU time for each CPU
+@ kernel.percpu.cpu.sys sys CPU time for each CPU
+@ kernel.percpu.cpu.intr interrupt CPU time for each CPU
+@ kernel.percpu.cpu.idle idle CPU time for each CPU
+
+@ hinv.ncpu number of CPUs in the system
+@ hinv.ndisk number of disks in the system
+@ hinv.physmem total system memory
+@ hinv.pagesize kernel page size
+@ hinv.cpu.vendor system's CPU vendor
+@ hinv.cpu.model system's CPU model
+@ hinv.cpu.arch system's machine dependent CPU architecture type
+
+@ swap.length total swap space size
+@ swap.used reserved (or allocated) swap space
+@ swap.free available swap space
+
+@ swap.pagesin pages read from external storage to service page faults
+@ swap.pagesout dirty pages written to swap devices
+When the rate of page writes is non-zero, this is the most useful
+indication severe demand for physical memory.
+@ swap.in number of swap in operations
+@ swap.out number of swap out operations
+
+@ disk.dev.read Count of read operations per disk
+@ disk.dev.write Count of write operations per disk
+@ disk.dev.total Count of read or write operations (IOPs) per disk
+@ disk.dev.read_bytes Count of bytes read from each disk
+@ disk.dev.write_bytes Count of bytes written to each disk
+@ disk.dev.total_bytes Count of bytes transferred to or from each disk
+@ disk.dev.blkread Count of blocks read from each disk
+@ disk.dev.blkwrite Count of blocks written to each disk
+@ disk.dev.blktotal Count of blocks transferred to or from each disk
+@ disk.all.read Count of read operations across all disks
+@ disk.all.write Count of write operations across all disks
+@ disk.all.total Count of read or write operations (IOPs) across all disks
+@ disk.all.read_bytes Count of bytes read from all disks
+@ disk.all.write_bytes Count of bytes written to all disks
+@ disk.all.total_bytes Count of bytes transferred to or from all disks
+@ disk.all.blkread Count of blocks read from all disks
+@ disk.all.blkwrite Count of blocks written to all disks
+@ disk.all.blktotal Count of blocks transferred to or from all disks
+
+@ mem.util.all Total memory managed by the system
+@ mem.util.used Memory that is actively in use
+Equals "all" minus "free" minus "inactive" minus "cached".
+@ mem.util.free Unallocated and free memory
+@ mem.util.bufmem Memory associated with active buffer I/O
+@ mem.util.cached Cached memory
+Unmodified (clean and cached) pages from files in filesystems.
+@ mem.util.wired Wired or pinned memory that cannot be paged out
+@ mem.util.active Recently accessed memory
+@ mem.util.inactive Memory that is in use, but has not be accessed recently
+@ mem.util.avail Available memory
+Free plus inactive plus cached.
+
+@ network.interface.mtu Maximum Transfer Unit for each network interface
+@ network.interface.up "UP" state for each network interface
+@ network.interface.baudrate Data baudrate for each network interface
+@ network.interface.in.bytes Bytes received on each network interface
+@ network.interface.in.packets Packets received on each network interface
+@ network.interface.in.mcasts Multicast packets received on each network interface
+@ network.interface.in.errors Input errors on each network interface
+@ network.interface.in.drops Dropped packets on each network interface
+@ network.interface.out.bytes Bytes transmitted on each network interface
+@ network.interface.out.packets Packets transmitted on each network interface
+@ network.interface.out.mcasts Multicast packets transmitted on each network interface
+@ network.interface.out.errors Output errors on each network interface
+@ network.interface.out.collisions Output collisions on each network interface
+@ network.interface.total.bytes Bytes received or transmitted on each network interface
+@ network.interface.total.packets Packets received or transmitted on each network interface
+@ network.interface.total.mcasts Multicast packets received or transmitted on each network interface
+@ network.interface.total.errors Input or output errors on each network interface
diff --git a/src/pmdas/freebsd/netif.c b/src/pmdas/freebsd/netif.c
new file mode 100644
index 0000000..c19d7f3
--- /dev/null
+++ b/src/pmdas/freebsd/netif.c
@@ -0,0 +1,225 @@
+/*
+ * FreeBSD Kernel PMDA - network interface metrics
+ *
+ * Copyright (c) 2012 Ken McDonell. 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+
+#include "freebsd.h"
+
+#define WARN_INIT 1
+#define WARN_READ_HEAD 2
+
+void
+refresh_netif_metrics(void)
+{
+ int i;
+ int sts;
+ unsigned long kaddr;
+ struct ifnethead ifnethead;
+ struct ifnet ifnet;
+ struct ifnet *ifp;
+ static int warn = 0; /* warn once control */
+
+ /*
+ * Not sure that the order of chained netif structs is invariant,
+ * especially if interfaces are added to the configuration after
+ * initial system boot ... so mark all the instances as inactive
+ * and re-match based on the interface name
+ */
+ pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_INACTIVE);
+
+ kaddr = symbols[KERN_IFNET].n_value;
+ if (kvmp == NULL || kaddr == 0) {
+ /* no network interface metrics for us today ... */
+ if ((warn & WARN_INIT) == 0) {
+ fprintf(stderr, "refresh_netif_metrics: Warning: cannot get any network interface metrics\n");
+ warn |= WARN_INIT;
+ }
+ return;
+ }
+
+ /*
+ * Kernel data structures for the linked list of network interface
+ * information.
+ *
+ * _ifnet -> struct ifnethead {
+ * struct ifnet *tqh_first;
+ * struct ifnet **tqh_last;
+ * ...
+ * }
+ *
+ * and within an ifnet struct (declared in <net/if_var.h>) we find
+ * the linked list maintained in if_link, the external interface
+ * name in if_xname[] and if_data which is a nested if_data stuct
+ * (declared in <net/if.h>) that contains many of the goodies we're
+ * after, e.g. u_char ifi_type, u_long ifi_mtu, u_long ifi_baudrate,
+ * u_long ifi_ipackets, u_long ifi_opackets, u_long ifi_ibytes,
+ * u_long ifi_obytes, etc.
+ */
+ if (kvm_read(kvmp, kaddr, (char *)&ifnethead, sizeof(ifnethead)) != sizeof(ifnethead)) {
+ if ((warn & WARN_READ_HEAD) == 0) {
+ fprintf(stderr, "refresh_netif_metrics: Warning: kvm_read: ifnethead: %s\n", kvm_geterr(kvmp));
+ warn |= WARN_READ_HEAD;
+ }
+ return;
+ }
+
+ for (i = 0; ; i++) {
+ if (i == 0)
+ kaddr = (unsigned long)TAILQ_FIRST(&ifnethead);
+ else
+ kaddr = (unsigned long)TAILQ_NEXT(&ifnet, if_link);
+
+ if (kaddr == 0)
+ break;
+
+ if (kvm_read(kvmp, kaddr, (char *)&ifnet, sizeof(ifnet)) != sizeof(ifnet)) {
+ fprintf(stderr, "refresh_netif_metrics: Error: kvm_read: ifnet[%d]: %s\n", i, kvm_geterr(kvmp));
+ return;
+ }
+
+ /* skip network interfaces that are not interesting ... */
+ if (strcmp(ifnet.if_xname, "lo0") == 0)
+ continue;
+
+ sts = pmdaCacheLookupName(indomtab[NETIF_INDOM].it_indom, ifnet.if_xname, NULL, (void **)&ifp);
+ if (sts == PMDA_CACHE_ACTIVE) {
+ fprintf(stderr, "refresh_netif_metrics: Warning: duplicate name (%s) in network interface indom\n", ifnet.if_xname);
+ continue;
+ }
+ else if (sts == PMDA_CACHE_INACTIVE) {
+ /* reactivate an existing entry */
+ pmdaCacheStore(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_ADD, ifnet.if_xname, (void *)ifp);
+ }
+ else {
+ /* new entry */
+ ifp = (struct ifnet *)malloc(sizeof(*ifp));
+ if (ifp == NULL) {
+ fprintf(stderr, "Error: struct ifnet alloc failed for network interface \"%s\"\n", ifnet.if_xname);
+ __pmNoMem("refresh_netif_metrics", sizeof(*ifp), PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ pmdaCacheStore(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_ADD, ifnet.if_xname, (void *)ifp);
+ }
+ memcpy((void *)ifp, (void *)&ifnet, sizeof(*ifp));
+ }
+}
+
+int
+do_netif_metrics(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+ struct ifnet *ifp;
+ int sts = 0;
+
+ if (inst != PM_IN_NULL) {
+ /*
+ * per-network interface metrics
+ */
+ sts = pmdaCacheLookup(indomtab[NETIF_INDOM].it_indom, inst, NULL, (void **)&ifp);
+ if (sts == PMDA_CACHE_ACTIVE) {
+ sts = 1;
+ /* cluster and domain already checked, just need item ... */
+ switch (pmid_item(mdesc->m_desc.pmid)) {
+ case 0: /* network.interface.mtu */
+ atom->ul = (__uint32_t)ifp->if_data.ifi_mtu;
+ break;
+
+ case 1: /* network.interface.up */
+ atom->ul = (ifp->if_flags & IFF_UP) == IFF_UP;
+ break;
+
+ case 2: /* network.interface.baudrate */
+ atom->ull = ifp->if_data.ifi_baudrate;
+ break;
+
+ case 3: /* network.interface.in.bytes */
+ atom->ull = ifp->if_data.ifi_ibytes;
+ break;
+
+ case 4: /* network.interface.in.packets */
+ atom->ull = ifp->if_data.ifi_ipackets;
+ break;
+
+ case 5: /* network.interface.in.mcasts */
+ atom->ull = ifp->if_data.ifi_imcasts;
+ break;
+
+ case 6: /* network.interface.in.errors */
+ atom->ull = ifp->if_data.ifi_ierrors;
+ break;
+
+ case 7: /* network.interface.in.drops */
+ atom->ull = ifp->if_data.ifi_iqdrops;
+ break;
+
+ case 8: /* network.interface.out.bytes */
+ atom->ull = ifp->if_data.ifi_obytes;
+ break;
+
+ case 9: /* network.interface.out.packets */
+ atom->ull = ifp->if_data.ifi_opackets;
+ break;
+
+ case 10: /* network.interface.out.mcasts */
+ atom->ull = ifp->if_data.ifi_omcasts;
+ break;
+
+ case 11: /* network.interface.out.errors */
+ atom->ull = ifp->if_data.ifi_oerrors;
+ break;
+
+ case 12: /* network.interface.out.collisions */
+ atom->ull = ifp->if_data.ifi_collisions;
+ break;
+
+ case 13: /* network.interface.total.bytes */
+ atom->ull = ifp->if_data.ifi_ibytes + ifp->if_data.ifi_obytes;
+ break;
+
+ case 14: /* network.interface.total.packets */
+ atom->ull = ifp->if_data.ifi_ipackets + ifp->if_data.ifi_opackets;
+ break;
+
+ case 15: /* network.interface.total.mcasts */
+ atom->ull = ifp->if_data.ifi_imcasts + ifp->if_data.ifi_omcasts;
+ break;
+
+ case 16: /* network.interface.total.errors */
+ atom->ull = ifp->if_data.ifi_ierrors + ifp->if_data.ifi_oerrors;
+ break;
+
+ default:
+ sts = PM_ERR_PMID;
+ break;
+ }
+ }
+ else
+ sts = 0;
+ }
+
+ return sts;
+}
diff --git a/src/pmdas/freebsd/root_freebsd b/src/pmdas/freebsd/root_freebsd
new file mode 100644
index 0000000..6f56d7e
--- /dev/null
+++ b/src/pmdas/freebsd/root_freebsd
@@ -0,0 +1,172 @@
+/*
+ * Metrics for FreeBSD kernel PMDA
+ *
+ * Copyright (c) 2012 Ken McDonell 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.
+ */
+
+/*
+ * the domain for the FreeBSD PMDA ...
+ */
+#ifndef FREEBSD
+#define FREEBSD 85
+#endif
+
+root {
+ hinv
+ kernel
+ disk
+ mem
+ network
+ swap
+}
+
+hinv {
+ ncpu FREEBSD:0:0
+ ndisk FREEBSD:1:0
+ physmem FREEBSD:0:1
+ pagesize FREEBSD:1:3
+ cpu
+}
+
+hinv.cpu {
+ vendor FREEBSD:0:15
+ model FREEBSD:0:16
+ arch FREEBSD:0:17
+}
+
+kernel {
+ all
+ percpu
+}
+
+kernel.all {
+ pswitch FREEBSD:0:22
+ syscall FREEBSD:0:23
+ intr FREEBSD:0:24
+ hz FREEBSD:0:13
+ load FREEBSD:0:2
+ cpu
+}
+
+kernel.all.cpu {
+ user FREEBSD:0:3
+ nice FREEBSD:0:4
+ sys FREEBSD:0:5
+ intr FREEBSD:0:6
+ idle FREEBSD:0:7
+}
+
+kernel.percpu {
+ cpu
+}
+
+kernel.percpu.cpu {
+ user FREEBSD:0:8
+ nice FREEBSD:0:9
+ sys FREEBSD:0:10
+ intr FREEBSD:0:11
+ idle FREEBSD:0:12
+}
+
+disk {
+ dev
+ all
+}
+
+disk.dev {
+ read FREEBSD:2:0
+ write FREEBSD:2:1
+ total FREEBSD:2:2
+ read_bytes FREEBSD:2:3
+ write_bytes FREEBSD:2:4
+ total_bytes FREEBSD:2:5
+ blkread FREEBSD:2:12
+ blkwrite FREEBSD:2:13
+ blktotal FREEBSD:2:14
+}
+
+disk.all {
+ read FREEBSD:2:6
+ write FREEBSD:2:7
+ total FREEBSD:2:8
+ read_bytes FREEBSD:2:9
+ write_bytes FREEBSD:2:10
+ total_bytes FREEBSD:2:11
+ blkread FREEBSD:2:15
+ blkwrite FREEBSD:2:16
+ blktotal FREEBSD:2:17
+}
+
+mem {
+ util
+}
+
+mem.util {
+ all FREEBSD:1:4
+ used FREEBSD:1:5
+ free FREEBSD:1:6
+ bufmem FREEBSD:1:7
+ cached FREEBSD:1:8
+ wired FREEBSD:1:9
+ active FREEBSD:1:10
+ inactive FREEBSD:1:11
+ avail FREEBSD:1:12
+}
+
+network {
+ interface
+}
+
+network.interface {
+ mtu FREEBSD:3:0
+ up FREEBSD:3:1
+ baudrate FREEBSD:3:2
+ in
+ out
+ total
+}
+
+network.interface.in {
+ bytes FREEBSD:3:3
+ packets FREEBSD:3:4
+ mcasts FREEBSD:3:5
+ errors FREEBSD:3:6
+ drops FREEBSD:3:7
+}
+
+network.interface.out {
+ bytes FREEBSD:3:8
+ packets FREEBSD:3:9
+ mcasts FREEBSD:3:10
+ errors FREEBSD:3:11
+ collisions FREEBSD:3:12
+}
+
+network.interface.total {
+ bytes FREEBSD:3:13
+ packets FREEBSD:3:14
+ mcasts FREEBSD:3:15
+ errors FREEBSD:3:16
+}
+
+swap {
+ pagesin FREEBSD:0:18
+ pagesout FREEBSD:0:19
+ in FREEBSD:0:20
+ out FREEBSD:0:21
+ length FREEBSD:0:25
+ used FREEBSD:0:26
+ free FREEBSD:1:1
+}
+
+#undef FREEBSD