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