1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1991, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)sa.c 8.1 (Berkeley) 06/06/93"; 16 #endif /* not lint */ 17 18 /* 19 * Extensive modifications to internal data structures 20 * to allow arbitrary number of different commands and users added. 21 * 22 * Also allowed the digit option on the -v flag (interactive 23 * threshold compress) to be a digit string, so one can 24 * set the threshold > 9. 25 * 26 * Also added the -f flag, to force no interactive threshold 27 * compression with the -v flag. 28 * 29 * Robert Henry 30 * UC Berkeley 31 * 31jan81 32 */ 33 #include <stdio.h> 34 #include <ctype.h> 35 #include <sys/types.h> 36 #include <sys/acct.h> 37 #include <signal.h> 38 #include <utmp.h> 39 #include <pwd.h> 40 #include "pathnames.h" 41 42 /* interpret command time accounting */ 43 44 #define NC sizeof(acctbuf.ac_comm) 45 46 struct acct acctbuf; 47 int lflg; 48 int cflg; 49 int Dflg; 50 int dflg; 51 int iflg; 52 int jflg; 53 int Kflg; 54 int kflg; 55 int nflg; 56 int aflg; 57 int rflg; 58 int oflg; 59 int tflg; 60 int vflg; 61 int fflg; 62 int uflg; 63 int thres; 64 int sflg; 65 int bflg; 66 int mflg; 67 68 struct utmp utmp; 69 #define NAMELG (sizeof(utmp.ut_name)+1) 70 71 struct Olduser{ 72 int Us_cnt; 73 double Us_ctime; 74 double Us_io; 75 double Us_imem; 76 }; 77 78 struct user { 79 char name[NC]; /* this is <\001><user id><\000> */ 80 struct Olduser oldu; 81 char us_name[NAMELG]; 82 }; 83 #define us_cnt oldu.Us_cnt 84 #define us_ctime oldu.Us_ctime 85 #define us_io oldu.Us_io 86 #define us_imem oldu.Us_imem 87 88 /* 89 * We protect ourselves from preposterous user id's by looking 90 * through the passwd file for the highest uid allocated, and 91 * then adding 10 to that. 92 * This prevents the user structure from growing too large. 93 */ 94 #define USERSLOP 10 95 int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/ 96 97 struct process { 98 char name[NC]; 99 int count; 100 double realt; 101 double cput; 102 double syst; 103 double imem; 104 double io; 105 }; 106 107 union Tab{ 108 struct process p; 109 struct user u; 110 }; 111 112 typedef union Tab cell; 113 114 int (*cmp)(); /* compares 2 cells; set to appropriate func */ 115 cell *enter(); 116 struct user *finduser(); 117 struct user *wasuser(); 118 119 /* 120 * Table elements are keyed by the name of the file exec'ed. 121 * Because on large systems, many files can be exec'ed, 122 * a static table size may grow to be too large. 123 * 124 * Table elements are allocated in chunks dynamically, linked 125 * together so that they may be retrieved sequentially. 126 * 127 * An index into the table structure is provided by hashing through 128 * a seperate hash table. 129 * The hash table is segmented, and dynamically extendable. 130 * Realize that the hash table and accounting information is kept 131 * in different segments! 132 * 133 * We have a linked list of hash table segments; within each 134 * segment we use a quadratic rehash that touches no more than 1/2 135 * of the buckets in the hash table when probing. 136 * If the probe does not find the desired symbol, it moves to the 137 * next segment, or allocates a new segment. 138 * 139 * Hash table segments are kept on the linked list with the first 140 * segment always first (that will probably contain the 141 * most frequently executed commands) and 142 * the last added segment immediately after the first segment, 143 * to hopefully gain something by locality of reference. 144 * 145 * We store the per user information in the same structure as 146 * the per exec'ed file information. This allows us to use the 147 * same managers for both, as the number of user id's may be very 148 * large. 149 * User information is keyed by the first character in the name 150 * being a '\001', followed by four bytes of (long extended) 151 * user id number, followed by a null byte. 152 * The actual user names are kept in a seperate field of the 153 * user structure, and is filled in upon demand later. 154 * Iteration through all users by low user id to high user id 155 * is done by just probing the table, which is gross. 156 */ 157 #define USERKEY '\001' 158 #define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY)) 159 #define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY)) 160 161 #define TABDALLOP 500 162 struct allocbox{ 163 struct allocbox *nextalloc; 164 cell tabslots[TABDALLOP]; 165 }; 166 167 struct allocbox *allochead; /*head of chunk list*/ 168 struct allocbox *alloctail; /*tail*/ 169 struct allocbox *newbox; /*for creating a new chunk*/ 170 cell *nexttab; /*next table element that is free*/ 171 int tabsleft; /*slots left in current chunk*/ 172 int ntabs; 173 /* 174 * Iterate through all symbols in the symbol table in declaration 175 * order. 176 * struct allocbox *allocwalk; 177 * cell *sp, *ub; 178 * 179 * sp points to the desired item, allocwalk and ub are there 180 * to make the iteration go. 181 */ 182 183 #define DECLITERATE(allocwalk, walkpointer, ubpointer) \ 184 for(allocwalk = allochead; \ 185 allocwalk != 0; \ 186 allocwalk = allocwalk->nextalloc) \ 187 for (walkpointer = &allocwalk->tabslots[0],\ 188 ubpointer = &allocwalk->tabslots[TABDALLOP], \ 189 ubpointer = ubpointer > ( (cell *)alloctail) \ 190 ? nexttab : ubpointer ;\ 191 walkpointer < ubpointer; \ 192 walkpointer++ ) 193 194 #define TABCHUNKS(allocwalk, tabptr, size) \ 195 for (allocwalk = allochead; \ 196 allocwalk != 0; \ 197 allocwalk = allocwalk->nextalloc) \ 198 if ( \ 199 (tabptr = &allocwalk->tabslots[0]), \ 200 (size = \ 201 ( (&allocwalk->tabslots[TABDALLOP]) \ 202 > ((cell *)alloctail) \ 203 ) \ 204 ? (nexttab - tabptr) : TABDALLOP \ 205 ), \ 206 1 \ 207 ) 208 #define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \ 209 DECLITERATE(allocwalk, walkpointer, ubpointer) \ 210 if (ISPROCESS(walkpointer)) 211 212 #define USERITERATE(allocwalk, walkpointer, ubpointer) \ 213 DECLITERATE(allocwalk, walkpointer, ubpointer) \ 214 if (ISUSER(walkpointer)) 215 /* 216 * When we have to sort the segmented accounting table, we 217 * create a vector of sorted queues that is merged 218 * to sort the entire accounting table. 219 */ 220 struct chunkdesc { 221 cell *chunk_tp; 222 int chunk_n; 223 }; 224 225 /* 226 * Hash table segments and manager 227 */ 228 #define NHASH 1103 229 struct hashdallop { 230 int h_nused; 231 struct hashdallop *h_next; 232 cell *h_tab[NHASH]; 233 }; 234 struct hashdallop *htab; /* head of the list */ 235 int htabinstall; /* install the symbol */ 236 237 double treal; 238 double tcpu; 239 double tsys; 240 double tio; 241 double timem; 242 cell *junkp; 243 char *sname; 244 double ncom; 245 time_t expand(); 246 char *getname(); 247 248 /* 249 * usracct saves records of type Olduser. 250 * There is one record for every possible uid less than 251 * the largest uid seen in the previous usracct or in savacct. 252 * uid's that had no activity correspond to zero filled slots; 253 * thus one can index the file and get the user record out. 254 * It would be better to save only user information for users 255 * that the system knows about to save space, but that is not 256 * upward compatabile with the old system. 257 * 258 * In the old version of sa, uid's greater than 999 were not handled 259 * properly; this system will do that. 260 */ 261 262 #ifdef DEBUG 263 #define USRACCT "./usracct" 264 #define SAVACCT "./savacct" 265 #define ACCT "./acct" 266 #else 267 #define USRACCT _PATH_USRACCT 268 #define SAVACCT _PATH_SAVACCT 269 #define ACCT _PATH_ACCT 270 #endif DEBUG 271 272 273 char *usracct = USRACCT; 274 char *savacct = SAVACCT; 275 276 int cellcmp(); 277 cell *junkp = 0; 278 /* 279 * The threshold is built up from digits in the argv ; 280 * eg, -v1s0u1 281 * will build a value of thres of 101. 282 * 283 * If the threshold is zero after processing argv, it is set to 1 284 */ 285 int thres = 0; 286 int htabinstall = 1; 287 int maxuser = -1; 288 int (*cmp)(); 289 290 /* we assume pagesize is at least 1k */ 291 int pgdiv; 292 #define pgtok(x) ((x) * pgdiv) 293 294 extern tcmp(), ncmp(), bflgcmp(), dcmp(), Dcmp(), kcmp(), Kcmp(); 295 extern double sum(); 296 297 main(argc, argv) 298 char **argv; 299 { 300 FILE *ff; 301 double ft; 302 register struct allocbox *allocwalk; 303 register cell *tp, *ub; 304 int i, j, size, nchunks, smallest; 305 struct chunkdesc *chunkvector; 306 307 pgdiv = getpagesize() / 1024; 308 if (pgdiv == 0) 309 pgdiv = 1; 310 maxuser = USERSLOP + getmaxuid(); 311 312 tabinit(); 313 cmp = tcmp; 314 if (argc>1) 315 if (argv[1][0]=='-') { 316 argv++; 317 argc--; 318 for(i=1; argv[0][i]; i++) 319 switch(argv[0][i]) { 320 321 case 'o': 322 oflg++; 323 break; 324 325 case 'i': 326 iflg++; 327 break; 328 329 case 'b': 330 bflg++; 331 cmp = bflgcmp; 332 break; 333 334 case 'l': 335 lflg++; 336 break; 337 338 case 'c': 339 cflg++; 340 break; 341 342 case 'd': 343 dflg++; 344 cmp = dcmp; 345 break; 346 347 case 'D': 348 Dflg++; 349 cmp = Dcmp; 350 break; 351 352 case 'j': 353 jflg++; 354 break; 355 356 case 'k': 357 kflg++; 358 cmp = kcmp; 359 break; 360 361 case 'K': 362 Kflg++; 363 cmp = Kcmp; 364 break; 365 366 case 'n': 367 nflg++; 368 cmp = ncmp; 369 break; 370 371 case 'a': 372 aflg++; 373 break; 374 375 case 'r': 376 rflg++; 377 break; 378 379 case 't': 380 tflg++; 381 break; 382 383 case 's': 384 sflg++; 385 aflg++; 386 break; 387 388 case '0': 389 case '1': 390 case '2': 391 case '3': 392 case '4': 393 case '5': 394 case '6': 395 case '7': 396 case '8': 397 case '9': 398 thres = thres * 10 + (argv[0][i]-'0'); 399 break; 400 401 case 'v': 402 vflg++; 403 break; 404 405 case 'f': 406 fflg++; /* force v option; no tty interaction */ 407 break; 408 409 case 'u': 410 uflg++; 411 break; 412 413 case 'm': 414 mflg++; 415 break; 416 417 case 'U': 418 case 'S': 419 if (i != 1 || argv[0][2]) { /* gross! */ 420 fprintf(stderr, "-U and -S options must be separate\n"); 421 exit(1); 422 } 423 argc++, argv--; /* backup - yuk */ 424 goto doUS; 425 426 default: 427 fprintf(stderr, "Invalid option %c\n", argv[0][1]); 428 exit(1); 429 } 430 } 431 432 #define optfile(f) {if (argc < 2) \ 433 { fprintf(stderr, "Missing filename\n"); exit(1); } \ 434 argc--, argv++; f = argv[0]; } 435 436 doUS: 437 for (argc--, argv++; argc && argv[0][0] == '-'; argc--, argv++) { 438 switch(argv[0][1]) { 439 case 'U': 440 optfile(usracct); 441 break; 442 443 case 'S': 444 optfile(savacct); 445 break; 446 447 default: 448 fprintf(stderr, "Invalid option %c\n", argv[0][1]); 449 exit(1); 450 } 451 } 452 453 if (thres == 0) 454 thres = 1; 455 if (iflg==0) 456 init(); 457 if (argc<1) 458 doacct(ACCT); 459 else while (argc--) 460 doacct(*argv++); 461 if (uflg) { 462 return; 463 } 464 465 /* 466 * cleanup pass 467 * put junk together 468 */ 469 470 if (vflg) 471 strip(); 472 if(!aflg) 473 PROCESSITERATE(allocwalk, tp, ub){ 474 for(j=0; j<NC; j++) 475 if(tp->p.name[j] == '?') 476 goto yes; 477 if(tp->p.count != 1) 478 continue; 479 yes: 480 if(junkp == 0) 481 junkp = enter("***other"); 482 junkp->p.count += tp->p.count; 483 junkp->p.realt += tp->p.realt; 484 junkp->p.cput += tp->p.cput; 485 junkp->p.syst += tp->p.syst; 486 junkp->p.imem += tp->p.imem; 487 junkp->p.io += tp->p.io; 488 tp->p.name[0] = 0; 489 } 490 if (sflg) { 491 signal(SIGINT, SIG_IGN); 492 if ((ff = fopen(usracct, "w")) != NULL) { 493 static struct user ZeroUser = {0}; 494 struct user *up; 495 int uid; 496 /* 497 * Write out just enough user slots, 498 * filling with zero slots for users that 499 * weren't found. 500 * The file can be indexed directly by uid 501 * to get the correct record. 502 */ 503 for (uid = 0; uid < maxuser; uid++){ 504 if ( (up = wasuser(uid)) != 0) 505 fwrite((char *)&(up->oldu), 506 sizeof(struct Olduser),1,ff); 507 else 508 fwrite((char *)&(ZeroUser.oldu), 509 sizeof(struct Olduser),1,ff); 510 } 511 } 512 if ((ff = fopen(savacct, "w")) == NULL) { 513 printf("Can't save\n"); 514 exit(0); 515 } 516 PROCESSITERATE(allocwalk, tp, ub) 517 fwrite((char *)&(tp->p), sizeof(struct process), 1, ff); 518 fclose(ff); 519 creat(sname, 0644); 520 signal(SIGINT, SIG_DFL); 521 } 522 /* 523 * sort and print 524 */ 525 if (mflg) { 526 printmoney(); 527 exit(0); 528 } 529 column(ncom, treal, tcpu, tsys, timem, tio); 530 printf("\n"); 531 532 /* 533 * the fragmented table is sorted by sorting each fragment 534 * and then merging. 535 */ 536 nchunks = 0; 537 TABCHUNKS(allocwalk, tp, size){ 538 qsort(tp, size, sizeof(cell), cellcmp); 539 nchunks ++; 540 } 541 chunkvector = (struct chunkdesc *)calloc(nchunks, 542 sizeof(struct chunkdesc)); 543 nchunks = 0; 544 TABCHUNKS(allocwalk, tp, size){ 545 chunkvector[nchunks].chunk_tp = tp; 546 chunkvector[nchunks].chunk_n = size; 547 nchunks++; 548 } 549 for(; nchunks; ){ 550 /* 551 * Find the smallest element at the head of the queues. 552 */ 553 smallest = 0; 554 for (i = 1; i < nchunks; i++){ 555 if (cellcmp(chunkvector[i].chunk_tp, 556 chunkvector[smallest].chunk_tp) < 0) 557 smallest = i; 558 } 559 tp = chunkvector[smallest].chunk_tp++; 560 /* 561 * If this queue is drained, drop the chunk count, 562 * and readjust the queues. 563 */ 564 if (--chunkvector[smallest].chunk_n == 0){ 565 nchunks--; 566 for (i = smallest; i < nchunks; i++) 567 chunkvector[i] = chunkvector[i+1]; 568 } 569 if (ISPROCESS(tp)){ 570 ft = tp->p.count; 571 column(ft, tp->p.realt, tp->p.cput, 572 tp->p.syst, tp->p.imem, tp->p.io); 573 printf(" %.14s\n", tp->p.name); 574 } 575 } /* iterate to merge the lists */ 576 } 577 578 printmoney() 579 { 580 register i; 581 register char *cp; 582 register struct user *up; 583 584 getnames(); /* fetches all of the names! */ 585 for (i = 0; i < maxuser; i++) { 586 if ( (up = wasuser(i)) != 0){ 587 if (up->us_cnt) { 588 if (up->us_name[0]) 589 printf("%-8s", up->us_name); 590 else 591 printf("%-8d", i); 592 printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n", 593 up->us_cnt, up->us_ctime / 60, 594 up->us_io, 595 up->us_imem / AHZ); 596 } 597 } 598 } 599 } 600 601 column(n, a, b, c, d, e) 602 double n, a, b, c, d, e; 603 { 604 605 printf("%8.0f", n); 606 if(cflg) { 607 if(n == ncom) 608 printf("%9s", ""); else 609 printf("%8.2f%%", 100.*n/ncom); 610 } 611 col(n, a, treal, "re"); 612 if (oflg) 613 col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s"); 614 else if(lflg) { 615 col(n, b, tcpu, "u"); 616 col(n, c, tsys, "s"); 617 } else 618 col(n, b+c, tcpu+tsys, "cp"); 619 if(tflg) 620 printf("%8.1fre/cp", a/(b+c)); 621 if(dflg || !Dflg) 622 printf("%10.0favio", e/(n?n:1)); 623 else 624 printf("%10.0ftio", e); 625 if (kflg || !Kflg) 626 printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0)); 627 else 628 printf("%10.0fk*sec", d/AHZ); 629 } 630 631 col(n, a, m, cp) 632 double n, a, m; 633 char *cp; 634 { 635 636 if(jflg) 637 printf("%11.2f%s", a/(n*(double)AHZ), cp); else 638 printf("%11.2f%s", a/(60.*(double)AHZ), cp); 639 if(cflg) { 640 if(a == m) 641 printf("%9s", ""); else 642 printf("%8.2f%%", 100.*a/m); 643 } 644 } 645 646 doacct(f) 647 char *f; 648 { 649 FILE *ff; 650 long x, y, z; 651 struct acct fbuf; 652 register char *cp; 653 register int c; 654 register struct user *up; 655 register cell *tp; 656 #ifdef DEBUG 657 int nrecords = 0; 658 #endif DEBUG 659 660 if (sflg && sname) { 661 printf("Only 1 file with -s\n"); 662 exit(0); 663 } 664 if (sflg) 665 sname = f; 666 if ((ff = fopen(f, "r"))==NULL) { 667 printf("Can't open %s\n", f); 668 return; 669 } 670 while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) { 671 #ifdef DEBUG 672 if (++nrecords % 1000 == 0) 673 printf("Input record from %s number %d\n", 674 f, nrecords); 675 #endif DEBUG 676 for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++) 677 if (!isascii(*cp) || iscntrl(*cp)) 678 *cp = '?'; 679 if (cp == fbuf.ac_comm) 680 *cp++ = '?'; 681 if (fbuf.ac_flag&AFORK) { 682 if (cp >= &fbuf.ac_comm[NC]) 683 cp = &fbuf.ac_comm[NC-1]; 684 *cp++ = '*'; 685 } 686 if (cp < &fbuf.ac_comm[NC]) 687 *cp = '\0'; 688 x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime); 689 y = pgtok((u_short)fbuf.ac_mem); 690 z = expand(fbuf.ac_io) / AHZ; 691 if (uflg) { 692 printf("%3d %6.2f cpu %8luk mem %6ld io %.*s\n", 693 fbuf.ac_uid, x/(double)AHZ, y, z, NC, fbuf.ac_comm); 694 continue; 695 } 696 up = finduser(fbuf.ac_uid); 697 if (up == 0) 698 continue; /* preposterous user id */ 699 up->us_cnt++; 700 up->us_ctime += x/(double)AHZ; 701 up->us_imem += x * y; 702 up->us_io += z; 703 ncom += 1.0; 704 705 tp = enter(fbuf.ac_comm); 706 tp->p.imem += x * y; 707 timem += x * y; 708 tp->p.count++; 709 x = expand(fbuf.ac_etime); 710 tp->p.realt += x; 711 treal += x; 712 x = expand(fbuf.ac_utime); 713 tp->p.cput += x; 714 tcpu += x; 715 x = expand(fbuf.ac_stime); 716 tp->p.syst += x; 717 tsys += x; 718 tp->p.io += z; 719 tio += z; 720 } 721 fclose(ff); 722 } 723 724 /* 725 * Generalized cell compare routine, to cast out users 726 */ 727 cellcmp(p1, p2) 728 cell *p1, *p2; 729 { 730 if (ISPROCESS(p1)){ 731 if (ISPROCESS(p2)) 732 return((*cmp)(p1, p2)); 733 return(-1); 734 } 735 if (ISPROCESS(p2)) 736 return(1); 737 return(0); 738 } 739 740 ncmp(p1, p2) 741 cell *p1, *p2; 742 { 743 744 if(p1->p.count == p2->p.count) 745 return(tcmp(p1, p2)); 746 if(rflg) 747 return(p1->p.count - p2->p.count); 748 return(p2->p.count - p1->p.count); 749 } 750 751 bflgcmp(p1, p2) 752 cell *p1, *p2; 753 { 754 double f1, f2; 755 double sum(); 756 757 f1 = sum(p1)/p1->p.count; 758 f2 = sum(p2)/p2->p.count; 759 if(f1 < f2) { 760 if(rflg) 761 return(-1); 762 return(1); 763 } 764 if(f1 > f2) { 765 if(rflg) 766 return(1); 767 return(-1); 768 } 769 return(0); 770 } 771 772 Kcmp(p1, p2) 773 cell *p1, *p2; 774 { 775 776 if (p1->p.imem < p2->p.imem) { 777 if(rflg) 778 return(-1); 779 return(1); 780 } 781 if (p1->p.imem > p2->p.imem) { 782 if(rflg) 783 return(1); 784 return(-1); 785 } 786 return(0); 787 } 788 789 kcmp(p1, p2) 790 cell *p1, *p2; 791 { 792 double a1, a2; 793 794 a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1); 795 a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1); 796 if (a1 < a2) { 797 if(rflg) 798 return(-1); 799 return(1); 800 } 801 if (a1 > a2) { 802 if(rflg) 803 return(1); 804 return(-1); 805 } 806 return(0); 807 } 808 809 dcmp(p1, p2) 810 cell *p1, *p2; 811 { 812 double a1, a2; 813 814 a1 = p1->p.io / (p1->p.count?p1->p.count:1); 815 a2 = p2->p.io / (p2->p.count?p2->p.count:1); 816 if (a1 < a2) { 817 if(rflg) 818 return(-1); 819 return(1); 820 } 821 if (a1 > a2) { 822 if(rflg) 823 return(1); 824 return(-1); 825 } 826 return(0); 827 } 828 829 Dcmp(p1, p2) 830 cell *p1, *p2; 831 { 832 833 if (p1->p.io < p2->p.io) { 834 if(rflg) 835 return(-1); 836 return(1); 837 } 838 if (p1->p.io > p2->p.io) { 839 if(rflg) 840 return(1); 841 return(-1); 842 } 843 return(0); 844 } 845 846 tcmp(p1, p2) 847 cell *p1, *p2; 848 { 849 extern double sum(); 850 double f1, f2; 851 852 f1 = sum(p1); 853 f2 = sum(p2); 854 if(f1 < f2) { 855 if(rflg) 856 return(-1); 857 return(1); 858 } 859 if(f1 > f2) { 860 if(rflg) 861 return(1); 862 return(-1); 863 } 864 return(0); 865 } 866 867 double sum(p) 868 cell *p; 869 { 870 871 if(p->p.name[0] == 0) 872 return(0.0); 873 return( p->p.cput + p->p.syst); 874 } 875 876 init() 877 { 878 struct user userbuf; 879 struct process tbuf; 880 register cell *tp; 881 register struct user *up; 882 int uid; 883 FILE *f; 884 885 if ((f = fopen(savacct, "r")) == NULL) 886 goto gshm; 887 while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) { 888 tp = enter(tbuf.name); 889 ncom += tbuf.count; 890 tp->p.count = tbuf.count; 891 treal += tbuf.realt; 892 tp->p.realt = tbuf.realt; 893 tcpu += tbuf.cput; 894 tp->p.cput = tbuf.cput; 895 tsys += tbuf.syst; 896 tp->p.syst = tbuf.syst; 897 tio += tbuf.io; 898 tp->p.io = tbuf.io; 899 timem += tbuf.imem; 900 tp->p.imem = tbuf.imem; 901 } 902 fclose(f); 903 gshm: 904 if ((f = fopen(usracct, "r")) == NULL) 905 return; 906 for(uid = 0; 907 fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1; 908 uid++){ 909 if (userbuf.us_cnt){ 910 up = finduser(uid); 911 if (up == 0) 912 continue; /* preposterous user id */ 913 up->oldu = userbuf.oldu; 914 } 915 } 916 fclose(f); 917 } 918 919 strip() 920 { 921 int c; 922 register struct allocbox *allocwalk; 923 register cell *tp, *ub, *junkp; 924 925 if (fflg) 926 printf("Categorizing commands used %d times or fewer as **junk**\n", 927 thres); 928 junkp = enter("**junk**"); 929 PROCESSITERATE(allocwalk, tp, ub){ 930 if (tp->p.name[0] && tp->p.count <= thres) { 931 if (!fflg) 932 printf("%.14s--", tp->p.name); 933 if (fflg || ((c=getchar())=='y')) { 934 tp->p.name[0] = '\0'; 935 junkp->p.count += tp->p.count; 936 junkp->p.realt += tp->p.realt; 937 junkp->p.cput += tp->p.cput; 938 junkp->p.syst += tp->p.syst; 939 junkp->p.imem += tp->p.imem; 940 junkp->p.io += tp->p.io; 941 } 942 if (!fflg) 943 while (c && c!='\n') 944 c = getchar(); 945 } 946 } 947 } 948 949 time_t 950 expand(t) 951 unsigned t; 952 { 953 register time_t nt; 954 955 nt = t&017777; 956 t >>= 13; 957 while (t!=0) { 958 t--; 959 nt <<= 3; 960 } 961 return(nt); 962 } 963 964 static char UserKey[NAMELG + 2]; 965 966 char * 967 makekey(uid) 968 int uid; 969 { 970 (void)sprintf(UserKey+1, "%04x", uid); 971 UserKey[0] = USERKEY; 972 return(UserKey); 973 } 974 975 struct user * 976 wasuser(uid) 977 int uid; 978 { 979 struct user *tp; 980 981 htabinstall = 0; 982 tp = finduser(uid); 983 htabinstall = 1; 984 return(tp); 985 } 986 987 /* 988 * Only call this if you really want to insert it in the table! 989 */ 990 struct user * 991 finduser(uid) 992 int uid; 993 { 994 995 if (uid > maxuser){ 996 fprintf(stderr, "Preposterous user id, %d: ignored\n", uid); 997 return(0); 998 } 999 return((struct user*)enter(makekey(uid))); 1000 } 1001 1002 /* 1003 * Set the names of all users in the password file. 1004 * We will later not print those that didn't do anything. 1005 */ 1006 getnames() 1007 { 1008 register struct user *tp; 1009 register struct passwd *pw; 1010 struct passwd *getpwent(); 1011 1012 setpwent(); 1013 while (pw = getpwent()){ 1014 /* use first name in passwd file for duplicate uid's */ 1015 if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0])) 1016 strncpy(tp->us_name, pw->pw_name, NAMELG); 1017 } 1018 endpwent(); 1019 } 1020 1021 int 1022 getmaxuid() 1023 { 1024 register struct user *tp; 1025 register struct passwd *pw; 1026 struct passwd *getpwent(); 1027 int maxuid = -1; 1028 1029 setpwent(); 1030 while(pw = getpwent()){ 1031 if (pw->pw_uid > maxuid) 1032 maxuid = pw->pw_uid; 1033 } 1034 endpwent(); 1035 return(maxuid); 1036 } 1037 1038 tabinit() 1039 { 1040 allochead = 0; 1041 alloctail = 0; 1042 nexttab = 0; 1043 tabsleft = 0; 1044 htab = 0; 1045 ntabs = 0; 1046 htaballoc(); /* get the first part of the hash table */ 1047 } 1048 1049 #define ALLOCQTY sizeof (struct allocbox) 1050 cell * 1051 taballoc() 1052 { 1053 1054 if (tabsleft == 0){ 1055 newbox = (struct allocbox *)calloc(1, ALLOCQTY); 1056 tabsleft = TABDALLOP; 1057 nexttab = &newbox->tabslots[0]; 1058 if (alloctail == 0){ 1059 allochead = alloctail = newbox; 1060 } else { 1061 alloctail->nextalloc = newbox; 1062 alloctail = newbox; 1063 } 1064 } 1065 --tabsleft; 1066 ++ntabs; 1067 #ifdef DEBUG 1068 if (ntabs % 100 == 0) 1069 printf("##Accounting table slot # %d\n", ntabs); 1070 #endif DEBUG 1071 return(nexttab++); 1072 } 1073 1074 htaballoc() 1075 { 1076 register struct hashdallop *new; 1077 #ifdef DEBUG 1078 static int ntables = 0; 1079 1080 printf("%%%New hash table chunk allocated, number %d\n", ++ntables); 1081 #endif DEBUG 1082 new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop)); 1083 if (htab == 0) 1084 htab = new; 1085 else { /* add AFTER the 1st slot */ 1086 new->h_next = htab->h_next; 1087 htab->h_next = new; 1088 } 1089 } 1090 1091 #define HASHCLOGGED (NHASH / 2) 1092 /* 1093 * Lookup a symbol passed in as the argument. 1094 * 1095 * We take pains to avoid function calls; this function 1096 * is called quite frequently, and the calling overhead 1097 * contributes significantly to the overall execution speed of sa. 1098 */ 1099 cell * 1100 enter(name) 1101 char *name; 1102 { 1103 static int initialprobe; 1104 register cell **hp; 1105 register char *from, *to; 1106 register int len, nprobes; 1107 static struct hashdallop *hdallop, *emptyhd; 1108 static cell **emptyslot, **hp_ub; 1109 1110 emptyslot = 0; 1111 for (nprobes = 0, from = name, len = 0; 1112 *from && len < NC; 1113 nprobes <<= 2, nprobes += *from++, len++) 1114 continue; 1115 nprobes += from[-1] << 5; 1116 nprobes %= NHASH; 1117 if (nprobes < 0) 1118 nprobes += NHASH; 1119 1120 initialprobe = nprobes; 1121 for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){ 1122 for (hp = &(hdallop->h_tab[initialprobe]), 1123 nprobes = 1, 1124 hp_ub = &(hdallop->h_tab[NHASH]); 1125 (*hp) && (nprobes < NHASH); 1126 hp += nprobes, 1127 hp -= (hp >= hp_ub) ? NHASH:0, 1128 nprobes += 2) 1129 { 1130 from = name; 1131 to = (*hp)->p.name; 1132 1133 for (len = 0; (len<NC) && *from; len++) 1134 if (*from++ != *to++) 1135 goto nextprobe; 1136 if (len >= NC) /*both are maximal length*/ 1137 return(*hp); 1138 if (*to == 0) /*assert *from == 0*/ 1139 return(*hp); 1140 nextprobe: ; 1141 } 1142 if (*hp == 0 && emptyslot == 0 && 1143 hdallop->h_nused < HASHCLOGGED) { 1144 emptyslot = hp; 1145 emptyhd = hdallop; 1146 } 1147 } 1148 if (emptyslot == 0) { 1149 htaballoc(); 1150 hdallop = htab->h_next; /* aren't we smart! */ 1151 hp = &hdallop->h_tab[initialprobe]; 1152 } else { 1153 hdallop = emptyhd; 1154 hp = emptyslot; 1155 } 1156 if (htabinstall){ 1157 *hp = taballoc(); 1158 hdallop->h_nused++; 1159 for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++) 1160 if ((*to++ = *from++) == '\0') 1161 break; 1162 return(*hp); 1163 } 1164 return(0); 1165 } 1166