|
|
1.1 root 1: #ifndef lint
2: static char *sccsid = "@(#)sa.c 4.4 (Berkeley) 83/08/11";
3: #endif
4:
5: /*
6: * Extensive modifications to internal data structures
7: * to allow arbitrary number of different commands and users added.
8: *
9: * Also allowed the digit option on the -v flag (interactive
10: * threshold compress) to be a digit string, so one can
11: * set the threshold > 9.
12: *
13: * Also added the -f flag, to force no interactive threshold
14: * compression with the -v flag.
15: *
16: * Robert Henry
17: * UC Berkeley
18: * 31jan81
19: */
20: #include <stdio.h>
21: #include <sys/types.h>
22: #include <sys/acct.h>
23: #include <signal.h>
24: #include <utmp.h>
25: #include <pwd.h>
26:
27: /* interpret command time accounting */
28:
29: #define NC sizeof(acctbuf.ac_comm)
30:
31: struct acct acctbuf;
32: int lflg;
33: int cflg;
34: int Dflg;
35: int dflg;
36: int iflg;
37: int jflg;
38: int Kflg;
39: int kflg;
40: int nflg;
41: int aflg;
42: int rflg;
43: int oflg;
44: int tflg;
45: int vflg;
46: int fflg;
47: int uflg;
48: int thres;
49: int sflg;
50: int bflg;
51: int mflg;
52:
53: struct utmp utmp;
54: #define NAMELG (sizeof(utmp.ut_name)+1)
55:
56: struct Olduser{
57: int Us_cnt;
58: double Us_ctime;
59: double Us_io;
60: double Us_imem;
61: };
62:
63: struct user {
64: char name[NC]; /* this is <\001><user id><\000> */
65: struct Olduser oldu;
66: char us_name[NAMELG];
67: };
68: #define us_cnt oldu.Us_cnt
69: #define us_ctime oldu.Us_ctime
70: #define us_io oldu.Us_io
71: #define us_imem oldu.Us_imem
72:
73: /*
74: * We protect ourselves from preposterous user id's by looking
75: * through the passwd file for the highest uid allocated, and
76: * then adding 10 to that.
77: * This prevents the user structure from growing too large.
78: */
79: #define USERSLOP 10
80: int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/
81:
82: struct process {
83: char name[NC];
84: int count;
85: double realt;
86: double cput;
87: double syst;
88: double imem;
89: double io;
90: };
91:
92: union Tab{
93: struct process p;
94: struct user u;
95: };
96:
97: typedef union Tab cell;
98:
99: int (*cmp)(); /* compares 2 cells; set to appropriate func */
100: cell *enter();
101: struct user *finduser();
102: struct user *wasuser();
103:
104: /*
105: * Table elements are keyed by the name of the file exec'ed.
106: * Because on large systems, many files can be exec'ed,
107: * a static table size may grow to be too large.
108: *
109: * Table elements are allocated in chunks dynamically, linked
110: * together so that they may be retrieved sequentially.
111: *
112: * An index into the table structure is provided by hashing through
113: * a seperate hash table.
114: * The hash table is segmented, and dynamically extendable.
115: * Realize that the hash table and accounting information is kept
116: * in different segments!
117: *
118: * We have a linked list of hash table segments; within each
119: * segment we use a quadratic rehash that touches no more than 1/2
120: * of the buckets in the hash table when probing.
121: * If the probe does not find the desired symbol, it moves to the
122: * next segment, or allocates a new segment.
123: *
124: * Hash table segments are kept on the linked list with the first
125: * segment always first (that will probably contain the
126: * most frequently executed commands) and
127: * the last added segment immediately after the first segment,
128: * to hopefully gain something by locality of reference.
129: *
130: * We store the per user information in the same structure as
131: * the per exec'ed file information. This allows us to use the
132: * same managers for both, as the number of user id's may be very
133: * large.
134: * User information is keyed by the first character in the name
135: * being a '\001', followed by four bytes of (long extended)
136: * user id number, followed by a null byte.
137: * The actual user names are kept in a seperate field of the
138: * user structure, and is filled in upon demand later.
139: * Iteration through all users by low user id to high user id
140: * is done by just probing the table, which is gross.
141: */
142: #define USERKEY '\001'
143: #define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY))
144: #define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY))
145:
146: #define TABDALLOP 500
147: struct allocbox{
148: struct allocbox *nextalloc;
149: cell tabslots[TABDALLOP];
150: };
151:
152: struct allocbox *allochead; /*head of chunk list*/
153: struct allocbox *alloctail; /*tail*/
154: struct allocbox *newbox; /*for creating a new chunk*/
155: cell *nexttab; /*next table element that is free*/
156: int tabsleft; /*slots left in current chunk*/
157: int ntabs;
158: /*
159: * Iterate through all symbols in the symbol table in declaration
160: * order.
161: * struct allocbox *allocwalk;
162: * cell *sp, *ub;
163: *
164: * sp points to the desired item, allocwalk and ub are there
165: * to make the iteration go.
166: */
167:
168: #define DECLITERATE(allocwalk, walkpointer, ubpointer) \
169: for(allocwalk = allochead; \
170: allocwalk != 0; \
171: allocwalk = allocwalk->nextalloc) \
172: for (walkpointer = &allocwalk->tabslots[0],\
173: ubpointer = &allocwalk->tabslots[TABDALLOP], \
174: ubpointer = ubpointer > ( (cell *)alloctail) \
175: ? nexttab : ubpointer ;\
176: walkpointer < ubpointer; \
177: walkpointer++ )
178:
179: #define TABCHUNKS(allocwalk, tabptr, size) \
180: for (allocwalk = allochead; \
181: allocwalk != 0; \
182: allocwalk = allocwalk->nextalloc) \
183: if ( \
184: (tabptr = &allocwalk->tabslots[0]), \
185: (size = \
186: ( (&allocwalk->tabslots[TABDALLOP]) \
187: > ((cell *)alloctail) \
188: ) \
189: ? (nexttab - tabptr) : TABDALLOP \
190: ), \
191: 1 \
192: )
193: #define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
194: DECLITERATE(allocwalk, walkpointer, ubpointer) \
195: if (ISPROCESS(walkpointer))
196:
197: #define USERITERATE(allocwalk, walkpointer, ubpointer) \
198: DECLITERATE(allocwalk, walkpointer, ubpointer) \
199: if (ISUSER(walkpointer))
200: /*
201: * When we have to sort the segmented accounting table, we
202: * create a vector of sorted queues that is merged
203: * to sort the entire accounting table.
204: */
205: struct chunkdesc {
206: cell *chunk_tp;
207: int chunk_n;
208: };
209:
210: /*
211: * Hash table segments and manager
212: */
213: #define NHASH 1103
214: struct hashdallop {
215: int h_nused;
216: struct hashdallop *h_next;
217: cell *h_tab[NHASH];
218: };
219: struct hashdallop *htab; /* head of the list */
220: int htabinstall; /* install the symbol */
221:
222: double treal;
223: double tcpu;
224: double tsys;
225: double tio;
226: double timem;
227: cell *junkp;
228: char *sname;
229: double ncom;
230: time_t expand();
231: char *getname();
232:
233: /*
234: * usracct saves records of type Olduser.
235: * There is one record for every possible uid less than
236: * the largest uid seen in the previous usracct or in savacct.
237: * uid's that had no activity correspond to zero filled slots;
238: * thus one can index the file and get the user record out.
239: * It would be better to save only user information for users
240: * that the system knows about to save space, but that is not
241: * upward compatabile with the old system.
242: *
243: * In the old version of sa, uid's greater than 999 were not handled
244: * properly; this system will do that.
245: */
246:
247: #ifdef DEBUG
248: #define USRACCT "./usracct"
249: #define SAVACCT "./savacct"
250: #define ACCT "./acct"
251: #else
252: #define USRACCT "/usr/adm/usracct"
253: #define SAVACCT "/usr/adm/savacct"
254: #define ACCT "/usr/adm/acct"
255: #endif DEBUG
256:
257:
258: int cellcmp();
259: cell *junkp = 0;
260: /*
261: * The threshold is built up from digits in the argv ;
262: * eg, -v1s0u1
263: * will build a value of thres of 101.
264: *
265: * If the threshold is zero after processing argv, it is set to 1
266: */
267: int thres = 0;
268: int htabinstall = 1;
269: int maxuser = -1;
270: int (*cmp)();
271:
272: extern tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
273: extern double sum();
274:
275: main(argc, argv)
276: char **argv;
277: {
278: FILE *ff;
279: double ft;
280: register struct allocbox *allocwalk;
281: register cell *tp, *ub;
282: int i, j, size, nchunks, smallest;
283: struct chunkdesc *chunkvector;
284:
285: maxuser = USERSLOP + getmaxuid();
286:
287: tabinit();
288: cmp = tcmp;
289: if (argc>1)
290: if (argv[1][0]=='-') {
291: argv++;
292: argc--;
293: for(i=1; argv[0][i]; i++)
294: switch(argv[0][i]) {
295:
296: case 'o':
297: oflg++;
298: break;
299:
300: case 'i':
301: iflg++;
302: break;
303:
304: case 'b':
305: bflg++;
306: cmp = bcmp;
307: break;
308:
309: case 'l':
310: lflg++;
311: break;
312:
313: case 'c':
314: cflg++;
315: break;
316:
317: case 'd':
318: dflg++;
319: cmp = dcmp;
320: break;
321:
322: case 'D':
323: Dflg++;
324: cmp = Dcmp;
325: break;
326:
327: case 'j':
328: jflg++;
329: break;
330:
331: case 'k':
332: kflg++;
333: cmp = kcmp;
334: break;
335:
336: case 'K':
337: Kflg++;
338: cmp = Kcmp;
339: break;
340:
341: case 'n':
342: nflg++;
343: cmp = ncmp;
344: break;
345:
346: case 'a':
347: aflg++;
348: break;
349:
350: case 'r':
351: rflg++;
352: break;
353:
354: case 't':
355: tflg++;
356: break;
357:
358: case 's':
359: sflg++;
360: aflg++;
361: break;
362:
363: case '0':
364: case '1':
365: case '2':
366: case '3':
367: case '4':
368: case '5':
369: case '6':
370: case '7':
371: case '8':
372: case '9':
373: thres = thres * 10 + (argv[0][i]-'0');
374: break;
375:
376: case 'v':
377: vflg++;
378: break;
379:
380: case 'f':
381: fflg++; /* force v option; no tty interaction */
382: break;
383:
384: case 'u':
385: uflg++;
386: break;
387:
388: case 'm':
389: mflg++;
390: break;
391: }
392: }
393: if (thres == 0)
394: thres = 1;
395: if (iflg==0)
396: init();
397: if (argc<2)
398: doacct(ACCT);
399: else while (--argc)
400: doacct(*++argv);
401: if (uflg) {
402: return;
403: }
404:
405: /*
406: * cleanup pass
407: * put junk together
408: */
409:
410: if (vflg)
411: strip();
412: if(!aflg)
413: PROCESSITERATE(allocwalk, tp, ub){
414: for(j=0; j<NC; j++)
415: if(tp->p.name[j] == '?')
416: goto yes;
417: if(tp->p.count != 1)
418: continue;
419: yes:
420: if(junkp == 0)
421: junkp = enter("***other");
422: junkp->p.count += tp->p.count;
423: junkp->p.realt += tp->p.realt;
424: junkp->p.cput += tp->p.cput;
425: junkp->p.syst += tp->p.syst;
426: junkp->p.imem += tp->p.imem;
427: junkp->p.io += tp->p.io;
428: tp->p.name[0] = 0;
429: }
430: if (sflg) {
431: signal(SIGINT, SIG_IGN);
432: if ((ff = fopen(USRACCT, "w")) != NULL) {
433: static struct user ZeroUser = {0};
434: struct user *up;
435: int uid;
436: /*
437: * Write out just enough user slots,
438: * filling with zero slots for users that
439: * weren't found.
440: * The file can be indexed directly by uid
441: * to get the correct record.
442: */
443: for (uid = 0; uid < maxuser; uid++){
444: if ( (up = wasuser(uid)) != 0)
445: fwrite((char *)&(up->oldu),
446: sizeof(struct Olduser),1,ff);
447: else
448: fwrite((char *)&(ZeroUser.oldu),
449: sizeof(struct Olduser),1,ff);
450: }
451: }
452: if ((ff = fopen(SAVACCT, "w")) == NULL) {
453: printf("Can't save\n");
454: exit(0);
455: }
456: PROCESSITERATE(allocwalk, tp, ub)
457: fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
458: fclose(ff);
459: creat(sname, 0644);
460: signal(SIGINT, SIG_DFL);
461: }
462: /*
463: * sort and print
464: */
465: if (mflg) {
466: printmoney();
467: exit(0);
468: }
469: column(ncom, treal, tcpu, tsys, timem, tio);
470: printf("\n");
471:
472: /*
473: * the fragmented table is sorted by sorting each fragment
474: * and then merging.
475: */
476: nchunks = 0;
477: TABCHUNKS(allocwalk, tp, size){
478: qsort(tp, size, sizeof(cell), cellcmp);
479: nchunks ++;
480: }
481: chunkvector = (struct chunkdesc *)calloc(nchunks,
482: sizeof(struct chunkdesc));
483: nchunks = 0;
484: TABCHUNKS(allocwalk, tp, size){
485: chunkvector[nchunks].chunk_tp = tp;
486: chunkvector[nchunks].chunk_n = size;
487: nchunks++;
488: }
489: for(; nchunks; ){
490: /*
491: * Find the smallest element at the head of the queues.
492: */
493: smallest = 0;
494: for (i = 1; i < nchunks; i++){
495: if (cellcmp(chunkvector[i].chunk_tp,
496: chunkvector[smallest].chunk_tp) < 0)
497: smallest = i;
498: }
499: tp = chunkvector[smallest].chunk_tp++;
500: /*
501: * If this queue is drained, drop the chunk count,
502: * and readjust the queues.
503: */
504: if (--chunkvector[smallest].chunk_n == 0){
505: nchunks--;
506: for (i = smallest; i < nchunks; i++)
507: chunkvector[i] = chunkvector[i+1];
508: }
509: if (ISPROCESS(tp)){
510: ft = tp->p.count;
511: column(ft, tp->p.realt, tp->p.cput,
512: tp->p.syst, tp->p.imem, tp->p.io);
513: printf(" %.14s\n", tp->p.name);
514: }
515: } /* iterate to merge the lists */
516: }
517:
518: printmoney()
519: {
520: register i;
521: register char *cp;
522: register struct user *up;
523:
524: getnames(); /* fetches all of the names! */
525: for (i = 0; i < maxuser; i++) {
526: if ( (up = wasuser(i)) != 0){
527: if (up->us_cnt) {
528: if (up->us_name[0])
529: printf("%-8s", up->us_name);
530: else
531: printf("%-8d", i);
532: printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
533: up->us_cnt, up->us_ctime / 60,
534: up->us_io,
535: up->us_imem / (60 * 2));
536: }
537: }
538: }
539: }
540:
541: column(n, a, b, c, d, e)
542: double n, a, b, c, d, e;
543: {
544:
545: printf("%8.0f", n);
546: if(cflg) {
547: if(n == ncom)
548: printf("%9s", ""); else
549: printf("%8.2f%%", 100.*n/ncom);
550: }
551: col(n, a, treal, "re");
552: if (oflg)
553: col(n, 3600*(b/(b+c)), tcpu+tsys, "u/s");
554: else if(lflg) {
555: col(n, b, tcpu, "u");
556: col(n, c, tsys, "s");
557: } else
558: col(n, b+c, tcpu+tsys, "cp");
559: if(tflg)
560: printf("%8.1f", a/(b+c), "re/cp");
561: if(dflg || !Dflg)
562: printf("%10.0favio", e/(n?n:1));
563: else
564: printf("%10.0ftio", e);
565: if (kflg || !Kflg)
566: printf("%10.0fk", d/(2*((b+c)!=0.0?(b+c):1.0)));
567: else
568: printf("%10.0fk*sec", d/(2*60));
569: }
570:
571: col(n, a, m, cp)
572: double n, a, m;
573: char *cp;
574: {
575:
576: if(jflg)
577: printf("%11.2f%s", a/(n*60.), cp); else
578: printf("%11.2f%s", a/3600., cp);
579: if(cflg) {
580: if(a == m)
581: printf("%9s", ""); else
582: printf("%8.2f%%", 100.*a/m);
583: }
584: }
585:
586: doacct(f)
587: char *f;
588: {
589: FILE *ff;
590: long x, y, z;
591: struct acct fbuf;
592: register char *cp;
593: register int c;
594: register struct user *up;
595: register cell *tp;
596: #ifdef DEBUG
597: int nrecords = 0;
598: #endif DEBUG
599:
600: if (sflg && sname) {
601: printf("Only 1 file with -s\n");
602: exit(0);
603: }
604: if (sflg)
605: sname = f;
606: if ((ff = fopen(f, "r"))==NULL) {
607: printf("Can't open %s\n", f);
608: return;
609: }
610: while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
611: #ifdef DEBUG
612: if (++nrecords % 1000 == 0)
613: printf("Input record from %s number %d\n",
614: f, nrecords);
615: #endif DEBUG
616: if (fbuf.ac_comm[0]==0) {
617: fbuf.ac_comm[0] = '?';
618: }
619: for (cp = fbuf.ac_comm; cp < &fbuf.ac_comm[NC]; cp++) {
620: c = *cp & 0377;
621: if (c && (c < ' ' || c >= 0200)) {
622: *cp = '?';
623: }
624: }
625: if (fbuf.ac_flag&AFORK) {
626: for (cp=fbuf.ac_comm; cp < &fbuf.ac_comm[NC]; cp++)
627: if (*cp==0) {
628: *cp = '*';
629: break;
630: }
631: }
632: x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime);
633: y = fbuf.ac_mem;
634: z = expand(fbuf.ac_io);
635: if (uflg) {
636: printf("%3d%6.1fcp %6dmem %6dio %.14s\n",
637: fbuf.ac_uid, x, y, z, fbuf.ac_comm);
638: continue;
639: }
640: up = finduser(fbuf.ac_uid);
641: if (up == 0)
642: continue; /* preposterous user id */
643: up->us_cnt++;
644: up->us_ctime += x;
645: up->us_imem += x * y;
646: up->us_io += z;
647: ncom += 1.0;
648:
649: tp = enter(fbuf.ac_comm);
650: tp->p.imem += x * y;
651: timem += x * y;
652: tp->p.count++;
653: x = expand(fbuf.ac_etime);
654: tp->p.realt += x;
655: treal += x;
656: x = expand(fbuf.ac_utime);
657: tp->p.cput += x;
658: tcpu += x;
659: x = expand(fbuf.ac_stime);
660: tp->p.syst += x;
661: tsys += x;
662: tp->p.io += z;
663: tio += z;
664: }
665: fclose(ff);
666: }
667:
668: /*
669: * Generalized cell compare routine, to cast out users
670: */
671: cellcmp(p1, p2)
672: cell *p1, *p2;
673: {
674: if (ISPROCESS(p1)){
675: if (ISPROCESS(p2))
676: return((*cmp)(p1, p2));
677: return(-1);
678: }
679: if (ISPROCESS(p2))
680: return(1);
681: return(0);
682: }
683:
684: ncmp(p1, p2)
685: cell *p1, *p2;
686: {
687:
688: if(p1->p.count == p2->p.count)
689: return(tcmp(p1, p2));
690: if(rflg)
691: return(p1->p.count - p2->p.count);
692: return(p2->p.count - p1->p.count);
693: }
694:
695: bcmp(p1, p2)
696: cell *p1, *p2;
697: {
698: double f1, f2;
699: double sum();
700:
701: f1 = sum(p1)/p1->p.count;
702: f2 = sum(p2)/p2->p.count;
703: if(f1 < f2) {
704: if(rflg)
705: return(-1);
706: return(1);
707: }
708: if(f1 > f2) {
709: if(rflg)
710: return(1);
711: return(-1);
712: }
713: return(0);
714: }
715:
716: Kcmp(p1, p2)
717: cell *p1, *p2;
718: {
719:
720: if (p1->p.imem < p2->p.imem) {
721: if(rflg)
722: return(-1);
723: return(1);
724: }
725: if (p1->p.imem > p2->p.imem) {
726: if(rflg)
727: return(1);
728: return(-1);
729: }
730: return(0);
731: }
732:
733: kcmp(p1, p2)
734: cell *p1, *p2;
735: {
736: double a1, a2;
737:
738: a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1);
739: a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1);
740: if (a1 < a2) {
741: if(rflg)
742: return(-1);
743: return(1);
744: }
745: if (a1 > a2) {
746: if(rflg)
747: return(1);
748: return(-1);
749: }
750: return(0);
751: }
752:
753: dcmp(p1, p2)
754: cell *p1, *p2;
755: {
756: double a1, a2;
757:
758: a1 = p1->p.io / (p1->p.count?p1->p.count:1);
759: a2 = p2->p.io / (p2->p.count?p2->p.count:1);
760: if (a1 < a2) {
761: if(rflg)
762: return(-1);
763: return(1);
764: }
765: if (a1 > a2) {
766: if(rflg)
767: return(1);
768: return(-1);
769: }
770: return(0);
771: }
772:
773: Dcmp(p1, p2)
774: cell *p1, *p2;
775: {
776:
777: if (p1->p.io < p2->p.io) {
778: if(rflg)
779: return(-1);
780: return(1);
781: }
782: if (p1->p.io > p2->p.io) {
783: if(rflg)
784: return(1);
785: return(-1);
786: }
787: return(0);
788: }
789:
790: tcmp(p1, p2)
791: cell *p1, *p2;
792: {
793: extern double sum();
794: double f1, f2;
795:
796: f1 = sum(p1);
797: f2 = sum(p2);
798: if(f1 < f2) {
799: if(rflg)
800: return(-1);
801: return(1);
802: }
803: if(f1 > f2) {
804: if(rflg)
805: return(1);
806: return(-1);
807: }
808: return(0);
809: }
810:
811: double sum(p)
812: cell *p;
813: {
814:
815: if(p->p.name[0] == 0)
816: return(0.0);
817: return( p->p.cput + p->p.syst);
818: }
819:
820: init()
821: {
822: struct user userbuf;
823: struct process tbuf;
824: register cell *tp;
825: register struct user *up;
826: int uid;
827: FILE *f;
828:
829: if ((f = fopen(SAVACCT, "r")) == NULL)
830: goto gshm;
831: while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
832: tp = enter(tbuf.name);
833: ncom += tbuf.count;
834: tp->p.count = tbuf.count;
835: treal += tbuf.realt;
836: tp->p.realt = tbuf.realt;
837: tcpu += tbuf.cput;
838: tp->p.cput = tbuf.cput;
839: tsys += tbuf.syst;
840: tp->p.syst = tbuf.syst;
841: tio += tbuf.io;
842: tp->p.io = tbuf.io;
843: timem += tbuf.imem;
844: tp->p.imem = tbuf.imem;
845: }
846: fclose(f);
847: gshm:
848: if ((f = fopen(USRACCT, "r")) == NULL)
849: return;
850: for(uid = 0;
851: fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1;
852: uid++){
853: if (userbuf.us_cnt){
854: up = finduser(uid);
855: if (up == 0)
856: continue; /* preposterous user id */
857: up->oldu = userbuf.oldu;
858: }
859: }
860: fclose(f);
861: }
862:
863: strip()
864: {
865: int c;
866: register struct allocbox *allocwalk;
867: register cell *tp, *ub, *junkp;
868:
869: if (fflg)
870: printf("Categorizing commands used %d times or fewer as **junk**\n",
871: thres);
872: junkp = enter("**junk**");
873: PROCESSITERATE(allocwalk, tp, ub){
874: if (tp->p.name[0] && tp->p.count <= thres) {
875: if (!fflg)
876: printf("%.14s--", tp->p.name);
877: if (fflg || ((c=getchar())=='y')) {
878: tp->p.name[0] = '\0';
879: junkp->p.count += tp->p.count;
880: junkp->p.realt += tp->p.realt;
881: junkp->p.cput += tp->p.cput;
882: junkp->p.syst += tp->p.syst;
883: junkp->p.imem += tp->p.imem;
884: junkp->p.io += tp->p.io;
885: }
886: if (!fflg)
887: while (c && c!='\n')
888: c = getchar();
889: }
890: }
891: }
892:
893: time_t
894: expand(t)
895: unsigned t;
896: {
897: register time_t nt;
898:
899: nt = t&017777;
900: t >>= 13;
901: while (t!=0) {
902: t--;
903: nt <<= 3;
904: }
905: return(nt);
906: }
907:
908: static char UserKey[NAMELG + 2];
909:
910: char *
911: makekey(uid)
912: int uid;
913: {
914: sprintf(UserKey+1, "%04x", uid);
915: UserKey[0] = USERKEY;
916: return(UserKey);
917: }
918:
919: struct user *
920: wasuser(uid)
921: int uid;
922: {
923: struct user *tp;
924:
925: htabinstall = 0;
926: tp = finduser(uid);
927: htabinstall = 1;
928: return(tp);
929: }
930:
931: /*
932: * Only call this if you really want to insert it in the table!
933: */
934: struct user *
935: finduser(uid)
936: int uid;
937: {
938:
939: if (uid > maxuser){
940: fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
941: return(0);
942: }
943: return((struct user*)enter(makekey(uid)));
944: }
945:
946: /*
947: * Set the names of all users in the password file.
948: * We will later not print those that didn't do anything.
949: */
950: getnames()
951: {
952: register struct user *tp;
953: register struct passwd *pw;
954: struct passwd *getpwent();
955:
956: setpwent();
957: while (pw = getpwent()){
958: if ( (tp = wasuser(pw->pw_uid)) != 0)
959: strncpy(tp->us_name, pw->pw_name, NAMELG);
960: }
961: endpwent();
962: }
963:
964: int
965: getmaxuid()
966: {
967: register struct user *tp;
968: register struct passwd *pw;
969: struct passwd *getpwent();
970: int maxuid = -1;
971:
972: setpwent();
973: while(pw = getpwent()){
974: if (pw->pw_uid > maxuid)
975: maxuid = pw->pw_uid;
976: }
977: endpwent();
978: return(maxuid);
979: }
980:
981: tabinit()
982: {
983: allochead = 0;
984: alloctail = 0;
985: nexttab = 0;
986: tabsleft = 0;
987: htab = 0;
988: ntabs = 0;
989: htaballoc(); /* get the first part of the hash table */
990: }
991:
992: #define ALLOCQTY sizeof (struct allocbox)
993: cell *
994: taballoc()
995: {
996:
997: if (tabsleft == 0){
998: newbox = (struct allocbox *)calloc(1, ALLOCQTY);
999: tabsleft = TABDALLOP;
1000: nexttab = &newbox->tabslots[0];
1001: if (alloctail == 0){
1002: allochead = alloctail = newbox;
1003: } else {
1004: alloctail->nextalloc = newbox;
1005: alloctail = newbox;
1006: }
1007: }
1008: --tabsleft;
1009: ++ntabs;
1010: #ifdef DEBUG
1011: if (ntabs % 100 == 0)
1012: printf("##Accounting table slot # %d\n", ntabs);
1013: #endif DEBUG
1014: return(nexttab++);
1015: }
1016:
1017: htaballoc()
1018: {
1019: register struct hashdallop *new;
1020: #ifdef DEBUG
1021: static int ntables = 0;
1022:
1023: printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
1024: #endif DEBUG
1025: new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
1026: if (htab == 0)
1027: htab = new;
1028: else { /* add AFTER the 1st slot */
1029: new->h_next = htab->h_next;
1030: htab->h_next = new;
1031: }
1032: }
1033:
1034: #define HASHCLOGGED (NHASH / 2)
1035: /*
1036: * Lookup a symbol passed in as the argument.
1037: *
1038: * We take pains to avoid function calls; this function
1039: * is called quite frequently, and the calling overhead
1040: * contributes significantly to the overall execution speed of sa.
1041: */
1042: cell *
1043: enter(name)
1044: char *name;
1045: {
1046: static int initialprobe;
1047: register cell **hp;
1048: register char *from, *to;
1049: register int len, nprobes;
1050: static struct hashdallop *hdallop, *emptyhd;
1051: static cell **emptyslot, **hp_ub;
1052:
1053: emptyslot = 0;
1054: for (nprobes = 0, from = name, len = 0;
1055: *from && len < NC;
1056: nprobes <<= 2, nprobes += *from++, len++)
1057: continue;
1058: nprobes += from[-1] << 5;
1059: nprobes %= NHASH;
1060: if (nprobes < 0)
1061: nprobes += NHASH;
1062:
1063: initialprobe = nprobes;
1064: for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
1065: for (hp = &(hdallop->h_tab[initialprobe]),
1066: nprobes = 1,
1067: hp_ub = &(hdallop->h_tab[NHASH]);
1068: (*hp) && (nprobes < NHASH);
1069: hp += nprobes,
1070: hp -= (hp >= hp_ub) ? NHASH:0,
1071: nprobes += 2)
1072: {
1073: from = name;
1074: to = (*hp)->p.name;
1075:
1076: for (len = 0; (len<NC) && *from; len++)
1077: if (*from++ != *to++)
1078: goto nextprobe;
1079: if (len >= NC) /*both are maximal length*/
1080: return(*hp);
1081: if (*to == 0) /*assert *from == 0*/
1082: return(*hp);
1083: nextprobe: ;
1084: }
1085: if (*hp == 0 && emptyslot == 0 &&
1086: hdallop->h_nused < HASHCLOGGED) {
1087: emptyslot = hp;
1088: emptyhd = hdallop;
1089: }
1090: }
1091: if (emptyslot == 0) {
1092: htaballoc();
1093: hdallop = htab->h_next; /* aren't we smart! */
1094: hp = &hdallop->h_tab[initialprobe];
1095: } else {
1096: hdallop = emptyhd;
1097: hp = emptyslot;
1098: }
1099: if (htabinstall){
1100: *hp = taballoc();
1101: hdallop->h_nused++;
1102: for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
1103: if ((*to++ = *from++) == '\0')
1104: break;
1105: return(*hp);
1106: }
1107: return(0);
1108: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.