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