Home | History | Annotate | Line # | Download | only in sa
      1  1.28  dholland /* $NetBSD: main.c,v 1.28 2010/06/10 06:28:33 dholland Exp $ */
      2   1.9       cgd 
      3   1.1       cgd /*
      4   1.1       cgd  * Copyright (c) 1994 Christopher G. Demetriou
      5   1.1       cgd  * All rights reserved.
      6  1.10       cgd  *
      7   1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8   1.1       cgd  * modification, are permitted provided that the following conditions
      9   1.1       cgd  * are met:
     10   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15   1.1       cgd  * 3. All advertising materials mentioning features or use of this software
     16   1.1       cgd  *    must display the following acknowledgement:
     17  1.10       cgd  *          This product includes software developed for the
     18  1.17     grant  *          NetBSD Project.  See http://www.NetBSD.org/ for
     19  1.10       cgd  *          information about NetBSD.
     20   1.1       cgd  * 4. The name of the author may not be used to endorse or promote products
     21  1.10       cgd  *    derived from this software without specific prior written permission.
     22  1.10       cgd  *
     23   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24   1.1       cgd  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     25   1.1       cgd  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     26   1.1       cgd  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     27   1.1       cgd  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     28   1.1       cgd  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29   1.1       cgd  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30   1.1       cgd  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31   1.1       cgd  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     32   1.1       cgd  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  1.10       cgd  *
     34  1.10       cgd  * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
     35   1.1       cgd  */
     36   1.1       cgd 
     37   1.7     lukem #include <sys/cdefs.h>
     38   1.7     lukem #ifndef lint
     39  1.22     lukem __COPYRIGHT("@(#) Copyright (c) 1994\
     40  1.22     lukem  Christopher G. Demetriou.  All rights reserved.");
     41   1.1       cgd 
     42  1.28  dholland __RCSID("$NetBSD: main.c,v 1.28 2010/06/10 06:28:33 dholland Exp $");
     43   1.1       cgd #endif
     44   1.1       cgd 
     45   1.1       cgd /*
     46   1.1       cgd  * sa:	system accounting
     47   1.1       cgd  */
     48   1.1       cgd 
     49   1.1       cgd #include <sys/types.h>
     50   1.1       cgd #include <sys/acct.h>
     51   1.1       cgd #include <ctype.h>
     52   1.1       cgd #include <err.h>
     53  1.11  christos #include <vis.h>
     54   1.1       cgd #include <fcntl.h>
     55   1.1       cgd #include <signal.h>
     56   1.1       cgd #include <stdio.h>
     57   1.1       cgd #include <stdlib.h>
     58   1.5       cgd #include <string.h>
     59   1.1       cgd #include <unistd.h>
     60   1.1       cgd #include "extern.h"
     61   1.1       cgd #include "pathnames.h"
     62   1.1       cgd 
     63  1.28  dholland static int acct_load(const char *, int);
     64  1.26  dholland static u_quad_t decode_comp_t(comp_t);
     65  1.26  dholland static int cmp_comm(const char *, const char *);
     66  1.26  dholland static int cmp_usrsys(const DBT *, const DBT *);
     67  1.26  dholland static int cmp_avgusrsys(const DBT *, const DBT *);
     68  1.26  dholland static int cmp_dkio(const DBT *, const DBT *);
     69  1.26  dholland static int cmp_avgdkio(const DBT *, const DBT *);
     70  1.26  dholland static int cmp_cpumem(const DBT *, const DBT *);
     71  1.26  dholland static int cmp_avgcpumem(const DBT *, const DBT *);
     72  1.26  dholland static int cmp_calls(const DBT *, const DBT *);
     73  1.26  dholland static void usage(void) __dead;
     74   1.1       cgd 
     75   1.1       cgd int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
     76   1.1       cgd int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
     77   1.1       cgd int cutoff = 1;
     78   1.1       cgd 
     79  1.24     lukem static const char	*dfltargv[] = { _PATH_ACCT };
     80  1.24     lukem static const int	dfltargc = (sizeof(dfltargv)/sizeof(char *));
     81   1.1       cgd 
     82   1.1       cgd /* default to comparing by sum of user + system time */
     83   1.1       cgd cmpf_t   sa_cmp = cmp_usrsys;
     84   1.1       cgd 
     85   1.1       cgd int
     86  1.27  dholland main(int argc, char **argv)
     87   1.1       cgd {
     88   1.6      mark 	int ch;
     89   1.1       cgd 	int error;
     90   1.1       cgd 
     91   1.7     lukem 	error = 0;
     92   1.1       cgd 	while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -1)
     93   1.1       cgd 		switch (ch) {
     94  1.11  christos 		case 'a':
     95  1.11  christos 			/* print all commands */
     96  1.11  christos 			aflag = 1;
     97  1.11  christos 			break;
     98  1.11  christos 		case 'b':
     99  1.11  christos 			/* sort by per-call user/system time average */
    100  1.11  christos 			bflag = 1;
    101  1.11  christos 			sa_cmp = cmp_avgusrsys;
    102  1.11  christos 			break;
    103  1.11  christos 		case 'c':
    104  1.11  christos 			/* print percentage total time */
    105  1.11  christos 			cflag = 1;
    106  1.11  christos 			break;
    107  1.11  christos 		case 'd':
    108  1.11  christos 			/* sort by averge number of disk I/O ops */
    109  1.11  christos 			dflag = 1;
    110  1.11  christos 			sa_cmp = cmp_avgdkio;
    111  1.11  christos 			break;
    112  1.11  christos 		case 'D':
    113  1.11  christos 			/* print and sort by total disk I/O ops */
    114  1.11  christos 			Dflag = 1;
    115  1.11  christos 			sa_cmp = cmp_dkio;
    116  1.11  christos 			break;
    117  1.11  christos 		case 'f':
    118  1.11  christos 			/* force no interactive threshold comprison */
    119  1.11  christos 			fflag = 1;
    120  1.11  christos 			break;
    121  1.11  christos 		case 'i':
    122  1.11  christos 			/* do not read in summary file */
    123  1.11  christos 			iflag = 1;
    124  1.11  christos 			break;
    125  1.11  christos 		case 'j':
    126  1.11  christos 			/* instead of total minutes, give sec/call */
    127  1.11  christos 			jflag = 1;
    128  1.11  christos 			break;
    129  1.11  christos 		case 'k':
    130  1.19       wiz 			/* sort by CPU-time average memory usage */
    131  1.11  christos 			kflag = 1;
    132  1.11  christos 			sa_cmp = cmp_avgcpumem;
    133  1.11  christos 			break;
    134  1.11  christos 		case 'K':
    135  1.19       wiz 			/* print and sort by CPU-storage integral */
    136  1.11  christos 			sa_cmp = cmp_cpumem;
    137  1.11  christos 			Kflag = 1;
    138  1.11  christos 			break;
    139  1.11  christos 		case 'l':
    140  1.15       wiz 			/* separate system and user time */
    141  1.11  christos 			lflag = 1;
    142  1.11  christos 			break;
    143  1.11  christos 		case 'm':
    144  1.11  christos 			/* print procs and time per-user */
    145  1.11  christos 			mflag = 1;
    146  1.11  christos 			break;
    147  1.11  christos 		case 'n':
    148  1.11  christos 			/* sort by number of calls */
    149  1.11  christos 			sa_cmp = cmp_calls;
    150  1.11  christos 			break;
    151  1.11  christos 		case 'q':
    152  1.11  christos 			/* quiet; error messages only */
    153  1.11  christos 			qflag = 1;
    154  1.11  christos 			break;
    155  1.11  christos 		case 'r':
    156  1.11  christos 			/* reverse order of sort */
    157  1.11  christos 			rflag = 1;
    158  1.11  christos 			break;
    159  1.11  christos 		case 's':
    160  1.11  christos 			/* merge accounting file into summaries */
    161  1.11  christos 			sflag = 1;
    162  1.11  christos 			break;
    163  1.11  christos 		case 't':
    164  1.11  christos 			/* report ratio of user and system times */
    165  1.11  christos 			tflag = 1;
    166  1.11  christos 			break;
    167  1.11  christos 		case 'u':
    168  1.11  christos 			/* first, print uid and command name */
    169  1.11  christos 			uflag = 1;
    170  1.11  christos 			break;
    171  1.11  christos 		case 'v':
    172  1.11  christos 			/* cull junk */
    173  1.11  christos 			vflag = 1;
    174  1.11  christos 			cutoff = atoi(optarg);
    175  1.11  christos 			break;
    176  1.11  christos 		case '?':
    177  1.11  christos 		default:
    178  1.11  christos 			usage();
    179  1.11  christos 			/*NOTREACHED*/
    180   1.1       cgd 		}
    181   1.1       cgd 
    182   1.1       cgd 	argc -= optind;
    183   1.1       cgd 	argv += optind;
    184   1.1       cgd 
    185   1.1       cgd 	/* various argument checking */
    186   1.1       cgd 	if (fflag && !vflag)
    187   1.1       cgd 		errx(1, "only one of -f requires -v");
    188   1.1       cgd 	if (fflag && aflag)
    189   1.1       cgd 		errx(1, "only one of -a and -v may be specified");
    190   1.1       cgd 	/* XXX need more argument checking */
    191   1.1       cgd 
    192   1.1       cgd 	if (!uflag) {
    193   1.1       cgd 		/* initialize tables */
    194   1.1       cgd 		if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
    195   1.1       cgd 			errx(1, "process accounting initialization failed");
    196   1.1       cgd 		if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
    197   1.1       cgd 			errx(1, "user accounting initialization failed");
    198   1.1       cgd 	}
    199   1.1       cgd 
    200   1.1       cgd 	if (argc == 0) {
    201   1.1       cgd 		argc = dfltargc;
    202  1.24     lukem 		argv = __UNCONST(dfltargv);
    203   1.1       cgd 	}
    204   1.1       cgd 
    205   1.1       cgd 	/* for each file specified */
    206   1.1       cgd 	for (; argc > 0; argc--, argv++) {
    207   1.1       cgd 		int	fd;
    208   1.1       cgd 
    209   1.1       cgd 		/*
    210   1.1       cgd 		 * load the accounting data from the file.
    211   1.1       cgd 		 * if it fails, go on to the next file.
    212   1.1       cgd 		 */
    213   1.1       cgd 		fd = acct_load(argv[0], sflag);
    214   1.1       cgd 		if (fd < 0)
    215   1.1       cgd 			continue;
    216   1.1       cgd 
    217   1.1       cgd 		if (!uflag && sflag) {
    218   1.1       cgd #ifndef DEBUG
    219   1.1       cgd 			sigset_t nmask, omask;
    220   1.1       cgd 			int unmask = 1;
    221   1.1       cgd 
    222   1.1       cgd 			/*
    223   1.1       cgd 			 * block most signals so we aren't interrupted during
    224   1.1       cgd 			 * the update.
    225   1.1       cgd 			 */
    226   1.1       cgd 			if (sigfillset(&nmask) == -1) {
    227   1.1       cgd 				warn("sigfillset");
    228   1.1       cgd 				unmask = 0;
    229   1.1       cgd 				error = 1;
    230   1.1       cgd 			}
    231   1.1       cgd 			if (unmask &&
    232   1.1       cgd 			    (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) {
    233   1.1       cgd 				warn("couldn't set signal mask ");
    234   1.1       cgd 				unmask = 0;
    235   1.1       cgd 				error = 1;
    236   1.1       cgd 			}
    237   1.1       cgd #endif /* DEBUG */
    238   1.1       cgd 
    239   1.1       cgd 			/*
    240   1.1       cgd 			 * truncate the accounting data file ASAP, to avoid
    241   1.1       cgd 			 * losing data.  don't worry about errors in updating
    242   1.1       cgd 			 * the saved stats; better to underbill than overbill,
    243   1.1       cgd 			 * but we want every accounting record intact.
    244   1.1       cgd 			 */
    245   1.1       cgd 			if (ftruncate(fd, 0) == -1) {
    246   1.7     lukem 				warn("couldn't truncate %s", *argv);
    247   1.1       cgd 				error = 1;
    248   1.1       cgd 			}
    249   1.1       cgd 
    250   1.1       cgd 			/*
    251   1.1       cgd 			 * update saved user and process accounting data.
    252   1.1       cgd 			 * note errors for later.
    253   1.1       cgd 			 */
    254   1.1       cgd 			if (pacct_update() != 0 || usracct_update() != 0)
    255   1.1       cgd 				error = 1;
    256   1.1       cgd 
    257   1.1       cgd #ifndef DEBUG
    258   1.1       cgd 			/*
    259   1.1       cgd 			 * restore signals
    260   1.1       cgd 			 */
    261   1.1       cgd 			if (unmask &&
    262   1.1       cgd 			    (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) {
    263   1.1       cgd 				warn("couldn't restore signal mask");
    264   1.1       cgd 				error = 1;
    265   1.1       cgd 			}
    266   1.1       cgd #endif /* DEBUG */
    267   1.1       cgd 		}
    268   1.1       cgd 
    269   1.1       cgd 		/*
    270   1.1       cgd 		 * close the opened accounting file
    271   1.1       cgd 		 */
    272   1.1       cgd 		if (close(fd) == -1) {
    273   1.7     lukem 			warn("close %s", *argv);
    274   1.1       cgd 			error = 1;
    275   1.1       cgd 		}
    276   1.1       cgd 	}
    277   1.1       cgd 
    278   1.1       cgd 	if (!uflag && !qflag) {
    279   1.1       cgd 		/* print any results we may have obtained. */
    280   1.1       cgd 		if (!mflag)
    281   1.1       cgd 			pacct_print();
    282   1.1       cgd 		else
    283   1.1       cgd 			usracct_print();
    284   1.1       cgd 	}
    285   1.1       cgd 
    286   1.1       cgd 	if (!uflag) {
    287   1.1       cgd 		/* finally, deallocate databases */
    288   1.1       cgd 		if (sflag || (!mflag && !qflag))
    289   1.1       cgd 			pacct_destroy();
    290   1.1       cgd 		if (sflag || (mflag && !qflag))
    291   1.1       cgd 			usracct_destroy();
    292   1.1       cgd 	}
    293   1.1       cgd 
    294   1.1       cgd 	exit(error);
    295   1.1       cgd }
    296   1.1       cgd 
    297   1.1       cgd static int
    298  1.28  dholland acct_load(const char *pn, int wr)
    299   1.1       cgd {
    300   1.1       cgd 	struct acct ac;
    301   1.1       cgd 	struct cmdinfo ci;
    302  1.23     lukem 	size_t i;
    303  1.11  christos 	FILE *fp;
    304   1.1       cgd 
    305   1.1       cgd 	/*
    306   1.1       cgd 	 * open the file
    307   1.1       cgd 	 */
    308  1.11  christos 	fp = fopen(pn, wr ? "r+" : "r");
    309  1.11  christos 	if (fp == NULL) {
    310   1.1       cgd 		warn("open %s %s", pn, wr ? "for read/write" : "read-only");
    311   1.1       cgd 		return (-1);
    312   1.1       cgd 	}
    313   1.1       cgd 
    314   1.1       cgd 	/*
    315   1.1       cgd 	 * read all we can; don't stat and open because more processes
    316   1.1       cgd 	 * could exit, and we'd miss them
    317   1.1       cgd 	 */
    318  1.12  christos 	for (;;) {
    319   1.1       cgd 		/* get one accounting entry and punt if there's an error */
    320  1.11  christos 		if (fread(&ac, sizeof(struct acct), 1, fp) != 1) {
    321  1.11  christos 			if (feof(fp))
    322  1.11  christos 				break;
    323  1.11  christos 			if (ferror(fp))
    324  1.11  christos 				warn("error reading %s", pn);
    325  1.11  christos 			else
    326  1.11  christos 				warnx("short read of accounting data in %s",
    327  1.11  christos 				    pn);
    328   1.1       cgd 			break;
    329  1.11  christos 		}
    330   1.1       cgd 
    331   1.1       cgd 		/* decode it */
    332   1.1       cgd 		ci.ci_calls = 1;
    333   1.4   mycroft 		for (i = 0; i < sizeof(ac.ac_comm) && ac.ac_comm[i] != '\0';
    334   1.1       cgd 		    i++) {
    335   1.1       cgd 			char c = ac.ac_comm[i];
    336   1.1       cgd 
    337  1.20       dsl 			if (!isascii(c) || iscntrl((unsigned char)c)) {
    338   1.1       cgd 				ci.ci_comm[i] = '?';
    339   1.1       cgd 				ci.ci_flags |= CI_UNPRINTABLE;
    340   1.1       cgd 			} else
    341   1.1       cgd 				ci.ci_comm[i] = c;
    342   1.1       cgd 		}
    343   1.1       cgd 		if (ac.ac_flag & AFORK)
    344   1.1       cgd 			ci.ci_comm[i++] = '*';
    345   1.1       cgd 		ci.ci_comm[i++] = '\0';
    346   1.1       cgd 		ci.ci_etime = decode_comp_t(ac.ac_etime);
    347   1.1       cgd 		ci.ci_utime = decode_comp_t(ac.ac_utime);
    348   1.1       cgd 		ci.ci_stime = decode_comp_t(ac.ac_stime);
    349   1.1       cgd 		ci.ci_uid = ac.ac_uid;
    350   1.1       cgd 		ci.ci_mem = ac.ac_mem;
    351   1.1       cgd 		ci.ci_io = decode_comp_t(ac.ac_io) / AHZ;
    352   1.1       cgd 
    353   1.1       cgd 		if (!uflag) {
    354   1.1       cgd 			/* and enter it into the usracct and pacct databases */
    355   1.1       cgd 			if (sflag || (!mflag && !qflag))
    356   1.1       cgd 				pacct_add(&ci);
    357   1.1       cgd 			if (sflag || (mflag && !qflag))
    358   1.1       cgd 				usracct_add(&ci);
    359   1.1       cgd 		} else if (!qflag)
    360  1.19       wiz 			printf("%6u %12.2f CPU %12lluk mem %12llu io %s\n",
    361   1.1       cgd 			    ci.ci_uid,
    362   1.1       cgd 			    (ci.ci_utime + ci.ci_stime) / (double) AHZ,
    363   1.8       mrg 			    (unsigned long long)ci.ci_mem,
    364   1.8       mrg 			    (unsigned long long)ci.ci_io, ci.ci_comm);
    365   1.1       cgd 	}
    366   1.1       cgd 
    367   1.1       cgd 	/* finally, return the file descriptor for possible truncation */
    368  1.11  christos 	return (fileno(fp));
    369   1.1       cgd }
    370   1.1       cgd 
    371   1.1       cgd static u_quad_t
    372  1.25  drochner decode_comp_t(comp_t comp)
    373   1.1       cgd {
    374   1.1       cgd 	u_quad_t rv;
    375   1.1       cgd 
    376   1.1       cgd 	/*
    377   1.1       cgd 	 * for more info on the comp_t format, see:
    378   1.1       cgd 	 *	/usr/src/sys/kern/kern_acct.c
    379   1.1       cgd 	 *	/usr/src/sys/sys/acct.h
    380   1.1       cgd 	 *	/usr/src/usr.bin/lastcomm/lastcomm.c
    381   1.1       cgd 	 */
    382   1.1       cgd 	rv = comp & 0x1fff;	/* 13 bit fraction */
    383   1.1       cgd 	comp >>= 13;		/* 3 bit base-8 exponent */
    384   1.1       cgd 	while (comp--)
    385   1.1       cgd 		rv <<= 3;
    386   1.1       cgd 
    387   1.1       cgd 	return (rv);
    388   1.1       cgd }
    389   1.1       cgd 
    390   1.1       cgd /* sort commands, doing the right thing in terms of reversals */
    391   1.1       cgd static int
    392  1.27  dholland cmp_comm(const char *s1, const char *s2)
    393   1.1       cgd {
    394   1.1       cgd 	int rv;
    395   1.1       cgd 
    396   1.1       cgd 	rv = strcmp(s1, s2);
    397   1.1       cgd 	if (rv == 0)
    398   1.1       cgd 		rv = -1;
    399   1.1       cgd 	return (rflag ? rv : -rv);
    400   1.1       cgd }
    401   1.1       cgd 
    402   1.1       cgd /* sort by total user and system time */
    403   1.1       cgd static int
    404  1.27  dholland cmp_usrsys(const DBT *d1, const DBT *d2)
    405   1.1       cgd {
    406   1.3   mycroft 	struct cmdinfo c1, c2;
    407   1.1       cgd 	u_quad_t t1, t2;
    408   1.1       cgd 
    409   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    410   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    411   1.1       cgd 
    412   1.3   mycroft 	t1 = c1.ci_utime + c1.ci_stime;
    413   1.3   mycroft 	t2 = c2.ci_utime + c2.ci_stime;
    414   1.1       cgd 
    415   1.1       cgd 	if (t1 < t2)
    416   1.1       cgd 		return -1;
    417   1.1       cgd 	else if (t1 == t2)
    418   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    419   1.1       cgd 	else
    420   1.1       cgd 		return 1;
    421   1.1       cgd }
    422   1.1       cgd 
    423   1.1       cgd /* sort by average user and system time */
    424   1.1       cgd static int
    425  1.27  dholland cmp_avgusrsys(const DBT *d1, const DBT *d2)
    426   1.1       cgd {
    427   1.3   mycroft 	struct cmdinfo c1, c2;
    428   1.1       cgd 	double t1, t2;
    429   1.1       cgd 
    430   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    431   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    432   1.1       cgd 
    433   1.3   mycroft 	t1 = c1.ci_utime + c1.ci_stime;
    434   1.3   mycroft 	t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1);
    435   1.1       cgd 
    436   1.3   mycroft 	t2 = c2.ci_utime + c2.ci_stime;
    437   1.3   mycroft 	t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1);
    438   1.1       cgd 
    439   1.1       cgd 	if (t1 < t2)
    440   1.1       cgd 		return -1;
    441   1.1       cgd 	else if (t1 == t2)
    442   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    443   1.1       cgd 	else
    444   1.1       cgd 		return 1;
    445   1.1       cgd }
    446   1.1       cgd 
    447   1.1       cgd /* sort by total number of disk I/O operations */
    448   1.1       cgd static int
    449  1.27  dholland cmp_dkio(const DBT *d1, const DBT *d2)
    450   1.1       cgd {
    451   1.3   mycroft 	struct cmdinfo c1, c2;
    452   1.1       cgd 
    453   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    454   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    455   1.1       cgd 
    456   1.3   mycroft 	if (c1.ci_io < c2.ci_io)
    457   1.1       cgd 		return -1;
    458   1.3   mycroft 	else if (c1.ci_io == c2.ci_io)
    459   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    460   1.1       cgd 	else
    461   1.1       cgd 		return 1;
    462   1.1       cgd }
    463   1.1       cgd 
    464   1.1       cgd /* sort by average number of disk I/O operations */
    465   1.1       cgd static int
    466  1.27  dholland cmp_avgdkio(const DBT *d1, const DBT *d2)
    467   1.1       cgd {
    468   1.3   mycroft 	struct cmdinfo c1, c2;
    469   1.1       cgd 	double n1, n2;
    470   1.1       cgd 
    471   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    472   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    473   1.1       cgd 
    474   1.3   mycroft 	n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
    475   1.3   mycroft 	n2 = (double) c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1);
    476   1.1       cgd 
    477   1.1       cgd 	if (n1 < n2)
    478   1.1       cgd 		return -1;
    479   1.1       cgd 	else if (n1 == n2)
    480   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    481   1.1       cgd 	else
    482   1.1       cgd 		return 1;
    483   1.1       cgd }
    484   1.1       cgd 
    485  1.19       wiz /* sort by the CPU-storage integral */
    486   1.1       cgd static int
    487  1.27  dholland cmp_cpumem(const DBT *d1, const DBT *d2)
    488   1.1       cgd {
    489   1.3   mycroft 	struct cmdinfo c1, c2;
    490   1.1       cgd 
    491   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    492   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    493   1.1       cgd 
    494   1.3   mycroft 	if (c1.ci_mem < c2.ci_mem)
    495   1.1       cgd 		return -1;
    496   1.3   mycroft 	else if (c1.ci_mem == c2.ci_mem)
    497   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    498   1.1       cgd 	else
    499   1.1       cgd 		return 1;
    500   1.1       cgd }
    501   1.1       cgd 
    502  1.19       wiz /* sort by the CPU-time average memory usage */
    503   1.1       cgd static int
    504  1.27  dholland cmp_avgcpumem(const DBT *d1, const DBT *d2)
    505   1.1       cgd {
    506   1.3   mycroft 	struct cmdinfo c1, c2;
    507   1.1       cgd 	u_quad_t t1, t2;
    508   1.1       cgd 	double n1, n2;
    509   1.1       cgd 
    510   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    511   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    512   1.1       cgd 
    513   1.3   mycroft 	t1 = c1.ci_utime + c1.ci_stime;
    514   1.3   mycroft 	t2 = c2.ci_utime + c2.ci_stime;
    515   1.1       cgd 
    516   1.3   mycroft 	n1 = (double) c1.ci_mem / (double) (t1 ? t1 : 1);
    517   1.3   mycroft 	n2 = (double) c2.ci_mem / (double) (t2 ? t2 : 1);
    518   1.1       cgd 
    519   1.1       cgd 	if (n1 < n2)
    520   1.1       cgd 		return -1;
    521   1.1       cgd 	else if (n1 == n2)
    522   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    523   1.1       cgd 	else
    524   1.1       cgd 		return 1;
    525   1.1       cgd }
    526   1.1       cgd 
    527   1.1       cgd /* sort by the number of invocations */
    528   1.1       cgd static int
    529  1.27  dholland cmp_calls(const DBT *d1, const DBT *d2)
    530   1.1       cgd {
    531   1.3   mycroft 	struct cmdinfo c1, c2;
    532   1.1       cgd 
    533   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    534   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    535   1.1       cgd 
    536   1.3   mycroft 	if (c1.ci_calls < c2.ci_calls)
    537   1.1       cgd 		return -1;
    538   1.3   mycroft 	else if (c1.ci_calls == c2.ci_calls)
    539   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    540   1.1       cgd 	else
    541   1.1       cgd 		return 1;
    542  1.11  christos }
    543  1.11  christos 
    544  1.11  christos static void
    545  1.27  dholland usage(void)
    546  1.11  christos {
    547  1.14       cgd 
    548  1.11  christos 	(void)fprintf(stderr,
    549  1.18      jmmv 	    "usage: %s [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n",
    550  1.14       cgd 	    getprogname());
    551  1.11  christos 	exit(0);
    552  1.11  christos }
    553  1.11  christos 
    554  1.11  christos const char *
    555  1.27  dholland fmt(const DBT *key)
    556  1.11  christos {
    557  1.11  christos 	static char *buf = NULL;
    558  1.11  christos 	static size_t len = 0;
    559  1.16    itojun 	char *nbuf;
    560  1.16    itojun 
    561  1.16    itojun 	if (len < key->size * 4 + 1) {
    562  1.16    itojun 		nbuf = realloc(buf, key->size * 4 + 1);
    563  1.16    itojun 		if (!nbuf)
    564  1.16    itojun 			err(1, "realloc");
    565  1.16    itojun 		buf = nbuf;
    566  1.16    itojun 		len = key->size * 4 + 1;
    567  1.16    itojun 	}
    568  1.11  christos 	(void)strvisx(buf, key->data, key->size, 0);
    569  1.11  christos 	return buf;
    570   1.1       cgd }
    571