Merge branch 'vendor/LIBARCHIVE' (early part)
[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  * $FreeBSD: src/usr.sbin/sa/pdb.c,v 1.7 1999/08/28 01:19:53 peter Exp $
31  * $DragonFly: src/usr.sbin/sa/pdb.c,v 1.4 2005/12/05 02:40:28 swildner Exp $
32  */
33
34 #include <sys/types.h>
35 #include <sys/acct.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include "extern.h"
42 #include "pathnames.h"
43
44 static int check_junk(struct cmdinfo *);
45 static void add_ci(const struct cmdinfo *, struct cmdinfo *);
46 static void print_ci(const struct cmdinfo *, const struct cmdinfo *);
47
48 static DB       *pacct_db;
49
50 int
51 pacct_init(void)
52 {
53         DB *saved_pacct_db;
54         int error;
55
56         pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL);
57         if (pacct_db == NULL)
58                 return (-1);
59
60         error = 0;
61         if (!iflag) {
62                 DBT key, data;
63                 int serr, nerr;
64
65                 saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDONLY, 0, DB_BTREE,
66                     NULL);
67                 if (saved_pacct_db == NULL) {
68                         error = errno == ENOENT ? 0 : -1;
69                         if (error)
70                                 warn("retrieving process accounting summary");
71                         goto out;
72                 }
73
74                 serr = DB_SEQ(saved_pacct_db, &key, &data, R_FIRST);
75                 if (serr < 0) {
76                         warn("retrieving process accounting summary");
77                         error = -1;
78                         goto closeout;
79                 }
80                 while (serr == 0) {
81                         nerr = DB_PUT(pacct_db, &key, &data, 0);
82                         if (nerr < 0) {
83                                 warn("initializing process accounting stats");
84                                 error = -1;
85                                 break;
86                         }
87
88                         serr = DB_SEQ(saved_pacct_db, &key, &data, R_NEXT);
89                         if (serr < 0) {
90                                 warn("retrieving process accounting summary");
91                                 error = -1;
92                                 break;
93                         }
94                 }
95
96 closeout:       if (DB_CLOSE(saved_pacct_db) < 0) {
97                         warn("closing process accounting summary");
98                         error = -1;
99                 }
100         }
101
102 out:    if (error != 0)
103                 pacct_destroy();
104         return (error);
105 }
106
107 void
108 pacct_destroy(void)
109 {
110         if (DB_CLOSE(pacct_db) < 0)
111                 warn("destroying process accounting stats");
112 }
113
114 int
115 pacct_add(const struct cmdinfo *ci)
116 {
117         DBT key, data;
118         struct cmdinfo newci;
119         char keydata[sizeof ci->ci_comm];
120         int rv;
121
122         bcopy(ci->ci_comm, &keydata, sizeof keydata);
123         key.data = &keydata;
124         key.size = strlen(keydata);
125
126         rv = DB_GET(pacct_db, &key, &data, 0);
127         if (rv < 0) {
128                 warn("get key %s from process accounting stats", ci->ci_comm);
129                 return (-1);
130         } else if (rv == 0) {   /* it's there; copy whole thing */
131                 /* XXX compare size if paranoid */
132                 /* add the old data to the new data */
133                 bcopy(data.data, &newci, data.size);
134         } else {                /* it's not there; zero it and copy the key */
135                 bzero(&newci, sizeof newci);
136                 bcopy(key.data, newci.ci_comm, key.size);
137         }
138
139         add_ci(ci, &newci);
140
141         data.data = &newci;
142         data.size = sizeof newci;
143         rv = DB_PUT(pacct_db, &key, &data, 0);
144         if (rv < 0) {
145                 warn("add key %s to process accounting stats", ci->ci_comm);
146                 return (-1);
147         } else if (rv == 1) {
148                 warnx("duplicate key %s in process accounting stats",
149                     ci->ci_comm);
150                 return (-1);
151         }
152
153         return (0);
154 }
155
156 int
157 pacct_update(void)
158 {
159         DB *saved_pacct_db;
160         DBT key, data;
161         int error, serr, nerr;
162
163         saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
164             DB_BTREE, NULL);
165         if (saved_pacct_db == NULL) {
166                 warn("creating process accounting summary");
167                 return (-1);
168         }
169
170         error = 0;
171
172         serr = DB_SEQ(pacct_db, &key, &data, R_FIRST);
173         if (serr < 0) {
174                 warn("retrieving process accounting stats");
175                 error = -1;
176         }
177         while (serr == 0) {
178                 nerr = DB_PUT(saved_pacct_db, &key, &data, 0);
179                 if (nerr < 0) {
180                         warn("saving process accounting summary");
181                         error = -1;
182                         break;
183                 }
184
185                 serr = DB_SEQ(pacct_db, &key, &data, R_NEXT);
186                 if (serr < 0) {
187                         warn("retrieving process accounting stats");
188                         error = -1;
189                         break;
190                 }
191         }
192
193         if (DB_SYNC(saved_pacct_db, 0) < 0) {
194                 warn("syncing process accounting summary");
195                 error = -1;
196         }
197         if (DB_CLOSE(saved_pacct_db) < 0) {
198                 warn("closing process accounting summary");
199                 error = -1;
200         }
201         return error;
202 }
203
204 void
205 pacct_print(void)
206 {
207         BTREEINFO bti;
208         DBT key, data, ndata;
209         DB *output_pacct_db;
210         struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
211         int rv;
212
213         bzero(&ci_total, sizeof ci_total);
214         strcpy(ci_total.ci_comm, "");
215         bzero(&ci_other, sizeof ci_other);
216         strcpy(ci_other.ci_comm, "***other");
217         bzero(&ci_junk, sizeof ci_junk);
218         strcpy(ci_junk.ci_comm, "**junk**");
219
220         /*
221          * Retrieve them into new DB, sorted by appropriate key.
222          * At the same time, cull 'other' and 'junk'
223          */
224         bzero(&bti, sizeof bti);
225         bti.compare = sa_cmp;
226         output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
227         if (output_pacct_db == NULL) {
228                 warn("couldn't sort process accounting stats");
229                 return;
230         }
231
232         ndata.data = NULL;
233         ndata.size = 0;
234         rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
235         if (rv < 0)
236                 warn("retrieving process accounting stats");
237         while (rv == 0) {
238                 cip = (struct cmdinfo *) data.data;
239                 bcopy(cip, &ci, sizeof ci);
240
241                 /* add to total */
242                 add_ci(&ci, &ci_total);
243
244                 if (vflag && ci.ci_calls <= cutoff &&
245                     (fflag || check_junk(&ci))) {
246                         /* put it into **junk** */
247                         add_ci(&ci, &ci_junk);
248                         goto next;
249                 }
250                 if (!aflag &&
251                     ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
252                         /* put into ***other */
253                         add_ci(&ci, &ci_other);
254                         goto next;
255                 }
256                 rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
257                 if (rv < 0)
258                         warn("sorting process accounting stats");
259
260 next:           rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
261                 if (rv < 0)
262                         warn("retrieving process accounting stats");
263         }
264
265         /* insert **junk** and ***other */
266         if (ci_junk.ci_calls != 0) {
267                 data.data = &ci_junk;
268                 data.size = sizeof ci_junk;
269                 rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
270                 if (rv < 0)
271                         warn("sorting process accounting stats");
272         }
273         if (ci_other.ci_calls != 0) {
274                 data.data = &ci_other;
275                 data.size = sizeof ci_other;
276                 rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
277                 if (rv < 0)
278                         warn("sorting process accounting stats");
279         }
280
281         /* print out the total */
282         print_ci(&ci_total, &ci_total);
283
284         /* print out; if reversed, print first (smallest) first */
285         rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
286         if (rv < 0)
287                 warn("retrieving process accounting report");
288         while (rv == 0) {
289                 cip = (struct cmdinfo *) data.data;
290                 bcopy(cip, &ci, sizeof ci);
291
292                 print_ci(&ci, &ci_total);
293
294                 rv = DB_SEQ(output_pacct_db, &data, &ndata,
295                     rflag ? R_NEXT : R_PREV);
296                 if (rv < 0)
297                         warn("retrieving process accounting report");
298         }
299         DB_CLOSE(output_pacct_db);
300 }
301
302 static int
303 check_junk(struct cmdinfo *cip)
304 {
305         char *cp;
306         size_t len;
307
308         fprintf(stderr, "%s (%qu) -- ", cip->ci_comm, cip->ci_calls);
309         cp = fgetln(stdin, &len);
310
311         return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0;
312 }
313
314 static void
315 add_ci(const struct cmdinfo *fromcip, struct cmdinfo *tocip)
316 {
317         tocip->ci_calls += fromcip->ci_calls;
318         tocip->ci_etime += fromcip->ci_etime;
319         tocip->ci_utime += fromcip->ci_utime;
320         tocip->ci_stime += fromcip->ci_stime;
321         tocip->ci_mem += fromcip->ci_mem;
322         tocip->ci_io += fromcip->ci_io;
323 }
324
325 static void
326 print_ci(const struct cmdinfo *cip, const struct cmdinfo *totalcip)
327 {
328         double t, c;
329         int uflow;
330
331         c = cip->ci_calls ? cip->ci_calls : 1;
332         t = (cip->ci_utime + cip->ci_stime) / (double) AHZ;
333         if (t < 0.01) {
334                 t = 0.01;
335                 uflow = 1;
336         } else
337                 uflow = 0;
338
339         printf("%8qu ", cip->ci_calls);
340         if (cflag) {
341                 if (cip != totalcip)
342                         printf(" %4.2f%%  ",
343                             cip->ci_calls / (double) totalcip->ci_calls);
344                 else
345                         printf(" %4s   ", "");
346         }
347
348         if (jflag)
349                 printf("%11.2fre ", cip->ci_etime / (double) (AHZ * c));
350         else
351                 printf("%11.2fre ", cip->ci_etime / (60.0 * AHZ));
352         if (cflag) {
353                 if (cip != totalcip)
354                         printf(" %4.2f%%  ",
355                             cip->ci_etime / (double) totalcip->ci_etime);
356                 else
357                         printf(" %4s   ", "");
358         }
359
360         if (!lflag) {
361                 if (jflag)
362                         printf("%11.2fcp ", t / (double) cip->ci_calls);
363                 else
364                         printf("%11.2fcp ", t / 60.0);
365                 if (cflag) {
366                         if (cip != totalcip)
367                                 printf(" %4.2f%%  ",
368                                     (cip->ci_utime + cip->ci_stime) / (double)
369                                     (totalcip->ci_utime + totalcip->ci_stime));
370                         else
371                                 printf(" %4s   ", "");
372                 }
373         } else {
374                 if (jflag)
375                         printf("%11.2fu ", cip->ci_utime / (double) (AHZ * c));
376                 else
377                         printf("%11.2fu ", cip->ci_utime / (60.0 * AHZ));
378                 if (cflag) {
379                         if (cip != totalcip)
380                                 printf(" %4.2f%%  ", cip->ci_utime / (double) totalcip->ci_utime);
381                         else
382                                 printf(" %4s   ", "");
383                 }
384                 if (jflag)
385                         printf("%11.2fs ", cip->ci_stime / (double) (AHZ * c));
386                 else
387                         printf("%11.2fs ", cip->ci_stime / (60.0 * AHZ));
388                 if (cflag) {
389                         if (cip != totalcip)
390                                 printf(" %4.2f%%  ", cip->ci_stime / (double) totalcip->ci_stime);
391                         else
392                                 printf(" %4s   ", "");
393                 }
394         }
395
396         if (tflag)
397                 if (!uflow)
398                         printf("%8.2fre/cp ", cip->ci_etime / (double) (cip->ci_utime + cip->ci_stime));
399                 else
400                         printf("*ignore*      ");
401
402         if (Dflag)
403                 printf("%10qutio ", cip->ci_io);
404         else
405                 printf("%8.0favio ", cip->ci_io / c);
406
407         if (Kflag)
408                 printf("%10quk*sec ", cip->ci_mem);
409         else
410                 printf("%8.0fk ", cip->ci_mem / t);
411
412         printf("  %s\n", cip->ci_comm);
413 }