2 * Copyright (c) 1994 Christopher G. Demetriou
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Christopher G. Demetriou.
16 * 4. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 static const char copyright[] =
34 "@(#) Copyright (c) 1994 Christopher G. Demetriou\n\
35 All rights reserved.\n";
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
42 * sa: system accounting
45 #include <sys/types.h>
57 #include "pathnames.h"
59 static int acct_load(char *, int);
60 static u_quad_t decode_comp_t(comp_t);
61 static int cmp_comm(const char *, const char *);
62 static int cmp_usrsys(const DBT *, const DBT *);
63 static int cmp_avgusrsys(const DBT *, const DBT *);
64 static int cmp_dkio(const DBT *, const DBT *);
65 static int cmp_avgdkio(const DBT *, const DBT *);
66 static int cmp_cpumem(const DBT *, const DBT *);
67 static int cmp_avgcpumem(const DBT *, const DBT *);
68 static int cmp_calls(const DBT *, const DBT *);
69 static void usage(void);
71 int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
72 int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
75 static char *dfltargv[] = { NULL };
76 static int dfltargc = (sizeof dfltargv/sizeof(char *));
78 /* default to comparing by sum of user + system time */
79 cmpf_t sa_cmp = cmp_usrsys;
82 main(int argc, char **argv)
85 char pathacct[] = _PATH_ACCT;
88 dfltargv[0] = pathacct;
90 while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -1)
93 /* print all commands */
97 /* sort by per-call user/system time average */
99 sa_cmp = cmp_avgusrsys;
102 /* print percentage total time */
106 /* sort by averge number of disk I/O ops */
108 sa_cmp = cmp_avgdkio;
111 /* print and sort by total disk I/O ops */
116 /* force no interactive threshold comprison */
120 /* do not read in summary file */
124 /* instead of total minutes, give sec/call */
128 /* sort by cpu-time average memory usage */
130 sa_cmp = cmp_avgcpumem;
133 /* print and sort by cpu-storage integral */
138 /* separate system and user time */
142 /* print procs and time per-user */
146 /* sort by number of calls */
150 /* quiet; error messages only */
154 /* reverse order of sort */
158 /* merge accounting file into summaries */
162 /* report ratio of user and system times */
166 /* first, print uid and command name */
172 cutoff = atoi(optarg);
182 /* various argument checking */
184 errx(1, "only one of -f requires -v");
186 errx(1, "only one of -a and -v may be specified");
187 /* XXX need more argument checking */
190 /* initialize tables */
191 if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
192 errx(1, "process accounting initialization failed");
193 if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
194 errx(1, "user accounting initialization failed");
202 /* for each file specified */
203 for (; argc > 0; argc--, argv++) {
207 * load the accounting data from the file.
208 * if it fails, go on to the next file.
210 fd = acct_load(argv[0], sflag);
214 if (!uflag && sflag) {
216 sigset_t nmask, omask;
220 * block most signals so we aren't interrupted during
223 if (sigfillset(&nmask) == -1) {
229 (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) {
230 warn("couldn't set signal mask");
237 * truncate the accounting data file ASAP, to avoid
238 * losing data. don't worry about errors in updating
239 * the saved stats; better to underbill than overbill,
240 * but we want every accounting record intact.
242 if (ftruncate(fd, 0) == -1) {
243 warn("couldn't truncate %s", *argv);
248 * update saved user and process accounting data.
249 * note errors for later.
251 if (pacct_update() != 0 || usracct_update() != 0)
259 (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) {
260 warn("couldn't restore signal mask");
267 * close the opened accounting file
269 if (close(fd) == -1) {
270 warn("close %s", *argv);
275 if (!uflag && !qflag) {
276 /* print any results we may have obtained. */
284 /* finally, deallocate databases */
285 if (sflag || (!mflag && !qflag))
287 if (sflag || (mflag && !qflag))
297 (void)fprintf(stderr,
298 "usage: sa [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n");
315 fd = open(pn, wr ? O_RDWR : O_RDONLY, 0);
317 warn("open %s %s", pn, wr ? "for read/write" : "read-only");
322 * read all we can; don't stat and open because more processes
323 * could exit, and we'd miss them
326 /* get one accounting entry and punt if there's an error */
327 rv = read(fd, &ac, sizeof(struct acct));
329 warn("error reading %s", pn);
330 else if (rv > 0 && rv < (int)sizeof(struct acct))
331 warnx("short read of accounting data in %s", pn);
332 if (rv != sizeof(struct acct))
337 for (i = 0; i < (int)sizeof ac.ac_comm && ac.ac_comm[i] != '\0';
339 char c = ac.ac_comm[i];
341 if (!isascii(c) || iscntrl(c)) {
343 ci.ci_flags |= CI_UNPRINTABLE;
347 if (ac.ac_flag & AFORK)
348 ci.ci_comm[i++] = '*';
349 ci.ci_comm[i++] = '\0';
350 ci.ci_etime = decode_comp_t(ac.ac_etime);
351 ci.ci_utime = decode_comp_t(ac.ac_utime);
352 ci.ci_stime = decode_comp_t(ac.ac_stime);
353 ci.ci_uid = ac.ac_uid;
354 ci.ci_mem = ac.ac_mem;
355 ci.ci_io = decode_comp_t(ac.ac_io) / AHZ;
358 /* and enter it into the usracct and pacct databases */
359 if (sflag || (!mflag && !qflag))
361 if (sflag || (mflag && !qflag))
364 printf("%6lu %12.2f cpu %12juk mem %12ju io %s\n",
366 (ci.ci_utime + ci.ci_stime) / (double) AHZ,
367 (uintmax_t)ci.ci_mem, (uintmax_t)ci.ci_io,
371 /* finally, return the file descriptor for possible truncation */
382 * for more info on the comp_t format, see:
383 * /usr/src/sys/kern/kern_acct.c
384 * /usr/src/sys/sys/acct.h
385 * /usr/src/usr.bin/lastcomm/lastcomm.c
387 rv = comp & 0x1fff; /* 13 bit fraction */
388 comp >>= 13; /* 3 bit base-8 exponent */
395 /* sort commands, doing the right thing in terms of reversals */
405 return (rflag ? rv : -rv);
408 /* sort by total user and system time */
413 struct cmdinfo c1, c2;
416 memcpy(&c1, d1->data, sizeof(c1));
417 memcpy(&c2, d2->data, sizeof(c2));
419 t1 = c1.ci_utime + c1.ci_stime;
420 t2 = c2.ci_utime + c2.ci_stime;
425 return (cmp_comm(c1.ci_comm, c2.ci_comm));
430 /* sort by average user and system time */
432 cmp_avgusrsys(d1, d2)
435 struct cmdinfo c1, c2;
438 memcpy(&c1, d1->data, sizeof(c1));
439 memcpy(&c2, d2->data, sizeof(c2));
441 t1 = c1.ci_utime + c1.ci_stime;
442 t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1);
444 t2 = c2.ci_utime + c2.ci_stime;
445 t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1);
450 return (cmp_comm(c1.ci_comm, c2.ci_comm));
455 /* sort by total number of disk I/O operations */
460 struct cmdinfo c1, c2;
462 memcpy(&c1, d1->data, sizeof(c1));
463 memcpy(&c2, d2->data, sizeof(c2));
465 if (c1.ci_io < c2.ci_io)
467 else if (c1.ci_io == c2.ci_io)
468 return (cmp_comm(c1.ci_comm, c2.ci_comm));
473 /* sort by average number of disk I/O operations */
478 struct cmdinfo c1, c2;
481 memcpy(&c1, d1->data, sizeof(c1));
482 memcpy(&c2, d2->data, sizeof(c2));
484 n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
485 n2 = (double) c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1);
490 return (cmp_comm(c1.ci_comm, c2.ci_comm));
495 /* sort by the cpu-storage integral */
500 struct cmdinfo c1, c2;
502 memcpy(&c1, d1->data, sizeof(c1));
503 memcpy(&c2, d2->data, sizeof(c2));
505 if (c1.ci_mem < c2.ci_mem)
507 else if (c1.ci_mem == c2.ci_mem)
508 return (cmp_comm(c1.ci_comm, c2.ci_comm));
513 /* sort by the cpu-time average memory usage */
515 cmp_avgcpumem(d1, d2)
518 struct cmdinfo c1, c2;
522 memcpy(&c1, d1->data, sizeof(c1));
523 memcpy(&c2, d2->data, sizeof(c2));
525 t1 = c1.ci_utime + c1.ci_stime;
526 t2 = c2.ci_utime + c2.ci_stime;
528 n1 = (double) c1.ci_mem / (double) (t1 ? t1 : 1);
529 n2 = (double) c2.ci_mem / (double) (t2 ? t2 : 1);
534 return (cmp_comm(c1.ci_comm, c2.ci_comm));
539 /* sort by the number of invocations */
544 struct cmdinfo c1, c2;
546 memcpy(&c1, d1->data, sizeof(c1));
547 memcpy(&c2, d2->data, sizeof(c2));
549 if (c1.ci_calls < c2.ci_calls)
551 else if (c1.ci_calls == c2.ci_calls)
552 return (cmp_comm(c1.ci_comm, c2.ci_comm));