1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 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 by Christopher G. Demetriou. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/types.h> 37 #include <sys/acct.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdbool.h> 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include "extern.h" 46 #include "pathnames.h" 47 48 static int check_junk(const struct cmdinfo *); 49 static void add_ci(const struct cmdinfo *, struct cmdinfo *); 50 static void print_ci(const struct cmdinfo *, const struct cmdinfo *); 51 52 static DB *pacct_db; 53 54 /* Legacy format in AHZV1 units. */ 55 struct cmdinfov1 { 56 char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */ 57 uid_t ci_uid; /* user id */ 58 u_quad_t ci_calls; /* number of calls */ 59 u_quad_t ci_etime; /* elapsed time */ 60 u_quad_t ci_utime; /* user time */ 61 u_quad_t ci_stime; /* system time */ 62 u_quad_t ci_mem; /* memory use */ 63 u_quad_t ci_io; /* number of disk i/o ops */ 64 u_int ci_flags; /* flags; see below */ 65 }; 66 67 /* 68 * Convert a v1 data record into the current version. 69 * Return 0 if OK, -1 on error, setting errno. 70 */ 71 static int 72 v1_to_v2(DBT *key __unused, DBT *data) 73 { 74 struct cmdinfov1 civ1; 75 static struct cmdinfo civ2; 76 77 if (data->size != sizeof(civ1)) { 78 errno = EFTYPE; 79 return (-1); 80 } 81 memcpy(&civ1, data->data, data->size); 82 memset(&civ2, 0, sizeof(civ2)); 83 memcpy(civ2.ci_comm, civ1.ci_comm, sizeof(civ2.ci_comm)); 84 civ2.ci_uid = civ1.ci_uid; 85 civ2.ci_calls = civ1.ci_calls; 86 civ2.ci_etime = ((double)civ1.ci_etime / AHZV1) * 1000000; 87 civ2.ci_utime = ((double)civ1.ci_utime / AHZV1) * 1000000; 88 civ2.ci_stime = ((double)civ1.ci_stime / AHZV1) * 1000000; 89 civ2.ci_mem = civ1.ci_mem; 90 civ2.ci_io = civ1.ci_io; 91 civ2.ci_flags = civ1.ci_flags; 92 data->size = sizeof(civ2); 93 data->data = &civ2; 94 return (0); 95 } 96 97 /* Copy pdb_file to in-memory pacct_db. */ 98 int 99 pacct_init(void) 100 { 101 return (db_copy_in(&pacct_db, pdb_file, "process accounting", 102 NULL, v1_to_v2)); 103 } 104 105 void 106 pacct_destroy(void) 107 { 108 db_destroy(pacct_db, "process accounting"); 109 } 110 111 int 112 pacct_add(const struct cmdinfo *ci) 113 { 114 DBT key, data; 115 struct cmdinfo newci; 116 char keydata[sizeof ci->ci_comm]; 117 int rv; 118 119 bcopy(ci->ci_comm, &keydata, sizeof keydata); 120 key.data = &keydata; 121 key.size = strlen(keydata); 122 123 rv = DB_GET(pacct_db, &key, &data, 0); 124 if (rv < 0) { 125 warn("get key %s from process accounting stats", ci->ci_comm); 126 return (-1); 127 } else if (rv == 0) { /* it's there; copy whole thing */ 128 /* XXX compare size if paranoid */ 129 /* add the old data to the new data */ 130 bcopy(data.data, &newci, data.size); 131 } else { /* it's not there; zero it and copy the key */ 132 bzero(&newci, sizeof newci); 133 bcopy(key.data, newci.ci_comm, key.size); 134 } 135 136 add_ci(ci, &newci); 137 138 data.data = &newci; 139 data.size = sizeof newci; 140 rv = DB_PUT(pacct_db, &key, &data, 0); 141 if (rv < 0) { 142 warn("add key %s to process accounting stats", ci->ci_comm); 143 return (-1); 144 } else if (rv == 1) { 145 warnx("duplicate key %s in process accounting stats", 146 ci->ci_comm); 147 return (-1); 148 } 149 150 return (0); 151 } 152 153 /* Copy in-memory pacct_db to pdb_file. */ 154 int 155 pacct_update(void) 156 { 157 return (db_copy_out(pacct_db, pdb_file, "process accounting", 158 NULL)); 159 } 160 161 void 162 pacct_print(void) 163 { 164 BTREEINFO bti; 165 DBT key, data, ndata; 166 DB *output_pacct_db; 167 struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk; 168 int rv; 169 170 bzero(&ci_total, sizeof ci_total); 171 strcpy(ci_total.ci_comm, ""); 172 bzero(&ci_other, sizeof ci_other); 173 strcpy(ci_other.ci_comm, "***other"); 174 bzero(&ci_junk, sizeof ci_junk); 175 strcpy(ci_junk.ci_comm, "**junk**"); 176 177 /* 178 * Retrieve them into new DB, sorted by appropriate key. 179 * At the same time, cull 'other' and 'junk' 180 */ 181 bzero(&bti, sizeof bti); 182 bti.compare = sa_cmp; 183 output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti); 184 if (output_pacct_db == NULL) { 185 warn("couldn't sort process accounting stats"); 186 return; 187 } 188 189 ndata.data = NULL; 190 ndata.size = 0; 191 rv = DB_SEQ(pacct_db, &key, &data, R_FIRST); 192 if (rv < 0) 193 warn("retrieving process accounting stats"); 194 while (rv == 0) { 195 cip = (struct cmdinfo *) data.data; 196 bcopy(cip, &ci, sizeof ci); 197 198 /* add to total */ 199 add_ci(&ci, &ci_total); 200 201 if (vflag && ci.ci_calls <= cutoff && 202 (fflag || check_junk(&ci))) { 203 /* put it into **junk** */ 204 add_ci(&ci, &ci_junk); 205 goto next; 206 } 207 if (!aflag && 208 ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) { 209 /* put into ***other */ 210 add_ci(&ci, &ci_other); 211 goto next; 212 } 213 rv = DB_PUT(output_pacct_db, &data, &ndata, 0); 214 if (rv < 0) 215 warn("sorting process accounting stats"); 216 217 next: rv = DB_SEQ(pacct_db, &key, &data, R_NEXT); 218 if (rv < 0) 219 warn("retrieving process accounting stats"); 220 } 221 222 /* insert **junk** and ***other */ 223 if (ci_junk.ci_calls != 0) { 224 data.data = &ci_junk; 225 data.size = sizeof ci_junk; 226 rv = DB_PUT(output_pacct_db, &data, &ndata, 0); 227 if (rv < 0) 228 warn("sorting process accounting stats"); 229 } 230 if (ci_other.ci_calls != 0) { 231 data.data = &ci_other; 232 data.size = sizeof ci_other; 233 rv = DB_PUT(output_pacct_db, &data, &ndata, 0); 234 if (rv < 0) 235 warn("sorting process accounting stats"); 236 } 237 238 /* print out the total */ 239 print_ci(&ci_total, &ci_total); 240 241 /* print out; if reversed, print first (smallest) first */ 242 rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST); 243 if (rv < 0) 244 warn("retrieving process accounting report"); 245 while (rv == 0) { 246 cip = (struct cmdinfo *) data.data; 247 bcopy(cip, &ci, sizeof ci); 248 249 print_ci(&ci, &ci_total); 250 251 rv = DB_SEQ(output_pacct_db, &data, &ndata, 252 rflag ? R_NEXT : R_PREV); 253 if (rv < 0) 254 warn("retrieving process accounting report"); 255 } 256 DB_CLOSE(output_pacct_db); 257 } 258 259 static int 260 check_junk(const struct cmdinfo *cip) 261 { 262 char *cp; 263 size_t len; 264 265 fprintf(stderr, "%s (%ju) -- ", cip->ci_comm, (uintmax_t)cip->ci_calls); 266 cp = fgetln(stdin, &len); 267 268 return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0; 269 } 270 271 static void 272 add_ci(const struct cmdinfo *fromcip, struct cmdinfo *tocip) 273 { 274 tocip->ci_calls += fromcip->ci_calls; 275 tocip->ci_etime += fromcip->ci_etime; 276 tocip->ci_utime += fromcip->ci_utime; 277 tocip->ci_stime += fromcip->ci_stime; 278 tocip->ci_mem += fromcip->ci_mem; 279 tocip->ci_io += fromcip->ci_io; 280 } 281 282 static void 283 print_ci(const struct cmdinfo *cip, const struct cmdinfo *totalcip) 284 { 285 double t, c; 286 int uflow; 287 288 c = cip->ci_calls ? cip->ci_calls : 1; 289 t = (cip->ci_utime + cip->ci_stime) / 1000000; 290 if (t < 0.01) { 291 t = 0.01; 292 uflow = 1; 293 } else 294 uflow = 0; 295 296 printf("%8ju ", (uintmax_t)cip->ci_calls); 297 if (cflag) { 298 if (cip != totalcip) 299 printf(" %4.1f%% ", cip->ci_calls / 300 (double)totalcip->ci_calls * 100); 301 else 302 printf(" %4s ", ""); 303 } 304 305 if (jflag) 306 printf("%11.3fre ", cip->ci_etime / (1000000 * c)); 307 else 308 printf("%11.3fre ", cip->ci_etime / (60.0 * 1000000)); 309 if (cflag) { 310 if (cip != totalcip) 311 printf(" %4.1f%% ", cip->ci_etime / 312 totalcip->ci_etime * 100); 313 else 314 printf(" %4s ", ""); 315 } 316 317 if (!lflag) { 318 if (jflag) 319 printf("%11.3fcp ", t / (double) cip->ci_calls); 320 else 321 printf("%11.2fcp ", t / 60.0); 322 if (cflag) { 323 if (cip != totalcip) 324 printf(" %4.1f%% ", 325 (cip->ci_utime + cip->ci_stime) / 326 (totalcip->ci_utime + totalcip->ci_stime) * 327 100); 328 else 329 printf(" %4s ", ""); 330 } 331 } else { 332 if (jflag) 333 printf("%11.3fu ", cip->ci_utime / (1000000 * c)); 334 else 335 printf("%11.2fu ", cip->ci_utime / (60.0 * 1000000)); 336 if (cflag) { 337 if (cip != totalcip) 338 printf(" %4.1f%% ", cip->ci_utime / 339 (double)totalcip->ci_utime * 100); 340 else 341 printf(" %4s ", ""); 342 } 343 if (jflag) 344 printf("%11.3fs ", cip->ci_stime / (1000000 * c)); 345 else 346 printf("%11.2fs ", cip->ci_stime / (60.0 * 1000000)); 347 if (cflag) { 348 if (cip != totalcip) 349 printf(" %4.1f%% ", cip->ci_stime / 350 (double)totalcip->ci_stime * 100); 351 else 352 printf(" %4s ", ""); 353 } 354 } 355 356 if (tflag) { 357 if (!uflow) 358 printf("%8.2fre/cp ", 359 cip->ci_etime / 360 (cip->ci_utime + cip->ci_stime)); 361 else 362 printf("*ignore* "); 363 } 364 365 if (Dflag) 366 printf("%10.0fio ", cip->ci_io); 367 else 368 printf("%8.0favio ", cip->ci_io / c); 369 370 if (Kflag) 371 printf("%10.0fk*sec ", cip->ci_mem); 372 else 373 printf("%8.0fk ", cip->ci_mem / t); 374 375 printf(" %s\n", cip->ci_comm); 376 } 377