Skip site navigation (1)Skip section navigation (2)
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>