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