1 /* $NetBSD: main.c,v 1.24 2009/04/28 08:32:56 lukem Exp $ */
4 * Copyright (c) 1994 Christopher G. Demetriou
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the
18 * NetBSD Project. See http://www.NetBSD.org/ for
19 * information about NetBSD.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
37 #include <sys/cdefs.h>
39 __COPYRIGHT("@(#) Copyright (c) 1994\
40 Christopher G. Demetriou. All rights reserved.");
42 __RCSID("$NetBSD: main.c,v 1.24 2009/04/28 08:32:56 lukem Exp $");
46 * sa: system accounting
49 #include <sys/types.h>
61 #include "pathnames.h"
63 static int acct_load
__P((char *, int));
64 static u_quad_t decode_comp_t
__P((comp_t
));
65 static int cmp_comm
__P((const char *, const char *));
66 static int cmp_usrsys
__P((const DBT
*, const DBT
*));
67 static int cmp_avgusrsys
__P((const DBT
*, const DBT
*));
68 static int cmp_dkio
__P((const DBT
*, const DBT
*));
69 static int cmp_avgdkio
__P((const DBT
*, const DBT
*));
70 static int cmp_cpumem
__P((const DBT
*, const DBT
*));
71 static int cmp_avgcpumem
__P((const DBT
*, const DBT
*));
72 static int cmp_calls
__P((const DBT
*, const DBT
*));
73 static void usage
__P((void)) __dead
;
75 int aflag
, bflag
, cflag
, dflag
, Dflag
, fflag
, iflag
, jflag
, kflag
;
76 int Kflag
, lflag
, mflag
, qflag
, rflag
, sflag
, tflag
, uflag
, vflag
;
79 static const char *dfltargv
[] = { _PATH_ACCT
};
80 static const int dfltargc
= (sizeof(dfltargv
)/sizeof(char *));
82 /* default to comparing by sum of user + system time */
83 cmpf_t sa_cmp
= cmp_usrsys
;
94 while ((ch
= getopt(argc
, argv
, "abcdDfijkKlmnqrstuv:")) != -1)
97 /* print all commands */
101 /* sort by per-call user/system time average */
103 sa_cmp
= cmp_avgusrsys
;
106 /* print percentage total time */
110 /* sort by averge number of disk I/O ops */
112 sa_cmp
= cmp_avgdkio
;
115 /* print and sort by total disk I/O ops */
120 /* force no interactive threshold comprison */
124 /* do not read in summary file */
128 /* instead of total minutes, give sec/call */
132 /* sort by CPU-time average memory usage */
134 sa_cmp
= cmp_avgcpumem
;
137 /* print and sort by CPU-storage integral */
142 /* separate system and user time */
146 /* print procs and time per-user */
150 /* sort by number of calls */
154 /* quiet; error messages only */
158 /* reverse order of sort */
162 /* merge accounting file into summaries */
166 /* report ratio of user and system times */
170 /* first, print uid and command name */
176 cutoff
= atoi(optarg
);
187 /* various argument checking */
189 errx(1, "only one of -f requires -v");
191 errx(1, "only one of -a and -v may be specified");
192 /* XXX need more argument checking */
195 /* initialize tables */
196 if ((sflag
|| (!mflag
&& !qflag
)) && pacct_init() != 0)
197 errx(1, "process accounting initialization failed");
198 if ((sflag
|| (mflag
&& !qflag
)) && usracct_init() != 0)
199 errx(1, "user accounting initialization failed");
204 argv
= __UNCONST(dfltargv
);
207 /* for each file specified */
208 for (; argc
> 0; argc
--, argv
++) {
212 * load the accounting data from the file.
213 * if it fails, go on to the next file.
215 fd
= acct_load(argv
[0], sflag
);
219 if (!uflag
&& sflag
) {
221 sigset_t nmask
, omask
;
225 * block most signals so we aren't interrupted during
228 if (sigfillset(&nmask
) == -1) {
234 (sigprocmask(SIG_BLOCK
, &nmask
, &omask
) == -1)) {
235 warn("couldn't set signal mask ");
242 * truncate the accounting data file ASAP, to avoid
243 * losing data. don't worry about errors in updating
244 * the saved stats; better to underbill than overbill,
245 * but we want every accounting record intact.
247 if (ftruncate(fd
, 0) == -1) {
248 warn("couldn't truncate %s", *argv
);
253 * update saved user and process accounting data.
254 * note errors for later.
256 if (pacct_update() != 0 || usracct_update() != 0)
264 (sigprocmask(SIG_SETMASK
, &omask
, NULL
) == -1)) {
265 warn("couldn't restore signal mask");
272 * close the opened accounting file
274 if (close(fd
) == -1) {
275 warn("close %s", *argv
);
280 if (!uflag
&& !qflag
) {
281 /* print any results we may have obtained. */
289 /* finally, deallocate databases */
290 if (sflag
|| (!mflag
&& !qflag
))
292 if (sflag
|| (mflag
&& !qflag
))
312 fp
= fopen(pn
, wr
? "r+" : "r");
314 warn("open %s %s", pn
, wr
? "for read/write" : "read-only");
319 * read all we can; don't stat and open because more processes
320 * could exit, and we'd miss them
323 /* get one accounting entry and punt if there's an error */
324 if (fread(&ac
, sizeof(struct acct
), 1, fp
) != 1) {
328 warn("error reading %s", pn
);
330 warnx("short read of accounting data in %s",
337 for (i
= 0; i
< sizeof(ac
.ac_comm
) && ac
.ac_comm
[i
] != '\0';
339 char c
= ac
.ac_comm
[i
];
341 if (!isascii(c
) || iscntrl((unsigned char)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("%6u %12.2f CPU %12lluk mem %12llu io %s\n",
366 (ci
.ci_utime
+ ci
.ci_stime
) / (double) AHZ
,
367 (unsigned long long)ci
.ci_mem
,
368 (unsigned long long)ci
.ci_io
, ci
.ci_comm
);
371 /* finally, return the file descriptor for possible truncation */
376 decode_comp_t(comp_t comp
)
381 * for more info on the comp_t format, see:
382 * /usr/src/sys/kern/kern_acct.c
383 * /usr/src/sys/sys/acct.h
384 * /usr/src/usr.bin/lastcomm/lastcomm.c
386 rv
= comp
& 0x1fff; /* 13 bit fraction */
387 comp
>>= 13; /* 3 bit base-8 exponent */
394 /* sort commands, doing the right thing in terms of reversals */
404 return (rflag
? rv
: -rv
);
407 /* sort by total user and system time */
412 struct cmdinfo c1
, c2
;
415 memcpy(&c1
, d1
->data
, sizeof(c1
));
416 memcpy(&c2
, d2
->data
, sizeof(c2
));
418 t1
= c1
.ci_utime
+ c1
.ci_stime
;
419 t2
= c2
.ci_utime
+ c2
.ci_stime
;
424 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
429 /* sort by average user and system time */
431 cmp_avgusrsys(d1
, d2
)
434 struct cmdinfo c1
, c2
;
437 memcpy(&c1
, d1
->data
, sizeof(c1
));
438 memcpy(&c2
, d2
->data
, sizeof(c2
));
440 t1
= c1
.ci_utime
+ c1
.ci_stime
;
441 t1
/= (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
443 t2
= c2
.ci_utime
+ c2
.ci_stime
;
444 t2
/= (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
449 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
454 /* sort by total number of disk I/O operations */
459 struct cmdinfo c1
, c2
;
461 memcpy(&c1
, d1
->data
, sizeof(c1
));
462 memcpy(&c2
, d2
->data
, sizeof(c2
));
464 if (c1
.ci_io
< c2
.ci_io
)
466 else if (c1
.ci_io
== c2
.ci_io
)
467 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
472 /* sort by average number of disk I/O operations */
477 struct cmdinfo c1
, c2
;
480 memcpy(&c1
, d1
->data
, sizeof(c1
));
481 memcpy(&c2
, d2
->data
, sizeof(c2
));
483 n1
= (double) c1
.ci_io
/ (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
484 n2
= (double) c2
.ci_io
/ (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
489 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
494 /* sort by the CPU-storage integral */
499 struct cmdinfo c1
, c2
;
501 memcpy(&c1
, d1
->data
, sizeof(c1
));
502 memcpy(&c2
, d2
->data
, sizeof(c2
));
504 if (c1
.ci_mem
< c2
.ci_mem
)
506 else if (c1
.ci_mem
== c2
.ci_mem
)
507 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
512 /* sort by the CPU-time average memory usage */
514 cmp_avgcpumem(d1
, d2
)
517 struct cmdinfo c1
, c2
;
521 memcpy(&c1
, d1
->data
, sizeof(c1
));
522 memcpy(&c2
, d2
->data
, sizeof(c2
));
524 t1
= c1
.ci_utime
+ c1
.ci_stime
;
525 t2
= c2
.ci_utime
+ c2
.ci_stime
;
527 n1
= (double) c1
.ci_mem
/ (double) (t1
? t1
: 1);
528 n2
= (double) c2
.ci_mem
/ (double) (t2
? t2
: 1);
533 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
538 /* sort by the number of invocations */
543 struct cmdinfo c1
, c2
;
545 memcpy(&c1
, d1
->data
, sizeof(c1
));
546 memcpy(&c2
, d2
->data
, sizeof(c2
));
548 if (c1
.ci_calls
< c2
.ci_calls
)
550 else if (c1
.ci_calls
== c2
.ci_calls
)
551 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
560 (void)fprintf(stderr
,
561 "usage: %s [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n",
570 static char *buf
= NULL
;
571 static size_t len
= 0;
574 if (len
< key
->size
* 4 + 1) {
575 nbuf
= realloc(buf
, key
->size
* 4 + 1);
579 len
= key
->size
* 4 + 1;
581 (void)strvisx(buf
, key
->data
, key
->size
, 0);