xref: /dragonfly/usr.sbin/sa/pdb.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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  */
32 
33 #include <sys/types.h>
34 #include <sys/acct.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include "extern.h"
41 #include "pathnames.h"
42 
43 static int check_junk(struct cmdinfo *);
44 static void add_ci(const struct cmdinfo *, struct cmdinfo *);
45 static void print_ci(const struct cmdinfo *, const struct cmdinfo *);
46 
47 static DB *pacct_db;
48 
49 int
pacct_init(void)50 pacct_init(void)
51 {
52           DB *saved_pacct_db;
53           int error;
54 
55           pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL);
56           if (pacct_db == NULL)
57                     return (-1);
58 
59           error = 0;
60           if (!iflag) {
61                     DBT key, data;
62                     int serr, nerr;
63 
64                     saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDONLY, 0, DB_BTREE,
65                         NULL);
66                     if (saved_pacct_db == NULL) {
67                               error = errno == ENOENT ? 0 : -1;
68                               if (error)
69                                         warn("retrieving process accounting summary");
70                               goto out;
71                     }
72 
73                     serr = DB_SEQ(saved_pacct_db, &key, &data, R_FIRST);
74                     if (serr < 0) {
75                               warn("retrieving process accounting summary");
76                               error = -1;
77                               goto closeout;
78                     }
79                     while (serr == 0) {
80                               nerr = DB_PUT(pacct_db, &key, &data, 0);
81                               if (nerr < 0) {
82                                         warn("initializing process accounting stats");
83                                         error = -1;
84                                         break;
85                               }
86 
87                               serr = DB_SEQ(saved_pacct_db, &key, &data, R_NEXT);
88                               if (serr < 0) {
89                                         warn("retrieving process accounting summary");
90                                         error = -1;
91                                         break;
92                               }
93                     }
94 
95 closeout: if (DB_CLOSE(saved_pacct_db) < 0) {
96                               warn("closing process accounting summary");
97                               error = -1;
98                     }
99           }
100 
101 out:      if (error != 0)
102                     pacct_destroy();
103           return (error);
104 }
105 
106 void
pacct_destroy(void)107 pacct_destroy(void)
108 {
109           if (DB_CLOSE(pacct_db) < 0)
110                     warn("destroying process accounting stats");
111 }
112 
113 int
pacct_add(const struct cmdinfo * ci)114 pacct_add(const struct cmdinfo *ci)
115 {
116           DBT key, data;
117           struct cmdinfo newci;
118           char keydata[sizeof ci->ci_comm];
119           int rv;
120 
121           bcopy(ci->ci_comm, &keydata, sizeof keydata);
122           key.data = &keydata;
123           key.size = strlen(keydata);
124 
125           rv = DB_GET(pacct_db, &key, &data, 0);
126           if (rv < 0) {
127                     warn("get key %s from process accounting stats", ci->ci_comm);
128                     return (-1);
129           } else if (rv == 0) {         /* it's there; copy whole thing */
130                     /* XXX compare size if paranoid */
131                     /* add the old data to the new data */
132                     bcopy(data.data, &newci, data.size);
133           } else {            /* it's not there; zero it and copy the key */
134                     bzero(&newci, sizeof newci);
135                     bcopy(key.data, newci.ci_comm, key.size);
136           }
137 
138           add_ci(ci, &newci);
139 
140           data.data = &newci;
141           data.size = sizeof newci;
142           rv = DB_PUT(pacct_db, &key, &data, 0);
143           if (rv < 0) {
144                     warn("add key %s to process accounting stats", ci->ci_comm);
145                     return (-1);
146           } else if (rv == 1) {
147                     warnx("duplicate key %s in process accounting stats",
148                         ci->ci_comm);
149                     return (-1);
150           }
151 
152           return (0);
153 }
154 
155 int
pacct_update(void)156 pacct_update(void)
157 {
158           DB *saved_pacct_db;
159           DBT key, data;
160           int error, serr, nerr;
161 
162           saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
163               DB_BTREE, NULL);
164           if (saved_pacct_db == NULL) {
165                     warn("creating process accounting summary");
166                     return (-1);
167           }
168 
169           error = 0;
170 
171           serr = DB_SEQ(pacct_db, &key, &data, R_FIRST);
172           if (serr < 0) {
173                     warn("retrieving process accounting stats");
174                     error = -1;
175           }
176           while (serr == 0) {
177                     nerr = DB_PUT(saved_pacct_db, &key, &data, 0);
178                     if (nerr < 0) {
179                               warn("saving process accounting summary");
180                               error = -1;
181                               break;
182                     }
183 
184                     serr = DB_SEQ(pacct_db, &key, &data, R_NEXT);
185                     if (serr < 0) {
186                               warn("retrieving process accounting stats");
187                               error = -1;
188                               break;
189                     }
190           }
191 
192           if (DB_SYNC(saved_pacct_db, 0) < 0) {
193                     warn("syncing process accounting summary");
194                     error = -1;
195           }
196           if (DB_CLOSE(saved_pacct_db) < 0) {
197                     warn("closing process accounting summary");
198                     error = -1;
199           }
200           return error;
201 }
202 
203 void
pacct_print(void)204 pacct_print(void)
205 {
206           BTREEINFO bti;
207           DBT key, data, ndata;
208           DB *output_pacct_db;
209           struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
210           int rv;
211 
212           bzero(&ci_total, sizeof ci_total);
213           strcpy(ci_total.ci_comm, "");
214           bzero(&ci_other, sizeof ci_other);
215           strcpy(ci_other.ci_comm, "***other");
216           bzero(&ci_junk, sizeof ci_junk);
217           strcpy(ci_junk.ci_comm, "**junk**");
218 
219           /*
220            * Retrieve them into new DB, sorted by appropriate key.
221            * At the same time, cull 'other' and 'junk'
222            */
223           bzero(&bti, sizeof bti);
224           bti.compare = sa_cmp;
225           output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
226           if (output_pacct_db == NULL) {
227                     warn("couldn't sort process accounting stats");
228                     return;
229           }
230 
231           ndata.data = NULL;
232           ndata.size = 0;
233           rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
234           if (rv < 0)
235                     warn("retrieving process accounting stats");
236           while (rv == 0) {
237                     cip = (struct cmdinfo *) data.data;
238                     bcopy(cip, &ci, sizeof ci);
239 
240                     /* add to total */
241                     add_ci(&ci, &ci_total);
242 
243                     if (vflag && ci.ci_calls <= cutoff &&
244                         (fflag || check_junk(&ci))) {
245                               /* put it into **junk** */
246                               add_ci(&ci, &ci_junk);
247                               goto next;
248                     }
249                     if (!aflag &&
250                         ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
251                               /* put into ***other */
252                               add_ci(&ci, &ci_other);
253                               goto next;
254                     }
255                     rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
256                     if (rv < 0)
257                               warn("sorting process accounting stats");
258 
259 next:               rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
260                     if (rv < 0)
261                               warn("retrieving process accounting stats");
262           }
263 
264           /* insert **junk** and ***other */
265           if (ci_junk.ci_calls != 0) {
266                     data.data = &ci_junk;
267                     data.size = sizeof ci_junk;
268                     rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
269                     if (rv < 0)
270                               warn("sorting process accounting stats");
271           }
272           if (ci_other.ci_calls != 0) {
273                     data.data = &ci_other;
274                     data.size = sizeof ci_other;
275                     rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
276                     if (rv < 0)
277                               warn("sorting process accounting stats");
278           }
279 
280           /* print out the total */
281           print_ci(&ci_total, &ci_total);
282 
283           /* print out; if reversed, print first (smallest) first */
284           rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
285           if (rv < 0)
286                     warn("retrieving process accounting report");
287           while (rv == 0) {
288                     cip = (struct cmdinfo *) data.data;
289                     bcopy(cip, &ci, sizeof ci);
290 
291                     print_ci(&ci, &ci_total);
292 
293                     rv = DB_SEQ(output_pacct_db, &data, &ndata,
294                         rflag ? R_NEXT : R_PREV);
295                     if (rv < 0)
296                               warn("retrieving process accounting report");
297           }
298           DB_CLOSE(output_pacct_db);
299 }
300 
301 static int
check_junk(struct cmdinfo * cip)302 check_junk(struct cmdinfo *cip)
303 {
304           char *cp;
305           size_t len;
306 
307           fprintf(stderr, "%s (%ju) -- ", cip->ci_comm,
308               (uintmax_t)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
add_ci(const struct cmdinfo * fromcip,struct cmdinfo * tocip)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
print_ci(const struct cmdinfo * cip,const struct cmdinfo * totalcip)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("%8ju ", (uintmax_t)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 
403           if (Dflag)
404                     printf("%10jutio ", (uintmax_t)cip->ci_io);
405           else
406                     printf("%8.0favio ", cip->ci_io / c);
407 
408           if (Kflag)
409                     printf("%10juk*sec ", (uintmax_t)cip->ci_mem);
410           else
411                     printf("%8.0fk ", cip->ci_mem / t);
412 
413           printf("  %s\n", cip->ci_comm);
414 }
415