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