1: #ifndef lint 2: static char *sccsid = "@(#)sa.c 4.9 (Berkeley) 12/12/84"; 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: 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: }