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