New feature, provide a display that shows the amount of IO processes

are doing.  Toggle this mode by hitting "m" or passing the command line
option "-m io" to top(1).  This allows one to identify disk bandwidth
hogs much easier.
This commit is contained in:
Alfred Perlstein 2004-07-01 09:12:38 +00:00
parent 6cc1cdf47b
commit db6bb7fc01
4 changed files with 196 additions and 14 deletions

View File

@ -20,6 +20,8 @@ top \- display and update information about the top cpu processes
] [
.BI \-U username
] [
.BI \-m io | cpu
] [
.I number
]
.SH DESCRIPTION
@ -87,6 +89,9 @@ Do not display the
.I top
process.
.TP
.BI \-m display
Display either 'cpu' or 'io' statistics. Default is 'cpu'.
.TP
.B \-n
Use \*(lqnon-interactive\*(rq mode. This is identical to \*(lqbatch\*(rq
mode.
@ -214,6 +219,9 @@ will make
.I top
show one final display and then immediately exit.
.TP
.B m
Toggle the display between 'cpu' and 'io' modes.
.TP
.B n or #
Change the number of processes to display (prompt for new number).
.TP

View File

@ -99,6 +99,7 @@ extern int (*proc_compares[])();
#else
extern int proc_compare();
#endif
extern int io_compare();
time_t time();
caddr_t get_process_info();
@ -192,9 +193,9 @@ char *argv[];
fd_set readfds;
#ifdef ORDER
static char command_chars[] = "\f qh?en#sdkriIutHo";
static char command_chars[] = "\f qh?en#sdkriIutHmo";
#else
static char command_chars[] = "\f qh?en#sdkriIutH";
static char command_chars[] = "\f qh?en#sdkriIutHm";
#endif
/* these defines enumerate the "strchr"s of the commands in command_chars */
#define CMD_redraw 0
@ -215,8 +216,9 @@ char *argv[];
#define CMD_user 14
#define CMD_selftog 15
#define CMD_thrtog 16
#define CMD_viewtog 17
#ifdef ORDER
#define CMD_order 17
#define CMD_order 18
#endif
/* set the buffer for stdout */
@ -272,7 +274,7 @@ char *argv[];
optind = 1;
}
while ((i = getopt(ac, av, "SIHbinquvs:d:U:o:t")) != EOF)
while ((i = getopt(ac, av, "SIHbinquvs:d:U:m:o:t")) != EOF)
{
switch(i)
{
@ -352,6 +354,20 @@ char *argv[];
}
break;
case 'm': /* select sort order */
if (strcmp(optarg, "io") == 0) {
displaymode = DISP_IO;
} else if (strcmp(optarg, "cpu") == 0) {
displaymode = DISP_CPU;
} else {
fprintf(stderr,
"%s: warning: `-m' option can only take args "
"'io' or 'cpu'\n",
myname);
exit(1);
}
break;
case 'o': /* select sort order */
#ifdef ORDER
order_name = optarg;
@ -545,18 +561,25 @@ Usage: %s [-HISbinqut] [-d x] [-s x] [-o field] [-U username] [number]\n",
while ((displays == -1) || (displays-- > 0))
{
int (*compare)();
/* get the current stats */
get_system_info(&system_info);
if (displaymode == DISP_CPU) {
#ifdef ORDER
compare = proc_compares[order_index];
#else
compare = proc_compare;
#endif
} else {
compare = io_compare;
}
/* get the current set of processes */
processes =
get_process_info(&system_info,
&ps,
#ifdef ORDER
proc_compares[order_index]);
#else
proc_compare);
#endif
get_process_info(&system_info, &ps, compare);
/* display the load averages */
(*d_loadave)(system_info.last_pid,
@ -968,6 +991,14 @@ Usage: %s [-HISbinqut] [-d x] [-s x] [-o field] [-U username] [number]\n",
ps.thread ? "D" : "Not d");
putchar('\r');
break;
case CMD_viewtog:
if (++displaymode == DISP_MAX)
displaymode = 0;
header_text = format_header(uname_field);
display_header(Yes);
d_header = i_header;
reset_display();
break;
#ifdef ORDER
case CMD_order:
new_message(MT_standout,

View File

@ -1,3 +1,6 @@
/*
* $FreeBSD$
*/
/*
* Top - a top users display for Berkeley Unix
*
@ -34,3 +37,6 @@ char *version_string();
#define NUM_AVERAGES 3
enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX };
extern enum displaymodes displaymode;

View File

@ -63,11 +63,13 @@ static void getsysctl(char *, void *, size_t);
extern char* printable(char *);
int swapmode(int *retavail, int *retfree);
static int smpmode;
enum displaymodes displaymode;
static int namelength;
static int cmdlengthdelta;
/* Prototypes for top internals */
void quit(int);
int compare_pid(const void *a, const void *b);
/* get_process_info passes back a handle. This is what it looks like: */
@ -87,12 +89,23 @@ struct handle
/* what we consider to be process size: */
#define PROCSIZE(pp) ((pp)->ki_size / 1024)
#define RU(pp) (&(pp)->ki_rusage)
#define RUTOT(pp) \
(RU(pp)->ru_inblock + RU(pp)->ru_oublock + RU(pp)->ru_majflt)
/* definitions for indices in the nlist array */
/*
* These definitions control the format of the per-process area
*/
static char io_header[] =
" PID %-*.*s READ WRITE FAULT TOTAL COMMAND";
#define io_Proc_format \
"%5d %-*.*s %6d %6d %6d %6d %.*s"
static char smp_header[] =
" PID %-*.*s PRI NICE SIZE RES STATE C TIME WCPU CPU COMMAND";
@ -176,6 +189,10 @@ static int onproc = -1;
static int pref_len;
static struct kinfo_proc *pbase;
static struct kinfo_proc **pref;
static struct kinfo_proc *previous_procs;
static struct kinfo_proc **previous_pref;
static int previous_proc_count = 0;
static int previous_proc_count_max = 0;
/* these are for getting the memory statistics */
@ -263,8 +280,18 @@ format_header(uname_field)
{
static char Header[128];
const char *prehead;
snprintf(Header, sizeof(Header), smpmode ? smp_header : up_header,
switch (displaymode) {
case DISP_CPU:
prehead = smpmode ? smp_header : up_header;
break;
case DISP_IO:
prehead = io_header;
break;
}
snprintf(Header, sizeof(Header), prehead,
namelength, namelength, uname_field);
cmdlengthdelta = strlen(Header) - 7;
@ -387,6 +414,43 @@ get_system_info(si)
}
}
const struct kinfo_proc *
get_old_proc(struct kinfo_proc *pp)
{
struct kinfo_proc **oldpp, *oldp;
if (previous_proc_count == 0)
return (NULL);
oldpp = bsearch(&pp, previous_pref, previous_proc_count,
sizeof(struct kinfo_proc *), compare_pid);
if (oldpp == NULL)
return (NULL);
oldp = *oldpp;
if (bcmp(&oldp->ki_start, &pp->ki_start, sizeof(pp->ki_start)) != 0)
return (NULL);
return (oldp);
}
long
get_io_total(struct kinfo_proc *pp)
{
const struct kinfo_proc *oldp;
static struct kinfo_proc dummy;
long ret;
oldp = get_old_proc(pp);
if (oldp == NULL) {
bzero(&dummy, sizeof(dummy));
oldp = &dummy;
}
ret =
(RU(pp)->ru_inblock - RU(oldp)->ru_inblock) +
(RU(pp)->ru_oublock - RU(oldp)->ru_oublock) +
(RU(pp)->ru_majflt - RU(oldp)->ru_majflt);
return (ret);
}
static struct handle handle;
caddr_t
@ -409,6 +473,29 @@ get_process_info(si, sel, compare)
int show_uid;
int show_command;
/*
* Save the previous process info.
*/
if (previous_proc_count_max < nproc) {
free(previous_procs);
previous_procs = malloc(nproc * sizeof(struct kinfo_proc));
free(previous_pref);
previous_pref = malloc(nproc * sizeof(struct kinfo_proc *));
if (previous_procs == NULL || previous_pref == NULL) {
(void) fprintf(stderr, "top: Out of memory.\n");
quit(23);
}
previous_proc_count_max = nproc;
}
if (nproc) {
for (i = 0; i < nproc; i++)
previous_pref[i] = &previous_procs[i];
bcopy(pbase, previous_procs, nproc * sizeof(struct kinfo_proc));
qsort(previous_pref, nproc,
sizeof(struct kinfo_proc *), compare_pid);
}
previous_proc_count = nproc;
pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
if (nproc > onproc)
pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
@ -447,8 +534,9 @@ get_process_info(si, sel, compare)
total_procs++;
process_states[(unsigned char) pp->ki_stat]++;
if ((pp->ki_stat != SZOMB) &&
(show_idle || (pp->ki_pctcpu != 0) ||
(pp->ki_stat == SRUN)) &&
(displaymode == DISP_CPU &&
(show_idle || (pp->ki_pctcpu != 0) || pp->ki_stat == SRUN)) ||
(show_idle || (displaymode == DISP_IO && get_io_total(pp))) &&
(!show_uid || pp->ki_ruid == (uid_t)sel->uid))
{
/*
@ -494,11 +582,13 @@ format_next_process(handle, get_userid)
char *(*get_userid)();
{
struct kinfo_proc *pp;
const struct kinfo_proc *oldp;
long cputime;
double pct;
struct handle *hp;
char status[16];
int state;
struct rusage ru, *rup;
/* find and remember the next proc structure */
hp = (struct handle *)handle;
@ -561,6 +651,30 @@ format_next_process(handle, get_userid)
break;
}
if (displaymode == DISP_IO) {
oldp = get_old_proc(pp);
if (oldp != NULL) {
ru.ru_inblock = RU(pp)->ru_inblock - RU(oldp)->ru_inblock;
ru.ru_oublock = RU(pp)->ru_oublock - RU(oldp)->ru_oublock;
ru.ru_majflt = RU(pp)->ru_majflt - RU(oldp)->ru_majflt;
rup = &ru;
} else {
rup = RU(pp);
}
sprintf(fmt, io_Proc_format,
pp->ki_pid,
namelength, namelength,
(*get_userid)(pp->ki_ruid),
(int)rup->ru_inblock,
(int)rup->ru_oublock,
(int)rup->ru_majflt,
(int)(rup->ru_inblock + rup->ru_oublock + rup->ru_majflt),
screen_width > cmdlengthdelta ?
screen_width - cmdlengthdelta : 0,
printable(pp->ki_comm));
return (fmt);
}
/* format this entry */
sprintf(fmt,
smpmode ? smp_Proc_format : up_Proc_format,
@ -617,6 +731,19 @@ getsysctl(name, ptr, len)
/* comparison routines for qsort */
int
compare_pid(p1, p2)
const void *p1, *p2;
{
const struct kinfo_proc * const *pp1 = p1;
const struct kinfo_proc * const *pp2 = p2;
if ((*pp2)->ki_pid < 0 || (*pp1)->ki_pid < 0)
abort();
return ((*pp1)->ki_pid - (*pp2)->ki_pid);
}
/*
* proc_compare - comparison function for "qsort"
* Compares the resource consumption of two processes using five
@ -814,6 +941,16 @@ compare_prio(pp1, pp2)
}
#endif
int
io_compare(pp1, pp2)
struct kinfo_proc **pp1, **pp2;
{
long t1, t2;
t1 = get_io_total(*pp1);
t2 = get_io_total(*pp2);
return (t2 - t1);
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.