pdb.c revision 169857
1206451Smarius/*
2206453Smarius * Copyright (c) 1994 Christopher G. Demetriou
3206451Smarius * All rights reserved.
4206451Smarius *
5206451Smarius * Redistribution and use in source and binary forms, with or without
6206451Smarius * modification, are permitted provided that the following conditions
7206451Smarius * are met:
8206451Smarius * 1. Redistributions of source code must retain the above copyright
9206451Smarius *    notice, this list of conditions and the following disclaimer.
10206451Smarius * 2. Redistributions in binary form must reproduce the above copyright
11206451Smarius *    notice, this list of conditions and the following disclaimer in the
12206451Smarius *    documentation and/or other materials provided with the distribution.
13206451Smarius * 3. All advertising materials mentioning features or use of this software
14206451Smarius *    must display the following acknowledgement:
15206451Smarius *      This product includes software developed by Christopher G. Demetriou.
16206451Smarius * 4. The name of the author may not be used to endorse or promote products
17206451Smarius *    derived from this software without specific prior written permission
18206451Smarius *
19206451Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20206451Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21206451Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22206451Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23206451Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24206451Smarius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25206451Smarius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26206451Smarius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27206451Smarius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28206451Smarius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29206451Smarius */
30206451Smarius
31206451Smarius#include <sys/cdefs.h>
32206451Smarius__FBSDID("$FreeBSD: head/usr.sbin/sa/pdb.c 169857 2007-05-22 06:51:38Z dds $");
33206451Smarius
34206451Smarius#include <sys/types.h>
35206451Smarius#include <sys/acct.h>
36206451Smarius#include <err.h>
37206451Smarius#include <errno.h>
38206451Smarius#include <fcntl.h>
39206451Smarius#include <stdbool.h>
40206451Smarius#include <stdint.h>
41206451Smarius#include <stdio.h>
42206451Smarius#include <string.h>
43206451Smarius#include "extern.h"
44206451Smarius#include "pathnames.h"
45206451Smarius
46206451Smariusstatic int check_junk(const struct cmdinfo *);
47206451Smariusstatic void add_ci(const struct cmdinfo *, struct cmdinfo *);
48206451Smariusstatic void print_ci(const struct cmdinfo *, const struct cmdinfo *);
49206451Smarius
50206451Smariusstatic DB	*pacct_db;
51206451Smarius
52206451Smarius/* Legacy format in AHZV1 units. */
53206451Smariusstruct cmdinfov1 {
54206451Smarius	char		ci_comm[MAXCOMLEN+2];	/* command name (+ '*') */
55206451Smarius	uid_t		ci_uid;			/* user id */
56206451Smarius	u_quad_t	ci_calls;		/* number of calls */
57206451Smarius	u_quad_t	ci_etime;		/* elapsed time */
58206451Smarius	u_quad_t	ci_utime;		/* user time */
59206451Smarius	u_quad_t	ci_stime;		/* system time */
60206451Smarius	u_quad_t	ci_mem;			/* memory use */
61206451Smarius	u_quad_t	ci_io;			/* number of disk i/o ops */
62206451Smarius	u_int		ci_flags;		/* flags; see below */
63206451Smarius};
64206451Smarius
65206451Smarius/*
66206451Smarius * Convert a v1 data record into the current version.
67206451Smarius * Return 0 if OK, -1 on error, setting errno.
68206451Smarius */
69206451Smariusstatic int
70206451Smariusv1_to_v2(DBT *key __unused, DBT *data)
71206451Smarius{
72206451Smarius	struct cmdinfov1 civ1;
73206451Smarius	static struct cmdinfo civ2;
74206451Smarius
75206451Smarius	if (data->size != sizeof(civ1)) {
76206451Smarius		errno = EFTYPE;
77206451Smarius		return (-1);
78206451Smarius	}
79206451Smarius	memcpy(&civ1, data->data, data->size);
80206451Smarius	memset(&civ2, 0, sizeof(civ2));
81206451Smarius	memcpy(civ2.ci_comm, civ1.ci_comm, sizeof(civ2.ci_comm));
82206451Smarius	civ2.ci_uid = civ1.ci_uid;
83206451Smarius	civ2.ci_calls = civ1.ci_calls;
84206451Smarius	civ2.ci_etime = ((double)civ1.ci_etime / AHZV1) * 1000000;
85206451Smarius	civ2.ci_utime = ((double)civ1.ci_utime / AHZV1) * 1000000;
86206451Smarius	civ2.ci_stime = ((double)civ1.ci_stime / AHZV1) * 1000000;
87206451Smarius	civ2.ci_mem = civ1.ci_mem;
88206451Smarius	civ2.ci_io = civ1.ci_io;
89206451Smarius	civ2.ci_flags = civ1.ci_flags;
90206451Smarius	data->size = sizeof(civ2);
91206451Smarius	data->data = &civ2;
92206451Smarius	return (0);
93206451Smarius}
94206451Smarius
95206451Smarius/* Copy pdb_file to in-memory pacct_db. */
96206451Smariusint
97206451Smariuspacct_init()
98206451Smarius{
99206451Smarius	return (db_copy_in(&pacct_db, pdb_file, "process accounting",
100206451Smarius	    NULL, v1_to_v2));
101206451Smarius}
102206451Smarius
103206451Smariusvoid
104206451Smariuspacct_destroy()
105206451Smarius{
106206451Smarius	db_destroy(pacct_db, "process accounting");
107206451Smarius}
108206451Smarius
109206451Smariusint
110206451Smariuspacct_add(const struct cmdinfo *ci)
111206451Smarius{
112206451Smarius	DBT key, data;
113206451Smarius	struct cmdinfo newci;
114206451Smarius	char keydata[sizeof ci->ci_comm];
115206451Smarius	int rv;
116206451Smarius
117206451Smarius	bcopy(ci->ci_comm, &keydata, sizeof keydata);
118206451Smarius	key.data = &keydata;
119206451Smarius	key.size = strlen(keydata);
120206451Smarius
121206451Smarius	rv = DB_GET(pacct_db, &key, &data, 0);
122206451Smarius	if (rv < 0) {
123206451Smarius		warn("get key %s from process accounting stats", ci->ci_comm);
124206451Smarius		return (-1);
125206451Smarius	} else if (rv == 0) {	/* it's there; copy whole thing */
126206451Smarius		/* XXX compare size if paranoid */
127206451Smarius		/* add the old data to the new data */
128206451Smarius		bcopy(data.data, &newci, data.size);
129206451Smarius	} else {		/* it's not there; zero it and copy the key */
130206451Smarius		bzero(&newci, sizeof newci);
131206451Smarius		bcopy(key.data, newci.ci_comm, key.size);
132206451Smarius	}
133206451Smarius
134206451Smarius	add_ci(ci, &newci);
135206451Smarius
136206451Smarius	data.data = &newci;
137206451Smarius	data.size = sizeof newci;
138206451Smarius	rv = DB_PUT(pacct_db, &key, &data, 0);
139206451Smarius	if (rv < 0) {
140206451Smarius		warn("add key %s to process accounting stats", ci->ci_comm);
141206451Smarius		return (-1);
142206451Smarius	} else if (rv == 1) {
143206451Smarius		warnx("duplicate key %s in process accounting stats",
144206451Smarius		    ci->ci_comm);
145206451Smarius		return (-1);
146206451Smarius	}
147206451Smarius
148206451Smarius	return (0);
149206451Smarius}
150206451Smarius
151206451Smarius/* Copy in-memory pacct_db to pdb_file. */
152206451Smariusint
153206451Smariuspacct_update()
154206451Smarius{
155206451Smarius	return (db_copy_out(pacct_db, pdb_file, "process accounting",
156206451Smarius	    NULL));
157206451Smarius}
158206451Smarius
159206451Smariusvoid
160206451Smariuspacct_print()
161206451Smarius{
162206451Smarius	BTREEINFO bti;
163206451Smarius	DBT key, data, ndata;
164206451Smarius	DB *output_pacct_db;
165206451Smarius	struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
166206451Smarius	int rv;
167206451Smarius
168206451Smarius	bzero(&ci_total, sizeof ci_total);
169206451Smarius	strcpy(ci_total.ci_comm, "");
170206451Smarius	bzero(&ci_other, sizeof ci_other);
171206451Smarius	strcpy(ci_other.ci_comm, "***other");
172206451Smarius	bzero(&ci_junk, sizeof ci_junk);
173206451Smarius	strcpy(ci_junk.ci_comm, "**junk**");
174206451Smarius
175206451Smarius	/*
176206451Smarius	 * Retrieve them into new DB, sorted by appropriate key.
177206451Smarius	 * At the same time, cull 'other' and 'junk'
178206451Smarius	 */
179206451Smarius	bzero(&bti, sizeof bti);
180206451Smarius	bti.compare = sa_cmp;
181206451Smarius	output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
182206451Smarius	if (output_pacct_db == NULL) {
183206451Smarius		warn("couldn't sort process accounting stats");
184206451Smarius		return;
185206451Smarius	}
186206451Smarius
187206451Smarius	ndata.data = NULL;
188206451Smarius	ndata.size = 0;
189206451Smarius	rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
190206451Smarius	if (rv < 0)
191206451Smarius		warn("retrieving process accounting stats");
192206451Smarius	while (rv == 0) {
193206451Smarius		cip = (struct cmdinfo *) data.data;
194206451Smarius		bcopy(cip, &ci, sizeof ci);
195206451Smarius
196206451Smarius		/* add to total */
197206451Smarius		add_ci(&ci, &ci_total);
198206451Smarius
199206451Smarius		if (vflag && ci.ci_calls <= cutoff &&
200206451Smarius		    (fflag || check_junk(&ci))) {
201206451Smarius			/* put it into **junk** */
202206451Smarius			add_ci(&ci, &ci_junk);
203206451Smarius			goto next;
204206451Smarius		}
205206451Smarius		if (!aflag &&
206206451Smarius		    ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
207206451Smarius			/* put into ***other */
208206451Smarius			add_ci(&ci, &ci_other);
209206451Smarius			goto next;
210206451Smarius		}
211206451Smarius		rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
212206451Smarius		if (rv < 0)
213206451Smarius			warn("sorting process accounting stats");
214206451Smarius
215206451Smariusnext:		rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
216206451Smarius		if (rv < 0)
217206451Smarius			warn("retrieving process accounting stats");
218206451Smarius	}
219206451Smarius
220206451Smarius	/* insert **junk** and ***other */
221206451Smarius	if (ci_junk.ci_calls != 0) {
222206451Smarius		data.data = &ci_junk;
223206451Smarius		data.size = sizeof ci_junk;
224206451Smarius		rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
225206451Smarius		if (rv < 0)
226206451Smarius			warn("sorting process accounting stats");
227206451Smarius	}
228206451Smarius	if (ci_other.ci_calls != 0) {
229206451Smarius		data.data = &ci_other;
230206451Smarius		data.size = sizeof ci_other;
231206451Smarius		rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
232206451Smarius		if (rv < 0)
233206451Smarius			warn("sorting process accounting stats");
234206451Smarius	}
235206451Smarius
236206451Smarius	/* print out the total */
237206451Smarius	print_ci(&ci_total, &ci_total);
238206451Smarius
239206451Smarius	/* print out; if reversed, print first (smallest) first */
240206451Smarius	rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
241206451Smarius	if (rv < 0)
242206451Smarius		warn("retrieving process accounting report");
243206451Smarius	while (rv == 0) {
244206451Smarius		cip = (struct cmdinfo *) data.data;
245206451Smarius		bcopy(cip, &ci, sizeof ci);
246206451Smarius
247206451Smarius		print_ci(&ci, &ci_total);
248206451Smarius
249206451Smarius		rv = DB_SEQ(output_pacct_db, &data, &ndata,
250206451Smarius		    rflag ? R_NEXT : R_PREV);
251206451Smarius		if (rv < 0)
252206451Smarius			warn("retrieving process accounting report");
253206451Smarius	}
254206451Smarius	DB_CLOSE(output_pacct_db);
255206451Smarius}
256206451Smarius
257206451Smariusstatic int
258206451Smariuscheck_junk(const struct cmdinfo *cip)
259206451Smarius{
260206451Smarius	char *cp;
261206451Smarius	size_t len;
262225931Smarius
263225931Smarius	fprintf(stderr, "%s (%ju) -- ", cip->ci_comm, (uintmax_t)cip->ci_calls);
264225931Smarius	cp = fgetln(stdin, &len);
265206451Smarius
266206451Smarius	return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0;
267206451Smarius}
268206451Smarius
269206451Smariusstatic void
270206451Smariusadd_ci(const struct cmdinfo *fromcip, struct cmdinfo *tocip)
271206451Smarius{
272206451Smarius	tocip->ci_calls += fromcip->ci_calls;
273206451Smarius	tocip->ci_etime += fromcip->ci_etime;
274206451Smarius	tocip->ci_utime += fromcip->ci_utime;
275206451Smarius	tocip->ci_stime += fromcip->ci_stime;
276206451Smarius	tocip->ci_mem += fromcip->ci_mem;
277206451Smarius	tocip->ci_io += fromcip->ci_io;
278206451Smarius}
279206451Smarius
280206451Smariusstatic void
281206451Smariusprint_ci(const struct cmdinfo *cip, const struct cmdinfo *totalcip)
282206451Smarius{
283225931Smarius	double t, c;
284225931Smarius	int uflow;
285225931Smarius
286206451Smarius	c = cip->ci_calls ? cip->ci_calls : 1;
287206451Smarius	t = (cip->ci_utime + cip->ci_stime) / 1000000;
288206451Smarius	if (t < 0.01) {
289225931Smarius		t = 0.01;
290206451Smarius		uflow = 1;
291206451Smarius	} else
292206451Smarius		uflow = 0;
293206451Smarius
294206451Smarius	printf("%8ju ", (uintmax_t)cip->ci_calls);
295206451Smarius	if (cflag) {
296227843Smarius		if (cip != totalcip)
297206451Smarius			printf(" %4.1f%%  ", cip->ci_calls /
298206451Smarius			    (double)totalcip->ci_calls * 100);
299206451Smarius		else
300206451Smarius			printf(" %4s   ", "");
301206451Smarius	}
302247574Smarius
303206451Smarius	if (jflag)
304206451Smarius		printf("%11.3fre ", cip->ci_etime / (1000000 * c));
305206451Smarius	else
306206451Smarius		printf("%11.3fre ", cip->ci_etime / (60.0 * 1000000));
307206451Smarius	if (cflag) {
308206451Smarius		if (cip != totalcip)
309206451Smarius			printf(" %4.1f%%  ", cip->ci_etime /
310206451Smarius			    totalcip->ci_etime * 100);
311206451Smarius		else
312206451Smarius			printf(" %4s   ", "");
313206451Smarius	}
314206451Smarius
315206451Smarius	if (!lflag) {
316206451Smarius		if (jflag)
317206451Smarius			printf("%11.3fcp ", t / (double) cip->ci_calls);
318206451Smarius		else
319206451Smarius			printf("%11.2fcp ", t / 60.0);
320206451Smarius		if (cflag) {
321206451Smarius			if (cip != totalcip)
322206451Smarius				printf(" %4.1f%%  ",
323206451Smarius				    (cip->ci_utime + cip->ci_stime) /
324206451Smarius				    (totalcip->ci_utime + totalcip->ci_stime) *
325206451Smarius				    100);
326206451Smarius			else
327206451Smarius				printf(" %4s   ", "");
328330446Seadler		}
329206451Smarius	} else {
330206451Smarius		if (jflag)
331206451Smarius			printf("%11.3fu ", cip->ci_utime / (1000000 * c));
332206451Smarius		else
333206451Smarius			printf("%11.2fu ", cip->ci_utime / (60.0 * 1000000));
334206451Smarius		if (cflag) {
335206451Smarius			if (cip != totalcip)
336206451Smarius				printf(" %4.1f%%  ", cip->ci_utime /
337206451Smarius				    (double)totalcip->ci_utime * 100);
338206451Smarius			else
339206451Smarius				printf(" %4s   ", "");
340206451Smarius		}
341206451Smarius		if (jflag)
342225931Smarius			printf("%11.3fs ", cip->ci_stime / (1000000 * c));
343225931Smarius		else
344206451Smarius			printf("%11.2fs ", cip->ci_stime / (60.0 * 1000000));
345206451Smarius		if (cflag) {
346206451Smarius			if (cip != totalcip)
347206451Smarius				printf(" %4.1f%%  ", cip->ci_stime /
348206451Smarius				    (double)totalcip->ci_stime * 100);
349206451Smarius			else
350206451Smarius				printf(" %4s   ", "");
351206451Smarius		}
352206451Smarius	}
353206451Smarius
354206451Smarius	if (tflag) {
355206451Smarius		if (!uflow)
356206451Smarius			printf("%8.2fre/cp ",
357206451Smarius			    cip->ci_etime /
358206451Smarius			    (cip->ci_utime + cip->ci_stime));
359206451Smarius		else
360206451Smarius			printf("*ignore*      ");
361247574Smarius	}
362206451Smarius
363206451Smarius	if (Dflag)
364206451Smarius		printf("%10.0fio ", cip->ci_io);
365206451Smarius	else
366206451Smarius		printf("%8.0favio ", cip->ci_io / c);
367206451Smarius
368206451Smarius	if (Kflag)
369206451Smarius		printf("%10.0fk*sec ", cip->ci_mem);
370206451Smarius	else
371206451Smarius		printf("%8.0fk ", cip->ci_mem / t);
372206451Smarius
373206451Smarius	printf("  %s\n", cip->ci_comm);
374206451Smarius}
375206451Smarius