|
|
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.