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