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