pdb.c revision 99829
13125Sdg/*
23125Sdg * Copyright (c) 1994 Christopher G. Demetriou
33125Sdg * All rights reserved.
43125Sdg *
53125Sdg * Redistribution and use in source and binary forms, with or without
63125Sdg * modification, are permitted provided that the following conditions
73125Sdg * are met:
83125Sdg * 1. Redistributions of source code must retain the above copyright
93125Sdg *    notice, this list of conditions and the following disclaimer.
103125Sdg * 2. Redistributions in binary form must reproduce the above copyright
113125Sdg *    notice, this list of conditions and the following disclaimer in the
123125Sdg *    documentation and/or other materials provided with the distribution.
133125Sdg * 3. All advertising materials mentioning features or use of this software
143125Sdg *    must display the following acknowledgement:
153125Sdg *      This product includes software developed by Christopher G. Demetriou.
163125Sdg * 4. The name of the author may not be used to endorse or promote products
173125Sdg *    derived from this software without specific prior written permission
183125Sdg *
193125Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
203125Sdg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
213125Sdg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
223125Sdg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
233125Sdg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
243125Sdg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
253125Sdg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
263125Sdg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
273125Sdg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
283125Sdg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
293125Sdg */
303125Sdg
3130425Scharnier#ifndef lint
3230425Scharnierstatic const char rcsid[] =
3350479Speter  "$FreeBSD: head/usr.sbin/sa/pdb.c 99829 2002-07-11 22:11:20Z alfred $";
3430425Scharnier#endif /* not lint */
353125Sdg
363125Sdg#include <sys/types.h>
373125Sdg#include <sys/acct.h>
383125Sdg#include <err.h>
393125Sdg#include <errno.h>
403125Sdg#include <fcntl.h>
413125Sdg#include <stdio.h>
4213558Smpp#include <string.h>
433125Sdg#include "extern.h"
443125Sdg#include "pathnames.h"
453125Sdg
463125Sdgstatic int check_junk __P((struct cmdinfo *));
473125Sdgstatic void add_ci __P((const struct cmdinfo *, struct cmdinfo *));
483125Sdgstatic void print_ci __P((const struct cmdinfo *, const struct cmdinfo *));
493125Sdg
503125Sdgstatic DB	*pacct_db;
513125Sdg
523125Sdgint
533125Sdgpacct_init()
543125Sdg{
553125Sdg	DB *saved_pacct_db;
563125Sdg	int error;
573125Sdg
583125Sdg	pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL);
593125Sdg	if (pacct_db == NULL)
603125Sdg		return (-1);
613125Sdg
623125Sdg	error = 0;
633125Sdg	if (!iflag) {
643125Sdg		DBT key, data;
653125Sdg		int serr, nerr;
663125Sdg
673125Sdg		saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDONLY, 0, DB_BTREE,
683125Sdg		    NULL);
693125Sdg		if (saved_pacct_db == NULL) {
703125Sdg			error = errno == ENOENT ? 0 : -1;
713125Sdg			if (error)
723125Sdg				warn("retrieving process accounting summary");
733125Sdg			goto out;
743125Sdg		}
753125Sdg
763125Sdg		serr = DB_SEQ(saved_pacct_db, &key, &data, R_FIRST);
773125Sdg		if (serr < 0) {
783125Sdg			warn("retrieving process accounting summary");
793125Sdg			error = -1;
803125Sdg			goto closeout;
813125Sdg		}
823125Sdg		while (serr == 0) {
833125Sdg			nerr = DB_PUT(pacct_db, &key, &data, 0);
843125Sdg			if (nerr < 0) {
853125Sdg				warn("initializing process accounting stats");
863125Sdg				error = -1;
873125Sdg				break;
883125Sdg			}
893125Sdg
903125Sdg			serr = DB_SEQ(saved_pacct_db, &key, &data, R_NEXT);
913125Sdg			if (serr < 0) {
923125Sdg				warn("retrieving process accounting summary");
933125Sdg				error = -1;
943125Sdg				break;
953125Sdg			}
963125Sdg		}
973125Sdg
983125Sdgcloseout:	if (DB_CLOSE(saved_pacct_db) < 0) {
993125Sdg			warn("closing process accounting summary");
1003125Sdg			error = -1;
1013125Sdg		}
1023125Sdg	}
1033125Sdg
1043125Sdgout:	if (error != 0)
1053125Sdg		pacct_destroy();
1063125Sdg	return (error);
1073125Sdg}
1083125Sdg
1093125Sdgvoid
1103125Sdgpacct_destroy()
1113125Sdg{
1123125Sdg	if (DB_CLOSE(pacct_db) < 0)
1133125Sdg		warn("destroying process accounting stats");
1143125Sdg}
1153125Sdg
1163125Sdgint
1173125Sdgpacct_add(ci)
1183125Sdg	const struct cmdinfo *ci;
1193125Sdg{
1203125Sdg	DBT key, data;
1213125Sdg	struct cmdinfo newci;
1223125Sdg	char keydata[sizeof ci->ci_comm];
1233125Sdg	int rv;
1243125Sdg
1253125Sdg	bcopy(ci->ci_comm, &keydata, sizeof keydata);
1263125Sdg	key.data = &keydata;
1273125Sdg	key.size = strlen(keydata);
1283125Sdg
1293125Sdg	rv = DB_GET(pacct_db, &key, &data, 0);
1303125Sdg	if (rv < 0) {
1313125Sdg		warn("get key %s from process accounting stats", ci->ci_comm);
1323125Sdg		return (-1);
1333125Sdg	} else if (rv == 0) {	/* it's there; copy whole thing */
1343125Sdg		/* XXX compare size if paranoid */
1353125Sdg		/* add the old data to the new data */
1363125Sdg		bcopy(data.data, &newci, data.size);
1373125Sdg	} else {		/* it's not there; zero it and copy the key */
1383125Sdg		bzero(&newci, sizeof newci);
1393125Sdg		bcopy(key.data, newci.ci_comm, key.size);
1403125Sdg	}
1418857Srgrimes
1423125Sdg	add_ci(ci, &newci);
1433125Sdg
1448857Srgrimes	data.data = &newci;
1453125Sdg	data.size = sizeof newci;
1463125Sdg	rv = DB_PUT(pacct_db, &key, &data, 0);
1473125Sdg	if (rv < 0) {
1483125Sdg		warn("add key %s to process accounting stats", ci->ci_comm);
1493125Sdg		return (-1);
1503125Sdg	} else if (rv == 1) {
1513125Sdg		warnx("duplicate key %s in process accounting stats",
1523125Sdg		    ci->ci_comm);
1533125Sdg		return (-1);
1543125Sdg	}
1553125Sdg
1563125Sdg	return (0);
1573125Sdg}
1583125Sdg
1593125Sdgint
1603125Sdgpacct_update()
1613125Sdg{
1623125Sdg	DB *saved_pacct_db;
1633125Sdg	DBT key, data;
1643125Sdg	int error, serr, nerr;
1653125Sdg
1663125Sdg	saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
1673125Sdg	    DB_BTREE, NULL);
1683125Sdg	if (saved_pacct_db == NULL) {
1693125Sdg		warn("creating process accounting summary");
1703125Sdg		return (-1);
1713125Sdg	}
1723125Sdg
1733125Sdg	error = 0;
1743125Sdg
1753125Sdg	serr = DB_SEQ(pacct_db, &key, &data, R_FIRST);
1763125Sdg	if (serr < 0) {
1773125Sdg		warn("retrieving process accounting stats");
1783125Sdg		error = -1;
1793125Sdg	}
1803125Sdg	while (serr == 0) {
1813125Sdg		nerr = DB_PUT(saved_pacct_db, &key, &data, 0);
1823125Sdg		if (nerr < 0) {
1833125Sdg			warn("saving process accounting summary");
1843125Sdg			error = -1;
1853125Sdg			break;
1863125Sdg		}
1873125Sdg
1883125Sdg		serr = DB_SEQ(pacct_db, &key, &data, R_NEXT);
1893125Sdg		if (serr < 0) {
1903125Sdg			warn("retrieving process accounting stats");
1913125Sdg			error = -1;
1923125Sdg			break;
1933125Sdg		}
1943125Sdg	}
1953125Sdg
1963125Sdg	if (DB_SYNC(saved_pacct_db, 0) < 0) {
1973125Sdg		warn("syncing process accounting summary");
1983125Sdg		error = -1;
1993125Sdg	}
2003125Sdg	if (DB_CLOSE(saved_pacct_db) < 0) {
2013125Sdg		warn("closing process accounting summary");
2023125Sdg		error = -1;
2033125Sdg	}
2043125Sdg	return error;
2053125Sdg}
2063125Sdg
2073125Sdgvoid
2083125Sdgpacct_print()
2093125Sdg{
2103125Sdg	BTREEINFO bti;
2113125Sdg	DBT key, data, ndata;
2123125Sdg	DB *output_pacct_db;
2133125Sdg	struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
2143125Sdg	int rv;
2153125Sdg
2163125Sdg	bzero(&ci_total, sizeof ci_total);
2173125Sdg	strcpy(ci_total.ci_comm, "");
2183125Sdg	bzero(&ci_other, sizeof ci_other);
2193125Sdg	strcpy(ci_other.ci_comm, "***other");
2203125Sdg	bzero(&ci_junk, sizeof ci_junk);
2213125Sdg	strcpy(ci_junk.ci_comm, "**junk**");
2223125Sdg
2233125Sdg	/*
2243125Sdg	 * Retrieve them into new DB, sorted by appropriate key.
2253125Sdg	 * At the same time, cull 'other' and 'junk'
2263125Sdg	 */
2273125Sdg	bzero(&bti, sizeof bti);
2283125Sdg	bti.compare = sa_cmp;
2293125Sdg	output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
2303125Sdg	if (output_pacct_db == NULL) {
2313125Sdg		warn("couldn't sort process accounting stats");
2323125Sdg		return;
2333125Sdg	}
2343125Sdg
2353125Sdg	ndata.data = NULL;
2363125Sdg	ndata.size = 0;
2373125Sdg	rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
2383125Sdg	if (rv < 0)
2393125Sdg		warn("retrieving process accounting stats");
2403125Sdg	while (rv == 0) {
2413125Sdg		cip = (struct cmdinfo *) data.data;
2423125Sdg		bcopy(cip, &ci, sizeof ci);
2433125Sdg
2443125Sdg		/* add to total */
2453125Sdg		add_ci(&ci, &ci_total);
2463125Sdg
2473125Sdg		if (vflag && ci.ci_calls <= cutoff &&
2483125Sdg		    (fflag || check_junk(&ci))) {
2493125Sdg			/* put it into **junk** */
2503125Sdg			add_ci(&ci, &ci_junk);
2513125Sdg			goto next;
2523125Sdg		}
2533125Sdg		if (!aflag &&
2543125Sdg		    ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
2553125Sdg			/* put into ***other */
2563125Sdg			add_ci(&ci, &ci_other);
2573125Sdg			goto next;
2583125Sdg		}
2593125Sdg		rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
2603125Sdg		if (rv < 0)
2613125Sdg			warn("sorting process accounting stats");
2623125Sdg
2633125Sdgnext:		rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
2643125Sdg		if (rv < 0)
2653125Sdg			warn("retrieving process accounting stats");
2663125Sdg	}
2673125Sdg
2683125Sdg	/* insert **junk** and ***other */
2693125Sdg	if (ci_junk.ci_calls != 0) {
2703125Sdg		data.data = &ci_junk;
2713125Sdg		data.size = sizeof ci_junk;
2723125Sdg		rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
2733125Sdg		if (rv < 0)
2743125Sdg			warn("sorting process accounting stats");
2753125Sdg	}
2763125Sdg	if (ci_other.ci_calls != 0) {
2773125Sdg		data.data = &ci_other;
2783125Sdg		data.size = sizeof ci_other;
2793125Sdg		rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
2803125Sdg		if (rv < 0)
2813125Sdg			warn("sorting process accounting stats");
2823125Sdg	}
2833125Sdg
2843125Sdg	/* print out the total */
2853125Sdg	print_ci(&ci_total, &ci_total);
2863125Sdg
2873125Sdg	/* print out; if reversed, print first (smallest) first */
2883125Sdg	rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
2893125Sdg	if (rv < 0)
2903125Sdg		warn("retrieving process accounting report");
2913125Sdg	while (rv == 0) {
2923125Sdg		cip = (struct cmdinfo *) data.data;
2933125Sdg		bcopy(cip, &ci, sizeof ci);
2943125Sdg
2953125Sdg		print_ci(&ci, &ci_total);
2963125Sdg
2973125Sdg		rv = DB_SEQ(output_pacct_db, &data, &ndata,
2983125Sdg		    rflag ? R_NEXT : R_PREV);
2993125Sdg		if (rv < 0)
3003125Sdg			warn("retrieving process accounting report");
3013125Sdg	}
3023125Sdg	DB_CLOSE(output_pacct_db);
3033125Sdg}
3043125Sdg
3053125Sdgstatic int
3063125Sdgcheck_junk(cip)
3073125Sdg	struct cmdinfo *cip;
3083125Sdg{
3093125Sdg	char *cp;
3103125Sdg	size_t len;
3113125Sdg
3123125Sdg	fprintf(stderr, "%s (%qu) -- ", cip->ci_comm, cip->ci_calls);
3133125Sdg	cp = fgetln(stdin, &len);
3143125Sdg
3153125Sdg	return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0;
3163125Sdg}
3173125Sdg
3183125Sdgstatic void
3193125Sdgadd_ci(fromcip, tocip)
3203125Sdg	const struct cmdinfo *fromcip;
3213125Sdg	struct cmdinfo *tocip;
3223125Sdg{
3233125Sdg	tocip->ci_calls += fromcip->ci_calls;
3243125Sdg	tocip->ci_etime += fromcip->ci_etime;
3253125Sdg	tocip->ci_utime += fromcip->ci_utime;
3263125Sdg	tocip->ci_stime += fromcip->ci_stime;
3273125Sdg	tocip->ci_mem += fromcip->ci_mem;
3283125Sdg	tocip->ci_io += fromcip->ci_io;
3293125Sdg}
3303125Sdg
3313125Sdgstatic void
3323125Sdgprint_ci(cip, totalcip)
3333125Sdg	const struct cmdinfo *cip, *totalcip;
3343125Sdg{
3353125Sdg	double t, c;
3363125Sdg	int uflow;
3373125Sdg
3383125Sdg	c = cip->ci_calls ? cip->ci_calls : 1;
3393125Sdg	t = (cip->ci_utime + cip->ci_stime) / (double) AHZ;
3403125Sdg	if (t < 0.01) {
3413125Sdg		t = 0.01;
3423125Sdg		uflow = 1;
3433125Sdg	} else
3443125Sdg		uflow = 0;
3453125Sdg
3463125Sdg	printf("%8qu ", cip->ci_calls);
3473125Sdg	if (cflag) {
3483125Sdg		if (cip != totalcip)
3493125Sdg			printf(" %4.2f%%  ",
3503125Sdg			    cip->ci_calls / (double) totalcip->ci_calls);
3513125Sdg		else
3523125Sdg			printf(" %4s   ", "");
3533125Sdg	}
3543125Sdg
3553125Sdg	if (jflag)
3563125Sdg		printf("%11.2fre ", cip->ci_etime / (double) (AHZ * c));
3573125Sdg	else
3583125Sdg		printf("%11.2fre ", cip->ci_etime / (60.0 * AHZ));
3593125Sdg	if (cflag) {
3603125Sdg		if (cip != totalcip)
3613125Sdg			printf(" %4.2f%%  ",
3623125Sdg			    cip->ci_etime / (double) totalcip->ci_etime);
3633125Sdg		else
3643125Sdg			printf(" %4s   ", "");
3653125Sdg	}
3663125Sdg
3673125Sdg	if (!lflag) {
3683125Sdg		if (jflag)
3693125Sdg			printf("%11.2fcp ", t / (double) cip->ci_calls);
3703125Sdg		else
3713125Sdg			printf("%11.2fcp ", t / 60.0);
3723125Sdg		if (cflag) {
3733125Sdg			if (cip != totalcip)
3743125Sdg				printf(" %4.2f%%  ",
3753125Sdg				    (cip->ci_utime + cip->ci_stime) / (double)
3763125Sdg				    (totalcip->ci_utime + totalcip->ci_stime));
3773125Sdg			else
3783125Sdg				printf(" %4s   ", "");
3793125Sdg		}
3803125Sdg	} else {
3813125Sdg		if (jflag)
3823125Sdg			printf("%11.2fu ", cip->ci_utime / (double) (AHZ * c));
3833125Sdg		else
3843125Sdg			printf("%11.2fu ", cip->ci_utime / (60.0 * AHZ));
3853125Sdg		if (cflag) {
3863125Sdg			if (cip != totalcip)
3873125Sdg				printf(" %4.2f%%  ", cip->ci_utime / (double) totalcip->ci_utime);
3883125Sdg			else
3893125Sdg				printf(" %4s   ", "");
3903125Sdg		}
3913125Sdg		if (jflag)
3923125Sdg			printf("%11.2fs ", cip->ci_stime / (double) (AHZ * c));
3933125Sdg		else
3943125Sdg			printf("%11.2fs ", cip->ci_stime / (60.0 * AHZ));
3953125Sdg		if (cflag) {
3963125Sdg			if (cip != totalcip)
3973125Sdg				printf(" %4.2f%%  ", cip->ci_stime / (double) totalcip->ci_stime);
3983125Sdg			else
3993125Sdg				printf(" %4s   ", "");
4003125Sdg		}
4013125Sdg	}
4023125Sdg
40399829Salfred	if (tflag) {
4043125Sdg		if (!uflow)
40599829Salfred			printf("%8.2fre/cp ",
40699829Salfred			    cip->ci_etime /
40799829Salfred			    (double) (cip->ci_utime + cip->ci_stime));
4083125Sdg		else
40913558Smpp			printf("*ignore*      ");
41099829Salfred	}
4113125Sdg
4123125Sdg	if (Dflag)
4133125Sdg		printf("%10qutio ", cip->ci_io);
4143125Sdg	else
4153125Sdg		printf("%8.0favio ", cip->ci_io / c);
4163125Sdg
4173125Sdg	if (Kflag)
4183125Sdg		printf("%10quk*sec ", cip->ci_mem);
4193125Sdg	else
4203125Sdg		printf("%8.0fk ", cip->ci_mem / t);
4213125Sdg
4223125Sdg	printf("  %s\n", cip->ci_comm);
4233125Sdg}
424