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