1 #ifndef lint 2 static char *sccsid = "@(#)sa.c 4.3 (Berkeley) 83/04/04"; 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 <sys/types.h> 22 #include <sys/acct.h> 23 #include <signal.h> 24 #include <utmp.h> 25 #include <pwd.h> 26 27 /* interpret command time accounting */ 28 29 #define NC sizeof(acctbuf.ac_comm) 30 31 struct acct acctbuf; 32 int lflg; 33 int cflg; 34 int Dflg; 35 int dflg; 36 int iflg; 37 int jflg; 38 int Kflg; 39 int kflg; 40 int nflg; 41 int aflg; 42 int rflg; 43 int oflg; 44 int tflg; 45 int vflg; 46 int fflg; 47 int uflg; 48 int thres; 49 int sflg; 50 int bflg; 51 int mflg; 52 53 struct utmp utmp; 54 #define NAMELG (sizeof(utmp.ut_name)+1) 55 56 struct Olduser{ 57 int Us_cnt; 58 double Us_ctime; 59 double Us_io; 60 double Us_imem; 61 }; 62 63 struct user { 64 char name[NC]; /* this is <\001><user id><\000> */ 65 struct Olduser oldu; 66 char us_name[NAMELG]; 67 }; 68 #define us_cnt oldu.Us_cnt 69 #define us_ctime oldu.Us_ctime 70 #define us_io oldu.Us_io 71 #define us_imem oldu.Us_imem 72 73 /* 74 * We protect ourselves from preposterous user id's by looking 75 * through the passwd file for the highest uid allocated, and 76 * then adding 10 to that. 77 * This prevents the user structure from growing too large. 78 */ 79 #define USERSLOP 10 80 int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/ 81 82 struct process { 83 char name[NC]; 84 int count; 85 double realt; 86 double cput; 87 double syst; 88 double imem; 89 double io; 90 }; 91 92 union Tab{ 93 struct process p; 94 struct user u; 95 }; 96 97 typedef union Tab cell; 98 99 int (*cmp)(); /* compares 2 cells; set to appropriate func */ 100 cell *enter(); 101 struct user *finduser(); 102 struct user *wasuser(); 103 104 /* 105 * Table elements are keyed by the name of the file exec'ed. 106 * Because on large systems, many files can be exec'ed, 107 * a static table size may grow to be too large. 108 * 109 * Table elements are allocated in chunks dynamically, linked 110 * together so that they may be retrieved sequentially. 111 * 112 * An index into the table structure is provided by hashing through 113 * a seperate hash table. 114 * The hash table is segmented, and dynamically extendable. 115 * Realize that the hash table and accounting information is kept 116 * in different segments! 117 * 118 * We have a linked list of hash table segments; within each 119 * segment we use a quadratic rehash that touches no more than 1/2 120 * of the buckets in the hash table when probing. 121 * If the probe does not find the desired symbol, it moves to the 122 * next segment, or allocates a new segment. 123 * 124 * Hash table segments are kept on the linked list with the first 125 * segment always first (that will probably contain the 126 * most frequently executed commands) and 127 * the last added segment immediately after the first segment, 128 * to hopefully gain something by locality of reference. 129 * 130 * We store the per user information in the same structure as 131 * the per exec'ed file information. This allows us to use the 132 * same managers for both, as the number of user id's may be very 133 * large. 134 * User information is keyed by the first character in the name 135 * being a '\001', followed by four bytes of (long extended) 136 * user id number, followed by a null byte. 137 * The actual user names are kept in a seperate field of the 138 * user structure, and is filled in upon demand later. 139 * Iteration through all users by low user id to high user id 140 * is done by just probing the table, which is gross. 141 */ 142 #define USERKEY '\001' 143 #define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY)) 144 #define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY)) 145 146 #define TABDALLOP 500 147 struct allocbox{ 148 struct allocbox *nextalloc; 149 cell tabslots[TABDALLOP]; 150 }; 151 152 struct allocbox *allochead; /*head of chunk list*/ 153 struct allocbox *alloctail; /*tail*/ 154 struct allocbox *newbox; /*for creating a new chunk*/ 155 cell *nexttab; /*next table element that is free*/ 156 int tabsleft; /*slots left in current chunk*/ 157 int ntabs; 158 /* 159 * Iterate through all symbols in the symbol table in declaration 160 * order. 161 * struct allocbox *allocwalk; 162 * cell *sp, *ub; 163 * 164 * sp points to the desired item, allocwalk and ub are there 165 * to make the iteration go. 166 */ 167 168 #define DECLITERATE(allocwalk, walkpointer, ubpointer) \ 169 for(allocwalk = allochead; \ 170 allocwalk != 0; \ 171 allocwalk = allocwalk->nextalloc) \ 172 for (walkpointer = &allocwalk->tabslots[0],\ 173 ubpointer = &allocwalk->tabslots[TABDALLOP], \ 174 ubpointer = ubpointer > ( (cell *)alloctail) \ 175 ? nexttab : ubpointer ;\ 176 walkpointer < ubpointer; \ 177 walkpointer++ ) 178 179 #define TABCHUNKS(allocwalk, tabptr, size) \ 180 for (allocwalk = allochead; \ 181 allocwalk != 0; \ 182 allocwalk = allocwalk->nextalloc) \ 183 if ( \ 184 (tabptr = &allocwalk->tabslots[0]), \ 185 (size = \ 186 ( (&allocwalk->tabslots[TABDALLOP]) \ 187 > ((cell *)alloctail) \ 188 ) \ 189 ? (nexttab - tabptr) : TABDALLOP \ 190 ), \ 191 1 \ 192 ) 193 #define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \ 194 DECLITERATE(allocwalk, walkpointer, ubpointer) \ 195 if (ISPROCESS(walkpointer)) 196 197 #define USERITERATE(allocwalk, walkpointer, ubpointer) \ 198 DECLITERATE(allocwalk, walkpointer, ubpointer) \ 199 if (ISUSER(walkpointer)) 200 /* 201 * When we have to sort the segmented accounting table, we 202 * create a vector of sorted queues that is merged 203 * to sort the entire accounting table. 204 */ 205 struct chunkdesc { 206 cell *chunk_tp; 207 int chunk_n; 208 }; 209 210 /* 211 * Hash table segments and manager 212 */ 213 #define NHASH 1103 214 struct hashdallop { 215 int h_nused; 216 struct hashdallop *h_next; 217 cell *h_tab[NHASH]; 218 }; 219 struct hashdallop *htab; /* head of the list */ 220 int htabinstall; /* install the symbol */ 221 222 double treal; 223 double tcpu; 224 double tsys; 225 double tio; 226 double timem; 227 cell *junkp; 228 char *sname; 229 double ncom; 230 time_t expand(); 231 char *getname(); 232 233 /* 234 * usracct saves records of type Olduser. 235 * There is one record for every possible uid less than 236 * the largest uid seen in the previous usracct or in savacct. 237 * uid's that had no activity correspond to zero filled slots; 238 * thus one can index the file and get the user record out. 239 * It would be better to save only user information for users 240 * that the system knows about to save space, but that is not 241 * upward compatabile with the old system. 242 * 243 * In the old version of sa, uid's greater than 999 were not handled 244 * properly; this system will do that. 245 */ 246 247 #ifdef DEBUG 248 #define USRACCT "./usracct" 249 #define SAVACCT "./savacct" 250 #define ACCT "./acct" 251 #else 252 #define USRACCT "/usr/adm/usracct" 253 #define SAVACCT "/usr/adm/savacct" 254 #define ACCT "/usr/adm/acct" 255 #endif DEBUG 256 257 258 int cellcmp(); 259 cell *junkp = 0; 260 /* 261 * The threshold is built up from digits in the argv ; 262 * eg, -v1s0u1 263 * will build a value of thres of 101. 264 * 265 * If the threshold is zero after processing argv, it is set to 1 266 */ 267 int thres = 0; 268 int htabinstall = 1; 269 int maxuser = -1; 270 int (*cmp)(); 271 272 extern tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp(); 273 extern double sum(); 274 275 main(argc, argv) 276 char **argv; 277 { 278 FILE *ff; 279 double ft; 280 register struct allocbox *allocwalk; 281 register cell *tp, *ub; 282 int i, j, size, nchunks, smallest; 283 struct chunkdesc *chunkvector; 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, y, z, fbuf.ac_comm); 638 continue; 639 } 640 up = finduser(fbuf.ac_uid); 641 if (up == 0) 642 continue; /* preposterous user id */ 643 up->us_cnt++; 644 up->us_ctime += x; 645 up->us_imem += x * y; 646 up->us_io += z; 647 ncom += 1.0; 648 649 tp = enter(fbuf.ac_comm); 650 tp->p.imem += x * y; 651 timem += x * y; 652 tp->p.count++; 653 x = expand(fbuf.ac_etime); 654 tp->p.realt += x; 655 treal += x; 656 x = expand(fbuf.ac_utime); 657 tp->p.cput += x; 658 tcpu += x; 659 x = expand(fbuf.ac_stime); 660 tp->p.syst += x; 661 tsys += x; 662 tp->p.io += z; 663 tio += z; 664 } 665 fclose(ff); 666 } 667 668 /* 669 * Generalized cell compare routine, to cast out users 670 */ 671 cellcmp(p1, p2) 672 cell *p1, *p2; 673 { 674 if (ISPROCESS(p1)){ 675 if (ISPROCESS(p2)) 676 return(cmp(p1, p2)); 677 return(-1); 678 } 679 if (ISPROCESS(p2)) 680 return(1); 681 return(0); 682 } 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 910 char * 911 makekey(uid) 912 int uid; 913 { 914 sprintf(UserKey+1, "%04x", uid); 915 UserKey[0] = USERKEY; 916 return(UserKey); 917 } 918 919 struct user * 920 wasuser(uid) 921 int uid; 922 { 923 struct user *tp; 924 925 htabinstall = 0; 926 tp = finduser(uid); 927 htabinstall = 1; 928 return(tp); 929 } 930 931 /* 932 * Only call this if you really want to insert it in the table! 933 */ 934 struct user * 935 finduser(uid) 936 int uid; 937 { 938 939 if (uid > maxuser){ 940 fprintf(stderr, "Preposterous user id, %d: ignored\n", uid); 941 return(0); 942 } 943 return((struct user*)enter(makekey(uid))); 944 } 945 946 /* 947 * Set the names of all users in the password file. 948 * We will later not print those that didn't do anything. 949 */ 950 getnames() 951 { 952 register struct user *tp; 953 register struct passwd *pw; 954 struct passwd *getpwent(); 955 956 setpwent(); 957 while (pw = getpwent()){ 958 if ( (tp = wasuser(pw->pw_uid)) != 0) 959 strncpy(tp->us_name, pw->pw_name, NAMELG); 960 } 961 endpwent(); 962 } 963 964 int 965 getmaxuid() 966 { 967 register struct user *tp; 968 register struct passwd *pw; 969 struct passwd *getpwent(); 970 int maxuid = -1; 971 972 setpwent(); 973 while(pw = getpwent()){ 974 if (pw->pw_uid > maxuid) 975 maxuid = pw->pw_uid; 976 } 977 endpwent(); 978 return(maxuid); 979 } 980 981 tabinit() 982 { 983 allochead = 0; 984 alloctail = 0; 985 nexttab = 0; 986 tabsleft = 0; 987 htab = 0; 988 ntabs = 0; 989 htaballoc(); /* get the first part of the hash table */ 990 } 991 992 #define ALLOCQTY sizeof (struct allocbox) 993 cell * 994 taballoc() 995 { 996 997 if (tabsleft == 0){ 998 newbox = (struct allocbox *)calloc(1, ALLOCQTY); 999 tabsleft = TABDALLOP; 1000 nexttab = &newbox->tabslots[0]; 1001 if (alloctail == 0){ 1002 allochead = alloctail = newbox; 1003 } else { 1004 alloctail->nextalloc = newbox; 1005 alloctail = newbox; 1006 } 1007 } 1008 --tabsleft; 1009 ++ntabs; 1010 #ifdef DEBUG 1011 if (ntabs % 100 == 0) 1012 printf("##Accounting table slot # %d\n", ntabs); 1013 #endif DEBUG 1014 return(nexttab++); 1015 } 1016 1017 htaballoc() 1018 { 1019 register struct hashdallop *new; 1020 #ifdef DEBUG 1021 static int ntables = 0; 1022 1023 printf("%%%New hash table chunk allocated, number %d\n", ++ntables); 1024 #endif DEBUG 1025 new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop)); 1026 if (htab == 0) 1027 htab = new; 1028 else { /* add AFTER the 1st slot */ 1029 new->h_next = htab->h_next; 1030 htab->h_next = new; 1031 } 1032 } 1033 1034 #define HASHCLOGGED (NHASH / 2) 1035 /* 1036 * Lookup a symbol passed in as the argument. 1037 * 1038 * We take pains to avoid function calls; this function 1039 * is called quite frequently, and the calling overhead 1040 * contributes significantly to the overall execution speed of sa. 1041 */ 1042 cell * 1043 enter(name) 1044 char *name; 1045 { 1046 static int initialprobe; 1047 register cell **hp; 1048 register char *from, *to; 1049 register int len, nprobes; 1050 static struct hashdallop *hdallop, *emptyhd; 1051 static cell **emptyslot, **hp_ub; 1052 1053 emptyslot = 0; 1054 for (nprobes = 0, from = name, len = 0; 1055 *from && len < NC; 1056 nprobes <<= 2, nprobes += *from++, len++) 1057 continue; 1058 nprobes += from[-1] << 5; 1059 nprobes %= NHASH; 1060 if (nprobes < 0) 1061 nprobes += NHASH; 1062 1063 initialprobe = nprobes; 1064 for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){ 1065 for (hp = &(hdallop->h_tab[initialprobe]), 1066 nprobes = 1, 1067 hp_ub = &(hdallop->h_tab[NHASH]); 1068 (*hp) && (nprobes < NHASH); 1069 hp += nprobes, 1070 hp -= (hp >= hp_ub) ? NHASH:0, 1071 nprobes += 2) 1072 { 1073 from = name; 1074 to = (*hp)->p.name; 1075 1076 for (len = 0; (len<NC) && *from; len++) 1077 if (*from++ != *to++) 1078 goto nextprobe; 1079 if (len >= NC) /*both are maximal length*/ 1080 return(*hp); 1081 if (*to == 0) /*assert *from == 0*/ 1082 return(*hp); 1083 nextprobe: ; 1084 } 1085 if (*hp == 0 && emptyslot == 0 && 1086 hdallop->h_nused < HASHCLOGGED) { 1087 emptyslot = hp; 1088 emptyhd = hdallop; 1089 } 1090 } 1091 if (emptyslot == 0) { 1092 htaballoc(); 1093 hdallop = htab->h_next; /* aren't we smart! */ 1094 hp = &hdallop->h_tab[initialprobe]; 1095 } else { 1096 hdallop = emptyhd; 1097 hp = emptyslot; 1098 } 1099 if (htabinstall){ 1100 *hp = taballoc(); 1101 hdallop->h_nused++; 1102 for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++) 1103 if ((*to++ = *from++) == '\0') 1104 break; 1105 return(*hp); 1106 } 1107 return(0); 1108 } 1109