Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / sa / pdb.c
1 /*
2  * Copyright (c) 1994 Christopher G. Demetriou
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Christopher G. Demetriou.
16  * 4. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #ifndef lint
32 static const char rcsid[] =
33   "$FreeBSD: src/usr.sbin/sa/pdb.c,v 1.7 1999/08/28 01:19:53 peter Exp $";
34 #endif /* not lint */
35
36 #include <sys/types.h>
37 #include <sys/acct.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include "extern.h"
44 #include "pathnames.h"
45
46 static int check_junk __P((struct cmdinfo *));
47 static void add_ci __P((const struct cmdinfo *, struct cmdinfo *));
48 static void print_ci __P((const struct cmdinfo *, const struct cmdinfo *));
49
50 static DB       *pacct_db;
51
52 int
53 pacct_init()
54 {
55         DB *saved_pacct_db;
56         int error;
57
58         pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL);
59         if (pacct_db == NULL)
60                 return (-1);
61
62         error = 0;
63         if (!iflag) {
64                 DBT key, data;
65                 int serr, nerr;
66
67                 saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDONLY, 0, DB_BTREE,
68                     NULL);
69                 if (saved_pacct_db == NULL) {
70                         error = errno == ENOENT ? 0 : -1;
71                         if (error)
72                                 warn("retrieving process accounting summary");
73                         goto out;
74                 }
75
76                 serr = DB_SEQ(saved_pacct_db, &key, &data, R_FIRST);
77                 if (serr < 0) {
78                         warn("retrieving process accounting summary");
79                         error = -1;
80                         goto closeout;
81                 }
82                 while (serr == 0) {
83                         nerr = DB_PUT(pacct_db, &key, &data, 0);
84                         if (nerr < 0) {
85                                 warn("initializing process accounting stats");
86                                 error = -1;
87                                 break;
88                         }
89
90                         serr = DB_SEQ(saved_pacct_db, &key, &data, R_NEXT);
91                         if (serr < 0) {
92                                 warn("retrieving process accounting summary");
93                                 error = -1;
94                                 break;
95                         }
96                 }
97
98 closeout:       if (DB_CLOSE(saved_pacct_db) < 0) {
99                         warn("closing process accounting summary");
100                         error = -1;
101                 }
102         }
103
104 out:    if (error != 0)
105                 pacct_destroy();
106         return (error);
107 }
108
109 void
110 pacct_destroy()
111 {
112         if (DB_CLOSE(pacct_db) < 0)
113                 warn("destroying process accounting stats");
114 }
115
116 int
117 pacct_add(ci)
118         const struct cmdinfo *ci;
119 {
120         DBT key, data;
121         struct cmdinfo newci;
122         char keydata[sizeof ci->ci_comm];
123         int rv;
124
125         bcopy(ci->ci_comm, &keydata, sizeof keydata);
126         key.data = &keydata;
127         key.size = strlen(keydata);
128
129         rv = DB_GET(pacct_db, &key, &data, 0);
130         if (rv < 0) {
131                 warn("get key %s from process accounting stats", ci->ci_comm);
132                 return (-1);
133         } else if (rv == 0) {   /* it's there; copy whole thing */
134                 /* XXX compare size if paranoid */
135                 /* add the old data to the new data */
136                 bcopy(data.data, &newci, data.size);
137         } else {                /* it's not there; zero it and copy the key */
138                 bzero(&newci, sizeof newci);
139                 bcopy(key.data, newci.ci_comm, key.size);
140         }
141
142         add_ci(ci, &newci);
143
144         data.data = &newci;
145         data.size = sizeof newci;
146         rv = DB_PUT(pacct_db, &key, &data, 0);
147         if (rv < 0) {
148                 warn("add key %s to process accounting stats", ci->ci_comm);
149                 return (-1);
150         } else if (rv == 1) {
151                 warnx("duplicate key %s in process accounting stats",
152                     ci->ci_comm);
153                 return (-1);
154         }
155
156         return (0);
157 }
158
159 int
160 pacct_update()
161 {
162         DB *saved_pacct_db;
163         DBT key, data;
164         int error, serr, nerr;
165
166         saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
167             DB_BTREE, NULL);
168         if (saved_pacct_db == NULL) {
169                 warn("creating process accounting summary");
170                 return (-1);
171         }
172
173         error = 0;
174
175         serr = DB_SEQ(pacct_db, &key, &data, R_FIRST);
176         if (serr < 0) {
177                 warn("retrieving process accounting stats");
178                 error = -1;
179         }
180         while (serr == 0) {
181                 nerr = DB_PUT(saved_pacct_db, &key, &data, 0);
182                 if (nerr < 0) {
183                         warn("saving process accounting summary");
184                         error = -1;
185                         break;
186                 }
187
188                 serr = DB_SEQ(pacct_db, &key, &data, R_NEXT);
189                 if (serr < 0) {
190                         warn("retrieving process accounting stats");
191                         error = -1;
192                         break;
193                 }
194         }
195
196         if (DB_SYNC(saved_pacct_db, 0) < 0) {
197                 warn("syncing process accounting summary");
198                 error = -1;
199         }
200         if (DB_CLOSE(saved_pacct_db) < 0) {
201                 warn("closing process accounting summary");
202                 error = -1;
203         }
204         return error;
205 }
206
207 void
208 pacct_print()
209 {
210         BTREEINFO bti;
211         DBT key, data, ndata;
212         DB *output_pacct_db;
213         struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
214         int rv;
215
216         bzero(&ci_total, sizeof ci_total);
217         strcpy(ci_total.ci_comm, "");
218         bzero(&ci_other, sizeof ci_other);
219         strcpy(ci_other.ci_comm, "***other");
220         bzero(&ci_junk, sizeof ci_junk);
221         strcpy(ci_junk.ci_comm, "**junk**");
222
223         /*
224          * Retrieve them into new DB, sorted by appropriate key.
225          * At the same time, cull 'other' and 'junk'
226          */
227         bzero(&bti, sizeof bti);
228         bti.compare = sa_cmp;
229         output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
230         if (output_pacct_db == NULL) {
231                 warn("couldn't sort process accounting stats");
232                 return;
233         }
234
235         ndata.data = NULL;
236         ndata.size = 0;
237         rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
238         if (rv < 0)
239                 warn("retrieving process accounting stats");
240         while (rv == 0) {
241                 cip = (struct cmdinfo *) data.data;
242                 bcopy(cip, &ci, sizeof ci);
243
244                 /* add to total */
245                 add_ci(&ci, &ci_total);
246
247                 if (vflag && ci.ci_calls <= cutoff &&
248                     (fflag || check_junk(&ci))) {
249                         /* put it into **junk** */
250                         add_ci(&ci, &ci_junk);
251                         goto next;
252                 }
253                 if (!aflag &&
254                     ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
255                         /* put into ***other */
256                         add_ci(&ci, &ci_other);
257                         goto next;
258                 }
259                 rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
260                 if (rv < 0)
261                         warn("sorting process accounting stats");
262
263 next:           rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
264                 if (rv < 0)
265                         warn("retrieving process accounting stats");
266         }
267
268         /* insert **junk** and ***other */
269         if (ci_junk.ci_calls != 0) {
270                 data.data = &ci_junk;
271                 data.size = sizeof ci_junk;
272                 rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
273                 if (rv < 0)
274                         warn("sorting process accounting stats");
275         }
276         if (ci_other.ci_calls != 0) {
277                 data.data = &ci_other;
278                 data.size = sizeof ci_other;
279                 rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
280                 if (rv < 0)
281                         warn("sorting process accounting stats");
282         }
283
284         /* print out the total */
285         print_ci(&ci_total, &ci_total);
286
287         /* print out; if reversed, print first (smallest) first */
288         rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
289         if (rv < 0)
290                 warn("retrieving process accounting report");
291         while (rv == 0) {
292                 cip = (struct cmdinfo *) data.data;
293                 bcopy(cip, &ci, sizeof ci);
294
295                 print_ci(&ci, &ci_total);
296
297                 rv = DB_SEQ(output_pacct_db, &data, &ndata,
298                     rflag ? R_NEXT : R_PREV);
299                 if (rv < 0)
300                         warn("retrieving process accounting report");
301         }
302         DB_CLOSE(output_pacct_db);
303 }
304
305 static int
306 check_junk(cip)
307         struct cmdinfo *cip;
308 {
309         char *cp;
310         size_t len;
311
312         fprintf(stderr, "%s (%qu) -- ", cip->ci_comm, cip->ci_calls);
313         cp = fgetln(stdin, &len);
314
315         return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0;
316 }
317
318 static void
319 add_ci(fromcip, tocip)
320         const struct cmdinfo *fromcip;
321         struct cmdinfo *tocip;
322 {
323         tocip->ci_calls += fromcip->ci_calls;
324         tocip->ci_etime += fromcip->ci_etime;
325         tocip->ci_utime += fromcip->ci_utime;
326         tocip->ci_stime += fromcip->ci_stime;
327         tocip->ci_mem += fromcip->ci_mem;
328         tocip->ci_io += fromcip->ci_io;
329 }
330
331 static void
332 print_ci(cip, totalcip)
333         const struct cmdinfo *cip, *totalcip;
334 {
335         double t, c;
336         int uflow;
337
338         c = cip->ci_calls ? cip->ci_calls : 1;
339         t = (cip->ci_utime + cip->ci_stime) / (double) AHZ;
340         if (t < 0.01) {
341                 t = 0.01;
342                 uflow = 1;
343         } else
344                 uflow = 0;
345
346         printf("%8qu ", cip->ci_calls);
347         if (cflag) {
348                 if (cip != totalcip)
349                         printf(" %4.2f%%  ",
350                             cip->ci_calls / (double) totalcip->ci_calls);
351                 else
352                         printf(" %4s   ", "");
353         }
354
355         if (jflag)
356                 printf("%11.2fre ", cip->ci_etime / (double) (AHZ * c));
357         else
358                 printf("%11.2fre ", cip->ci_etime / (60.0 * AHZ));
359         if (cflag) {
360                 if (cip != totalcip)
361                         printf(" %4.2f%%  ",
362                             cip->ci_etime / (double) totalcip->ci_etime);
363                 else
364                         printf(" %4s   ", "");
365         }
366
367         if (!lflag) {
368                 if (jflag)
369                         printf("%11.2fcp ", t / (double) cip->ci_calls);
370                 else
371                         printf("%11.2fcp ", t / 60.0);
372                 if (cflag) {
373                         if (cip != totalcip)
374                                 printf(" %4.2f%%  ",
375                                     (cip->ci_utime + cip->ci_stime) / (double)
376                                     (totalcip->ci_utime + totalcip->ci_stime));
377                         else
378                                 printf(" %4s   ", "");
379                 }
380         } else {
381                 if (jflag)
382                         printf("%11.2fu ", cip->ci_utime / (double) (AHZ * c));
383                 else
384                         printf("%11.2fu ", cip->ci_utime / (60.0 * AHZ));
385                 if (cflag) {
386                         if (cip != totalcip)
387                                 printf(" %4.2f%%  ", cip->ci_utime / (double) totalcip->ci_utime);
388                         else
389                                 printf(" %4s   ", "");
390                 }
391                 if (jflag)
392                         printf("%11.2fs ", cip->ci_stime / (double) (AHZ * c));
393                 else
394                         printf("%11.2fs ", cip->ci_stime / (60.0 * AHZ));
395                 if (cflag) {
396                         if (cip != totalcip)
397                                 printf(" %4.2f%%  ", cip->ci_stime / (double) totalcip->ci_stime);
398                         else
399                                 printf(" %4s   ", "");
400                 }
401         }
402
403         if (tflag)
404                 if (!uflow)
405                         printf("%8.2fre/cp ", cip->ci_etime / (double) (cip->ci_utime + cip->ci_stime));
406                 else
407                         printf("*ignore*      ");
408
409         if (Dflag)
410                 printf("%10qutio ", cip->ci_io);
411         else
412                 printf("%8.0favio ", cip->ci_io / c);
413
414         if (Kflag)
415                 printf("%10quk*sec ", cip->ci_mem);
416         else
417                 printf("%8.0fk ", cip->ci_mem / t);
418
419         printf("  %s\n", cip->ci_comm);
420 }