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)
This commit is contained in:
Steve Wills 2017-09-14 18:20:28 +00:00
parent d394b033f4
commit e3e6a21819
Notes: svn2git 2021-03-31 03:12:20 +00:00
svn path=/head/; revision=449848
6 changed files with 522 additions and 0 deletions

View file

@ -203,6 +203,7 @@
SUBDIR += nsca-ng-client
SUBDIR += nsca27
SUBDIR += nsca27-client
SUBDIR += nstat
SUBDIR += nstreams
SUBDIR += observium
SUBDIR += ocsinventory-agent

32
net-mgmt/nstat/Makefile Normal file
View file

@ -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>

View file

@ -0,0 +1,4 @@
PROG= nstat
WARNS= 2
.include <bsd.prog.mk>

View file

@ -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

View file

@ -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);
}
}

4
net-mgmt/nstat/pkg-descr Normal file
View file

@ -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.