Add support for listing and dumping UVM history buffers.

This commit is contained in:
thorpej 1998-02-13 05:10:32 +00:00
parent 872181c2f2
commit 2ca4a3f1c2
2 changed files with 270 additions and 8 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: vmstat.8,v 1.13 1998/01/04 03:59:04 thorpej Exp $
.\" $NetBSD: vmstat.8,v 1.14 1998/02/13 05:10:33 thorpej Exp $
.\"
.\" Copyright (c) 1986, 1993
.\" The Regents of the University of California. All rights reserved.
@ -33,16 +33,16 @@
.\"
.\" @(#)vmstat.8 8.1 (Berkeley) 6/6/93
.\"
.TH VMSTAT 8 "June 6, 1993"
.TH VMSTAT 8 "February 12, 1998"
.UC 4
.SH NAME
vmstat \- report virtual memory statistics
.SH SYNOPSIS
.nf
.ft B
vmstat [ \-fimst ] [ \-c count ] [ \-M core ] [ \-N system ]
vmstat [ \-fHilmst ] [ \-h histname ] [ \-c count ] [ \-M core ]
.ti +5
[ \-w wait ] [ disks ]
[ \-N system ] [ \-w wait ] [ disks ]
.ft R
.fi
.SH DESCRIPTION
@ -99,6 +99,19 @@ If no repeat
.I count
is specified, the default is infinity.
.PP
The following options only apply if
.I vmstat
was built with support for UVM:
.TP
\-l
List the UVM histories being maintained by the kernel.
.TP
\-h
Dump the specified UVM history.
.TP
\-H
Dump all UVM histories.
.PP
By default,
.I vmstat
displays the following information:

View File

