Commit | Line | Data |
---|---|---|
651c99bb SL |
1 | #ifndef lint |
2 | static char *sccsid = "@(#)sa.c 4.3 (Berkeley) 83/04/04"; | |
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 BJ |
20 | #include <stdio.h> |
21 | #include <sys/types.h> | |
22 | #include <sys/acct.h> | |
23 | #include <signal.h> | |
32d08ae3 BJ |
24 | #include <utmp.h> |
25 | #include <pwd.h> | |
2efccdb1 BJ |
26 | |
27 | /* interpret command time accounting */ | |
28 | ||
2efccdb1 | 29 | #define NC sizeof(acctbuf.ac_comm) |
32d08ae3 | 30 | |
2efccdb1 BJ |
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; | |
32d08ae3 | 46 | int fflg; |
2efccdb1 | 47 | int uflg; |
32d08ae3 | 48 | int thres; |
2efccdb1 BJ |
49 | int sflg; |
50 | int bflg; | |
51 | int mflg; | |
52 | ||
32d08ae3 BJ |
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 | ||
2efccdb1 | 63 | struct user { |
32d08ae3 BJ |
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 | |
2efccdb1 | 72 | |
32d08ae3 BJ |
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 { | |
2efccdb1 BJ |
83 | char name[NC]; |
84 | int count; | |
85 | double realt; | |
86 | double cput; | |
87 | double syst; | |
88 | double imem; | |
89 | double io; | |
32d08ae3 BJ |
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 */ | |
2efccdb1 BJ |
221 | |
222 | double treal; | |
223 | double tcpu; | |
224 | double tsys; | |
225 | double tio; | |
226 | double timem; | |
32d08ae3 | 227 | cell *junkp; |
2efccdb1 BJ |
228 | char *sname; |
229 | double ncom; | |
230 | time_t expand(); | |
231 | char *getname(); | |
232 | ||
32d08ae3 BJ |
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 | \f | |
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 | ||
651c99bb SL |
272 | extern tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp(); |
273 | extern double sum(); | |
274 | ||
2efccdb1 | 275 | main(argc, argv) |
651c99bb | 276 | char **argv; |
2efccdb1 BJ |
277 | { |
278 | FILE *ff; | |
651c99bb SL |
279 | double ft; |
280 | register struct allocbox *allocwalk; | |
281 | register cell *tp, *ub; | |
282 | int i, j, size, nchunks, smallest; | |
283 | struct chunkdesc *chunkvector; | |
32d08ae3 BJ |
284 | |
285 | maxuser = USERSLOP + getmaxuid(); | |
286 | ||
287 | tabinit(); | |
2efccdb1 BJ |
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': | |
32d08ae3 | 373 | thres = thres * 10 + (argv[0][i]-'0'); |
2efccdb1 BJ |
374 | break; |
375 | ||
376 | case 'v': | |
377 | vflg++; | |
378 | break; | |
379 | ||
32d08ae3 BJ |
380 | case 'f': |
381 | fflg++; /* force v option; no tty interaction */ | |
382 | break; | |
383 | ||
2efccdb1 BJ |
384 | case 'u': |
385 | uflg++; | |
386 | break; | |
387 | ||
388 | case 'm': | |
389 | mflg++; | |
390 | break; | |
391 | } | |
392 | } | |
32d08ae3 BJ |
393 | if (thres == 0) |
394 | thres = 1; | |
2efccdb1 BJ |
395 | if (iflg==0) |
396 | init(); | |
397 | if (argc<2) | |
32d08ae3 | 398 | doacct(ACCT); |
2efccdb1 BJ |
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) | |
32d08ae3 | 413 | PROCESSITERATE(allocwalk, tp, ub){ |
2efccdb1 | 414 | for(j=0; j<NC; j++) |
32d08ae3 | 415 | if(tp->p.name[j] == '?') |
2efccdb1 | 416 | goto yes; |
32d08ae3 | 417 | if(tp->p.count != 1) |
2efccdb1 BJ |
418 | continue; |
419 | yes: | |
32d08ae3 | 420 | if(junkp == 0) |
2efccdb1 | 421 | junkp = enter("***other"); |
32d08ae3 BJ |
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; | |
2efccdb1 BJ |
429 | } |
430 | if (sflg) { | |
431 | signal(SIGINT, SIG_IGN); | |
32d08ae3 BJ |
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 | } | |
2efccdb1 | 451 | } |
32d08ae3 | 452 | if ((ff = fopen(SAVACCT, "w")) == NULL) { |
2efccdb1 BJ |
453 | printf("Can't save\n"); |
454 | exit(0); | |
455 | } | |
32d08ae3 BJ |
456 | PROCESSITERATE(allocwalk, tp, ub) |
457 | fwrite((char *)&(tp->p), sizeof(struct process), 1, ff); | |
2efccdb1 | 458 | fclose(ff); |
32d08ae3 | 459 | creat(sname, 0644); |
2efccdb1 BJ |
460 | signal(SIGINT, SIG_DFL); |
461 | } | |
462 | /* | |
463 | * sort and print | |
464 | */ | |
2efccdb1 BJ |
465 | if (mflg) { |
466 | printmoney(); | |
467 | exit(0); | |
468 | } | |
2efccdb1 BJ |
469 | column(ncom, treal, tcpu, tsys, timem, tio); |
470 | printf("\n"); | |
32d08ae3 BJ |
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++; | |
2efccdb1 | 488 | } |
32d08ae3 BJ |
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 */ | |
2efccdb1 BJ |
516 | } |
517 | ||
518 | printmoney() | |
519 | { | |
520 | register i; | |
2efccdb1 | 521 | register char *cp; |
32d08ae3 BJ |
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", | |
651c99bb | 533 | up->us_cnt, up->us_ctime / 60, |
32d08ae3 BJ |
534 | up->us_io, |
535 | up->us_imem / (60 * 2)); | |
536 | } | |
2efccdb1 BJ |
537 | } |
538 | } | |
539 | } | |
540 | ||
541 | column(n, a, b, c, d, e) | |
651c99bb | 542 | double n, a, b, c, d, e; |
2efccdb1 BJ |
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) | |
651c99bb SL |
572 | double n, a, m; |
573 | char *cp; | |
2efccdb1 BJ |
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 | { | |
2efccdb1 BJ |
589 | FILE *ff; |
590 | long x, y, z; | |
591 | struct acct fbuf; | |
592 | register char *cp; | |
593 | register int c; | |
651c99bb SL |
594 | register struct user *up; |
595 | register cell *tp; | |
32d08ae3 BJ |
596 | #ifdef DEBUG |
597 | int nrecords = 0; | |
598 | #endif DEBUG | |
2efccdb1 BJ |
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) { | |
32d08ae3 BJ |
611 | #ifdef DEBUG |
612 | if (++nrecords % 1000 == 0) | |
613 | printf("Input record from %s number %d\n", | |
614 | f, nrecords); | |
615 | #endif DEBUG | |
2efccdb1 BJ |
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", | |
651c99bb | 637 | fbuf.ac_uid, x, y, z, fbuf.ac_comm); |
2efccdb1 BJ |
638 | continue; |
639 | } | |
32d08ae3 BJ |
640 | up = finduser(fbuf.ac_uid); |
641 | if (up == 0) | |
642 | continue; /* preposterous user id */ | |
643 | up->us_cnt++; | |
651c99bb | 644 | up->us_ctime += x; |
32d08ae3 BJ |
645 | up->us_imem += x * y; |
646 | up->us_io += z; | |
2efccdb1 | 647 | ncom += 1.0; |
32d08ae3 BJ |
648 | |
649 | tp = enter(fbuf.ac_comm); | |
650 | tp->p.imem += x * y; | |
2efccdb1 | 651 | timem += x * y; |
32d08ae3 | 652 | tp->p.count++; |
651c99bb | 653 | x = expand(fbuf.ac_etime); |
32d08ae3 | 654 | tp->p.realt += x; |
2efccdb1 BJ |
655 | treal += x; |
656 | x = expand(fbuf.ac_utime); | |
32d08ae3 | 657 | tp->p.cput += x; |
2efccdb1 BJ |
658 | tcpu += x; |
659 | x = expand(fbuf.ac_stime); | |
32d08ae3 | 660 | tp->p.syst += x; |
2efccdb1 | 661 | tsys += x; |
32d08ae3 | 662 | tp->p.io += z; |
2efccdb1 BJ |
663 | tio += z; |
664 | } | |
665 | fclose(ff); | |
666 | } | |
667 | ||
32d08ae3 BJ |
668 | /* |
669 | * Generalized cell compare routine, to cast out users | |
670 | */ | |
671 | cellcmp(p1, p2) | |
651c99bb | 672 | cell *p1, *p2; |
32d08ae3 BJ |
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 | } | |
651c99bb | 683 | |
2efccdb1 | 684 | ncmp(p1, p2) |
651c99bb | 685 | cell *p1, *p2; |
2efccdb1 BJ |
686 | { |
687 | ||
32d08ae3 | 688 | if(p1->p.count == p2->p.count) |
2efccdb1 BJ |
689 | return(tcmp(p1, p2)); |
690 | if(rflg) | |
32d08ae3 BJ |
691 | return(p1->p.count - p2->p.count); |
692 | return(p2->p.count - p1->p.count); | |
2efccdb1 BJ |
693 | } |
694 | ||
695 | bcmp(p1, p2) | |
651c99bb | 696 | cell *p1, *p2; |
2efccdb1 BJ |
697 | { |
698 | double f1, f2; | |
699 | double sum(); | |
700 | ||
32d08ae3 BJ |
701 | f1 = sum(p1)/p1->p.count; |
702 | f2 = sum(p2)/p2->p.count; | |
2efccdb1 BJ |
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) | |
651c99bb | 717 | cell *p1, *p2; |
2efccdb1 BJ |
718 | { |
719 | ||
32d08ae3 | 720 | if (p1->p.imem < p2->p.imem) { |
2efccdb1 BJ |
721 | if(rflg) |
722 | return(-1); | |
723 | return(1); | |
724 | } | |
32d08ae3 | 725 | if (p1->p.imem > p2->p.imem) { |
2efccdb1 BJ |
726 | if(rflg) |
727 | return(1); | |
728 | return(-1); | |
729 | } | |
730 | return(0); | |
731 | } | |
732 | ||
733 | kcmp(p1, p2) | |
651c99bb | 734 | cell *p1, *p2; |
2efccdb1 BJ |
735 | { |
736 | double a1, a2; | |
737 | ||
32d08ae3 BJ |
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); | |
2efccdb1 BJ |
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) | |
651c99bb | 754 | cell *p1, *p2; |
2efccdb1 BJ |
755 | { |
756 | double a1, a2; | |
757 | ||
32d08ae3 BJ |
758 | a1 = p1->p.io / (p1->p.count?p1->p.count:1); |
759 | a2 = p2->p.io / (p2->p.count?p2->p.count:1); | |
2efccdb1 BJ |
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) | |
651c99bb | 774 | cell *p1, *p2; |
2efccdb1 BJ |
775 | { |
776 | ||
32d08ae3 | 777 | if (p1->p.io < p2->p.io) { |
2efccdb1 BJ |
778 | if(rflg) |
779 | return(-1); | |
780 | return(1); | |
781 | } | |
32d08ae3 | 782 | if (p1->p.io > p2->p.io) { |
2efccdb1 BJ |
783 | if(rflg) |
784 | return(1); | |
785 | return(-1); | |
786 | } | |
787 | return(0); | |
788 | } | |
789 | ||
790 | tcmp(p1, p2) | |
651c99bb | 791 | cell *p1, *p2; |
2efccdb1 BJ |
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) | |
651c99bb | 812 | cell *p; |
2efccdb1 BJ |
813 | { |
814 | ||
32d08ae3 | 815 | if(p->p.name[0] == 0) |
2efccdb1 | 816 | return(0.0); |
32d08ae3 | 817 | return( p->p.cput + p->p.syst); |
2efccdb1 BJ |
818 | } |
819 | ||
820 | init() | |
821 | { | |
651c99bb SL |
822 | struct user userbuf; |
823 | struct process tbuf; | |
824 | register cell *tp; | |
825 | register struct user *up; | |
826 | int uid; | |
2efccdb1 BJ |
827 | FILE *f; |
828 | ||
32d08ae3 | 829 | if ((f = fopen(SAVACCT, "r")) == NULL) |
2efccdb1 | 830 | goto gshm; |
32d08ae3 BJ |
831 | while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) { |
832 | tp = enter(tbuf.name); | |
2efccdb1 | 833 | ncom += tbuf.count; |
32d08ae3 | 834 | tp->p.count = tbuf.count; |
2efccdb1 | 835 | treal += tbuf.realt; |
32d08ae3 | 836 | tp->p.realt = tbuf.realt; |
2efccdb1 | 837 | tcpu += tbuf.cput; |
32d08ae3 | 838 | tp->p.cput = tbuf.cput; |
2efccdb1 | 839 | tsys += tbuf.syst; |
32d08ae3 | 840 | tp->p.syst = tbuf.syst; |
2efccdb1 | 841 | tio += tbuf.io; |
32d08ae3 | 842 | tp->p.io = tbuf.io; |
2efccdb1 | 843 | timem += tbuf.imem; |
32d08ae3 | 844 | tp->p.imem = tbuf.imem; |
2efccdb1 BJ |
845 | } |
846 | fclose(f); | |
847 | gshm: | |
32d08ae3 | 848 | if ((f = fopen(USRACCT, "r")) == NULL) |
2efccdb1 | 849 | return; |
32d08ae3 BJ |
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 | } | |
2efccdb1 BJ |
860 | fclose(f); |
861 | } | |
862 | ||
2efccdb1 BJ |
863 | strip() |
864 | { | |
651c99bb SL |
865 | int c; |
866 | register struct allocbox *allocwalk; | |
867 | register cell *tp, *ub, *junkp; | |
32d08ae3 BJ |
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; | |
2efccdb1 | 885 | } |
32d08ae3 BJ |
886 | if (!fflg) |
887 | while (c && c!='\n') | |
888 | c = getchar(); | |
2efccdb1 BJ |
889 | } |
890 | } | |
891 | } | |
892 | ||
893 | time_t | |
894 | expand(t) | |
651c99bb | 895 | unsigned t; |
2efccdb1 BJ |
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 | ||
651c99bb SL |
908 | static char UserKey[NAMELG + 2]; |
909 | ||
910 | char * | |
911 | makekey(uid) | |
912 | int uid; | |
32d08ae3 BJ |
913 | { |
914 | sprintf(UserKey+1, "%04x", uid); | |
915 | UserKey[0] = USERKEY; | |
916 | return(UserKey); | |
917 | } | |
2efccdb1 | 918 | |
651c99bb SL |
919 | struct user * |
920 | wasuser(uid) | |
921 | int uid; | |
32d08ae3 | 922 | { |
651c99bb SL |
923 | struct user *tp; |
924 | ||
32d08ae3 BJ |
925 | htabinstall = 0; |
926 | tp = finduser(uid); | |
927 | htabinstall = 1; | |
928 | return(tp); | |
929 | } | |
651c99bb | 930 | |
32d08ae3 BJ |
931 | /* |
932 | * Only call this if you really want to insert it in the table! | |
933 | */ | |
651c99bb SL |
934 | struct user * |
935 | finduser(uid) | |
936 | int uid; | |
32d08ae3 | 937 | { |
651c99bb | 938 | |
32d08ae3 BJ |
939 | if (uid > maxuser){ |
940 | fprintf(stderr, "Preposterous user id, %d: ignored\n", uid); | |
941 | return(0); | |
32d08ae3 | 942 | } |
651c99bb | 943 | return((struct user*)enter(makekey(uid))); |
32d08ae3 | 944 | } |
2efccdb1 | 945 | |
32d08ae3 BJ |
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() | |
2efccdb1 | 951 | { |
651c99bb | 952 | register struct user *tp; |
2efccdb1 | 953 | register struct passwd *pw; |
2efccdb1 BJ |
954 | struct passwd *getpwent(); |
955 | ||
32d08ae3 BJ |
956 | setpwent(); |
957 | while (pw = getpwent()){ | |
958 | if ( (tp = wasuser(pw->pw_uid)) != 0) | |
959 | strncpy(tp->us_name, pw->pw_name, NAMELG); | |
2efccdb1 | 960 | } |
2efccdb1 | 961 | endpwent(); |
2efccdb1 | 962 | } |
32d08ae3 | 963 | |
651c99bb SL |
964 | int |
965 | getmaxuid() | |
32d08ae3 | 966 | { |
651c99bb SL |
967 | register struct user *tp; |
968 | register struct passwd *pw; | |
969 | struct passwd *getpwent(); | |
970 | int maxuid = -1; | |
32d08ae3 BJ |
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) | |
651c99bb SL |
993 | cell * |
994 | taballoc() | |
32d08ae3 | 995 | { |
651c99bb | 996 | |
32d08ae3 BJ |
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 | { | |
651c99bb | 1019 | register struct hashdallop *new; |
32d08ae3 | 1020 | #ifdef DEBUG |
651c99bb SL |
1021 | static int ntables = 0; |
1022 | ||
32d08ae3 BJ |
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 | */ | |
651c99bb SL |
1042 | cell * |
1043 | enter(name) | |
1044 | char *name; | |
32d08ae3 | 1045 | { |
651c99bb SL |
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; | |
32d08ae3 BJ |
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); | |
651c99bb | 1108 | } |