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