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