Home | History | Annotate | Line # | Download | only in sa
main.c revision 1.16
      1  1.16    itojun /* $NetBSD: main.c,v 1.16 2003/09/19 06:26:09 itojun 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.10       cgd  *          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.7     lukem __COPYRIGHT("@(#) Copyright (c) 1994 Christopher G. Demetriou\n\
     40   1.7     lukem  All rights reserved.\n");
     41   1.1       cgd 
     42  1.16    itojun __RCSID("$NetBSD: main.c,v 1.16 2003/09/19 06:26:09 itojun 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.1       cgd static int	acct_load	__P((char *, int));
     64   1.1       cgd static u_quad_t	decode_comp_t	__P((comp_t));
     65   1.1       cgd static int	cmp_comm	__P((const char *, const char *));
     66   1.1       cgd static int	cmp_usrsys	__P((const DBT *, const DBT *));
     67   1.1       cgd static int	cmp_avgusrsys	__P((const DBT *, const DBT *));
     68   1.1       cgd static int	cmp_dkio	__P((const DBT *, const DBT *));
     69   1.1       cgd static int	cmp_avgdkio	__P((const DBT *, const DBT *));
     70   1.1       cgd static int	cmp_cpumem	__P((const DBT *, const DBT *));
     71   1.1       cgd static int	cmp_avgcpumem	__P((const DBT *, const DBT *));
     72   1.1       cgd static int	cmp_calls	__P((const DBT *, const DBT *));
     73  1.11  christos static void	usage		__P((void)) __attribute__((__noreturn__));
     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.1       cgd static char	*dfltargv[] = { _PATH_ACCT };
     80   1.4   mycroft static 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.1       cgd main(argc, argv)
     87   1.1       cgd 	int argc;
     88   1.1       cgd 	char **argv;
     89   1.1       cgd {
     90   1.6      mark 	int ch;
     91   1.1       cgd 	int error;
     92   1.1       cgd 
     93   1.7     lukem 	error = 0;
     94   1.1       cgd 	while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -1)
     95   1.1       cgd 		switch (ch) {
     96  1.11  christos 		case 'a':
     97  1.11  christos 			/* print all commands */
     98  1.11  christos 			aflag = 1;
     99  1.11  christos 			break;
    100  1.11  christos 		case 'b':
    101  1.11  christos 			/* sort by per-call user/system time average */
    102  1.11  christos 			bflag = 1;
    103  1.11  christos 			sa_cmp = cmp_avgusrsys;
    104  1.11  christos 			break;
    105  1.11  christos 		case 'c':
    106  1.11  christos 			/* print percentage total time */
    107  1.11  christos 			cflag = 1;
    108  1.11  christos 			break;
    109  1.11  christos 		case 'd':
    110  1.11  christos 			/* sort by averge number of disk I/O ops */
    111  1.11  christos 			dflag = 1;
    112  1.11  christos 			sa_cmp = cmp_avgdkio;
    113  1.11  christos 			break;
    114  1.11  christos 		case 'D':
    115  1.11  christos 			/* print and sort by total disk I/O ops */
    116  1.11  christos 			Dflag = 1;
    117  1.11  christos 			sa_cmp = cmp_dkio;
    118  1.11  christos 			break;
    119  1.11  christos 		case 'f':
    120  1.11  christos 			/* force no interactive threshold comprison */
    121  1.11  christos 			fflag = 1;
    122  1.11  christos 			break;
    123  1.11  christos 		case 'i':
    124  1.11  christos 			/* do not read in summary file */
    125  1.11  christos 			iflag = 1;
    126  1.11  christos 			break;
    127  1.11  christos 		case 'j':
    128  1.11  christos 			/* instead of total minutes, give sec/call */
    129  1.11  christos 			jflag = 1;
    130  1.11  christos 			break;
    131  1.11  christos 		case 'k':
    132  1.11  christos 			/* sort by cpu-time average memory usage */
    133  1.11  christos 			kflag = 1;
    134  1.11  christos 			sa_cmp = cmp_avgcpumem;
    135  1.11  christos 			break;
    136  1.11  christos 		case 'K':
    137  1.11  christos 			/* print and sort by cpu-storage integral */
    138  1.11  christos 			sa_cmp = cmp_cpumem;
    139  1.11  christos 			Kflag = 1;
    140  1.11  christos 			break;
    141  1.11  christos 		case 'l':
    142  1.15       wiz 			/* separate system and user time */
    143  1.11  christos 			lflag = 1;
    144  1.11  christos 			break;
    145  1.11  christos 		case 'm':
    146  1.11  christos 			/* print procs and time per-user */
    147  1.11  christos 			mflag = 1;
    148  1.11  christos 			break;
    149  1.11  christos 		case 'n':
    150  1.11  christos 			/* sort by number of calls */
    151  1.11  christos 			sa_cmp = cmp_calls;
    152  1.11  christos 			break;
    153  1.11  christos 		case 'q':
    154  1.11  christos 			/* quiet; error messages only */
    155  1.11  christos 			qflag = 1;
    156  1.11  christos 			break;
    157  1.11  christos 		case 'r':
    158  1.11  christos 			/* reverse order of sort */
    159  1.11  christos 			rflag = 1;
    160  1.11  christos 			break;
    161  1.11  christos 		case 's':
    162  1.11  christos 			/* merge accounting file into summaries */
    163  1.11  christos 			sflag = 1;
    164  1.11  christos 			break;
    165  1.11  christos 		case 't':
    166  1.11  christos 			/* report ratio of user and system times */
    167  1.11  christos 			tflag = 1;
    168  1.11  christos 			break;
    169  1.11  christos 		case 'u':
    170  1.11  christos 			/* first, print uid and command name */
    171  1.11  christos 			uflag = 1;
    172  1.11  christos 			break;
    173  1.11  christos 		case 'v':
    174  1.11  christos 			/* cull junk */
    175  1.11  christos 			vflag = 1;
    176  1.11  christos 			cutoff = atoi(optarg);
    177  1.11  christos 			break;
    178  1.11  christos 		case '?':
    179  1.11  christos 		default:
    180  1.11  christos 			usage();
    181  1.11  christos 			/*NOTREACHED*/
    182   1.1       cgd 		}
    183   1.1       cgd 
    184   1.1       cgd 	argc -= optind;
    185   1.1       cgd 	argv += optind;
    186   1.1       cgd 
    187   1.1       cgd 	/* various argument checking */
    188   1.1       cgd 	if (fflag && !vflag)
    189   1.1       cgd 		errx(1, "only one of -f requires -v");
    190   1.1       cgd 	if (fflag && aflag)
    191   1.1       cgd 		errx(1, "only one of -a and -v may be specified");
    192   1.1       cgd 	/* XXX need more argument checking */
    193   1.1       cgd 
    194   1.1       cgd 	if (!uflag) {
    195   1.1       cgd 		/* initialize tables */
    196   1.1       cgd 		if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
    197   1.1       cgd 			errx(1, "process accounting initialization failed");
    198   1.1       cgd 		if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
    199   1.1       cgd 			errx(1, "user accounting initialization failed");
    200   1.1       cgd 	}
    201   1.1       cgd 
    202   1.1       cgd 	if (argc == 0) {
    203   1.1       cgd 		argc = dfltargc;
    204   1.1       cgd 		argv = dfltargv;
    205   1.1       cgd 	}
    206   1.1       cgd 
    207   1.1       cgd 	/* for each file specified */
    208   1.1       cgd 	for (; argc > 0; argc--, argv++) {
    209   1.1       cgd 		int	fd;
    210   1.1       cgd 
    211   1.1       cgd 		/*
    212   1.1       cgd 		 * load the accounting data from the file.
    213   1.1       cgd 		 * if it fails, go on to the next file.
    214   1.1       cgd 		 */
    215   1.1       cgd 		fd = acct_load(argv[0], sflag);
    216   1.1       cgd 		if (fd < 0)
    217   1.1       cgd 			continue;
    218   1.1       cgd 
    219   1.1       cgd 		if (!uflag && sflag) {
    220   1.1       cgd #ifndef DEBUG
    221   1.1       cgd 			sigset_t nmask, omask;
    222   1.1       cgd 			int unmask = 1;
    223   1.1       cgd 
    224   1.1       cgd 			/*
    225   1.1       cgd 			 * block most signals so we aren't interrupted during
    226   1.1       cgd 			 * the update.
    227   1.1       cgd 			 */
    228   1.1       cgd 			if (sigfillset(&nmask) == -1) {
    229   1.1       cgd 				warn("sigfillset");
    230   1.1       cgd 				unmask = 0;
    231   1.1       cgd 				error = 1;
    232   1.1       cgd 			}
    233   1.1       cgd 			if (unmask &&
    234   1.1       cgd 			    (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) {
    235   1.1       cgd 				warn("couldn't set signal mask ");
    236   1.1       cgd 				unmask = 0;
    237   1.1       cgd 				error = 1;
    238   1.1       cgd 			}
    239   1.1       cgd #endif /* DEBUG */
    240   1.1       cgd 
    241   1.1       cgd 			/*
    242   1.1       cgd 			 * truncate the accounting data file ASAP, to avoid
    243   1.1       cgd 			 * losing data.  don't worry about errors in updating
    244   1.1       cgd 			 * the saved stats; better to underbill than overbill,
    245   1.1       cgd 			 * but we want every accounting record intact.
    246   1.1       cgd 			 */
    247   1.1       cgd 			if (ftruncate(fd, 0) == -1) {
    248   1.7     lukem 				warn("couldn't truncate %s", *argv);
    249   1.1       cgd 				error = 1;
    250   1.1       cgd 			}
    251   1.1       cgd 
    252   1.1       cgd 			/*
    253   1.1       cgd 			 * update saved user and process accounting data.
    254   1.1       cgd 			 * note errors for later.
    255   1.1       cgd 			 */
    256   1.1       cgd 			if (pacct_update() != 0 || usracct_update() != 0)
    257   1.1       cgd 				error = 1;
    258   1.1       cgd 
    259   1.1       cgd #ifndef DEBUG
    260   1.1       cgd 			/*
    261   1.1       cgd 			 * restore signals
    262   1.1       cgd 			 */
    263   1.1       cgd 			if (unmask &&
    264   1.1       cgd 			    (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) {
    265   1.1       cgd 				warn("couldn't restore signal mask");
    266   1.1       cgd 				error = 1;
    267   1.1       cgd 			}
    268   1.1       cgd #endif /* DEBUG */
    269   1.1       cgd 		}
    270   1.1       cgd 
    271   1.1       cgd 		/*
    272   1.1       cgd 		 * close the opened accounting file
    273   1.1       cgd 		 */
    274   1.1       cgd 		if (close(fd) == -1) {
    275   1.7     lukem 			warn("close %s", *argv);
    276   1.1       cgd 			error = 1;
    277   1.1       cgd 		}
    278   1.1       cgd 	}
    279   1.1       cgd 
    280   1.1       cgd 	if (!uflag && !qflag) {
    281   1.1       cgd 		/* print any results we may have obtained. */
    282   1.1       cgd 		if (!mflag)
    283   1.1       cgd 			pacct_print();
    284   1.1       cgd 		else
    285   1.1       cgd 			usracct_print();
    286   1.1       cgd 	}
    287   1.1       cgd 
    288   1.1       cgd 	if (!uflag) {
    289   1.1       cgd 		/* finally, deallocate databases */
    290   1.1       cgd 		if (sflag || (!mflag && !qflag))
    291   1.1       cgd 			pacct_destroy();
    292   1.1       cgd 		if (sflag || (mflag && !qflag))
    293   1.1       cgd 			usracct_destroy();
    294   1.1       cgd 	}
    295   1.1       cgd 
    296   1.1       cgd 	exit(error);
    297   1.1       cgd }
    298   1.1       cgd 
    299   1.1       cgd static int
    300   1.1       cgd acct_load(pn, wr)
    301   1.1       cgd 	char *pn;
    302   1.1       cgd 	int wr;
    303   1.1       cgd {
    304   1.1       cgd 	struct acct ac;
    305   1.1       cgd 	struct cmdinfo ci;
    306  1.11  christos 	int i;
    307  1.11  christos 	FILE *fp;
    308   1.1       cgd 
    309   1.1       cgd 	/*
    310   1.1       cgd 	 * open the file
    311   1.1       cgd 	 */
    312  1.11  christos 	fp = fopen(pn, wr ? "r+" : "r");
    313  1.11  christos 	if (fp == NULL) {
    314   1.1       cgd 		warn("open %s %s", pn, wr ? "for read/write" : "read-only");
    315   1.1       cgd 		return (-1);
    316   1.1       cgd 	}
    317   1.1       cgd 
    318   1.1       cgd 	/*
    319   1.1       cgd 	 * read all we can; don't stat and open because more processes
    320   1.1       cgd 	 * could exit, and we'd miss them
    321   1.1       cgd 	 */
    322  1.12  christos 	for (;;) {
    323   1.1       cgd 		/* get one accounting entry and punt if there's an error */
    324  1.11  christos 		if (fread(&ac, sizeof(struct acct), 1, fp) != 1) {
    325  1.11  christos 			if (feof(fp))
    326  1.11  christos 				break;
    327  1.11  christos 			if (ferror(fp))
    328  1.11  christos 				warn("error reading %s", pn);
    329  1.11  christos 			else
    330  1.11  christos 				warnx("short read of accounting data in %s",
    331  1.11  christos 				    pn);
    332   1.1       cgd 			break;
    333  1.11  christos 		}
    334   1.1       cgd 
    335   1.1       cgd 		/* decode it */
    336   1.1       cgd 		ci.ci_calls = 1;
    337   1.4   mycroft 		for (i = 0; i < sizeof(ac.ac_comm) && ac.ac_comm[i] != '\0';
    338   1.1       cgd 		    i++) {
    339   1.1       cgd 			char c = ac.ac_comm[i];
    340   1.1       cgd 
    341   1.1       cgd 			if (!isascii(c) || iscntrl(c)) {
    342   1.1       cgd 				ci.ci_comm[i] = '?';
    343   1.1       cgd 				ci.ci_flags |= CI_UNPRINTABLE;
    344   1.1       cgd 			} else
    345   1.1       cgd 				ci.ci_comm[i] = c;
    346   1.1       cgd 		}
    347   1.1       cgd 		if (ac.ac_flag & AFORK)
    348   1.1       cgd 			ci.ci_comm[i++] = '*';
    349   1.1       cgd 		ci.ci_comm[i++] = '\0';
    350   1.1       cgd 		ci.ci_etime = decode_comp_t(ac.ac_etime);
    351   1.1       cgd 		ci.ci_utime = decode_comp_t(ac.ac_utime);
    352   1.1       cgd 		ci.ci_stime = decode_comp_t(ac.ac_stime);
    353   1.1       cgd 		ci.ci_uid = ac.ac_uid;
    354   1.1       cgd 		ci.ci_mem = ac.ac_mem;
    355   1.1       cgd 		ci.ci_io = decode_comp_t(ac.ac_io) / AHZ;
    356   1.1       cgd 
    357   1.1       cgd 		if (!uflag) {
    358   1.1       cgd 			/* and enter it into the usracct and pacct databases */
    359   1.1       cgd 			if (sflag || (!mflag && !qflag))
    360   1.1       cgd 				pacct_add(&ci);
    361   1.1       cgd 			if (sflag || (mflag && !qflag))
    362   1.1       cgd 				usracct_add(&ci);
    363   1.1       cgd 		} else if (!qflag)
    364  1.13     lukem 			printf("%6u %12.2f cpu %12lluk mem %12llu io %s\n",
    365   1.1       cgd 			    ci.ci_uid,
    366   1.1       cgd 			    (ci.ci_utime + ci.ci_stime) / (double) AHZ,
    367   1.8       mrg 			    (unsigned long long)ci.ci_mem,
    368   1.8       mrg 			    (unsigned long long)ci.ci_io, ci.ci_comm);
    369   1.1       cgd 	}
    370   1.1       cgd 
    371   1.1       cgd 	/* finally, return the file descriptor for possible truncation */
    372  1.11  christos 	return (fileno(fp));
    373   1.1       cgd }
    374   1.1       cgd 
    375   1.1       cgd static u_quad_t
    376   1.1       cgd decode_comp_t(comp)
    377   1.1       cgd 	comp_t comp;
    378   1.1       cgd {
    379   1.1       cgd 	u_quad_t rv;
    380   1.1       cgd 
    381   1.1       cgd 	/*
    382   1.1       cgd 	 * for more info on the comp_t format, see:
    383   1.1       cgd 	 *	/usr/src/sys/kern/kern_acct.c
    384   1.1       cgd 	 *	/usr/src/sys/sys/acct.h
    385   1.1       cgd 	 *	/usr/src/usr.bin/lastcomm/lastcomm.c
    386   1.1       cgd 	 */
    387   1.1       cgd 	rv = comp & 0x1fff;	/* 13 bit fraction */
    388   1.1       cgd 	comp >>= 13;		/* 3 bit base-8 exponent */
    389   1.1       cgd 	while (comp--)
    390   1.1       cgd 		rv <<= 3;
    391   1.1       cgd 
    392   1.1       cgd 	return (rv);
    393   1.1       cgd }
    394   1.1       cgd 
    395   1.1       cgd /* sort commands, doing the right thing in terms of reversals */
    396   1.1       cgd static int
    397   1.1       cgd cmp_comm(s1, s2)
    398   1.1       cgd 	const char *s1, *s2;
    399   1.1       cgd {
    400   1.1       cgd 	int rv;
    401   1.1       cgd 
    402   1.1       cgd 	rv = strcmp(s1, s2);
    403   1.1       cgd 	if (rv == 0)
    404   1.1       cgd 		rv = -1;
    405   1.1       cgd 	return (rflag ? rv : -rv);
    406   1.1       cgd }
    407   1.1       cgd 
    408   1.1       cgd /* sort by total user and system time */
    409   1.1       cgd static int
    410   1.1       cgd cmp_usrsys(d1, d2)
    411   1.1       cgd 	const DBT *d1, *d2;
    412   1.1       cgd {
    413   1.3   mycroft 	struct cmdinfo c1, c2;
    414   1.1       cgd 	u_quad_t t1, t2;
    415   1.1       cgd 
    416   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    417   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    418   1.1       cgd 
    419   1.3   mycroft 	t1 = c1.ci_utime + c1.ci_stime;
    420   1.3   mycroft 	t2 = c2.ci_utime + c2.ci_stime;
    421   1.1       cgd 
    422   1.1       cgd 	if (t1 < t2)
    423   1.1       cgd 		return -1;
    424   1.1       cgd 	else if (t1 == t2)
    425   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    426   1.1       cgd 	else
    427   1.1       cgd 		return 1;
    428   1.1       cgd }
    429   1.1       cgd 
    430   1.1       cgd /* sort by average user and system time */
    431   1.1       cgd static int
    432   1.1       cgd cmp_avgusrsys(d1, d2)
    433   1.1       cgd 	const DBT *d1, *d2;
    434   1.1       cgd {
    435   1.3   mycroft 	struct cmdinfo c1, c2;
    436   1.1       cgd 	double t1, t2;
    437   1.1       cgd 
    438   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    439   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    440   1.1       cgd 
    441   1.3   mycroft 	t1 = c1.ci_utime + c1.ci_stime;
    442   1.3   mycroft 	t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1);
    443   1.1       cgd 
    444   1.3   mycroft 	t2 = c2.ci_utime + c2.ci_stime;
    445   1.3   mycroft 	t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1);
    446   1.1       cgd 
    447   1.1       cgd 	if (t1 < t2)
    448   1.1       cgd 		return -1;
    449   1.1       cgd 	else if (t1 == t2)
    450   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    451   1.1       cgd 	else
    452   1.1       cgd 		return 1;
    453   1.1       cgd }
    454   1.1       cgd 
    455   1.1       cgd /* sort by total number of disk I/O operations */
    456   1.1       cgd static int
    457   1.1       cgd cmp_dkio(d1, d2)
    458   1.1       cgd 	const DBT *d1, *d2;
    459   1.1       cgd {
    460   1.3   mycroft 	struct cmdinfo c1, c2;
    461   1.1       cgd 
    462   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    463   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    464   1.1       cgd 
    465   1.3   mycroft 	if (c1.ci_io < c2.ci_io)
    466   1.1       cgd 		return -1;
    467   1.3   mycroft 	else if (c1.ci_io == c2.ci_io)
    468   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    469   1.1       cgd 	else
    470   1.1       cgd 		return 1;
    471   1.1       cgd }
    472   1.1       cgd 
    473   1.1       cgd /* sort by average number of disk I/O operations */
    474   1.1       cgd static int
    475   1.1       cgd cmp_avgdkio(d1, d2)
    476   1.1       cgd 	const DBT *d1, *d2;
    477   1.1       cgd {
    478   1.3   mycroft 	struct cmdinfo c1, c2;
    479   1.1       cgd 	double n1, n2;
    480   1.1       cgd 
    481   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    482   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    483   1.1       cgd 
    484   1.3   mycroft 	n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
    485   1.3   mycroft 	n2 = (double) c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1);
    486   1.1       cgd 
    487   1.1       cgd 	if (n1 < n2)
    488   1.1       cgd 		return -1;
    489   1.1       cgd 	else if (n1 == n2)
    490   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    491   1.1       cgd 	else
    492   1.1       cgd 		return 1;
    493   1.1       cgd }
    494   1.1       cgd 
    495   1.1       cgd /* sort by the cpu-storage integral */
    496   1.1       cgd static int
    497   1.1       cgd cmp_cpumem(d1, d2)
    498   1.1       cgd 	const DBT *d1, *d2;
    499   1.1       cgd {
    500   1.3   mycroft 	struct cmdinfo c1, c2;
    501   1.1       cgd 
    502   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    503   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    504   1.1       cgd 
    505   1.3   mycroft 	if (c1.ci_mem < c2.ci_mem)
    506   1.1       cgd 		return -1;
    507   1.3   mycroft 	else if (c1.ci_mem == c2.ci_mem)
    508   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    509   1.1       cgd 	else
    510   1.1       cgd 		return 1;
    511   1.1       cgd }
    512   1.1       cgd 
    513   1.1       cgd /* sort by the cpu-time average memory usage */
    514   1.1       cgd static int
    515   1.1       cgd cmp_avgcpumem(d1, d2)
    516   1.1       cgd 	const DBT *d1, *d2;
    517   1.1       cgd {
    518   1.3   mycroft 	struct cmdinfo c1, c2;
    519   1.1       cgd 	u_quad_t t1, t2;
    520   1.1       cgd 	double n1, n2;
    521   1.1       cgd 
    522   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    523   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    524   1.1       cgd 
    525   1.3   mycroft 	t1 = c1.ci_utime + c1.ci_stime;
    526   1.3   mycroft 	t2 = c2.ci_utime + c2.ci_stime;
    527   1.1       cgd 
    528   1.3   mycroft 	n1 = (double) c1.ci_mem / (double) (t1 ? t1 : 1);
    529   1.3   mycroft 	n2 = (double) c2.ci_mem / (double) (t2 ? t2 : 1);
    530   1.1       cgd 
    531   1.1       cgd 	if (n1 < n2)
    532   1.1       cgd 		return -1;
    533   1.1       cgd 	else if (n1 == n2)
    534   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    535   1.1       cgd 	else
    536   1.1       cgd 		return 1;
    537   1.1       cgd }
    538   1.1       cgd 
    539   1.1       cgd /* sort by the number of invocations */
    540   1.1       cgd static int
    541   1.1       cgd cmp_calls(d1, d2)
    542   1.1       cgd 	const DBT *d1, *d2;
    543   1.1       cgd {
    544   1.3   mycroft 	struct cmdinfo c1, c2;
    545   1.1       cgd 
    546   1.3   mycroft 	memcpy(&c1, d1->data, sizeof(c1));
    547   1.3   mycroft 	memcpy(&c2, d2->data, sizeof(c2));
    548   1.1       cgd 
    549   1.3   mycroft 	if (c1.ci_calls < c2.ci_calls)
    550   1.1       cgd 		return -1;
    551   1.3   mycroft 	else if (c1.ci_calls == c2.ci_calls)
    552   1.3   mycroft 		return (cmp_comm(c1.ci_comm, c2.ci_comm));
    553   1.1       cgd 	else
    554   1.1       cgd 		return 1;
    555  1.11  christos }
    556  1.11  christos 
    557  1.11  christos static void
    558  1.11  christos usage()
    559  1.11  christos {
    560  1.14       cgd 
    561  1.11  christos 	(void)fprintf(stderr,
    562  1.11  christos 	    "Usage: %s [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n",
    563  1.14       cgd 	    getprogname());
    564  1.11  christos 	exit(0);
    565  1.11  christos }
    566  1.11  christos 
    567  1.11  christos const char *
    568  1.11  christos fmt(key)
    569  1.11  christos 	const DBT *key;
    570  1.11  christos {
    571  1.11  christos 	static char *buf = NULL;
    572  1.11  christos 	static size_t len = 0;
    573  1.16    itojun 	char *nbuf;
    574  1.16    itojun 
    575  1.16    itojun 	if (len < key->size * 4 + 1) {
    576  1.16    itojun 		nbuf = realloc(buf, key->size * 4 + 1);
    577  1.16    itojun 		if (!nbuf)
    578  1.16    itojun 			err(1, "realloc");
    579  1.16    itojun 		buf = nbuf;
    580  1.16    itojun 		len = key->size * 4 + 1;
    581  1.16    itojun 	}
    582  1.11  christos 	(void)strvisx(buf, key->data, key->size, 0);
    583  1.11  christos 	return buf;
    584   1.1       cgd }
    585