freebsd-skq/usr.bin/systat/devs.c
Michael Reifenberger 4551884596 systat -zarc to display disk activities like -vm
PR:		213310
Submitted by:	ota
MFH:		4 weeks
Differential Revision:	https://reviews.freebsd.org/D18726
2019-04-02 14:01:03 +00:00

444 lines
13 KiB
C

/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1998 Kenneth D. Merry.
* 2015 Yoshihiro Ota
* All rights reserved.
*
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/*-
* Copyright (c) 1980, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef lint
static const char sccsid[] = "@(#)disks.c 8.1 (Berkeley) 6/6/93";
#endif
#include <sys/types.h>
#include <sys/devicestat.h>
#include <sys/resource.h>
#include <ctype.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include "systat.h"
#include "extern.h"
#include "devs.h"
typedef enum {
DS_MATCHTYPE_NONE,
DS_MATCHTYPE_SPEC,
DS_MATCHTYPE_PATTERN
} last_match_type;
struct statinfo cur_dev, last_dev, run_dev;
last_match_type last_type;
struct device_selection *dev_select;
long generation;
int num_devices, num_selected;
int num_selections;
long select_generation;
struct devstat_match *matches = NULL;
int num_matches = 0;
char **specified_devices;
int num_devices_specified = 0;
static int dsmatchselect(const char *args, devstat_select_mode select_mode,
int maxshowdevs, struct statinfo *s1);
static int dsselect(const char *args, devstat_select_mode select_mode,
int maxshowdevs, struct statinfo *s1);
int
dsinit(int maxshowdevs)
{
/*
* Make sure that the userland devstat version matches the kernel
* devstat version. If not, exit and print a message informing
* the user of his mistake.
*/
if (devstat_checkversion(NULL) < 0)
errx(1, "%s", devstat_errbuf);
if( cur_dev.dinfo ) // init was alreay ran
return(1);
if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
warnx("%s", devstat_errbuf);
return(0);
}
cur_dev.dinfo = calloc(1, sizeof(struct devinfo));
last_dev.dinfo = calloc(1, sizeof(struct devinfo));
run_dev.dinfo = calloc(1, sizeof(struct devinfo));
generation = 0;
num_devices = 0;
num_selected = 0;
num_selections = 0;
select_generation = 0;
last_type = DS_MATCHTYPE_NONE;
if (devstat_getdevs(NULL, &cur_dev) == -1)
errx(1, "%s", devstat_errbuf);
num_devices = cur_dev.dinfo->numdevs;
generation = cur_dev.dinfo->generation;
dev_select = NULL;
/*
* At this point, selectdevs will almost surely indicate that the
* device list has changed, so we don't look for return values of 0
* or 1. If we get back -1, though, there is an error.
*/
if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
&select_generation, generation, cur_dev.dinfo->devices, num_devices,
NULL, 0, NULL, 0, DS_SELECT_ADD, maxshowdevs, 0) == -1)
errx(1, "%d %s", __LINE__, devstat_errbuf);
return(1);
}
void
dsgetinfo(struct statinfo* dev)
{
switch (devstat_getdevs(NULL, dev)) {
case -1:
errx(1, "%s", devstat_errbuf);
break;
case 1:
num_devices = dev->dinfo->numdevs;
generation = dev->dinfo->generation;
cmdkre("refresh", NULL);
break;
default:
break;
}
}
int
dscmd(const char *cmd, const char *args, int maxshowdevs, struct statinfo *s1)
{
int retval;
if (prefix(cmd, "display") || prefix(cmd, "add"))
return(dsselect(args, DS_SELECT_ADDONLY, maxshowdevs, s1));
if (prefix(cmd, "ignore") || prefix(cmd, "delete"))
return(dsselect(args, DS_SELECT_REMOVE, maxshowdevs, s1));
if (prefix(cmd, "show") || prefix(cmd, "only"))
return(dsselect(args, DS_SELECT_ONLY, maxshowdevs, s1));
if (prefix(cmd, "type") || prefix(cmd, "match"))
return(dsmatchselect(args, DS_SELECT_ONLY, maxshowdevs, s1));
if (prefix(cmd, "refresh")) {
retval = devstat_selectdevs(&dev_select, &num_selected,
&num_selections, &select_generation, generation,
s1->dinfo->devices, num_devices,
(last_type ==DS_MATCHTYPE_PATTERN) ? matches : NULL,
(last_type ==DS_MATCHTYPE_PATTERN) ? num_matches : 0,
(last_type == DS_MATCHTYPE_SPEC) ?specified_devices : NULL,
(last_type == DS_MATCHTYPE_SPEC) ?num_devices_specified : 0,
(last_type == DS_MATCHTYPE_NONE) ? DS_SELECT_ADD :
DS_SELECT_ADDONLY, maxshowdevs, 0);
if (retval == -1) {
warnx("%s", devstat_errbuf);
return(0);
} else if (retval == 1)
return(2);
}
if (prefix(cmd, "drives")) {
int i;
move(CMDLINE, 0);
clrtoeol();
for (i = 0; i < num_devices; i++) {
printw("%s%d ", s1->dinfo->devices[i].device_name,
s1->dinfo->devices[i].unit_number);
}
return(1);
}
return(0);
}
static int
dsmatchselect(const char *args, devstat_select_mode select_mode, int maxshowdevs,
struct statinfo *s1)
{
char **tempstr, *tmpstr, *tmpstr1;
char *tstr[100];
int num_args = 0;
int i;
int retval = 0;
if (!args) {
warnx("dsmatchselect: no arguments");
return(1);
}
/*
* Break the (pipe delimited) input string out into separate
* strings.
*/
tmpstr = tmpstr1 = strdup(args);
for (tempstr = tstr, num_args = 0;
(*tempstr = strsep(&tmpstr1, "|")) != NULL && (num_args < 100);
num_args++)
if (**tempstr != '\0')
if (++tempstr >= &tstr[100])
break;
free(tmpstr);
if (num_args > 99) {
warnx("dsmatchselect: too many match arguments");
return(0);
}
/*
* If we've gone through the matching code before, clean out
* previously used memory.
*/
if (num_matches > 0) {
free(matches);
matches = NULL;
num_matches = 0;
}
for (i = 0; i < num_args; i++) {
if (devstat_buildmatch(tstr[i], &matches, &num_matches) != 0) {
warnx("%s", devstat_errbuf);
return(0);
}
}
if (num_args > 0) {
last_type = DS_MATCHTYPE_PATTERN;
retval = devstat_selectdevs(&dev_select, &num_selected,
&num_selections, &select_generation, generation,
s1->dinfo->devices, num_devices, matches, num_matches,
NULL, 0, select_mode, maxshowdevs, 0);
if (retval == -1)
err(1, "device selection error");
else if (retval == 1)
return(2);
}
return(1);
}
static int
dsselect(const char *args, devstat_select_mode select_mode, int maxshowdevs,
struct statinfo *s1)
{
char *cp, *tmpstr, *tmpstr1, *buffer;
int i;
int retval = 0;
if (!args) {
warnx("dsselect: no argument");
return(1);
}
/*
* If we've gone through this code before, free previously
* allocated resources.
*/
if (num_devices_specified > 0) {
for (i = 0; i < num_devices_specified; i++)
free(specified_devices[i]);
free(specified_devices);
specified_devices = NULL;
num_devices_specified = 0;
}
/* do an initial malloc */
specified_devices = (char **)malloc(sizeof(char *));
tmpstr = tmpstr1 = strdup(args);
cp = strchr(tmpstr1, '\n');
if (cp)
*cp = '\0';
for (;;) {
for (cp = tmpstr1; *cp && isspace(*cp); cp++)
;
tmpstr1 = cp;
for (; *cp && !isspace(*cp); cp++)
;
if (*cp)
*cp++ = '\0';
if (cp - tmpstr1 == 0)
break;
for (i = 0; i < num_devices; i++) {
asprintf(&buffer, "%s%d", dev_select[i].device_name,
dev_select[i].unit_number);
if (strcmp(buffer, tmpstr1) == 0) {
num_devices_specified++;
specified_devices =(char **)realloc(
specified_devices,
sizeof(char *) *
num_devices_specified);
specified_devices[num_devices_specified -1]=
strdup(tmpstr1);
free(buffer);
break;
}
else
free(buffer);
}
if (i >= num_devices)
error("%s: unknown drive", args);
tmpstr1 = cp;
}
free(tmpstr);
if (num_devices_specified > 0) {
last_type = DS_MATCHTYPE_SPEC;
retval = devstat_selectdevs(&dev_select, &num_selected,
&num_selections, &select_generation, generation,
s1->dinfo->devices, num_devices, NULL, 0,
specified_devices, num_devices_specified,
select_mode, maxshowdevs, 0);
if (retval == -1)
err(1, "%s", devstat_errbuf);
else if (retval == 1)
return(2);
}
return(1);
}
void
dslabel(int maxdrives, int diskcol, int diskrow)
{
int i, j;
mvprintw(diskrow, diskcol, "Disks");
mvprintw(diskrow + 1, diskcol, "KB/t");
mvprintw(diskrow + 2, diskcol, "tps");
mvprintw(diskrow + 3, diskcol, "MB/s");
mvprintw(diskrow + 4, diskcol, "%%busy");
/*
* For now, we don't support a fourth disk statistic. So there's
* no point in providing a label for it. If someone can think of a
* fourth useful disk statistic, there is room to add it.
*/
/* mvprintw(diskrow + 4, diskcol, " msps"); */
j = 0;
for (i = 0; i < num_devices && j < maxdrives; i++)
if (dev_select[i].selected) {
char tmpstr[80];
sprintf(tmpstr, "%s%d", dev_select[i].device_name,
dev_select[i].unit_number);
mvprintw(diskrow, diskcol + 5 + 6 * j,
" %5.5s", tmpstr);
j++;
}
}
static void
dsshow2(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
{
long double transfers_per_second;
long double kb_per_transfer, mb_per_second;
long double elapsed_time, device_busy;
int di;
di = dev_select[dn].position;
if (then != NULL) {
/* Calculate relative to previous sample */
elapsed_time = now->snap_time - then->snap_time;
} else {
/* Calculate relative to device creation */
elapsed_time = now->snap_time - devstat_compute_etime(
&now->dinfo->devices[di].creation_time, NULL);
}
if (devstat_compute_statistics(&now->dinfo->devices[di], then ?
&then->dinfo->devices[di] : NULL, elapsed_time,
DSM_KB_PER_TRANSFER, &kb_per_transfer,
DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
DSM_MB_PER_SECOND, &mb_per_second,
DSM_BUSY_PCT, &device_busy,
DSM_NONE) != 0)
errx(1, "%s", devstat_errbuf);
lc = diskcol + lc * 6;
putlongdouble(kb_per_transfer, diskrow + 1, lc, 5, 2, 0);
putlongdouble(transfers_per_second, diskrow + 2, lc, 5, 0, 0);
putlongdouble(mb_per_second, diskrow + 3, lc, 5, 2, 0);
putlongdouble(device_busy, diskrow + 4, lc, 5, 0, 0);
}
static void
dsshow3(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
{
dsshow2(diskcol, diskrow, dn, lc, now, then);
}
void
dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct statinfo *then)
{
int i, lc;
for (i = 0, lc = 0; i < num_devices && lc < maxdrives; i++)
if (dev_select[i].selected)
dsshow3(diskcol, diskrow, i, ++lc, now, then);
}