2 * First stab at support for metrics in FreeBSD
3 * by Preston Smith <psmith@physics.purdue.edu>
4 * Wed Feb 27 14:55:33 EST 2002
5 * Improved by Brooks Davis <brooks@one-eyed-alien.net>,
7 * Tue Jul 15 16:42:22 EST 2003
9 * $Id: dfly-metrics.c,v 1.1 2006/04/30 22:28:44 joerg Exp $
17 #include <sys/param.h>
18 #include <sys/mount.h>
19 #include <sys/sysctl.h>
24 #include <vm/vm_param.h>
26 #include <sys/socket.h>
28 #include <net/if_dl.h>
29 #include <net/route.h>
37 #include "interface.h"
38 #include "libmetrics.h"
40 #define MIB_SWAPINFO_SIZE 3
42 #ifndef MIN_NET_POLL_INTERVAL
43 #define MIN_NET_POLL_INTERVAL 0.5
46 #ifndef MIN_CPU_POLL_INTERVAL
47 #define MIN_CPU_POLL_INTERVAL 0.5
51 #define UINT64_MAX ULLONG_MAX
54 #define VFCF_NONLOCAL (VFCF_NETWORK|VFCF_SYNTHETIC|VFCF_LOOPBACK)
56 #define timertod(tvp) \
57 ((double)(tvp)->tv_sec + (double)(tvp)->tv_usec/(1000*1000))
59 #ifndef XSWDEV_VERSION
60 #define XSWDEV_VERSION 1
77 static void get_netbw(double *, double *, double *, double *);
78 static uint64_t counterdiff(uint64_t, uint64_t, uint64_t, uint64_t);
81 static char *makenetvfslist(void);
82 static size_t regetmntinfo(struct statfs **, long, const char **);
83 static int checkvfsname(const char *, const char **);
84 static const char **makevfslist(char *);
85 static float find_disk_space(double *, double *);
87 static int use_vm_swap_info = 0;
88 static int mibswap[MIB_SWAPINFO_SIZE];
89 static size_t mibswap_size;
90 static kvm_t *kd = NULL;
95 * This function is called only once by the gmond. Use to
96 * initialize data structures, etc or just return SYNAPSE_SUCCESS;
104 * Try to use the vm.swap_info sysctl to gather swap data. If it
105 * isn't implemented, fall back to trying to old kvm based interface.
107 mibswap_size = MIB_SWAPINFO_SIZE;
108 if (sysctlnametomib("vm.swap_info", mibswap, &mibswap_size) == -1) {
109 kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "metric_init()");
112 * RELEASE versions of FreeBSD with the swap mib have a version
113 * of libkvm that doesn't need root for simple proc access so we
114 * just open /dev/null to give us a working handle here.
116 kd = kvm_open(_PATH_DEVNULL, NULL, NULL, O_RDONLY, "metric_init()");
117 use_vm_swap_info = 1;
119 pagesize = getpagesize();
121 /* Initalize some counters */
122 get_netbw(NULL, NULL, NULL, NULL);
125 val.int32 = SYNAPSE_SUCCESS;
130 cpu_num_func ( void )
135 if (kinfo_get_cpus(&ncpu))
143 cpu_speed_func ( void )
147 size_t len = sizeof(cpu_speed);
150 * machdep.tsc_freq is an i386/amd64 only feature, but it's the best
151 * we've got at the moment.
153 if (sysctlbyname("machdep.tsc_freq", &cpu_speed, &len, NULL, 0) == -1)
155 val.uint16 = cpu_speed /= 1000000;
161 mem_total_func ( void )
169 if (sysctlbyname("hw.physmem", &total, &len, NULL, 0) == -1)
171 val.uint32 = total / 1024;
177 swap_total_func ( void )
180 struct kvm_swap swap[1];
187 if (use_vm_swap_info) {
189 mibswap[mibswap_size] = n;
191 if (sysctl(mibswap, mibswap_size + 1, &xsw, &size, NULL, 0) == -1)
193 if (xsw.xsw_version != XSWDEV_VERSION)
195 totswap += xsw.xsw_nblks;
197 } else if(kd != NULL) {
198 n = kvm_getswapinfo(kd, swap, 1, 0);
199 if (n < 0 || swap[0].ksw_total == 0) {
202 totswap = swap[0].ksw_total;
205 val.uint32 = totswap * (pagesize / 1024);
210 boottime_func ( void )
213 struct timeval boottime;
216 size = sizeof(boottime);
217 if (sysctlbyname("kern.boottime", &boottime, &size, NULL, 0) == -1)
220 val.uint32 = (uint32_t) boottime.tv_sec;
226 sys_clock_func ( void )
230 val.uint32 = time(NULL);
235 machine_type_func ( void )
238 size_t len = sizeof(val.str);
240 if (sysctlbyname("hw.machine", val.str, &len, NULL, 0) == -1 ||
242 strlcpy(val.str, "unknown", sizeof(val.str));
248 os_name_func ( void )
251 size_t len = sizeof(val.str);
253 if (sysctlbyname("kern.ostype", val.str, &len, NULL, 0) == -1 ||
255 strlcpy(val.str, "DragonFly (unknown)", sizeof(val.str));
261 os_release_func ( void )
264 size_t len = sizeof(val.str);
266 if (sysctlbyname("kern.osrelease", val.str, &len, NULL, 0) == -1 ||
268 strlcpy(val.str, "unknown", sizeof(val.str));
273 /* Get the CPU state given by index, from kern.cp_time
274 * Use the constants in <sys/dkstat.h>
275 * CP_USER=0, CP_NICE=1, CP_SYS=2, CP_INTR=3, CP_IDLE=4
278 int cpu_state(int which) {
279 static struct kinfo_cputime cp_old, cp_diff;
280 static uint64_t total_change, half_change;
281 static struct timeval this_time, last_time;
283 struct kinfo_cputime cp_time;
284 struct timeval time_diff;
288 bzero(&cp_old, sizeof(cp_old));
289 bzero(&cp_diff, sizeof(cp_diff));
292 bzero(&last_time, sizeof(last_time));
296 gettimeofday(&this_time, NULL);
297 timersub(&this_time, &last_time, &time_diff);
298 if (timertod(&time_diff) < MIN_CPU_POLL_INTERVAL) {
301 last_time = this_time;
303 if (kinfo_get_sched_cputime(&cp_time)) {
304 warn("kinfo_get_sched_cputime");
307 cp_diff.cp_user = cp_time.cp_user - cp_old.cp_user;
308 cp_diff.cp_nice = cp_time.cp_nice - cp_old.cp_nice;
309 cp_diff.cp_sys = cp_time.cp_sys - cp_old.cp_sys;
310 cp_diff.cp_intr = cp_time.cp_intr - cp_old.cp_intr;
311 cp_diff.cp_idle = cp_time.cp_idle - cp_old.cp_idle;
312 total_change = cp_diff.cp_user + cp_diff.cp_nice + cp_diff.cp_sys
313 + cp_diff.cp_sys + cp_diff.cp_intr + cp_diff.cp_idle;
314 if (total_change == 0)
316 half_change = total_change >> 1;
321 return (cp_diff.cp_user * 100LL + half_change) / total_change;
323 return (cp_diff.cp_nice * 100LL + half_change) / total_change;
325 return (cp_diff.cp_sys * 100LL + half_change) / total_change;
327 return (cp_diff.cp_intr * 100LL + half_change) / total_change;
329 return (cp_diff.cp_idle * 100LL + half_change) / total_change;
336 cpu_user_func ( void )
340 val.f = (float) cpu_state(0);
346 cpu_nice_func ( void )
350 val.f = (float) cpu_state(1);
356 cpu_system_func ( void )
360 val.f = (float) cpu_state(2);
366 cpu_idle_func ( void )
370 val.f = (float) cpu_state(4);
376 ** FIXME - This metric is not valid on FreeBSD.
379 cpu_wio_func ( void )
388 ** FIXME - Idle time since startup. The scheduler apparently knows
389 ** this, but we it's fairly pointless so it's not exported.
392 cpu_aidle_func ( void )
400 cpu_intr_func ( void )
404 val.f = (float) cpu_state(3);
410 ** FIXME - This metric is not valid on FreeBSD.
413 cpu_sintr_func ( void )
421 load_one_func ( void )
433 load_five_func ( void )
445 load_fifteen_func ( void )
457 proc_total_func ( void )
462 sysctlbyname("kern.proc.all", NULL, &len, NULL, 0);
464 val.uint32 = (len / sizeof (struct kinfo_proc));
471 proc_run_func( void )
473 struct kinfo_proc *kp;
477 int what = KERN_PROC_ALL;
484 #ifdef KERN_PROC_NOTHREADS
485 what |= KERN_PROC_NOTHREADS
487 if ((kp = kvm_getprocs(kd, what, 0, &nentries)) == 0 || nentries < 0)
490 for (i = 0; i < nentries; kp++, i++) {
491 #ifdef KINFO_PROC_SIZE
494 state = kp->kp_proc.p_stat;
512 ** FIXME - The whole ganglia model of memory is bogus. Free memory is
513 ** generally a bad idea with a modern VM and so is reporting it. There
514 ** is simply no way to report a value for "free" memory that makes any
515 ** kind of sense. Free+inactive might be a decent value for "free".
518 mem_free_func ( void )
524 len = sizeof (free_pages);
525 if((sysctlbyname("vm.stats.vm.v_free_count", &free_pages, &len, NULL, 0)
526 == -1) || !len) free_pages = 0;
528 val.uint32 = free_pages * (pagesize / 1024);
533 ** FreeBSD don't seem to report this anywhere. It's actually quite
534 ** complicated as there is SysV shared memory, POSIX shared memory,
535 ** and mmap shared memory at a minimum.
538 mem_shared_func ( void )
548 ** FIXME - this isn't really valid. It lists some VFS buffer space,
549 ** but the real picture is much more complex.
552 mem_buffers_func ( void )
558 len = sizeof (buffers);
559 if((sysctlbyname("vfs.bufspace", &buffers, &len, NULL, 0) == -1) || !len)
563 val.uint32 = buffers;
568 ** FIXME - this isn't really valid. It lists some VM cache space,
569 ** but the real picture is more complex.
572 mem_cached_func ( void )
578 len = sizeof (cache);
579 if((sysctlbyname("vm.stats.vm.v_cache_count", &cache, &len, NULL, 0) == -1)
583 val.uint32 = cache * (pagesize / 1024);
588 swap_free_func ( void )
592 struct kvm_swap swap[1];
595 int totswap, usedswap, freeswap, n;
599 if (use_vm_swap_info) {
601 mibswap[mibswap_size] = n;
603 if (sysctl(mibswap, mibswap_size + 1, &xsw, &size, NULL, 0) == -1)
605 if (xsw.xsw_version != XSWDEV_VERSION)
607 totswap += xsw.xsw_nblks;
608 usedswap += xsw.xsw_used;
610 } else if(kd != NULL) {
611 n = kvm_getswapinfo(kd, swap, 1, 0);
612 totswap = swap[0].ksw_total;
613 usedswap = swap[0].ksw_used;
615 freeswap = totswap - usedswap;
616 val.uint32 = freeswap * (pagesize / 1024);
624 /* We want to find the minimum MTU (Max packet size) over all UP interfaces.
627 val.uint32 = get_min_mtu();
628 /* A val of 0 means there are no UP interfaces. Shouldn't happen. */
633 pkts_in_func ( void )
638 get_netbw(NULL, NULL, &in_pkts, NULL);
640 val.f = (float)in_pkts;
645 pkts_out_func ( void )
650 get_netbw(NULL, NULL, NULL, &out_pkts);
652 val.f = (float)out_pkts;
657 bytes_out_func ( void )
662 get_netbw(NULL, &out_bytes, NULL, NULL);
664 val.f = (float)out_bytes;
669 bytes_in_func ( void )
674 get_netbw(&in_bytes, NULL, NULL, NULL);
676 val.f = (float)in_bytes;
681 * Disk space reporting functions from Linux code. find_disk_space()
682 * body derived from FreeBSD df and mount code.
686 disk_free_func( void )
688 double total_free=0.0;
689 double total_size=0.0;
692 find_disk_space(&total_size, &total_free);
699 disk_total_func( void )
701 double total_free=0.0;
702 double total_size=0.0;
705 find_disk_space(&total_size, &total_free);
712 part_max_used_func( void )
714 double total_free=0.0;
715 double total_size=0.0;
719 most_full = find_disk_space(&total_size, &total_free);
727 * Copyright (c) 1980, 1983, 1990, 1993, 1994, 1995
728 * The Regents of the University of California. All rights reserved.
729 * (c) UNIX System Laboratories, Inc.
730 * All or some portions of this file are derived from material licensed
731 * to the University of California by American Telephone and Telegraph
732 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
733 * the permission of UNIX System Laboratories, Inc.
735 * Redistribution and use in source and binary forms, with or without
736 * modification, are permitted provided that the following conditions
738 * 1. Redistributions of source code must retain the above copyright
739 * notice, this list of conditions and the following disclaimer.
740 * 2. Redistributions in binary form must reproduce the above copyright
741 * notice, this list of conditions and the following disclaimer in the
742 * documentation and/or other materials provided with the distribution.
743 * 3. All advertising materials mentioning features or use of this software
744 * must display the following acknowledgement:
745 * This product includes software developed by the University of
746 * California, Berkeley and its contributors.
747 * 4. Neither the name of the University nor the names of its contributors
748 * may be used to endorse or promote products derived from this software
749 * without specific prior written permission.
751 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
752 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
753 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
754 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
755 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
756 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
757 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
758 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
759 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
760 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
764 * NOTE: The copyright of UC Berkeley's Berkeley Software Distribution
765 * ("BSD") source has been updated. The copyright addendum may be found
766 * at ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change.
770 find_disk_space(double *total, double *tot_avail)
772 struct statfs *mntbuf;
774 const char **vfslist;
777 size_t used, availblks;
778 const double reported_units = 1e9;
781 float most_full = 0.0;
788 netvfslist = makenetvfslist();
789 vfslist = makevfslist(netvfslist);
792 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
793 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);
794 for (i = 0; i < mntsize; i++) {
795 if ((mntbuf[i].f_flags & MNT_IGNORE) == 0) {
796 used = mntbuf[i].f_blocks - mntbuf[i].f_bfree;
797 availblks = mntbuf[i].f_bavail + used;
798 pct = (availblks == 0 ? 100.0 :
799 (double)used / (double)availblks * 100.0);
803 toru = reported_units/mntbuf[i].f_bsize;
804 *total += mntbuf[i].f_blocks / toru;
805 *tot_avail += mntbuf[i].f_bavail / toru;
813 * Make a pass over the file system info in ``mntbuf'' filtering out
814 * file system types not in vfslist and possibly re-stating to get
815 * current (not cached) info. Returns the new count of valid statfs bufs.
818 regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist)
821 struct statfs *mntbuf;
824 return (getmntinfo(mntbufp, MNT_WAIT));
827 for (j = 0, i = 0; i < mntsize; i++) {
828 if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
830 (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
837 checkvfsname(vfsname, vfslist)
839 const char **vfslist;
844 while (*vfslist != NULL) {
845 if (strcmp(vfsname, *vfslist) == 0)
862 if (fslist[0] == 'n' && fslist[1] == 'o') {
866 for (i = 0, nextcp = fslist; *nextcp; nextcp++)
869 if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) {
870 warnx("malloc failed");
876 while ((nextcp = strchr(nextcp, ',')) != NULL) {
887 char *str = NULL, *strptr, **listptr = NULL;
891 int mib[3], maxvfsconf;
893 struct ovfsconf *ptr;
895 mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
896 miblen=sizeof(maxvfsconf);
897 if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])),
898 &maxvfsconf, &miblen, NULL, 0)) {
899 warnx("sysctl failed");
903 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
904 warnx("malloc failed");
909 while ((ptr = getvfsent()) != NULL && cnt < maxvfsconf) {
910 if (ptr->vfc_flags & VFCF_NONLOCAL)
913 listptr[cnt] = strdup(ptr->vfc_name);
914 if (listptr[cnt] == NULL) {
915 warnx("malloc failed");
925 * Count up the string lengths, we need a extra byte to hold
926 * the between entries ',' or the NUL at the end.
928 for (i = 0; i < cnt; i++)
929 slen = strlen(listptr[i]) + 1;
930 /* Add 2 for initial "no". */
933 if ((str = malloc(slen)) == NULL) {
934 warnx("malloc failed");
940 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
941 strcpy(strptr, listptr[i]);
942 strptr += strlen(listptr[i]);
948 if (listptr != NULL) {
949 for(i = 0; i < cnt && listptr[i] != NULL; i++)
958 get_netbw(double *in_bytes, double *out_bytes,
959 double *in_pkts, double *out_pkts)
964 struct if_msghdr *ifm, *nextifm;
965 struct sockaddr_dl *sdl;
966 char *buf, *lim, *next;
971 static double ibytes, obytes, ipkts, opkts;
972 struct timeval this_time;
973 struct timeval time_diff;
974 struct traffic traffic;
975 static struct timeval last_time = {0,0};
976 static int indexes = 0;
977 static int *seen = NULL;
978 static struct traffic *lastcount = NULL;
979 static double o_ibytes, o_obytes, o_ipkts, o_opkts;
981 ibytes = obytes = ipkts = opkts = 0.0;
986 mib[3] = 0; /* address family */
987 mib[4] = NET_RT_IFLIST;
988 mib[5] = 0; /* interface index */
990 gettimeofday(&this_time, NULL);
991 timersub(&this_time, &last_time, &time_diff);
992 if (timertod(&time_diff) < MIN_NET_POLL_INTERVAL) {
996 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
997 errx(1, "iflist-sysctl-estimate");
998 if ((buf = malloc(needed)) == NULL)
1000 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
1001 errx(1, "actual retrieval of interface table");
1005 while (next < lim) {
1007 ifm = (struct if_msghdr *)next;
1009 if (ifm->ifm_type == RTM_IFINFO) {
1010 sdl = (struct sockaddr_dl *)(ifm + 1);
1012 fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
1013 fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
1015 fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
1016 fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
1021 next += ifm->ifm_msglen;
1022 while (next < lim) {
1023 nextifm = (struct if_msghdr *)next;
1025 if (nextifm->ifm_type != RTM_NEWADDR)
1028 next += nextifm->ifm_msglen;
1031 if ((ifm->ifm_flags & IFF_LOOPBACK) ||
1032 !(ifm->ifm_flags & IFF_UP))
1035 index = ifm->ifm_index;
1037 /* If we don't have a previous value yet, make a slot. */
1038 if (index >= indexes) {
1039 seen = realloc(seen, sizeof(*seen)*(index+1));
1040 lastcount = realloc(lastcount,
1041 sizeof(*lastcount)*(index+1));
1043 /* Initalize the new slots */
1044 for (i = indexes; i <= index; i++) {
1051 * If this is the first time we've seen this interface,
1052 * set the last values to the current ones. That causes
1053 * us to see no bandwidth on the interface the first
1054 * time, but that's OK.
1058 lastcount[index].in_bytes = ifm->ifm_data.ifi_ibytes;
1059 lastcount[index].out_bytes = ifm->ifm_data.ifi_obytes;
1060 lastcount[index].in_pkts = ifm->ifm_data.ifi_ipackets;
1061 lastcount[index].out_pkts = ifm->ifm_data.ifi_opackets;
1064 traffic.in_bytes = counterdiff(lastcount[index].in_bytes,
1065 ifm->ifm_data.ifi_ibytes, ULONG_MAX, 0);
1066 traffic.out_bytes = counterdiff(lastcount[index].out_bytes,
1067 ifm->ifm_data.ifi_obytes, ULONG_MAX, 0);
1068 traffic.in_pkts = counterdiff(lastcount[index].in_pkts,
1069 ifm->ifm_data.ifi_ipackets, ULONG_MAX, 0);
1070 traffic.out_pkts = counterdiff(lastcount[index].out_pkts,
1071 ifm->ifm_data.ifi_opackets, ULONG_MAX, 0);
1073 lastcount[index].in_bytes = ifm->ifm_data.ifi_ibytes;
1074 lastcount[index].out_bytes = ifm->ifm_data.ifi_obytes;
1075 lastcount[index].in_pkts = ifm->ifm_data.ifi_ipackets;
1076 lastcount[index].out_pkts = ifm->ifm_data.ifi_opackets;
1079 if_indextoname(index, name);
1080 printf("%s: \n", name);
1081 printf("\topackets=%llu ipackets=%llu\n",
1082 traffic.out_pkts, traffic.in_pkts);
1083 printf("\tobytes=%llu ibytes=%llu\n",
1084 traffic.out_bytes, traffic.in_bytes);
1087 if (timerisset(&last_time)) {
1088 ibytes += (double)traffic.in_bytes / timertod(&time_diff);
1089 obytes += (double)traffic.out_bytes / timertod(&time_diff);
1090 ipkts += (double)traffic.in_pkts / timertod(&time_diff);
1091 opkts += (double)traffic.out_pkts / timertod(&time_diff);
1096 /* Save the values from this time */
1097 last_time = this_time;
1104 if (in_bytes != NULL)
1105 *in_bytes = o_ibytes;
1106 if (out_bytes != NULL)
1107 *out_bytes = o_obytes;
1108 if (in_pkts != NULL)
1110 if (out_pkts != NULL)
1111 *out_pkts = o_opkts;
1115 counterdiff(uint64_t oldval, uint64_t newval, uint64_t maxval, uint64_t maxdiff)
1123 if (oldval > maxval || newval > maxval)
1127 * Tackle the easy case. Don't worry about maxdiff here because
1128 * we're SOL if it happens (i.e. assuming a reset just makes
1131 if (oldval <= newval)
1132 return (newval - oldval);
1135 * Now the tricky part. If we assume counters never get reset,
1136 * this is easy. Unfortunaly, they do get reset on some
1137 * systems, so we need to try and deal with that. Our huristic
1138 * is that if out difference is greater then maxdiff and newval
1139 * is less or equal to maxdiff, then we've probably been reset
1140 * rather then actually wrapping. Obviously, you need to be
1141 * careful to poll often enough that you won't exceed maxdiff or
1142 * you will get undersized numbers when you do wrap.
1144 diff = maxval - oldval + newval;
1145 if (diff > maxdiff && newval <= maxdiff)