]>
git.saurik.com Git - apple/system_cmds.git/blob - sa.tproj/main.c
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.
32 static const char copyright
[] =
33 "@(#) Copyright (c) 1994 Christopher G. Demetriou\n\
34 All rights reserved.\n";
38 static const char rcsid
[] =
39 "$FreeBSD: src/usr.sbin/sa/main.c,v 1.12 2002/07/15 16:05:15 des Exp $";
43 * sa: system accounting
46 #include <sys/types.h>
58 #include "pathnames.h"
60 static int acct_load(char *, int);
61 static u_quad_t
decode_comp_t(comp_t
);
62 static int cmp_comm(const char *, const char *);
63 static int cmp_usrsys(const DBT
*, const DBT
*);
64 static int cmp_avgusrsys(const DBT
*, const DBT
*);
65 static int cmp_dkio(const DBT
*, const DBT
*);
66 static int cmp_avgdkio(const DBT
*, const DBT
*);
67 static int cmp_cpumem(const DBT
*, const DBT
*);
68 static int cmp_avgcpumem(const DBT
*, const DBT
*);
69 static int cmp_calls(const DBT
*, const DBT
*);
70 static void usage(void);
72 int aflag
, bflag
, cflag
, dflag
, Dflag
, fflag
, iflag
, jflag
, kflag
;
73 int Kflag
, lflag
, mflag
, qflag
, rflag
, sflag
, tflag
, uflag
, vflag
;
76 static char *dfltargv
[] = { NULL
};
77 static int dfltargc
= (sizeof dfltargv
/sizeof(char *));
79 /* default to comparing by sum of user + system time */
80 cmpf_t sa_cmp
= cmp_usrsys
;
83 main(int argc
, char **argv
)
86 char pathacct
[] = _PATH_ACCT
;
89 dfltargv
[0] = pathacct
;
91 while ((ch
= getopt(argc
, argv
, "abcdDfijkKlmnqrstuv:")) != -1)
94 /* print all commands */
98 /* sort by per-call user/system time average */
100 sa_cmp
= cmp_avgusrsys
;
103 /* print percentage total time */
107 /* sort by averge number of disk I/O ops */
109 sa_cmp
= cmp_avgdkio
;
112 /* print and sort by total disk I/O ops */
117 /* force no interactive threshold comprison */
121 /* do not read in summary file */
125 /* instead of total minutes, give sec/call */
129 /* sort by cpu-time average memory usage */
131 sa_cmp
= cmp_avgcpumem
;
134 /* print and sort by cpu-storage integral */
139 /* separate system and user time */
143 /* print procs and time per-user */
147 /* sort by number of calls */
151 /* quiet; error messages only */
155 /* reverse order of sort */
159 /* merge accounting file into summaries */
163 /* report ratio of user and system times */
167 /* first, print uid and command name */
173 cutoff
= atoi(optarg
);
183 /* various argument checking */
185 errx(1, "only one of -f requires -v");
187 errx(1, "only one of -a and -v may be specified");
188 /* XXX need more argument checking */
191 /* initialize tables */
192 if ((sflag
|| (!mflag
&& !qflag
)) && pacct_init() != 0)
193 errx(1, "process accounting initialization failed");
194 if ((sflag
|| (mflag
&& !qflag
)) && usracct_init() != 0)
195 errx(1, "user accounting initialization failed");
203 /* for each file specified */
204 for (; argc
> 0; argc
--, argv
++) {
208 * load the accounting data from the file.
209 * if it fails, go on to the next file.
211 fd
= acct_load(argv
[0], sflag
);
215 if (!uflag
&& sflag
) {
217 sigset_t nmask
, omask
;
221 * block most signals so we aren't interrupted during
224 if (sigfillset(&nmask
) == -1) {
230 (sigprocmask(SIG_BLOCK
, &nmask
, &omask
) == -1)) {
231 warn("couldn't set signal mask");
238 * truncate the accounting data file ASAP, to avoid
239 * losing data. don't worry about errors in updating
240 * the saved stats; better to underbill than overbill,
241 * but we want every accounting record intact.
243 if (ftruncate(fd
, 0) == -1) {
244 warn("couldn't truncate %s", *argv
);
249 * update saved user and process accounting data.
250 * note errors for later.
252 if (pacct_update() != 0 || usracct_update() != 0)
260 (sigprocmask(SIG_SETMASK
, &omask
, NULL
) == -1)) {
261 warn("couldn't restore signal mask");
268 * close the opened accounting file
270 if (close(fd
) == -1) {
271 warn("close %s", *argv
);
276 if (!uflag
&& !qflag
) {
277 /* print any results we may have obtained. */
285 /* finally, deallocate databases */
286 if (sflag
|| (!mflag
&& !qflag
))
288 if (sflag
|| (mflag
&& !qflag
))
298 (void)fprintf(stderr
,
299 "usage: sa [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n");
316 fd
= open(pn
, wr
? O_RDWR
: O_RDONLY
, 0);
318 warn("open %s %s", pn
, wr
? "for read/write" : "read-only");
323 * read all we can; don't stat and open because more processes
324 * could exit, and we'd miss them
327 /* get one accounting entry and punt if there's an error */
328 rv
= read(fd
, &ac
, sizeof(struct acct
));
330 warn("error reading %s", pn
);
331 else if (rv
> 0 && rv
< (int)sizeof(struct acct
))
332 warnx("short read of accounting data in %s", pn
);
333 if (rv
!= sizeof(struct acct
))
338 for (i
= 0; i
< (int)sizeof ac
.ac_comm
&& ac
.ac_comm
[i
] != '\0';
340 char c
= ac
.ac_comm
[i
];
342 if (!isascii(c
) || iscntrl(c
)) {
344 ci
.ci_flags
|= CI_UNPRINTABLE
;
348 if (ac
.ac_flag
& AFORK
)
349 ci
.ci_comm
[i
++] = '*';
350 ci
.ci_comm
[i
++] = '\0';
351 ci
.ci_etime
= decode_comp_t(ac
.ac_etime
);
352 ci
.ci_utime
= decode_comp_t(ac
.ac_utime
);
353 ci
.ci_stime
= decode_comp_t(ac
.ac_stime
);
354 ci
.ci_uid
= ac
.ac_uid
;
355 ci
.ci_mem
= ac
.ac_mem
;
356 ci
.ci_io
= decode_comp_t(ac
.ac_io
) / AHZ
;
359 /* and enter it into the usracct and pacct databases */
360 if (sflag
|| (!mflag
&& !qflag
))
362 if (sflag
|| (mflag
&& !qflag
))
365 printf("%6lu %12.2f cpu %12juk mem %12ju io %s\n",
367 (ci
.ci_utime
+ ci
.ci_stime
) / (double) AHZ
,
368 (uintmax_t)ci
.ci_mem
, (uintmax_t)ci
.ci_io
,
372 /* finally, return the file descriptor for possible truncation */
383 * for more info on the comp_t format, see:
384 * /usr/src/sys/kern/kern_acct.c
385 * /usr/src/sys/sys/acct.h
386 * /usr/src/usr.bin/lastcomm/lastcomm.c
388 rv
= comp
& 0x1fff; /* 13 bit fraction */
389 comp
>>= 13; /* 3 bit base-8 exponent */
396 /* sort commands, doing the right thing in terms of reversals */
406 return (rflag
? rv
: -rv
);
409 /* sort by total user and system time */
414 struct cmdinfo c1
, c2
;
417 memcpy(&c1
, d1
->data
, sizeof(c1
));
418 memcpy(&c2
, d2
->data
, sizeof(c2
));
420 t1
= c1
.ci_utime
+ c1
.ci_stime
;
421 t2
= c2
.ci_utime
+ c2
.ci_stime
;
426 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
431 /* sort by average user and system time */
433 cmp_avgusrsys(d1
, d2
)
436 struct cmdinfo c1
, c2
;
439 memcpy(&c1
, d1
->data
, sizeof(c1
));
440 memcpy(&c2
, d2
->data
, sizeof(c2
));
442 t1
= c1
.ci_utime
+ c1
.ci_stime
;
443 t1
/= (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
445 t2
= c2
.ci_utime
+ c2
.ci_stime
;
446 t2
/= (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
451 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
456 /* sort by total number of disk I/O operations */
461 struct cmdinfo c1
, c2
;
463 memcpy(&c1
, d1
->data
, sizeof(c1
));
464 memcpy(&c2
, d2
->data
, sizeof(c2
));
466 if (c1
.ci_io
< c2
.ci_io
)
468 else if (c1
.ci_io
== c2
.ci_io
)
469 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
474 /* sort by average number of disk I/O operations */
479 struct cmdinfo c1
, c2
;
482 memcpy(&c1
, d1
->data
, sizeof(c1
));
483 memcpy(&c2
, d2
->data
, sizeof(c2
));
485 n1
= (double) c1
.ci_io
/ (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
486 n2
= (double) c2
.ci_io
/ (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
491 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
496 /* sort by the cpu-storage integral */
501 struct cmdinfo c1
, c2
;
503 memcpy(&c1
, d1
->data
, sizeof(c1
));
504 memcpy(&c2
, d2
->data
, sizeof(c2
));
506 if (c1
.ci_mem
< c2
.ci_mem
)
508 else if (c1
.ci_mem
== c2
.ci_mem
)
509 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
514 /* sort by the cpu-time average memory usage */
516 cmp_avgcpumem(d1
, d2
)
519 struct cmdinfo c1
, c2
;
523 memcpy(&c1
, d1
->data
, sizeof(c1
));
524 memcpy(&c2
, d2
->data
, sizeof(c2
));
526 t1
= c1
.ci_utime
+ c1
.ci_stime
;
527 t2
= c2
.ci_utime
+ c2
.ci_stime
;
529 n1
= (double) c1
.ci_mem
/ (double) (t1
? t1
: 1);
530 n2
= (double) c2
.ci_mem
/ (double) (t2
? t2
: 1);
535 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
540 /* sort by the number of invocations */
545 struct cmdinfo c1
, c2
;
547 memcpy(&c1
, d1
->data
, sizeof(c1
));
548 memcpy(&c2
, d2
->data
, sizeof(c2
));
550 if (c1
.ci_calls
< c2
.ci_calls
)
552 else if (c1
.ci_calls
== c2
.ci_calls
)
553 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));