@ -1,4 +1,41 @@
/* $NetBSD: vmstat.c,v 1.44 1998/02/09 13:11:26 mrg Exp $ */
/* $NetBSD: vmstat.c,v 1.45 1998/02/13 05:10:32 thorpej Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
* NASA Ames Research Center.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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, 1986, 1991, 1993
@ -43,7 +80,7 @@ __COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1991, 1993\n\
#if 0
static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 3/1/95";
#else
__RCSID("$NetBSD: vmstat.c,v 1.44 1998/02/09 13:11:26 mrg Exp $");
__RCSID("$NetBSD: vmstat.c,v 1.45 1998/02/13 05:10:32 thorpej Exp $");
#endif
#endif /* not lint */
@ -60,6 +97,7 @@ __RCSID("$NetBSD: vmstat.c,v 1.44 1998/02/09 13:11:26 mrg Exp $");
#include <sys/sysctl.h>
#include <sys/device.h>
#include <vm/vm.h>
#include <err.h>
#include <time.h>
#include <nlist.h>
#include <kvm.h>
@ -74,6 +112,10 @@ __RCSID("$NetBSD: vmstat.c,v 1.44 1998/02/09 13:11:26 mrg Exp $");
#include <limits.h>
#include "dkstats.h"
#if defined(UVM)
#include <uvm/uvm_stat.h>
#endif
struct nlist namelist[] = {
#define X_CPTIME 0
{ "_cp_time" },
@ -134,6 +176,10 @@ kvm_t *kd;
#define MEMSTAT 0x04
#define SUMSTAT 0x08
#define VMSTAT 0x20
#if defined(UVM)
#define HISTLIST 0x40
#define HISTDUMP 0x80
#endif
void cpustats __P((void));
void dkstats __P((void));
@ -149,6 +195,11 @@ long pct __P((long, long));
void usage __P((void));
void doforkst __P((void));
#if defined(UVM)
void hist_traverse __P((int, const char *));
void hist_dodump __P((struct uvm_history *));
#endif
int main __P((int, char **));
char **choosedrives __P((char **));
@ -170,10 +221,17 @@ main(argc, argv)
u_int interval;
int reps;
char errbuf[_POSIX2_LINE_MAX];
#if defined(UVM)
const char *histname = NULL;
#endif
memf = nlistf = NULL;
interval = reps = todo = 0;
#if defined(UVM)
while ((c = getopt(argc, argv, "c:fh:HilM:mN:stw:")) != -1) {
#else
while ((c = getopt(argc, argv, "c:fiM:mN:stw:")) != -1) {
#endif
switch (c) {
case 'c':
reps = atoi(optarg);
@ -181,9 +239,22 @@ main(argc, argv)
case 'f':
todo |= FORKSTAT;
break;
#if defined(UVM)
case 'h':
histname = optarg;
/* FALLTHROUGH */
case 'H':
todo |= HISTDUMP;
break;
#endif
case 'i':
todo |= INTRSTAT;
break;
#if defined(UVM)
case 'l':
todo |= HISTLIST;
break;
#endif
case 'M':
memf = optarg;
break;
@ -267,6 +338,13 @@ main(argc, argv)
} else if (reps)
interval = 1;
#if defined(UVM)
if (todo & (HISTLIST|HISTDUMP)) {
if ((todo & (HISTLIST|HISTDUMP)) == (HISTLIST|HISTDUMP))
errx(1, "you may list or dump, but not both!");
hist_traverse(todo, histname);
}
#endif
if (todo & FORKSTAT)
doforkst();
if (todo & MEMSTAT)
@ -938,12 +1016,183 @@ kread(nlx, addr, size)
}
}
#if defined(UVM)
struct nlist histnl[] = {
{ "_uvm_histories" },
#define X_UVM_HISTORIES 0
{ NULL },
};
/*
* Traverse the UVM history buffers, performing the requested action.
*
* Note, we assume that if we're not listing, we're dumping.
*/
void
hist_traverse(todo, histname)
int todo;
const char *histname;
{
struct uvm_history_head histhead;
struct uvm_history hist, *histkva;
char *name = NULL;
size_t namelen = 0;
if (kvm_nlist(kd, histnl) != 0) {
printf("UVM history is not compiled into the kernel.\n");
return;
}
if (kvm_read(kd, histnl[X_UVM_HISTORIES].n_value, &histhead,
sizeof(histhead)) != sizeof(histhead)) {
warnx("unable to read %s: %s",
histnl[X_UVM_HISTORIES].n_name, kvm_geterr(kd));
return;
}
if (histhead.lh_first == NULL) {
printf("No active UVM history logs.\n");
return;
}
if (todo & HISTLIST)
printf("Active UVM histories:");
for (histkva = histhead.lh_first; histkva != NULL;
histkva = hist.list.le_next) {
if (kvm_read(kd, (u_long)histkva, &hist, sizeof(hist)) !=
sizeof(hist)) {
warnx("unable to read history at %p: %s",
histkva, kvm_geterr(kd));
goto out;
}
if (hist.namelen > namelen) {
if (name != NULL)
free(name);
namelen = hist.namelen;
if ((name = malloc(namelen + 1)) == NULL)
err(1, "malloc history name");
}
if (kvm_read(kd, (u_long)hist.name, name, namelen) !=
namelen) {
warnx("unable to read history name at %p: %s",
hist.name, kvm_geterr(kd));
goto out;
}
name[namelen] = '\0';
if (todo & HISTLIST)
printf(" %s", name);
else {
/*
* If we're dumping all histories, do it, else
* check to see if this is the one we want.
*/
if (histname == NULL || strcmp(histname, name) == 0) {
if (histname == NULL)
printf("\nUVM history `%s':\n", name);
hist_dodump(&hist);
}
}
}
if (todo & HISTLIST)
printf("\n");
out:
if (name != NULL)
free(name);
}
/*
* Actually dump the history buffer at the specified KVA.
*/
void
hist_dodump(histp)
struct uvm_history *histp;
{
struct uvm_history_ent *histents, *e;
size_t histsize;
char *fmt = NULL, *fn = NULL;
size_t fmtlen = 0, fnlen = 0;
int i;
histsize = sizeof(struct uvm_history_ent) * histp->n;
if ((histents = malloc(histsize)) == NULL)
err(1, "malloc history entries");
memset(histents, 0, histsize);
if (kvm_read(kd, (u_long)histp->e, histents, histsize) != histsize) {
warnx("unable to read history entries at %p: %s",
histp->e, kvm_geterr(kd));
goto out;
}
i = histp->f;
do {
e = &histents[i];
if (e->fmt != NULL) {
if (e->fmtlen > fmtlen) {
if (fmt != NULL)
free(fmt);
fmtlen = e->fmtlen;
if ((fmt = malloc(fmtlen + 1)) == NULL)
err(1, "malloc printf format");
}
if (e->fnlen > fnlen) {
if (fn != NULL)
free(fn);
fnlen = e->fnlen;
if ((fn = malloc(fnlen + 1)) == NULL)
err(1, "malloc function name");
}
if (kvm_read(kd, (u_long)e->fmt, fmt, fmtlen)
!= fmtlen) {
warnx("unable to read printf format "
"at %p: %s", e->fmt, kvm_geterr(kd));
goto out;
}
fmt[fmtlen] = '\0';
if (kvm_read(kd, (u_long)e->fn, fn, fnlen) != fnlen) {
warnx("unable to read function name "
"at %p: %s", e->fn, kvm_geterr(kd));
goto out;
}
fn[fnlen] = '\0';
printf("%06ld.%06ld ", e->tv.tv_sec, e->tv.tv_usec);
printf("%s#%ld: ", fn, e->call);
printf(fmt, e->v[0], e->v[1], e->v[2], e->v[3]);
printf("\n");
}
i = (i + 1) % histp->n;
} while (i != histp->f);
out:
free(histents);
if (fmt != NULL)
free(fmt);
if (fn != NULL)
free(fn);
}
#endif /* UVM */
void
usage()
{
#if defined(UVM)
(void)fprintf(stderr,
"usage: vmstat [-ims] [-c count] [-M core] \
"usage: vmstat [-fHilms] [-h histname] [-c count] [-M core] \
[-N system] [-w wait] [disks]\n");
#else
(void)fprintf(stderr,
"usage: vmstat [-fims] [-c count] [-M core] \
[-N system] [-w wait] [disks]\n");
#endif /* UVM */
exit(1);
}