1: #if defined(DOSCCS) && !defined(lint) 2: static char *sccsid = "@(#)sa.c 4.9.2 (2.11BSD GTE) 1997/2/14"; 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 added the -f flag, to force no interactive threshold 10: * compression with the -v flag. 11: * 12: * Robert Henry 13: * UC Berkeley 14: * 31jan81 15: */ 16: 17: #include <stdio.h> 18: #include <ctype.h> 19: #include <sys/types.h> 20: #include <sys/acct.h> 21: #include <signal.h> 22: #include <utmp.h> 23: #include <pwd.h> 24: #include <sysexits.h> 25: #include <stdlib.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: uid_t 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: uid_t getmaxuid(); 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: double ncom; 230: time_t expand(); 231: 232: /* 233: * usracct saves records of type Olduser. 234: * There is one record for every possible uid less than 235: * the largest uid seen in the previous usracct or in savacct. 236: * uid's that had no activity correspond to zero filled slots; 237: * thus one can index the file and get the user record out. 238: * It would be better to save only user information for users 239: * that the system knows about to save space, but that is not 240: * upward compatabile with the old system. 241: * 242: * In the old version of sa, uid's greater than 999 were not handled 243: * properly; this system will do that. 244: */ 245: 246: #ifdef DEBUG 247: #define USRACCT "./usracct" 248: #define SAVACCT "./savacct" 249: #define ACCT "./acct" 250: #else 251: #define USRACCT "/usr/adm/usracct" 252: #define SAVACCT "/usr/adm/savacct" 253: #define ACCT "/usr/adm/acct" 254: #endif DEBUG 255: 256: char *usracct = USRACCT; 257: char *savacct = SAVACCT; 258: 259: int cellcmp(); 260: cell *junkp = 0; 261: int thres = 0; 262: int htabinstall = 1; 263: int (*cmp)(); 264: 265: /* we assume pagesize is at least 1k */ 266: int pgdiv; 267: #define pgtok(x) ((x) / pgdiv) 268: 269: extern tcmp(), ncmp(), Bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp(); 270: extern double sum(); 271: 272: main(argc, argv) 273: char **argv; 274: { 275: FILE *ff; 276: double ft; 277: register struct allocbox *allocwalk; 278: register cell *tp, *ub; 279: char *acctfn; 280: int i, j, size, nchunks, smallest, c; 281: struct chunkdesc *chunkvector; 282: 283: pgdiv = getpagesize() / 1024; 284: if (pgdiv == 0) 285: pgdiv = 1; 286: maxuser = getmaxuid(); 287: 288: tabinit(); 289: cmp = tcmp; 290: 291: while ((c = getopt(argc, argv, "oiblcdDjkKnartsv:fumU:S:")) != EOF) 292: { 293: switch (c) 294: { 295: case 'o': 296: oflg++; 297: break; 298: case 'i': 299: iflg++; 300: break; 301: case 'b': 302: bflg++; 303: cmp = Bcmp; 304: break; 305: case 'l': 306: lflg++; 307: break; 308: case 'c': 309: cflg++; 310: break; 311: case 'd': 312: dflg++; 313: cmp = dcmp; 314: break; 315: case 'D': 316: Dflg++; 317: cmp = Dcmp; 318: break; 319: case 'j': 320: jflg++; 321: break; 322: case 'k': 323: kflg++; 324: cmp = kcmp; 325: break; 326: case 'K': 327: Kflg++; 328: cmp = Kcmp; 329: break; 330: case 'n': 331: nflg++; 332: cmp = ncmp; 333: break; 334: case 'a': 335: aflg++; 336: break; 337: case 'r': 338: rflg++; 339: break; 340: case 't': 341: tflg++; 342: break; 343: case 's': 344: sflg++; 345: aflg++; 346: break; 347: case 'v': 348: vflg++; 349: thres = atoi(optarg); 350: break; 351: case 'f': 352: fflg++; /* force v option; no tty interaction */ 353: break; 354: case 'u': 355: uflg++; 356: break; 357: case 'm': 358: mflg++; 359: break; 360: case 'U': 361: usracct = optarg; 362: break; 363: case 'S': 364: savacct = optarg; 365: break; 366: default: 367: (void)usage(); 368: /* NOTREACHED */ 369: } 370: } 371: switch (argc - optind) 372: { 373: case 1: 374: acctfn = argv[optind]; 375: break; 376: case 0: 377: acctfn = ACCT; 378: break; 379: default: 380: (void)usage(); 381: /* NOTREACHED */ 382: } 383: 384: if (thres == 0) 385: thres = 1; 386: if (iflg==0) 387: init(); 388: doacct(acctfn); 389: if (uflg) 390: return; 391: 392: /* 393: * cleanup pass 394: * put junk together 395: */ 396: 397: if (vflg) 398: strip(); 399: if(!aflg) 400: PROCESSITERATE(allocwalk, tp, ub){ 401: for(j=0; j<NC; j++) 402: if(tp->p.name[j] == '?') 403: goto yes; 404: if(tp->p.count != 1) 405: continue; 406: yes: 407: if(junkp == 0) 408: junkp = enter("***other"); 409: junkp->p.count += tp->p.count; 410: junkp->p.realt += tp->p.realt; 411: junkp->p.cput += tp->p.cput; 412: junkp->p.syst += tp->p.syst; 413: junkp->p.imem += tp->p.imem; 414: junkp->p.io += tp->p.io; 415: tp->p.name[0] = 0; 416: } 417: if (sflg) { 418: signal(SIGINT, SIG_IGN); 419: if ((ff = fopen(usracct, "w")) != NULL) { 420: static struct user ZeroUser = {0}; 421: struct user *up; 422: uid_t uid; 423: /* 424: * Write out just enough user slots, 425: * filling with zero slots for users that 426: * weren't found. 427: * The file can be indexed directly by uid 428: * to get the correct record. 429: */ 430: for (uid = 0; uid < maxuser; uid++){ 431: if ( (up = wasuser(uid)) != 0) 432: fwrite((char *)&(up->oldu), 433: sizeof(struct Olduser),1,ff); 434: else 435: fwrite((char *)&(ZeroUser.oldu), 436: sizeof(struct Olduser),1,ff); 437: } 438: } 439: if ((ff = fopen(savacct, "w")) == NULL) { 440: printf("Can't save\n"); 441: exit(EX_OK); 442: } 443: PROCESSITERATE(allocwalk, tp, ub) 444: fwrite((char *)&(tp->p), sizeof(struct process), 1, ff); 445: fclose(ff); 446: signal(SIGINT, SIG_DFL); 447: } 448: /* 449: * sort and print 450: */ 451: if (mflg) { 452: printmoney(); 453: exit(EX_OK); 454: } 455: column(ncom, treal, tcpu, tsys, timem, tio); 456: printf("\n"); 457: 458: /* 459: * the fragmented table is sorted by sorting each fragment 460: * and then merging. 461: */ 462: nchunks = 0; 463: TABCHUNKS(allocwalk, tp, size){ 464: qsort(tp, size, sizeof(cell), cellcmp); 465: nchunks ++; 466: } 467: chunkvector = (struct chunkdesc *)calloc(nchunks, 468: sizeof(struct chunkdesc)); 469: nchunks = 0; 470: TABCHUNKS(allocwalk, tp, size){ 471: chunkvector[nchunks].chunk_tp = tp; 472: chunkvector[nchunks].chunk_n = size; 473: nchunks++; 474: } 475: for(; nchunks; ){ 476: /* 477: * Find the smallest element at the head of the queues. 478: */ 479: smallest = 0; 480: for (i = 1; i < nchunks; i++){ 481: if (cellcmp(chunkvector[i].chunk_tp, 482: chunkvector[smallest].chunk_tp) < 0) 483: smallest = i; 484: } 485: tp = chunkvector[smallest].chunk_tp++; 486: /* 487: * If this queue is drained, drop the chunk count, 488: * and readjust the queues. 489: */ 490: if (--chunkvector[smallest].chunk_n == 0){ 491: nchunks--; 492: for (i = smallest; i < nchunks; i++) 493: chunkvector[i] = chunkvector[i+1]; 494: } 495: if (ISPROCESS(tp)){ 496: ft = tp->p.count; 497: column(ft, tp->p.realt, tp->p.cput, 498: tp->p.syst, tp->p.imem, tp->p.io); 499: printf(" %.14s\n", tp->p.name); 500: } 501: } /* iterate to merge the lists */ 502: } 503: 504: void 505: usage() 506: { 507: fprintf(stderr, "Usage sa [-oiblcdDjkKnartsfum] [-S savacct] [-U usracct] [file]\n"); 508: exit(EX_USAGE); 509: } 510: 511: printmoney() 512: { 513: register uid_t uid; 514: register char *cp; 515: register struct user *up; 516: 517: getnames(); /* fetches all of the names! */ 518: for (uid = 0; uid < maxuser; uid++) { 519: if ( (up = wasuser(uid)) != 0){ 520: if (up->us_cnt) { 521: if (up->us_name[0]) 522: printf("%-8s", up->us_name); 523: else 524: printf("%-8u", uid); 525: printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n", 526: up->us_cnt, up->us_ctime / 60, 527: up->us_io, 528: up->us_imem / AHZ); 529: } 530: } 531: } 532: } 533: 534: column(n, a, b, c, d, e) 535: double n, a, b, c, d, e; 536: { 537: 538: printf("%8.0f", n); 539: if(cflg) { 540: if(n == ncom) 541: printf("%9s", ""); else 542: printf("%8.2f%%", 100.*n/ncom); 543: } 544: col(n, a, treal, "re"); 545: if (oflg) 546: col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s"); 547: else if(lflg) { 548: col(n, b, tcpu, "u"); 549: col(n, c, tsys, "s"); 550: } else 551: col(n, b+c, tcpu+tsys, "cp"); 552: if(tflg) 553: printf("%8.1fre/cp", a/(b+c)); 554: if(dflg || !Dflg) 555: printf("%10.0favio", e/(n?n:1)); 556: else 557: printf("%10.0ftio", e); 558: if (kflg || !Kflg) 559: printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0)); 560: else 561: printf("%10.0fk*sec", d/AHZ); 562: } 563: 564: col(n, a, m, cp) 565: double n, a, m; 566: char *cp; 567: { 568: 569: if(jflg) 570: printf("%11.2f%s", a/(n*(double)AHZ), cp); else 571: printf("%11.2f%s", a/(60.*(double)AHZ), cp); 572: if(cflg) { 573: if(a == m) 574: printf("%9s", ""); else 575: printf("%8.2f%%", 100.*a/m); 576: } 577: } 578: 579: doacct(f) 580: char *f; 581: { 582: FILE *ff; 583: long x, y, z; 584: struct acct fbuf; 585: register char *cp; 586: register int c; 587: register struct user *up; 588: register cell *tp; 589: #ifdef DEBUG 590: int nrecords = 0; 591: #endif DEBUG 592: 593: if ((ff = fopen(f, "r"))==NULL) { 594: fprintf(stderr, "Can't open %s\n", f); 595: return; 596: } 597: while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) { 598: #ifdef DEBUG 599: if (++nrecords % 1000 == 0) 600: fprintf(stderr, "Input record from %s number %d\n", 601: f, nrecords); 602: #endif DEBUG 603: for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++) 604: if (!isascii(*cp) || iscntrl(*cp)) 605: *cp = '?'; 606: if (cp == fbuf.ac_comm) 607: *cp++ = '?'; 608: if (fbuf.ac_flag&AFORK) { 609: if (cp >= &fbuf.ac_comm[NC]) 610: cp = &fbuf.ac_comm[NC-1]; 611: *cp++ = '*'; 612: } 613: if (cp < &fbuf.ac_comm[NC]) 614: *cp = '\0'; 615: x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime); 616: y = pgtok((u_short)fbuf.ac_mem); 617: z = expand(fbuf.ac_io) / AHZ; 618: if (uflg) { 619: printf("%3u %6.2f cpu %8luk mem %6ld io %.*s\n", 620: fbuf.ac_uid, x/(double)AHZ, y, z, NC, fbuf.ac_comm); 621: continue; 622: } 623: up = finduser(fbuf.ac_uid); 624: if (up == 0) 625: continue; /* preposterous user id */ 626: up->us_cnt++; 627: up->us_ctime += x/(double)AHZ; 628: up->us_imem += x * y; 629: up->us_io += z; 630: ncom += 1.0; 631: 632: tp = enter(fbuf.ac_comm); 633: tp->p.imem += x * y; 634: timem += x * y; 635: tp->p.count++; 636: x = expand(fbuf.ac_etime); 637: tp->p.realt += x; 638: treal += x; 639: x = expand(fbuf.ac_utime); 640: tp->p.cput += x; 641: tcpu += x; 642: x = expand(fbuf.ac_stime); 643: tp->p.syst += x; 644: tsys += x; 645: tp->p.io += z; 646: tio += z; 647: } 648: fclose(ff); 649: } 650: 651: /* 652: * Generalized cell compare routine, to cast out users 653: */ 654: cellcmp(p1, p2) 655: register cell *p1, *p2; 656: { 657: if (ISPROCESS(p1)){ 658: if (ISPROCESS(p2)) 659: return((*cmp)(p1, p2)); 660: return(-1); 661: } 662: if (ISPROCESS(p2)) 663: return(1); 664: return(0); 665: } 666: 667: ncmp(p1, p2) 668: register cell *p1, *p2; 669: { 670: 671: if(p1->p.count == p2->p.count) 672: return(tcmp(p1, p2)); 673: if(rflg) 674: return(p1->p.count - p2->p.count); 675: return(p2->p.count - p1->p.count); 676: } 677: 678: Bcmp(p1, p2) 679: cell *p1, *p2; 680: { 681: double f1, f2; 682: double sum(); 683: 684: f1 = sum(p1)/p1->p.count; 685: f2 = sum(p2)/p2->p.count; 686: if(f1 < f2) { 687: if(rflg) 688: return(-1); 689: return(1); 690: } 691: if(f1 > f2) { 692: if(rflg) 693: return(1); 694: return(-1); 695: } 696: return(0); 697: } 698: 699: Kcmp(p1, p2) 700: register cell *p1, *p2; 701: { 702: 703: if (p1->p.imem < p2->p.imem) { 704: if(rflg) 705: return(-1); 706: return(1); 707: } 708: if (p1->p.imem > p2->p.imem) { 709: if(rflg) 710: return(1); 711: return(-1); 712: } 713: return(0); 714: } 715: 716: kcmp(p1, p2) 717: register cell *p1, *p2; 718: { 719: double a1, a2; 720: 721: a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1); 722: a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1); 723: if (a1 < a2) { 724: if(rflg) 725: return(-1); 726: return(1); 727: } 728: if (a1 > a2) { 729: if(rflg) 730: return(1); 731: return(-1); 732: } 733: return(0); 734: } 735: 736: dcmp(p1, p2) 737: register cell *p1, *p2; 738: { 739: double a1, a2; 740: 741: a1 = p1->p.io / (p1->p.count?p1->p.count:1); 742: a2 = p2->p.io / (p2->p.count?p2->p.count:1); 743: if (a1 < a2) { 744: if(rflg) 745: return(-1); 746: return(1); 747: } 748: if (a1 > a2) { 749: if(rflg) 750: return(1); 751: return(-1); 752: } 753: return(0); 754: } 755: 756: Dcmp(p1, p2) 757: register cell *p1, *p2; 758: { 759: 760: if (p1->p.io < p2->p.io) { 761: if(rflg) 762: return(-1); 763: return(1); 764: } 765: if (p1->p.io > p2->p.io) { 766: if(rflg) 767: return(1); 768: return(-1); 769: } 770: return(0); 771: } 772: 773: tcmp(p1, p2) 774: cell *p1, *p2; 775: { 776: extern double sum(); 777: double f1, f2; 778: 779: f1 = sum(p1); 780: f2 = sum(p2); 781: if(f1 < f2) { 782: if(rflg) 783: return(-1); 784: return(1); 785: } 786: if(f1 > f2) { 787: if(rflg) 788: return(1); 789: return(-1); 790: } 791: return(0); 792: } 793: 794: double sum(p) 795: cell *p; 796: { 797: 798: if(p->p.name[0] == 0) 799: return(0.0); 800: return( p->p.cput + p->p.syst); 801: } 802: 803: init() 804: { 805: struct user userbuf; 806: struct process tbuf; 807: register cell *tp; 808: register struct user *up; 809: uid_t uid; 810: FILE *f; 811: 812: if ((f = fopen(savacct, "r")) == NULL) 813: goto gshm; 814: while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) { 815: tp = enter(tbuf.name); 816: ncom += tbuf.count; 817: tp->p.count = tbuf.count; 818: treal += tbuf.realt; 819: tp->p.realt = tbuf.realt; 820: tcpu += tbuf.cput; 821: tp->p.cput = tbuf.cput; 822: tsys += tbuf.syst; 823: tp->p.syst = tbuf.syst; 824: tio += tbuf.io; 825: tp->p.io = tbuf.io; 826: timem += tbuf.imem; 827: tp->p.imem = tbuf.imem; 828: } 829: fclose(f); 830: gshm: 831: if ((f = fopen(usracct, "r")) == NULL) 832: return; 833: for(uid = 0; 834: fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1; 835: uid++){ 836: if (userbuf.us_cnt){ 837: up = finduser(uid); 838: if (up == 0) 839: continue; /* preposterous user id */ 840: up->oldu = userbuf.oldu; 841: } 842: } 843: fclose(f); 844: } 845: 846: strip() 847: { 848: int c; 849: register struct allocbox *allocwalk; 850: register cell *tp, *ub, *junkp; 851: 852: if (fflg) 853: printf("Categorizing commands used %d times or fewer as **junk**\n", 854: thres); 855: junkp = enter("**junk**"); 856: PROCESSITERATE(allocwalk, tp, ub){ 857: if (tp->p.name[0] && tp->p.count <= thres) { 858: if (!fflg) 859: printf("%.14s--", tp->p.name); 860: if (fflg || ((c=getchar())=='y')) { 861: tp->p.name[0] = '\0'; 862: junkp->p.count += tp->p.count; 863: junkp->p.realt += tp->p.realt; 864: junkp->p.cput += tp->p.cput; 865: junkp->p.syst += tp->p.syst; 866: junkp->p.imem += tp->p.imem; 867: junkp->p.io += tp->p.io; 868: } 869: if (!fflg) 870: while (c && c!='\n') 871: c = getchar(); 872: } 873: } 874: } 875: 876: time_t 877: expand(t) 878: register unsigned int t; 879: { 880: time_t nt; 881: 882: nt = t&017777; 883: t >>= 13; 884: while (t!=0) { 885: t--; 886: nt <<= 3; 887: } 888: return(nt); 889: } 890: 891: static char UserKey[NAMELG + 2]; 892: 893: char * 894: makekey(uid) 895: uid_t uid; 896: { 897: sprintf(UserKey+1, "%04x", uid); 898: UserKey[0] = USERKEY; 899: return(UserKey); 900: } 901: 902: struct user * 903: wasuser(uid) 904: uid_t uid; 905: { 906: struct user *tp; 907: 908: htabinstall = 0; 909: tp = finduser(uid); 910: htabinstall = 1; 911: return(tp); 912: } 913: 914: /* 915: * Only call this if you really want to insert it in the table! 916: */ 917: struct user * 918: finduser(uid) 919: uid_t uid; 920: { 921: 922: if (uid > maxuser){ 923: fprintf(stderr, "Preposterous user id, %u: ignored\n", uid); 924: return(0); 925: } 926: return((struct user*)enter(makekey(uid))); 927: } 928: 929: /* 930: * Set the names of all users in the password file. 931: * We will later not print those that didn't do anything. 932: */ 933: getnames() 934: { 935: register struct user *tp; 936: register struct passwd *pw; 937: 938: setpwent(); 939: while (pw = getpwent()){ 940: /* use first name in passwd file for duplicate uid's */ 941: if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0])) 942: strncpy(tp->us_name, pw->pw_name, NAMELG); 943: } 944: endpwent(); 945: } 946: 947: uid_t 948: getmaxuid() 949: { 950: register struct user *tp; 951: register struct passwd *pw; 952: uid_t maxuid = 0; 953: 954: setpwent(); 955: while(pw = getpwent()){ 956: if (pw->pw_uid > maxuid) 957: maxuid = pw->pw_uid; 958: } 959: endpwent(); 960: return(maxuid); 961: } 962: 963: tabinit() 964: { 965: allochead = 0; 966: alloctail = 0; 967: nexttab = 0; 968: tabsleft = 0; 969: htab = 0; 970: ntabs = 0; 971: htaballoc(); /* get the first part of the hash table */ 972: } 973: 974: #define ALLOCQTY sizeof (struct allocbox) 975: cell * 976: taballoc() 977: { 978: 979: if (tabsleft == 0){ 980: newbox = (struct allocbox *)calloc(1, ALLOCQTY); 981: tabsleft = TABDALLOP; 982: nexttab = &newbox->tabslots[0]; 983: if (alloctail == 0){ 984: allochead = alloctail = newbox; 985: } else { 986: alloctail->nextalloc = newbox; 987: alloctail = newbox; 988: } 989: } 990: --tabsleft; 991: ++ntabs; 992: #ifdef DEBUG 993: if (ntabs % 100 == 0) 994: printf("##Accounting table slot # %d\n", ntabs); 995: #endif DEBUG 996: return(nexttab++); 997: } 998: 999: htaballoc() 1000: { 1001: register struct hashdallop *new; 1002: #ifdef DEBUG 1003: static int ntables = 0; 1004: 1005: printf("%%%New hash table chunk allocated, number %d\n", ++ntables); 1006: #endif DEBUG 1007: new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop)); 1008: if (htab == 0) 1009: htab = new; 1010: else { /* add AFTER the 1st slot */ 1011: new->h_next = htab->h_next; 1012: htab->h_next = new; 1013: } 1014: } 1015: 1016: #define HASHCLOGGED (NHASH / 2) 1017: /* 1018: * Lookup a symbol passed in as the argument. 1019: * 1020: * We take pains to avoid function calls; this function 1021: * is called quite frequently, and the calling overhead 1022: * contributes significantly to the overall execution speed of sa. 1023: */ 1024: cell * 1025: enter(name) 1026: char *name; 1027: { 1028: static int initialprobe; 1029: register cell **hp; 1030: register char *from, *to; 1031: register int len, nprobes; 1032: static struct hashdallop *hdallop, *emptyhd; 1033: static cell **emptyslot, **hp_ub; 1034: 1035: emptyslot = 0; 1036: for (nprobes = 0, from = name, len = 0; 1037: *from && len < NC; 1038: nprobes <<= 2, nprobes += *from++, len++) 1039: continue; 1040: nprobes += from[-1] << 5; 1041: nprobes %= NHASH; 1042: if (nprobes < 0) 1043: nprobes += NHASH; 1044: 1045: initialprobe = nprobes; 1046: for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){ 1047: for (hp = &(hdallop->h_tab[initialprobe]), 1048: nprobes = 1, 1049: hp_ub = &(hdallop->h_tab[NHASH]); 1050: (*hp) && (nprobes < NHASH); 1051: hp += nprobes, 1052: hp -= (hp >= hp_ub) ? NHASH:0, 1053: nprobes += 2) 1054: { 1055: from = name; 1056: to = (*hp)->p.name; 1057: 1058: for (len = 0; (len<NC) && *from; len++) 1059: if (*from++ != *to++) 1060: goto nextprobe; 1061: if (len >= NC) /*both are maximal length*/ 1062: return(*hp); 1063: if (*to == 0) /*assert *from == 0*/ 1064: return(*hp); 1065: nextprobe: ; 1066: } 1067: if (*hp == 0 && emptyslot == 0 && 1068: hdallop->h_nused < HASHCLOGGED) { 1069: emptyslot = hp; 1070: emptyhd = hdallop; 1071: } 1072: } 1073: if (emptyslot == 0) { 1074: htaballoc(); 1075: hdallop = htab->h_next; /* aren't we smart! */ 1076: hp = &hdallop->h_tab[initialprobe]; 1077: } else { 1078: hdallop = emptyhd; 1079: hp = emptyslot; 1080: } 1081: if (htabinstall){ 1082: *hp = taballoc(); 1083: hdallop->h_nused++; 1084: for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++) 1085: if ((*to++ = *from++) == '\0') 1086: break; 1087: return(*hp); 1088: } 1089: return(0); 1090: }