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