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