]>
git.saurik.com Git - apple/system_cmds.git/blob - system_cmds-597.1.1/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.
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: src/usr.sbin/sa/main.c,v 1.18 2007/05/22 06:51:38 dds Exp $");
42 * sa: system accounting
45 #include <sys/types.h>
58 #include "pathnames.h"
60 static FILE *acct_load(const char *, int);
62 static u_quad_t
decode_comp_t(comp_t
);
64 static int cmp_comm(const char *, const char *);
65 static int cmp_usrsys(const DBT
*, const DBT
*);
66 static int cmp_avgusrsys(const DBT
*, const DBT
*);
67 static int cmp_dkio(const DBT
*, const DBT
*);
68 static int cmp_avgdkio(const DBT
*, const DBT
*);
69 static int cmp_cpumem(const DBT
*, const DBT
*);
70 static int cmp_avgcpumem(const DBT
*, const DBT
*);
71 static int cmp_calls(const DBT
*, const DBT
*);
72 static void usage(void);
74 int aflag
, bflag
, cflag
, dflag
, Dflag
, fflag
, iflag
, jflag
, kflag
;
75 int Kflag
, lflag
, mflag
, qflag
, rflag
, sflag
, tflag
, uflag
, vflag
;
77 const char *pdb_file
= _PATH_SAVACCT
;
78 const char *usrdb_file
= _PATH_USRACCT
;
80 static char *dfltargv
[] = { NULL
};
81 static int dfltargc
= (sizeof dfltargv
/sizeof(char *));
83 /* default to comparing by sum of user + system time */
84 cmpf_t sa_cmp
= cmp_usrsys
;
87 main(int argc
, char **argv
)
90 char pathacct
[] = _PATH_ACCT
;
93 dfltargv
[0] = pathacct
;
95 while ((ch
= getopt(argc
, argv
, "abcdDfijkKlmnP:qrstuU:v:")) != -1)
98 /* print all commands */
102 /* sort by per-call user/system time average */
104 sa_cmp
= cmp_avgusrsys
;
107 /* print percentage total time */
111 /* sort by averge number of disk I/O ops */
113 sa_cmp
= cmp_avgdkio
;
116 /* print and sort by total disk I/O ops */
121 /* force no interactive threshold comprison */
125 /* do not read in summary file */
129 /* instead of total minutes, give sec/call */
133 /* sort by cpu-time average memory usage */
135 sa_cmp
= cmp_avgcpumem
;
138 /* print and sort by cpu-storage integral */
143 /* separate system and user time */
147 /* print procs and time per-user */
151 /* sort by number of calls */
155 /* specify program database summary file */
159 /* quiet; error messages only */
163 /* reverse order of sort */
167 /* merge accounting file into summaries */
171 /* report ratio of user and system times */
175 /* first, print uid and command name */
179 /* specify user database summary file */
185 cutoff
= atoi(optarg
);
195 /* various argument checking */
197 errx(1, "only one of -f requires -v");
199 errx(1, "only one of -a and -v may be specified");
200 /* XXX need more argument checking */
203 /* initialize tables */
204 if ((sflag
|| (!mflag
&& !qflag
)) && pacct_init() != 0)
205 errx(1, "process accounting initialization failed");
206 if ((sflag
|| (mflag
&& !qflag
)) && usracct_init() != 0)
207 errx(1, "user accounting initialization failed");
215 /* for each file specified */
216 for (; argc
> 0; argc
--, argv
++) {
218 * load the accounting data from the file.
219 * if it fails, go on to the next file.
221 f
= acct_load(argv
[0], sflag
);
225 if (!uflag
&& sflag
) {
227 sigset_t nmask
, omask
;
231 * block most signals so we aren't interrupted during
234 if (sigfillset(&nmask
) == -1) {
240 (sigprocmask(SIG_BLOCK
, &nmask
, &omask
) == -1)) {
241 warn("couldn't set signal mask");
248 * truncate the accounting data file ASAP, to avoid
249 * losing data. don't worry about errors in updating
250 * the saved stats; better to underbill than overbill,
251 * but we want every accounting record intact.
253 if (ftruncate(fileno(f
), 0) == -1) {
254 warn("couldn't truncate %s", *argv
);
259 * update saved user and process accounting data.
260 * note errors for later.
262 if (pacct_update() != 0 || usracct_update() != 0)
270 (sigprocmask(SIG_SETMASK
, &omask
, NULL
) == -1)) {
271 warn("couldn't restore signal mask");
278 * close the opened accounting file
280 if (fclose(f
) == EOF
) {
281 warn("fclose %s", *argv
);
286 if (!uflag
&& !qflag
) {
287 /* print any results we may have obtained. */
295 /* finally, deallocate databases */
296 if (sflag
|| (!mflag
&& !qflag
))
298 if (sflag
|| (mflag
&& !qflag
))
308 (void)fprintf(stderr
,
309 "usage: sa [-abcdDfijkKlmnqrstu] [-P file] [-U file] [-v cutoff] [file ...]\n");
314 acct_load(const char *pn
, int wr
)
329 f
= fopen(pn
, wr
? "r+" : "r");
331 warn("open %s %s", pn
, wr
? "for read/write" : "read-only");
336 * read all we can; don't stat and open because more processes
337 * could exit, and we'd miss them
340 /* get one accounting entry and punt if there's an error */
342 rv
= read(fileno(f
), &ac
, sizeof(struct acct
));
344 warn("error reading %s", pn
);
345 else if (rv
> 0 && rv
< (int)sizeof(struct acct
))
346 warnx("short read of accounting data in %s", pn
);
347 if (rv
!= sizeof(struct acct
))
350 rv
= readrec_forward(f
, &ac
);
353 warn("error reading %s", pn
);
360 for (i
= 0; i
< (int)sizeof ac
.ac_comm
&& ac
.ac_comm
[i
] != '\0';
362 char c
= ac
.ac_comm
[i
];
364 if (!isascii(c
) || iscntrl(c
)) {
366 ci
.ci_flags
|= CI_UNPRINTABLE
;
371 if (ac
.ac_flag
& AFORK
)
372 ci
.ci_comm
[i
++] = '*';
373 ci
.ci_comm
[i
++] = '\0';
374 ci
.ci_etime
= decode_comp_t(ac
.ac_etime
);
375 ci
.ci_utime
= decode_comp_t(ac
.ac_utime
);
376 ci
.ci_stime
= decode_comp_t(ac
.ac_stime
);
377 ci
.ci_uid
= ac
.ac_uid
;
378 ci
.ci_mem
= ac
.ac_mem
;
379 ci
.ci_io
= decode_comp_t(ac
.ac_io
) / AHZ
;
381 if (ac
.ac_flagx
& AFORK
)
382 ci
.ci_comm
[i
++] = '*';
383 ci
.ci_comm
[i
++] = '\0';
384 ci
.ci_etime
= ac
.ac_etime
;
385 ci
.ci_utime
= ac
.ac_utime
;
386 ci
.ci_stime
= ac
.ac_stime
;
387 ci
.ci_uid
= ac
.ac_uid
;
388 ci
.ci_mem
= ac
.ac_mem
;
393 /* and enter it into the usracct and pacct databases */
394 if (sflag
|| (!mflag
&& !qflag
))
396 if (sflag
|| (mflag
&& !qflag
))
400 printf("%6u %12.2f cpu %12juk mem %12ju io %s\n",
402 (ci
.ci_utime
+ ci
.ci_stime
) / (double) AHZ
,
403 (uintmax_t)ci
.ci_mem
, (uintmax_t)ci
.ci_io
,
406 printf("%6u %12.3lf cpu %12.0lfk mem %12.0lf io %s\n",
408 (ci
.ci_utime
+ ci
.ci_stime
) / 1000000,
414 /* Finally, return the file stream for possible truncation. */
419 static u_quad_t
decode_comp_t(comp_t comp
)
424 * for more info on the comp_t format, see:
425 * /usr/src/sys/kern/kern_acct.c
426 * /usr/src/sys/sys/acct.h
427 * /usr/src/usr.bin/lastcomm/lastcomm.c
429 rv
= comp
& 0x1fff; /* 13 bit fraction */
430 comp
>>= 13; /* 3 bit base-8 exponent */
438 /* sort commands, doing the right thing in terms of reversals */
440 cmp_comm(const char *s1
, const char *s2
)
447 return (rflag
? rv
: -rv
);
450 /* sort by total user and system time */
452 cmp_usrsys(const DBT
*d1
, const DBT
*d2
)
454 struct cmdinfo c1
, c2
;
461 memcpy(&c1
, d1
->data
, sizeof(c1
));
462 memcpy(&c2
, d2
->data
, sizeof(c2
));
464 t1
= c1
.ci_utime
+ c1
.ci_stime
;
465 t2
= c2
.ci_utime
+ c2
.ci_stime
;
470 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
475 /* sort by average user and system time */
477 cmp_avgusrsys(const DBT
*d1
, const DBT
*d2
)
479 struct cmdinfo c1
, c2
;
482 memcpy(&c1
, d1
->data
, sizeof(c1
));
483 memcpy(&c2
, d2
->data
, sizeof(c2
));
485 t1
= c1
.ci_utime
+ c1
.ci_stime
;
486 t1
/= (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
488 t2
= c2
.ci_utime
+ c2
.ci_stime
;
489 t2
/= (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
494 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
499 /* sort by total number of disk I/O operations */
501 cmp_dkio(const DBT
*d1
, const DBT
*d2
)
503 struct cmdinfo c1
, c2
;
505 memcpy(&c1
, d1
->data
, sizeof(c1
));
506 memcpy(&c2
, d2
->data
, sizeof(c2
));
508 if (c1
.ci_io
< c2
.ci_io
)
510 else if (c1
.ci_io
== c2
.ci_io
)
511 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
516 /* sort by average number of disk I/O operations */
518 cmp_avgdkio(const DBT
*d1
, const DBT
*d2
)
520 struct cmdinfo c1
, c2
;
523 memcpy(&c1
, d1
->data
, sizeof(c1
));
524 memcpy(&c2
, d2
->data
, sizeof(c2
));
527 n1
= (double) c1
.ci_io
/ (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
528 n2
= (double) c2
.ci_io
/ (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
530 n1
= c1
.ci_io
/ (double) (c1
.ci_calls
? c1
.ci_calls
: 1);
531 n2
= c2
.ci_io
/ (double) (c2
.ci_calls
? c2
.ci_calls
: 1);
537 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
542 /* sort by the cpu-storage integral */
544 cmp_cpumem(const DBT
*d1
, const DBT
*d2
)
546 struct cmdinfo c1
, c2
;
548 memcpy(&c1
, d1
->data
, sizeof(c1
));
549 memcpy(&c2
, d2
->data
, sizeof(c2
));
551 if (c1
.ci_mem
< c2
.ci_mem
)
553 else if (c1
.ci_mem
== c2
.ci_mem
)
554 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
559 /* sort by the cpu-time average memory usage */
561 cmp_avgcpumem(const DBT
*d1
, const DBT
*d2
)
563 struct cmdinfo c1
, c2
;
571 memcpy(&c1
, d1
->data
, sizeof(c1
));
572 memcpy(&c2
, d2
->data
, sizeof(c2
));
574 t1
= c1
.ci_utime
+ c1
.ci_stime
;
575 t2
= c2
.ci_utime
+ c2
.ci_stime
;
578 n1
= (double) c1
.ci_mem
/ (double) (t1
? t1
: 1);
579 n2
= (double) c2
.ci_mem
/ (double) (t2
? t2
: 1);
581 n1
= c1
.ci_mem
/ (t1
? t1
: 1);
582 n2
= c2
.ci_mem
/ (t2
? t2
: 1);
588 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));
593 /* sort by the number of invocations */
595 cmp_calls(const DBT
*d1
, const DBT
*d2
)
597 struct cmdinfo c1
, c2
;
599 memcpy(&c1
, d1
->data
, sizeof(c1
));
600 memcpy(&c2
, d2
->data
, sizeof(c2
));
602 if (c1
.ci_calls
< c2
.ci_calls
)
604 else if (c1
.ci_calls
== c2
.ci_calls
)
605 return (cmp_comm(c1
.ci_comm
, c2
.ci_comm
));