Date: Thu, 14 Sep 2017 18:20:28 +0000 (UTC) From: Steve Wills <swills@FreeBSD.org> To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r449848 - in head/net-mgmt: . nstat nstat/files Message-ID: <201709141820.v8EIKS9W040878@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: swills Date: Thu Sep 14 18:20:28 2017 New Revision: 449848 URL: https://svnweb.freebsd.org/changeset/ports/449848 Log: net-mgmt/nstat: create port nstat is a replacement for the most frequently used parts of vmstat, netstat (bw), and pmc-memory.x The advantage of using nstat is that it can run all in a single session, rather than having to use 3 terminal sessions to monitor a machine. Submitted by: gallatin (private email) Added: head/net-mgmt/nstat/ head/net-mgmt/nstat/Makefile (contents, props changed) head/net-mgmt/nstat/files/ head/net-mgmt/nstat/files/Makefile (contents, props changed) head/net-mgmt/nstat/files/nstat.1 (contents, props changed) head/net-mgmt/nstat/files/nstat.c (contents, props changed) head/net-mgmt/nstat/pkg-descr (contents, props changed) Modified: head/net-mgmt/Makefile Modified: head/net-mgmt/Makefile ============================================================================== --- head/net-mgmt/Makefile Thu Sep 14 17:56:50 2017 (r449847) +++ head/net-mgmt/Makefile Thu Sep 14 18:20:28 2017 (r449848) @@ -203,6 +203,7 @@ SUBDIR += nsca-ng-client SUBDIR += nsca27 SUBDIR += nsca27-client + SUBDIR += nstat SUBDIR += nstreams SUBDIR += observium SUBDIR += ocsinventory-agent Added: head/net-mgmt/nstat/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/net-mgmt/nstat/Makefile Thu Sep 14 18:20:28 2017 (r449848) @@ -0,0 +1,32 @@ +# $FreeBSD$ + +PORTNAME= nstat +PORTVERSION= 1.0 +CATEGORIES= net-mgmt +MASTER_SITES= # empty +DISTFILES= # empty + +MAINTAINER= swills@FreeBSD.org +COMMENT= Replacement for bw/netstat/vmstat/pcm-memory.x + +NO_FETCH= yes + +PLIST_FILES= bin/nstat \ + man/man1/nstat.1.gz + +do-extract: + @${MKDIR} ${WRKSRC} + @(cd ${FILESDIR} && ${CP} -R . ${WRKSRC}) + +do-install: + ${MKDIR} ${STAGEDIR}${PREFIX}/bin + ${INSTALL_PROGRAM} ${WRKSRC}/nstat ${STAGEDIR}${PREFIX}/bin + ${INSTALL_MAN} ${WRKSRC}/nstat.1.gz ${STAGEDIR}${PREFIX}/man/man1 + +.include <bsd.port.pre.mk> + +.if ${OPSYS} == FreeBSD && ${OSVERSION} <= 1200027 +BROKEN= Does not build without clock_nanosleep +.endif + +.include <bsd.port.post.mk> Added: head/net-mgmt/nstat/files/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/net-mgmt/nstat/files/Makefile Thu Sep 14 18:20:28 2017 (r449848) @@ -0,0 +1,4 @@ +PROG= nstat +WARNS= 2 + +.include <bsd.prog.mk> Added: head/net-mgmt/nstat/files/nstat.1 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/net-mgmt/nstat/files/nstat.1 Thu Sep 14 18:20:28 2017 (r449848) @@ -0,0 +1,58 @@ +.\" Copyright (c) 2017 +.\" Netflix Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 22, 2017 +.Dt NSTAT 1 +.Os +.Sh NAME +.Nm nstat +.Nd Display various stats. +.Sh SYNOPSIS +.Nm +.Op Fl m +.Op wait +.Sh DESCRIPTION +The +.Nm +utility will display system stats similar to netstat and vmstat. + +.Pp +The options are as follows: +.Bl -tag -width indent +.Fl m +run pcm-memory.x and report memory bandwidth statistics +.Pp +.Sh EXAMPLES +The command: +.Dl netstat -m 10 +will print what the system is doing every 10 +seconds, including reporting memory bandwith. + +.Sh SEE ALSO +.Xr netstat 1 , +.Xr vmstat 8 , +.Xr pcm-memory.x 1 + Added: head/net-mgmt/nstat/files/nstat.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/net-mgmt/nstat/files/nstat.c Thu Sep 14 18:20:28 2017 (r449848) @@ -0,0 +1,423 @@ +/*- + * Copyright (c) 2017 + * Netflix Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + */ + + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_mib.h> + +#include <netinet/tcp_fsm.h> + +#include <sys/resource.h> +#include <sys/ttycom.h> +#include <sys/vmmeter.h> + + +/* why is this only defined in the kernel */ +#define timespecadd(vvp, uvp) \ + do { \ + (vvp)->tv_sec += (uvp)->tv_sec; \ + (vvp)->tv_nsec += (uvp)->tv_nsec; \ + if ((vvp)->tv_nsec >= 1000000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_nsec -= 1000000000; \ + } \ + } while (0) + + +#define SWAP_CPU() { cpu_tmp = cpu; cpu = cpu_prev; cpu_prev = cpu_tmp; } +double +get_cpu(void) +{ + static long cpu_time[CPUSTATES]; + static long cpu_time_too[CPUSTATES]; + static long *cpu = cpu_time, *cpu_prev = cpu_time_too, *cpu_tmp; + long busy, idle; + size_t len; + int i, status; + + SWAP_CPU(); + len = sizeof(cpu_time); + status = sysctlbyname("kern.cp_time", cpu, &len, NULL, 0); + if (status) { + err(EX_OSERR, "Cant get CPU time"); + } + for (busy = 0, i = 0; i < CPUSTATES; i++) { + if (i != CP_IDLE) + busy += cpu[i] - cpu_prev[i]; + } + idle = cpu[CP_IDLE] - cpu_prev[CP_IDLE]; + + return ((double)busy / (double)(busy + idle) * 100.0); +} + +#define SWAP_VM() { vmm_tmp = vmm; vmm = vmm_prev; vmm_prev = vmm_tmp; } +struct my_vmmeter { + uint64_t v_syscall; + uint64_t v_swtch; + uint64_t v_intr; + uint64_t v_free_count; +}; + +void +get_vmm(u_int *syscall, u_int *csw, u_int *irq, double *free) +{ + static struct my_vmmeter vmm_arr[2]; + static struct my_vmmeter *vmm = &vmm_arr[0], *vmm_prev = &vmm_arr[1], + *vmm_tmp; + size_t len; + int status; + + SWAP_VM(); + len = sizeof(vmm->v_syscall); + status = sysctlbyname("vm.stats.sys.v_syscall", + &vmm->v_syscall, &len, NULL, 0); + status |= sysctlbyname("vm.stats.sys.v_swtch", + &vmm->v_swtch, &len, NULL, 0); + status |= sysctlbyname("vm.stats.sys.v_intr", + &vmm->v_intr, &len, NULL, 0); + status |= sysctlbyname("vm.stats.vm.v_free_count", + &vmm->v_free_count, &len, NULL, 0); + if (status) { + err(EX_OSERR, "Cant get CPU time"); + } + *syscall = vmm->v_syscall - vmm_prev->v_syscall; + *csw = vmm->v_swtch - vmm_prev->v_swtch; + *irq = vmm->v_intr - vmm_prev->v_intr; + *free = ((double)vmm->v_free_count * 4096.0) / + (1024.0 * 1024.0 * 1024.0); + +} + +static int +find_if(char *wanted) +{ + size_t len; + int if_maxidx, i; + int name[6]; + char ifn[128]; + + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_SYSTEM; + name[4] = IFMIB_IFCOUNT; + + len = sizeof(if_maxidx); + if (sysctl(name, 5, &if_maxidx, &len, 0, 0) < 0) + err(EX_OSERR, "can't get ifcnt"); + + + for (i = 0; i <= if_maxidx; i++) { + name[3] = IFMIB_IFDATA; + name[4] = i; + name[5] = IFDATA_DRIVERNAME; + len = sizeof(ifn); + if (sysctl(name, 6, ifn, &len, 0, 0) < 0) { + if (errno == ENOENT) + continue; + err(EX_OSERR, "can't get ifname"); + } + if (0 == strncmp(wanted, ifn, len)) + return (i); + } + printf("Could not find %s\n", wanted); + exit(ENOENT); +} + +static void +get_nic_stats(int if_idx, struct ifmibdata *ifm) +{ + size_t len; + int name[6]; + + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = if_idx; + name[5] = IFDATA_GENERAL; + len = sizeof(*ifm); + if (sysctl(name, 6, ifm, &len, 0, 0) < 0) + err(EX_OSERR, "could not get nic stats"); +} + +static char * +find_ifname(void) +{ + static char ifbuf[80]; + FILE *pipe; + char *p; + + + pipe = popen("netstat -4rn | grep default", "r"); + if (pipe == NULL) + err(EX_OSERR, "could not run netsat"); + (void)fread(ifbuf, sizeof(ifbuf) - 1, 1, pipe); + pclose(pipe); + p = &ifbuf[sizeof(ifbuf) - 1]; + while (*p != ' ') { + if (*p == '\n') + *p = '\0'; + p--; + if (p == ifbuf) + err(EX_DATAERR, "can't read def rt"); + } + /* we are point to a space, point to 1st ifname char */ + return (p + 1); +} + +static int +find_rows(void) +{ + struct winsize sz; + int rows; + + rows = 24; + if (isatty(STDOUT_FILENO)) { + if (0 == ioctl(STDOUT_FILENO, TIOCGWINSZ, &sz)) + rows = sz.ws_row; + } + return (rows); +} + +static void +get_pcm_mem(double interval, double *r, double *w, double *rw) +{ + static FILE * pipe = NULL; + static int pipe_fd; + char *open_str; + char buf[256]; + char *p; + int sem_cnt, ret_found; + ssize_t bytes; + + if (pipe == NULL) { + open_str = alloca(80); + snprintf(open_str, 80, + "pcm-memory.x -csv %f 2>/dev/null", interval * 0.99); + pipe = popen(open_str, "r"); + if (pipe == NULL) + err(EX_OSERR, "pipe"); + /* read one-time CSV hdrs */ + (void)fread(buf, sizeof(buf) - 1, 1, pipe); + pipe_fd = fileno(pipe); + fcntl(pipe_fd, F_SETFL, O_NONBLOCK); + } + bzero(buf, sizeof(buf)); + bytes = read(pipe_fd, buf, sizeof(buf)); + if (bytes == 0) + err(EX_OSERR, + "pcm-memory.x exited - kldload cpuctl?"); + + if (bytes == -1) { + if (errno == EAGAIN) + return; + err(EX_OSERR, "read from pcm-memory.x failed"); + } + + p = &buf[bytes]; + /* + * the CSV is formatted as: ...; ....; readbw; writebw; rwbw\n + * + * So we start at the end of the buffer, and look backwards for a + * return. Once found, we then count back 3 semi-colons, and then + * pass it to sscanf + */ + + ret_found = 0; + sem_cnt = 0; + while (p != buf && sem_cnt < 3) { + p--; + if (!ret_found && *p != '\n') + continue; + ret_found = 1; + if (*p == ';') + sem_cnt++; + } + if (sem_cnt == 3) { + p++; + sscanf(p, "%lf; %lf; %lf", r, w, rw); + } +} + +long +get_tcp_est() +{ + uint64_t tcps[TCP_NSTATES]; + size_t len = sizeof(tcps); + int status; + + status = sysctlbyname("net.inet.tcp.states", tcps, &len, NULL, 0); + if (status != 0) + err(EX_OSERR, "could not fetch tcp states"); + + return ((long)tcps[TCPS_ESTABLISHED]); +} + +#define SWAP_IFM() { ifm_tmp = ifm; ifm = ifm_prev; ifm_prev = ifm_tmp; } + + +static void +print_hdr(int do_pcm_mem) +{ + printf(" InMpps OMpps InGbs OGbs err TCP Est %%CPU syscalls csw irq GBfree"); + if (do_pcm_mem) + printf(" MemRd MemWr MemRW"); + printf("\n"); + +} + +static void +usage(char *name) +{ + fprintf(stderr, + "usage: %s [-m] [-I interface] [wait]\n", name); + exit(1); +} + +int +main(int argc, char **argv) +{ + struct ifmibdata ifmd[2]; + struct ifmibdata *ifm, *ifm_prev, *ifm_tmp; + char *ifname = NULL; + int c, if_idx; + double o_gbps, i_gbps, o_mpps, i_mpps; + long errs, est; + double interval = 1.0; + int rows = find_rows() - 1; + int row = 0; + int do_pcm_mem = 0; + u_int syscalls, csw, irq; + double free_mem; + double mem_rd = 0.0, mem_wr = 0.0, mem_rw = 0.0; + struct timespec interval_ts, deadline_ts; + + + while ((c = getopt(argc, argv, "mI:")) != -1) { + switch (c) { + case 'm': + do_pcm_mem = 1; + break; + case 'I': + ifname = optarg; + break; + default: + usage(argv[0]); + } + } + argc -= optind; + argv += optind; + + if (*argv) + interval = atof(*argv); + + interval_ts.tv_sec = (time_t)interval; + interval_ts.tv_nsec = (interval - (time_t)interval) * 1000000000; + + if (ifname == NULL) + ifname = find_ifname(); + + if_idx = find_if(ifname); + ifm = &ifmd[0]; + ifm_prev = &ifmd[1]; + + /* + * preload all the counters so 1st interval looks reasonable + */ + + if (do_pcm_mem) + get_pcm_mem(interval, &mem_rd, &mem_wr, &mem_rw); + get_nic_stats(if_idx, ifm); + est = get_tcp_est(); + get_vmm(&syscalls, &csw, &irq, &free_mem); + (void)get_cpu(); + usleep(interval * 1000 * 1000); + clock_gettime(CLOCK_UPTIME, &deadline_ts); + while (1) { + if (row == 0) + print_hdr(do_pcm_mem); + SWAP_IFM(); + get_nic_stats(if_idx, ifm); + i_gbps = ifm->ifmd_data.ifi_ibytes - + ifm_prev->ifmd_data.ifi_ibytes; + i_gbps = (8.0 * i_gbps) / (1000.0 * 1000.0 * 1000.0 * interval); + o_gbps = ifm->ifmd_data.ifi_obytes - + ifm_prev->ifmd_data.ifi_obytes; + o_gbps = (8.0 * o_gbps) / (1000.0 * 1000.0 * 1000.0 * interval); + i_mpps = ifm->ifmd_data.ifi_ipackets - + ifm_prev->ifmd_data.ifi_ipackets; + i_mpps = i_mpps / (1000 * 1000 * interval); + o_mpps = ifm->ifmd_data.ifi_opackets - + ifm_prev->ifmd_data.ifi_opackets; + o_mpps = o_mpps / (1000 * 1000 * interval); + + /* all errs .. should be rare, don't clutter output */ + errs = ifm->ifmd_data.ifi_oerrors - + ifm_prev->ifmd_data.ifi_oerrors; + errs += ifm->ifmd_data.ifi_ierrors - + ifm_prev->ifmd_data.ifi_ierrors; + errs += ifm->ifmd_data.ifi_oqdrops - + ifm_prev->ifmd_data.ifi_oqdrops; + errs += ifm->ifmd_data.ifi_iqdrops - + ifm_prev->ifmd_data.ifi_iqdrops; + + est = get_tcp_est(); + get_vmm(&syscalls, &csw, &irq, &free_mem); + printf("%6.2lf %6.2lf %6.2lf %6.2lf %2ld %6ld %4.2lf %6.0f %6.0f %6.0f %5.2lf", + i_mpps, o_mpps, i_gbps, o_gbps, errs, est, get_cpu(), + syscalls / interval, csw / interval, irq / interval, + free_mem); + + if (do_pcm_mem) { + get_pcm_mem(interval, &mem_rd, &mem_wr, &mem_rw); + printf(" %8.2f %8.2f %8.2f", mem_rd, mem_wr, mem_rw); + } + printf("\n"); + if (++row == rows) + row = 0; + + timespecadd(&deadline_ts, &interval_ts); + clock_nanosleep(CLOCK_UPTIME, TIMER_ABSTIME, &deadline_ts, NULL); + } +} Added: head/net-mgmt/nstat/pkg-descr ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/net-mgmt/nstat/pkg-descr Thu Sep 14 18:20:28 2017 (r449848) @@ -0,0 +1,4 @@ +nstat is a replacement for the most frequently used parts of vmstat, netstat +(bw), and pmc-memory.x The advantage of using nstat is that it can run all in a +single session, rather than having to use 3 terminal sessions to monitor a +machine.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201709141820.v8EIKS9W040878>