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