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