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