From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- system_cmds/sa.tproj/db.c | 211 ++++++++++++++ system_cmds/sa.tproj/extern.h | 127 ++++++++ system_cmds/sa.tproj/main.c | 608 +++++++++++++++++++++++++++++++++++++++ system_cmds/sa.tproj/pathnames.h | 35 +++ system_cmds/sa.tproj/pdb.c | 469 ++++++++++++++++++++++++++++++ system_cmds/sa.tproj/sa.8 | 262 +++++++++++++++++ system_cmds/sa.tproj/usrdb.c | 261 +++++++++++++++++ 7 files changed, 1973 insertions(+) create mode 100644 system_cmds/sa.tproj/db.c create mode 100644 system_cmds/sa.tproj/extern.h create mode 100644 system_cmds/sa.tproj/main.c create mode 100644 system_cmds/sa.tproj/pathnames.h create mode 100644 system_cmds/sa.tproj/pdb.c create mode 100644 system_cmds/sa.tproj/sa.8 create mode 100644 system_cmds/sa.tproj/usrdb.c (limited to 'system_cmds/sa.tproj') diff --git a/system_cmds/sa.tproj/db.c b/system_cmds/sa.tproj/db.c new file mode 100644 index 0000000..299eeea --- /dev/null +++ b/system_cmds/sa.tproj/db.c @@ -0,0 +1,211 @@ +/*- + * Copyright (c) 2007 Diomidis Spinellis + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: src/usr.sbin/sa/db.c,v 1.3 2008/02/21 07:12:56 grog Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +/* Key used to store the version of the database data elements. */ +#define VERSION_KEY "\0VERSION" + +/* + * Create the in-memory database, *mdb. + * If iflag is not set, fill-in mdb with the records of the disk-based + * database dbname. + * Upgrade old-version records by calling v1_to_v2. + * Return 0 if OK, -1 on error. + */ +int +db_copy_in(DB **mdb, const char *dbname, const char *uname, BTREEINFO *bti, + int (*v1_to_v2)(DBT *key, DBT *data)) +{ + DBT key, data; + DB *ddb; + int error, rv, version; + + if ((*mdb = dbopen(NULL, O_RDWR, 0, DB_BTREE, bti)) == NULL) + return (-1); + + if (iflag) + return (0); + + if ((ddb = dbopen(dbname, O_RDONLY, 0, DB_BTREE, bti)) == NULL) { + if (errno == ENOENT) + return (0); + warn("retrieving %s summary", uname); + db_destroy(*mdb, uname); + return (-1); + } + + error = 0; + + /* Obtain/set version. */ + version = 1; + key.data = &VERSION_KEY; + key.size = sizeof(VERSION_KEY); + + rv = DB_GET(ddb, &key, &data, 0); + if (rv < 0) { + warn("get version key from %s stats", uname); + error = -1; + goto closeout; + } else if (rv == 0) { /* It's there; verify version. */ + if (data.size != sizeof(version)) { + warnx("invalid version size %zd in %s", + data.size, uname); + error = -1; + goto closeout; + } + memcpy(&version, data.data, data.size); + if (version != 2) { + warnx("unsupported version %d in %s", + version, uname); + error = -1; + goto closeout; + } + } + + for (rv = DB_SEQ(ddb, &key, &data, R_FIRST); rv == 0; + rv = DB_SEQ(ddb, &key, &data, R_NEXT)) { + + /* See if this is a version record. */ + if (key.size == sizeof(VERSION_KEY) && + memcmp(key.data, VERSION_KEY, sizeof(VERSION_KEY)) == 0) + continue; + + /* Convert record from v1, if needed. */ + if (version == 1 && v1_to_v2(&key, &data) < 0) { + warn("converting %s stats", uname); + error = -1; + goto closeout; + } + + /* Copy record to the in-memory database. */ + if ((rv = DB_PUT(*mdb, &key, &data, 0)) < 0) { + warn("initializing %s stats", uname); + error = -1; + goto closeout; + } + } + if (rv < 0) { + warn("retrieving %s summary", uname); + error = -1; + } + +closeout: + if (DB_CLOSE(ddb) < 0) { + warn("closing %s summary", uname); + error = -1; + } + + if (error) + db_destroy(*mdb, uname); + return (error); +} + +/* + * Save the in-memory database mdb to the disk database dbname. + * Return 0 if OK, -1 on error. + */ +int +db_copy_out(DB *mdb, const char *dbname, const char *uname, BTREEINFO *bti) +{ + DB *ddb; + DBT key, data; + int error, rv, version; + + if ((ddb = dbopen(dbname, O_RDWR|O_CREAT|O_TRUNC, 0644, + DB_BTREE, bti)) == NULL) { + warn("creating %s summary", uname); + return (-1); + } + + error = 0; + + for (rv = DB_SEQ(mdb, &key, &data, R_FIRST); + rv == 0; rv = DB_SEQ(mdb, &key, &data, R_NEXT)) { + if ((rv = DB_PUT(ddb, &key, &data, 0)) < 0) { + warn("saving %s summary", uname); + error = -1; + goto out; + } + } + if (rv < 0) { + warn("retrieving %s stats", uname); + error = -1; + } + +out: +#ifndef __APPLE__ + /* Add a version record. */ + key.data = &VERSION_KEY; + key.size = sizeof(VERSION_KEY); + version = 2; + data.data = &version; + data.size = sizeof(version); + if ((rv = DB_PUT(ddb, &key, &data, 0)) < 0) { + warn("add version record to %s stats", uname); + error = -1; + } else if (rv == 1) { + warnx("duplicate version record in %s stats", uname); + error = -1; + } +#else + version = 1; // avoid unused warning +#endif + + if (DB_SYNC(ddb, 0) < 0) { + warn("syncing %s summary", uname); + error = -1; + } + if (DB_CLOSE(ddb) < 0) { + warn("closing %s summary", uname); + error = -1; + } + return error; +} + +void +db_destroy(DB *db, const char *uname) +{ + if (DB_CLOSE(db) < 0) + warn("destroying %s stats", uname); +} diff --git a/system_cmds/sa.tproj/extern.h b/system_cmds/sa.tproj/extern.h new file mode 100644 index 0000000..d6fcc17 --- /dev/null +++ b/system_cmds/sa.tproj/extern.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/sa/extern.h,v 1.7 2007/05/22 06:51:38 dds Exp $ + */ + +#include +#include +#include + +/* structures */ + +/* All times are stored in 1e-6s units. */ + +struct cmdinfo { + char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */ + uid_t ci_uid; /* user id */ +#ifdef __APPLE__ + u_quad_t ci_calls; /* number of calls */ + u_quad_t ci_etime; /* elapsed time */ + u_quad_t ci_utime; /* user time */ + u_quad_t ci_stime; /* system time */ + u_quad_t ci_mem; /* memory use */ + u_quad_t ci_io; /* number of disk i/o ops */ +#else + double ci_etime; /* elapsed time */ + double ci_utime; /* user time */ + double ci_stime; /* system time */ + double ci_mem; /* memory use */ + double ci_io; /* number of disk i/o ops */ +#endif + u_int ci_flags; /* flags; see below */ +}; +#define CI_UNPRINTABLE 0x0001 /* unprintable chars in name */ + +struct userinfo { + uid_t ui_uid; /* user id; for consistency */ + u_quad_t ui_calls; /* number of invocations */ +#ifdef __APPLE__ + u_quad_t ui_utime; /* user time */ + u_quad_t ui_stime; /* system time */ + u_quad_t ui_mem; /* memory use */ + u_quad_t ui_io; /* number of disk i/o ops */ +#else + double ui_utime; /* user time */ + double ui_stime; /* system time */ + double ui_mem; /* memory use */ + double ui_io; /* number of disk i/o ops */ +#endif +}; + +/* typedefs */ + +typedef int (*cmpf_t)(const DBT *, const DBT *); + +/* external functions in db.c */ +int db_copy_in(DB **mdb, const char *dbname, const char *name, + BTREEINFO *bti, int (*v1_to_v2)(DBT *key, DBT *data)); +int db_copy_out(DB *mdb, const char *dbname, const char *name, + BTREEINFO *bti); +void db_destroy(DB *db, const char *uname); + +/* external functions in pdb.c */ +int pacct_init(void); +void pacct_destroy(void); +int pacct_add(const struct cmdinfo *); +int pacct_update(void); +void pacct_print(void); + +#ifndef __APPLE__ +/* external functions in readrec.c */ +int readrec_forward(FILE *f, struct acctv2 *av2); +#endif + +/* external functions in usrdb.c */ +int usracct_init(void); +void usracct_destroy(void); +int usracct_add(const struct cmdinfo *); +int usracct_update(void); +void usracct_print(void); + +/* variables */ + +extern int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag; +extern int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag; +extern u_quad_t cutoff; +extern cmpf_t sa_cmp; +extern const char *pdb_file, *usrdb_file; + +/* some #defines to help with db's stupidity */ + +#define DB_CLOSE(db) \ + ((*(db)->close)(db)) +#define DB_GET(db, key, data, flags) \ + ((*(db)->get)((db), (key), (data), (flags))) +#define DB_PUT(db, key, data, flags) \ + ((*(db)->put)((db), (key), (data), (flags))) +#define DB_SYNC(db, flags) \ + ((*(db)->sync)((db), (flags))) +#define DB_SEQ(db, key, data, flags) \ + ((*(db)->seq)((db), (key), (data), (flags))) diff --git a/system_cmds/sa.tproj/main.c b/system_cmds/sa.tproj/main.c new file mode 100644 index 0000000..d8263f1 --- /dev/null +++ b/system_cmds/sa.tproj/main.c @@ -0,0 +1,608 @@ +/* + * Copyright (c) 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1994 Christopher G. Demetriou\n\ + All rights reserved.\n"; +#endif +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/sa/main.c,v 1.18 2007/05/22 06:51:38 dds Exp $"); + +/* + * sa: system accounting + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "extern.h" +#include "pathnames.h" + +static FILE *acct_load(const char *, int); +#ifdef __APPLE__ +static u_quad_t decode_comp_t(comp_t); +#endif +static int cmp_comm(const char *, const char *); +static int cmp_usrsys(const DBT *, const DBT *); +static int cmp_avgusrsys(const DBT *, const DBT *); +static int cmp_dkio(const DBT *, const DBT *); +static int cmp_avgdkio(const DBT *, const DBT *); +static int cmp_cpumem(const DBT *, const DBT *); +static int cmp_avgcpumem(const DBT *, const DBT *); +static int cmp_calls(const DBT *, const DBT *); +static void usage(void); + +int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag; +int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag; +u_quad_t cutoff = 1; +const char *pdb_file = _PATH_SAVACCT; +const char *usrdb_file = _PATH_USRACCT; + +static char *dfltargv[] = { NULL }; +static int dfltargc = (sizeof dfltargv/sizeof(char *)); + +/* default to comparing by sum of user + system time */ +cmpf_t sa_cmp = cmp_usrsys; + +int +main(int argc, char **argv) +{ + FILE *f; + char pathacct[] = _PATH_ACCT; + int ch, error = 0; + + dfltargv[0] = pathacct; + + while ((ch = getopt(argc, argv, "abcdDfijkKlmnP:qrstuU:v:")) != -1) + switch (ch) { + case 'a': + /* print all commands */ + aflag = 1; + break; + case 'b': + /* sort by per-call user/system time average */ + bflag = 1; + sa_cmp = cmp_avgusrsys; + break; + case 'c': + /* print percentage total time */ + cflag = 1; + break; + case 'd': + /* sort by averge number of disk I/O ops */ + dflag = 1; + sa_cmp = cmp_avgdkio; + break; + case 'D': + /* print and sort by total disk I/O ops */ + Dflag = 1; + sa_cmp = cmp_dkio; + break; + case 'f': + /* force no interactive threshold comprison */ + fflag = 1; + break; + case 'i': + /* do not read in summary file */ + iflag = 1; + break; + case 'j': + /* instead of total minutes, give sec/call */ + jflag = 1; + break; + case 'k': + /* sort by cpu-time average memory usage */ + kflag = 1; + sa_cmp = cmp_avgcpumem; + break; + case 'K': + /* print and sort by cpu-storage integral */ + sa_cmp = cmp_cpumem; + Kflag = 1; + break; + case 'l': + /* separate system and user time */ + lflag = 1; + break; + case 'm': + /* print procs and time per-user */ + mflag = 1; + break; + case 'n': + /* sort by number of calls */ + sa_cmp = cmp_calls; + break; + case 'P': + /* specify program database summary file */ + pdb_file = optarg; + break; + case 'q': + /* quiet; error messages only */ + qflag = 1; + break; + case 'r': + /* reverse order of sort */ + rflag = 1; + break; + case 's': + /* merge accounting file into summaries */ + sflag = 1; + break; + case 't': + /* report ratio of user and system times */ + tflag = 1; + break; + case 'u': + /* first, print uid and command name */ + uflag = 1; + break; + case 'U': + /* specify user database summary file */ + usrdb_file = optarg; + break; + case 'v': + /* cull junk */ + vflag = 1; + cutoff = atoi(optarg); + break; + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + + /* various argument checking */ + if (fflag && !vflag) + errx(1, "only one of -f requires -v"); + if (fflag && aflag) + errx(1, "only one of -a and -v may be specified"); + /* XXX need more argument checking */ + + if (!uflag) { + /* initialize tables */ + if ((sflag || (!mflag && !qflag)) && pacct_init() != 0) + errx(1, "process accounting initialization failed"); + if ((sflag || (mflag && !qflag)) && usracct_init() != 0) + errx(1, "user accounting initialization failed"); + } + + if (argc == 0) { + argc = dfltargc; + argv = dfltargv; + } + + /* for each file specified */ + for (; argc > 0; argc--, argv++) { + /* + * load the accounting data from the file. + * if it fails, go on to the next file. + */ + f = acct_load(argv[0], sflag); + if (f == NULL) + continue; + + if (!uflag && sflag) { +#ifndef DEBUG + sigset_t nmask, omask; + int unmask = 1; + + /* + * block most signals so we aren't interrupted during + * the update. + */ + if (sigfillset(&nmask) == -1) { + warn("sigfillset"); + unmask = 0; + error = 1; + } + if (unmask && + (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) { + warn("couldn't set signal mask"); + unmask = 0; + error = 1; + } +#endif /* DEBUG */ + + /* + * truncate the accounting data file ASAP, to avoid + * losing data. don't worry about errors in updating + * the saved stats; better to underbill than overbill, + * but we want every accounting record intact. + */ + if (ftruncate(fileno(f), 0) == -1) { + warn("couldn't truncate %s", *argv); + error = 1; + } + + /* + * update saved user and process accounting data. + * note errors for later. + */ + if (pacct_update() != 0 || usracct_update() != 0) + error = 1; + +#ifndef DEBUG + /* + * restore signals + */ + if (unmask && + (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) { + warn("couldn't restore signal mask"); + error = 1; + } +#endif /* DEBUG */ + } + + /* + * close the opened accounting file + */ + if (fclose(f) == EOF) { + warn("fclose %s", *argv); + error = 1; + } + } + + if (!uflag && !qflag) { + /* print any results we may have obtained. */ + if (!mflag) + pacct_print(); + else + usracct_print(); + } + + if (!uflag) { + /* finally, deallocate databases */ + if (sflag || (!mflag && !qflag)) + pacct_destroy(); + if (sflag || (mflag && !qflag)) + usracct_destroy(); + } + + exit(error); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: sa [-abcdDfijkKlmnqrstu] [-P file] [-U file] [-v cutoff] [file ...]\n"); + exit(1); +} + +static FILE * +acct_load(const char *pn, int wr) +{ +#ifdef __APPLE__ + struct acct ac; +#else + struct acctv2 ac; +#endif + struct cmdinfo ci; + ssize_t rv; + FILE *f; + int i; + + /* + * open the file + */ + f = fopen(pn, wr ? "r+" : "r"); + if (f == NULL) { + warn("open %s %s", pn, wr ? "for read/write" : "read-only"); + return (NULL); + } + + /* + * read all we can; don't stat and open because more processes + * could exit, and we'd miss them + */ + while (1) { + /* get one accounting entry and punt if there's an error */ +#ifdef __APPLE__ + rv = read(fileno(f), &ac, sizeof(struct acct)); + if (rv == -1) + warn("error reading %s", pn); + else if (rv > 0 && rv < (int)sizeof(struct acct)) + warnx("short read of accounting data in %s", pn); + if (rv != sizeof(struct acct)) + break; +#else + rv = readrec_forward(f, &ac); + if (rv != 1) { + if (rv == EOF) + warn("error reading %s", pn); + break; + } +#endif + + /* decode it */ + ci.ci_calls = 1; + for (i = 0; i < (int)sizeof ac.ac_comm && ac.ac_comm[i] != '\0'; + i++) { + char c = ac.ac_comm[i]; + + if (!isascii(c) || iscntrl(c)) { + ci.ci_comm[i] = '?'; + ci.ci_flags |= CI_UNPRINTABLE; + } else + ci.ci_comm[i] = c; + } +#ifdef __APPLE__ + if (ac.ac_flag & AFORK) + ci.ci_comm[i++] = '*'; + ci.ci_comm[i++] = '\0'; + ci.ci_etime = decode_comp_t(ac.ac_etime); + ci.ci_utime = decode_comp_t(ac.ac_utime); + ci.ci_stime = decode_comp_t(ac.ac_stime); + ci.ci_uid = ac.ac_uid; + ci.ci_mem = ac.ac_mem; + ci.ci_io = decode_comp_t(ac.ac_io) / AHZ; +#else + if (ac.ac_flagx & AFORK) + ci.ci_comm[i++] = '*'; + ci.ci_comm[i++] = '\0'; + ci.ci_etime = ac.ac_etime; + ci.ci_utime = ac.ac_utime; + ci.ci_stime = ac.ac_stime; + ci.ci_uid = ac.ac_uid; + ci.ci_mem = ac.ac_mem; + ci.ci_io = ac.ac_io; +#endif + + if (!uflag) { + /* and enter it into the usracct and pacct databases */ + if (sflag || (!mflag && !qflag)) + pacct_add(&ci); + if (sflag || (mflag && !qflag)) + usracct_add(&ci); + } else if (!qflag) +#ifdef __APPLE__ + printf("%6u %12.2f cpu %12juk mem %12ju io %s\n", + ci.ci_uid, + (ci.ci_utime + ci.ci_stime) / (double) AHZ, + (uintmax_t)ci.ci_mem, (uintmax_t)ci.ci_io, + ci.ci_comm); +#else + printf("%6u %12.3lf cpu %12.0lfk mem %12.0lf io %s\n", + ci.ci_uid, + (ci.ci_utime + ci.ci_stime) / 1000000, + ci.ci_mem, ci.ci_io, + ci.ci_comm); +#endif + } + + /* Finally, return the file stream for possible truncation. */ + return (f); +} + +#ifdef __APPLE__ +static u_quad_t decode_comp_t(comp_t comp) +{ + u_quad_t rv; + + /* + * for more info on the comp_t format, see: + * /usr/src/sys/kern/kern_acct.c + * /usr/src/sys/sys/acct.h + * /usr/src/usr.bin/lastcomm/lastcomm.c + */ + rv = comp & 0x1fff; /* 13 bit fraction */ + comp >>= 13; /* 3 bit base-8 exponent */ + while (comp--) + rv <<= 3; + + return (rv); +} +#endif + +/* sort commands, doing the right thing in terms of reversals */ +static int +cmp_comm(const char *s1, const char *s2) +{ + int rv; + + rv = strcmp(s1, s2); + if (rv == 0) + rv = -1; + return (rflag ? rv : -rv); +} + +/* sort by total user and system time */ +static int +cmp_usrsys(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; +#ifdef __APPLE__ + u_quad_t t1, t2; +#else + double t1, t2; +#endif + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + + t1 = c1.ci_utime + c1.ci_stime; + t2 = c2.ci_utime + c2.ci_stime; + + if (t1 < t2) + return -1; + else if (t1 == t2) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} + +/* sort by average user and system time */ +static int +cmp_avgusrsys(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; + double t1, t2; + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + + t1 = c1.ci_utime + c1.ci_stime; + t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1); + + t2 = c2.ci_utime + c2.ci_stime; + t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1); + + if (t1 < t2) + return -1; + else if (t1 == t2) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} + +/* sort by total number of disk I/O operations */ +static int +cmp_dkio(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + + if (c1.ci_io < c2.ci_io) + return -1; + else if (c1.ci_io == c2.ci_io) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} + +/* sort by average number of disk I/O operations */ +static int +cmp_avgdkio(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; + double n1, n2; + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + +#ifdef __APPLE__ + n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1); + n2 = (double) c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1); +#else + n1 = c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1); + n2 = c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1); +#endif + + if (n1 < n2) + return -1; + else if (n1 == n2) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} + +/* sort by the cpu-storage integral */ +static int +cmp_cpumem(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + + if (c1.ci_mem < c2.ci_mem) + return -1; + else if (c1.ci_mem == c2.ci_mem) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} + +/* sort by the cpu-time average memory usage */ +static int +cmp_avgcpumem(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; +#ifdef __APPLE__ + u_quad_t t1, t2; +#else + double t1, t2; +#endif + double n1, n2; + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + + t1 = c1.ci_utime + c1.ci_stime; + t2 = c2.ci_utime + c2.ci_stime; + +#ifdef __APPLE__ + n1 = (double) c1.ci_mem / (double) (t1 ? t1 : 1); + n2 = (double) c2.ci_mem / (double) (t2 ? t2 : 1); +#else + n1 = c1.ci_mem / (t1 ? t1 : 1); + n2 = c2.ci_mem / (t2 ? t2 : 1); +#endif + + if (n1 < n2) + return -1; + else if (n1 == n2) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} + +/* sort by the number of invocations */ +static int +cmp_calls(const DBT *d1, const DBT *d2) +{ + struct cmdinfo c1, c2; + + memcpy(&c1, d1->data, sizeof(c1)); + memcpy(&c2, d2->data, sizeof(c2)); + + if (c1.ci_calls < c2.ci_calls) + return -1; + else if (c1.ci_calls == c2.ci_calls) + return (cmp_comm(c1.ci_comm, c2.ci_comm)); + else + return 1; +} diff --git a/system_cmds/sa.tproj/pathnames.h b/system_cmds/sa.tproj/pathnames.h new file mode 100644 index 0000000..8cb7f44 --- /dev/null +++ b/system_cmds/sa.tproj/pathnames.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/sa/pathnames.h,v 1.4 1999/08/28 01:19:52 peter Exp $ + */ + +#define _PATH_ACCT "/var/account/acct" +#define _PATH_SAVACCT "/var/account/savacct" +#define _PATH_USRACCT "/var/account/usracct" diff --git a/system_cmds/sa.tproj/pdb.c b/system_cmds/sa.tproj/pdb.c new file mode 100644 index 0000000..928aad9 --- /dev/null +++ b/system_cmds/sa.tproj/pdb.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/usr.sbin/sa/pdb.c,v 1.14 2007/05/22 06:51:38 dds Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "extern.h" +#include "pathnames.h" + +static int check_junk(const struct cmdinfo *); +static void add_ci(const struct cmdinfo *, struct cmdinfo *); +static void print_ci(const struct cmdinfo *, const struct cmdinfo *); + +static DB *pacct_db; + +/* Legacy format in AHZV1 units. */ +struct cmdinfov1 { + char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */ + uid_t ci_uid; /* user id */ + u_quad_t ci_calls; /* number of calls */ + u_quad_t ci_etime; /* elapsed time */ + u_quad_t ci_utime; /* user time */ + u_quad_t ci_stime; /* system time */ + u_quad_t ci_mem; /* memory use */ + u_quad_t ci_io; /* number of disk i/o ops */ + u_int ci_flags; /* flags; see below */ +}; + +/* + * Convert a v1 data record into the current version. + * Return 0 if OK, -1 on error, setting errno. + */ +static int +v1_to_v2(DBT *key __unused, DBT *data) +{ + struct cmdinfov1 civ1; + static struct cmdinfo civ2; + + if (data->size != sizeof(civ1)) { + errno = EFTYPE; + return (-1); + } + memcpy(&civ1, data->data, data->size); + memset(&civ2, 0, sizeof(civ2)); + memcpy(civ2.ci_comm, civ1.ci_comm, sizeof(civ2.ci_comm)); + civ2.ci_uid = civ1.ci_uid; + civ2.ci_calls = civ1.ci_calls; + civ2.ci_etime = ((double)civ1.ci_etime / AHZV1) * 1000000; + civ2.ci_utime = ((double)civ1.ci_utime / AHZV1) * 1000000; + civ2.ci_stime = ((double)civ1.ci_stime / AHZV1) * 1000000; + civ2.ci_mem = civ1.ci_mem; + civ2.ci_io = civ1.ci_io; + civ2.ci_flags = civ1.ci_flags; + data->size = sizeof(civ2); + data->data = &civ2; + return (0); +} + +/* Copy pdb_file to in-memory pacct_db. */ +int +pacct_init(void) +{ + return (db_copy_in(&pacct_db, pdb_file, "process accounting", + NULL, v1_to_v2)); +} + +void +pacct_destroy(void) +{ + db_destroy(pacct_db, "process accounting"); +} + +int +pacct_add(const struct cmdinfo *ci) +{ + DBT key, data; + struct cmdinfo newci; + char keydata[sizeof ci->ci_comm]; + int rv; + + bcopy(ci->ci_comm, &keydata, sizeof keydata); + key.data = &keydata; + key.size = strlen(keydata); + + rv = DB_GET(pacct_db, &key, &data, 0); + if (rv < 0) { + warn("get key %s from process accounting stats", ci->ci_comm); + return (-1); + } else if (rv == 0) { /* it's there; copy whole thing */ + /* XXX compare size if paranoid */ + /* add the old data to the new data */ + bcopy(data.data, &newci, data.size); + } else { /* it's not there; zero it and copy the key */ + bzero(&newci, sizeof newci); + bcopy(key.data, newci.ci_comm, key.size); + } + + add_ci(ci, &newci); + + data.data = &newci; + data.size = sizeof newci; + rv = DB_PUT(pacct_db, &key, &data, 0); + if (rv < 0) { + warn("add key %s to process accounting stats", ci->ci_comm); + return (-1); + } else if (rv == 1) { + warnx("duplicate key %s in process accounting stats", + ci->ci_comm); + return (-1); + } + + return (0); +} + +/* Copy in-memory pacct_db to pdb_file. */ +int +pacct_update(void) +{ + return (db_copy_out(pacct_db, pdb_file, "process accounting", + NULL)); +} + +void +pacct_print(void) +{ + BTREEINFO bti; + DBT key, data, ndata; + DB *output_pacct_db; + struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk; + int rv; + + bzero(&ci_total, sizeof ci_total); + strcpy(ci_total.ci_comm, ""); + bzero(&ci_other, sizeof ci_other); + strcpy(ci_other.ci_comm, "***other"); + bzero(&ci_junk, sizeof ci_junk); + strcpy(ci_junk.ci_comm, "**junk**"); + + /* + * Retrieve them into new DB, sorted by appropriate key. + * At the same time, cull 'other' and 'junk' + */ + bzero(&bti, sizeof bti); + bti.compare = sa_cmp; + output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti); + if (output_pacct_db == NULL) { + warn("couldn't sort process accounting stats"); + return; + } + + ndata.data = NULL; + ndata.size = 0; + rv = DB_SEQ(pacct_db, &key, &data, R_FIRST); + if (rv < 0) + warn("retrieving process accounting stats"); + while (rv == 0) { + cip = (struct cmdinfo *) data.data; + bcopy(cip, &ci, sizeof ci); + + /* add to total */ + add_ci(&ci, &ci_total); + + if (vflag && ci.ci_calls <= cutoff && + (fflag || check_junk(&ci))) { + /* put it into **junk** */ + add_ci(&ci, &ci_junk); + goto next; + } + if (!aflag && + ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) { + /* put into ***other */ + add_ci(&ci, &ci_other); + goto next; + } + rv = DB_PUT(output_pacct_db, &data, &ndata, 0); + if (rv < 0) + warn("sorting process accounting stats"); + +next: rv = DB_SEQ(pacct_db, &key, &data, R_NEXT); + if (rv < 0) + warn("retrieving process accounting stats"); + } + + /* insert **junk** and ***other */ + if (ci_junk.ci_calls != 0) { + data.data = &ci_junk; + data.size = sizeof ci_junk; + rv = DB_PUT(output_pacct_db, &data, &ndata, 0); + if (rv < 0) + warn("sorting process accounting stats"); + } + if (ci_other.ci_calls != 0) { + data.data = &ci_other; + data.size = sizeof ci_other; + rv = DB_PUT(output_pacct_db, &data, &ndata, 0); + if (rv < 0) + warn("sorting process accounting stats"); + } + + /* print out the total */ + print_ci(&ci_total, &ci_total); + + /* print out; if reversed, print first (smallest) first */ + rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST); + if (rv < 0) + warn("retrieving process accounting report"); + while (rv == 0) { + cip = (struct cmdinfo *) data.data; + bcopy(cip, &ci, sizeof ci); + + print_ci(&ci, &ci_total); + + rv = DB_SEQ(output_pacct_db, &data, &ndata, + rflag ? R_NEXT : R_PREV); + if (rv < 0) + warn("retrieving process accounting report"); + } + DB_CLOSE(output_pacct_db); +} + +static int +check_junk(const struct cmdinfo *cip) +{ + char *cp; + size_t len; + + fprintf(stderr, "%s (%ju) -- ", cip->ci_comm, (uintmax_t)cip->ci_calls); + cp = fgetln(stdin, &len); + + return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0; +} + +static void +add_ci(const struct cmdinfo *fromcip, struct cmdinfo *tocip) +{ + tocip->ci_calls += fromcip->ci_calls; + tocip->ci_etime += fromcip->ci_etime; + tocip->ci_utime += fromcip->ci_utime; + tocip->ci_stime += fromcip->ci_stime; + tocip->ci_mem += fromcip->ci_mem; + tocip->ci_io += fromcip->ci_io; +} + +#ifdef __APPLE__ +static void +print_ci(const struct cmdinfo *cip, const struct cmdinfo *totalcip) +{ + double t, c; + int uflow; + + c = cip->ci_calls ? cip->ci_calls : 1; + t = (cip->ci_utime + cip->ci_stime) / (double) AHZ; + if (t < 0.01) { + t = 0.01; + uflow = 1; + } else + uflow = 0; + + printf("%8ju ", (uintmax_t)cip->ci_calls); + if (cflag) { + if (cip != totalcip) + printf(" %4.2f%% ", + cip->ci_calls / (double) totalcip->ci_calls); + else + printf(" %4s ", ""); + } + + if (jflag) + printf("%11.2fre ", cip->ci_etime / (double) (AHZ * c)); + else + printf("%11.2fre ", cip->ci_etime / (60.0 * AHZ)); + if (cflag) { + if (cip != totalcip) + printf(" %4.2f%% ", + cip->ci_etime / (double) totalcip->ci_etime); + else + printf(" %4s ", ""); + } + + if (!lflag) { + if (jflag) + printf("%11.2fcp ", t / (double) cip->ci_calls); + else + printf("%11.2fcp ", t / 60.0); + if (cflag) { + if (cip != totalcip) + printf(" %4.2f%% ", + (cip->ci_utime + cip->ci_stime) / (double) + (totalcip->ci_utime + totalcip->ci_stime)); + else + printf(" %4s ", ""); + } + } else { + if (jflag) + printf("%11.2fu ", cip->ci_utime / (double) (AHZ * c)); + else + printf("%11.2fu ", cip->ci_utime / (60.0 * AHZ)); + if (cflag) { + if (cip != totalcip) + printf(" %4.2f%% ", cip->ci_utime / (double) totalcip->ci_utime); + else + printf(" %4s ", ""); + } + if (jflag) + printf("%11.2fs ", cip->ci_stime / (double) (AHZ * c)); + else + printf("%11.2fs ", cip->ci_stime / (60.0 * AHZ)); + if (cflag) { + if (cip != totalcip) + printf(" %4.2f%% ", cip->ci_stime / (double) totalcip->ci_stime); + else + printf(" %4s ", ""); + } + } + + if (tflag) { + if (!uflow) + printf("%8.2fre/cp ", + cip->ci_etime / + (double) (cip->ci_utime + cip->ci_stime)); + else + printf("*ignore* "); + } + + if (Dflag) + printf("%10jutio ", (uintmax_t)cip->ci_io); + else + printf("%8.0favio ", cip->ci_io / c); + + if (Kflag) + printf("%10juk*sec ", (uintmax_t)cip->ci_mem); + else + printf("%8.0fk ", cip->ci_mem / t); + + printf(" %s\n", cip->ci_comm); +} +#else +static void +print_ci(const struct cmdinfo *cip, const struct cmdinfo *totalcip) +{ + double t, c; + int uflow; + + c = cip->ci_calls ? cip->ci_calls : 1; + t = (cip->ci_utime + cip->ci_stime) / 1000000; + if (t < 0.01) { + t = 0.01; + uflow = 1; + } else + uflow = 0; + + printf("%8ju ", (uintmax_t)cip->ci_calls); + if (cflag) { + if (cip != totalcip) + printf(" %4.1f%% ", cip->ci_calls / + (double)totalcip->ci_calls * 100); + else + printf(" %4s ", ""); + } + + if (jflag) + printf("%11.3fre ", cip->ci_etime / (1000000 * c)); + else + printf("%11.3fre ", cip->ci_etime / (60.0 * 1000000)); + if (cflag) { + if (cip != totalcip) + printf(" %4.1f%% ", cip->ci_etime / + totalcip->ci_etime * 100); + else + printf(" %4s ", ""); + } + + if (!lflag) { + if (jflag) + printf("%11.3fcp ", t / (double) cip->ci_calls); + else + printf("%11.2fcp ", t / 60.0); + if (cflag) { + if (cip != totalcip) + printf(" %4.1f%% ", + (cip->ci_utime + cip->ci_stime) / + (totalcip->ci_utime + totalcip->ci_stime) * + 100); + else + printf(" %4s ", ""); + } + } else { + if (jflag) + printf("%11.3fu ", cip->ci_utime / (1000000 * c)); + else + printf("%11.2fu ", cip->ci_utime / (60.0 * 1000000)); + if (cflag) { + if (cip != totalcip) + printf(" %4.1f%% ", cip->ci_utime / + (double)totalcip->ci_utime * 100); + else + printf(" %4s ", ""); + } + if (jflag) + printf("%11.3fs ", cip->ci_stime / (1000000 * c)); + else + printf("%11.2fs ", cip->ci_stime / (60.0 * 1000000)); + if (cflag) { + if (cip != totalcip) + printf(" %4.1f%% ", cip->ci_stime / + (double)totalcip->ci_stime * 100); + else + printf(" %4s ", ""); + } + } + + if (tflag) { + if (!uflow) + printf("%8.2fre/cp ", + cip->ci_etime / + (cip->ci_utime + cip->ci_stime)); + else + printf("*ignore* "); + } + + if (Dflag) + printf("%10.0fio ", cip->ci_io); + else + printf("%8.0favio ", cip->ci_io / c); + + if (Kflag) + printf("%10.0fk*sec ", cip->ci_mem); + else + printf("%8.0fk ", cip->ci_mem / t); + + printf(" %s\n", cip->ci_comm); +} +#endif diff --git a/system_cmds/sa.tproj/sa.8 b/system_cmds/sa.tproj/sa.8 new file mode 100644 index 0000000..1e9540c --- /dev/null +++ b/system_cmds/sa.tproj/sa.8 @@ -0,0 +1,262 @@ +.\" +.\" Copyright (c) 1994 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.sbin/sa/sa.8,v 1.20 2007/05/18 12:36:10 dds Exp $ +.\" +.Dd May 18, 2007 +.Dt SA 8 +.Os +.Sh NAME +.Nm sa +.Nd print system accounting statistics +.Sh SYNOPSIS +.Nm +.Op Fl abcdDfijkKlmnqrstu +.Op Fl P Ar file +.Op Fl U Ar file +.Op Fl v Ar cutoff +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility reports on, cleans up, +and generally maintains system +accounting files. +.Pp +The +.Nm +utility is able to condense the information in +.Pa /var/account/acct +into the summary files +.Pa /var/account/savacct +and +.Pa /var/account/usracct , +which contain system statistics according +to command name and login id, respectively. +This condensation is desirable because on a +large system, +.Pa /var/account/acct +can grow by hundreds of blocks per day. +The summary files are normally read before +the accounting file, so that reports include +all available information. +.Pp +If file names are supplied, they are read instead of +.Pa /var/account/acct . +After each file is read, if the summary +files are being updated, an updated summary will +be saved to disk. +Only one report is printed, +after the last file is processed. +.Pp +The labels used in the output indicate the following, except +where otherwise specified by individual options: +.Bl -tag -width k*sec +.It Dv avio +Average number of I/O operations per execution +.It Dv cp +Sum of user and system time, in minutes +.It Dv cpu +Same as +.Dv cp +.It Dv k +CPU-time averaged core usage, in 1k units +.It Dv k*sec +CPU storage integral, in 1k-core seconds +.It Dv re +Real time, in minutes +.It Dv s +System time, in minutes +.It Dv tio +Total number of I/O operations +.It Dv u +User time, in minutes +.El +.Pp +The options to +.Nm +are: +.Bl -tag -width Ds +.It Fl a +List all command names, including those containing unprintable +characters and those used only once. +By default, +.Nm +places all names containing unprintable characters and +those used only once under the name ``***other''. +.It Fl b +If printing command statistics, sort output by the sum of user and system +time divided by number of calls. +.It Fl c +In addition to the number of calls and the user, system and real times +for each command, print their percentage of the total over all commands. +.It Fl d +If printing command statistics, sort by the average number of disk +I/O operations. +If printing user statistics, print the average number of +disk I/O operations per user. +.It Fl D +If printing command statistics, sort and print by the total number +of disk I/O operations. +.It Fl f +Force no interactive threshold comparison with the +.Fl v +option. +.It Fl i +Do not read in the summary files. +.It Fl j +Instead of the total minutes per category, give seconds per call. +.It Fl k +If printing command statistics, sort by the cpu-time average memory +usage. +If printing user statistics, print the cpu-time average +memory usage. +.It Fl K +If printing command statistics, print and sort by the cpu-storage integral. +.It Fl l +Separate system and user time; normally they are combined. +.It Fl m +Print per-user statistics rather than per-command statistics. +.It Fl n +Sort by number of calls. +.It Fl P Ar file +Use the specified +.Ar file +for accessing the per-command accounting summary database, +instead of the default +.Pa /var/account/savacct . +.It Fl q +Create no output other than error messages. +.It Fl r +Reverse order of sort. +.It Fl s +Truncate the accounting files when done and merge their data +into the summary files. +.It Fl t +For each command, report the ratio of real time to the sum +of user and system cpu times. +If the cpu time is too small to report, ``*ignore*'' appears in +this field. +.It Fl U Ar file +Use the specified +.Ar file +for accessing the per-user accounting summary database, +instead of the default +.Pa /var/account/usracct . +.It Fl u +Superseding all other flags, for each entry +in the accounting file, print the user ID, total seconds of cpu usage, +total memory usage, number of I/O operations performed, and +command name. +.It Fl v Ar cutoff +For each command used +.Ar cutoff +times or fewer, print the command name and await a reply +from the terminal. +If the reply begins with ``y'', add +the command to the category ``**junk**''. +This flag is +used to strip garbage from the report. +.El +.Pp +By default, per-command statistics will be printed. +The number of +calls, the total elapsed time in minutes, total cpu and user time +in minutes, average number of I/O operations, and CPU-time +averaged core usage will be printed. +If the +.Fl m +option is specified, per-user statistics will be printed, including +the user name, the number of commands invoked, total cpu time used +(in minutes), total number of I/O operations, and CPU storage integral +for each user. +If the +.Fl u +option is specified, the uid, user and system time (in seconds), +CPU storage integral, I/O usage, and command name will be printed +for each entry in the accounting data file. +.Pp +If the +.Fl u +flag is specified, all flags other than +.Fl q +are ignored. +If the +.Fl m +flag is specified, only the +.Fl b , +.Fl d , +.Fl i , +.Fl k , +.Fl q , +and +.Fl s +flags are honored. +.Sh FILES +.Bl -tag -width /var/account/usracct -compact +.It Pa /var/account/acct +raw accounting data file +.It Pa /var/account/savacct +per-command accounting summary database +.It Pa /var/account/usracct +per-user accounting summary database +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr lastcomm 1 , +.Xr acct 5 , +.Xr ac 8 , +.Xr accton 8 +.Sh CAVEATS +While the behavior of the options in this version of +.Nm +was modeled after the original version, there are some intentional +differences and undoubtedly some unintentional ones as well. +In +particular, the +.Fl q +option has been added, and the +.Fl m +option now understands more options than it used to. +.Pp +The formats of the summary files created by this version of +.Nm +are very different from the those used by the original version. +This is not considered a problem, however, because the accounting record +format has changed as well (since user ids are now 32 bits). +.Sh AUTHORS +.An Chris G. Demetriou Aq cgd@postgres.berkeley.edu +.Sh BUGS +The number of options to this program is absurd, especially considering +that there is not much logic behind their lettering. +.Pp +The field labels should be more consistent. +.Pp +The VM system does not record the CPU storage integral. diff --git a/system_cmds/sa.tproj/usrdb.c b/system_cmds/sa.tproj/usrdb.c new file mode 100644 index 0000000..e47220a --- /dev/null +++ b/system_cmds/sa.tproj/usrdb.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/usr.sbin/sa/usrdb.c,v 1.16 2007/05/22 06:51:38 dds Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "extern.h" +#include "pathnames.h" + +static int uid_compare(const DBT *, const DBT *); + +static DB *usracct_db; + +/* Legacy format in AHZV1 units. */ +struct userinfov1 { + uid_t ui_uid; /* user id; for consistency */ + u_quad_t ui_calls; /* number of invocations */ + u_quad_t ui_utime; /* user time */ + u_quad_t ui_stime; /* system time */ + u_quad_t ui_mem; /* memory use */ + u_quad_t ui_io; /* number of disk i/o ops */ +}; + +/* + * Convert a v1 data record into the current version. + * Return 0 if OK, -1 on error, setting errno. + */ +static int +v1_to_v2(DBT *key, DBT *data) +{ + struct userinfov1 uiv1; + static struct userinfo uiv2; + static uid_t uid; + + if (key->size != sizeof(u_long) || data->size != sizeof(uiv1)) { + errno = EFTYPE; + return (-1); + } + + /* Convert key. */ + key->size = sizeof(uid_t); + uid = (uid_t)*(u_long *)(key->data); + key->data = &uid; + + /* Convert data. */ + memcpy(&uiv1, data->data, data->size); + memset(&uiv2, 0, sizeof(uiv2)); + uiv2.ui_uid = uiv1.ui_uid; + uiv2.ui_calls = uiv1.ui_calls; + uiv2.ui_utime = ((double)uiv1.ui_utime / AHZV1) * 1000000; + uiv2.ui_stime = ((double)uiv1.ui_stime / AHZV1) * 1000000; + uiv2.ui_mem = uiv1.ui_mem; + uiv2.ui_io = uiv1.ui_io; + data->size = sizeof(uiv2); + data->data = &uiv2; + + return (0); +} + +/* Copy usrdb_file to in-memory usracct_db. */ +int +usracct_init(void) +{ + BTREEINFO bti; + + bzero(&bti, sizeof bti); + bti.compare = uid_compare; + + return (db_copy_in(&usracct_db, usrdb_file, "user accounting", + &bti, v1_to_v2)); +} + +void +usracct_destroy(void) +{ + db_destroy(usracct_db, "user accounting"); +} + +int +usracct_add(const struct cmdinfo *ci) +{ + DBT key, data; + struct userinfo newui; + uid_t uid; + int rv; + + uid = ci->ci_uid; + key.data = &uid; + key.size = sizeof uid; + + rv = DB_GET(usracct_db, &key, &data, 0); + if (rv < 0) { + warn("get key %u from user accounting stats", uid); + return (-1); + } else if (rv == 0) { /* it's there; copy whole thing */ + /* add the old data to the new data */ + bcopy(data.data, &newui, data.size); + if (newui.ui_uid != uid) { + warnx("key %u != expected record number %u", + newui.ui_uid, uid); + warnx("inconsistent user accounting stats"); + return (-1); + } + } else { /* it's not there; zero it and copy the key */ + bzero(&newui, sizeof newui); + newui.ui_uid = ci->ci_uid; + } + + newui.ui_calls += ci->ci_calls; + newui.ui_utime += ci->ci_utime; + newui.ui_stime += ci->ci_stime; + newui.ui_mem += ci->ci_mem; + newui.ui_io += ci->ci_io; + + data.data = &newui; + data.size = sizeof newui; + rv = DB_PUT(usracct_db, &key, &data, 0); + if (rv < 0) { + warn("add key %u to user accounting stats", uid); + return (-1); + } else if (rv != 0) { + warnx("DB_PUT returned 1"); + return (-1); + } + + return (0); +} + +/* Copy in-memory usracct_db to usrdb_file. */ +int +usracct_update(void) +{ + BTREEINFO bti; + + bzero(&bti, sizeof bti); + bti.compare = uid_compare; + + return (db_copy_out(usracct_db, usrdb_file, "user accounting", + &bti)); +} + +void +usracct_print(void) +{ + DBT key, data; + struct userinfo uistore, *ui = &uistore; + double t; + int rv; + + rv = DB_SEQ(usracct_db, &key, &data, R_FIRST); + if (rv < 0) + warn("retrieving user accounting stats"); + + while (rv == 0) { + memcpy(ui, data.data, sizeof(struct userinfo)); + + printf("%-*s %9ju ", MAXLOGNAME - 1, + user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls); + +#ifdef __APPLE__ + t = (double) (ui->ui_utime + ui->ui_stime) / + (double) AHZ; + if (t < 0.0001) /* kill divide by zero */ + t = 0.0001; + + printf("%12.2f%s ", t / 60.0, "cpu"); + + /* ui->ui_calls is always != 0 */ + if (dflag) + printf("%12ju%s", + (uintmax_t)(ui->ui_io / ui->ui_calls), "avio"); + else + printf("%12ju%s", (uintmax_t)ui->ui_io, "tio"); + + /* t is always >= 0.0001; see above */ + if (kflag) + printf("%12.0f%s", ui->ui_mem / t, "k"); + else + printf("%12ju%s", (uintmax_t)ui->ui_mem, "k*sec"); +#else + t = (ui->ui_utime + ui->ui_stime) / 1000000; + if (t < 0.000001) /* kill divide by zero */ + t = 0.000001; + + printf("%12.2f%s ", t / 60.0, "cpu"); + + /* ui->ui_calls is always != 0 */ + if (dflag) + printf("%12.0f%s", + ui->ui_io / ui->ui_calls, "avio"); + else + printf("%12.0f%s", ui->ui_io, "tio"); + + /* t is always >= 0.000001; see above. */ + if (kflag) + printf("%12.0f%s", ui->ui_mem / t, "k"); + else + printf("%12.0f%s", ui->ui_mem, "k*sec"); +#endif + + printf("\n"); + + rv = DB_SEQ(usracct_db, &key, &data, R_NEXT); + if (rv < 0) + warn("retrieving user accounting stats"); + } +} + +static int +uid_compare(const DBT *k1, const DBT *k2) +{ + uid_t d1, d2; + + bcopy(k1->data, &d1, sizeof d1); + bcopy(k2->data, &d2, sizeof d2); + + if (d1 < d2) + return -1; + else if (d1 == d2) + return 0; + else + return 1; +} -- cgit v1.2.3-56-ge451