/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" /* * sar generates a report either from an input data file or by invoking sadc to * read system activity counters at the specified intervals. * * usage: sar [-ubdycwaqvmpgrkA] [-o file] t [n] * sar [-ubdycwaqvmpgrkA][-s hh:mm][-e hh:mm][-i ss][-f file] */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sa.h" #define PGTOBLK(x) ((x) * (pagesize >> 9)) #define BLKTOPG(x) ((x) / (pagesize >> 9)) #define BLKS(x) ((x) >> 9) static void prpass(int); static void prtopt(void); static void prtavg(void); static void prttim(void); static void prtmachid(void); static void prthdg(void); static void tsttab(void); static void update_counters(void); static void usage(void); static void fail(int, char *, ...); static void safe_zalloc(void **, int, int); static int safe_read(int, void *, size_t); static void safe_write(int, void *, size_t); static int safe_strtoi(char const *, char *); static void ulong_delta(uint64_t *, uint64_t *, uint64_t *, uint64_t *, int, int); static float denom(float); static float freq(float, float); static struct sa64 nx, ox, ax, dx; static iodevinfo_t *nxio, *oxio, *axio, *dxio; static struct tm *curt, args, arge; static int sflg, eflg, iflg, oflg, fflg; static int realtime, passno = 0, do_disk; static int t = 0, n = 0, lines = 0; static int hz; static int niodevs; static int tabflg; static char options[30], fopt[30]; static float tdiff, sec_diff, totsec_diff = 0.0, percent; static float start_time, end_time, isec; static int fin, fout; static pid_t childid; static int pipedes[2]; static char arg1[10], arg2[10]; static int pagesize; /* * To avoid overflow in the kmem allocation data, declare a copy of the * main kmeminfo_t type with larger data types. Use this for storing * the data held to display average values */ static struct kmeminfo_l { u_longlong_t km_mem[KMEM_NCLASS]; u_longlong_t km_alloc[KMEM_NCLASS]; u_longlong_t km_fail[KMEM_NCLASS]; } kmi; int main(int argc, char **argv) { char flnm[PATH_MAX], ofile[PATH_MAX]; char ccc; time_t temp; int i, jj = 0; pagesize = sysconf(_SC_PAGESIZE); /* * Process options with arguments and pack options * without arguments. */ while ((i = getopt(argc, argv, "ubdycwaqvmpgrkAo:s:e:i:f:")) != EOF) switch (ccc = (char)i) { case 'o': oflg++; if (strlcpy(ofile, optarg, sizeof (ofile)) >= sizeof (ofile)) { fail(2, "-o filename is too long: %s", optarg); } break; case 's': if (sscanf(optarg, "%d:%d:%d", &args.tm_hour, &args.tm_min, &args.tm_sec) < 1) fail(0, "-%c %s -- illegal option argument", ccc, optarg); else { sflg++, start_time = args.tm_hour*3600.0 + args.tm_min*60.0 + args.tm_sec; } break; case 'e': if (sscanf(optarg, "%d:%d:%d", &arge.tm_hour, &arge.tm_min, &arge.tm_sec) < 1) fail(0, "-%c %s -- illegal option argument", ccc, optarg); else { eflg++; end_time = arge.tm_hour*3600.0 + arge.tm_min*60.0 + arge.tm_sec; } break; case 'i': if (sscanf(optarg, "%f", &isec) < 1) fail(0, "-%c %s -- illegal option argument", ccc, optarg); else { if (isec > 0.0) iflg++; } break; case 'f': fflg++; if (strlcpy(flnm, optarg, sizeof (flnm)) >= sizeof (ofile)) { fail(2, "-f filename is too long: %s", optarg); } break; case '?': usage(); exit(1); break; default: /* * Check for repeated options. To make sure * that options[30] does not overflow. */ if (strchr(options, ccc) == NULL) (void) strncat(options, &ccc, 1); break; } /* * Are starting and ending times consistent? */ if ((sflg) && (eflg) && (end_time <= start_time)) fail(0, "ending time <= starting time"); /* * Determine if t and n arguments are given, and whether to run in real * time or from a file. */ switch (argc - optind) { case 0: /* Get input data from file */ if (fflg == 0) { temp = time(NULL); curt = localtime(&temp); (void) snprintf(flnm, PATH_MAX, "/var/adm/sa/sa%.2d", curt->tm_mday); } if ((fin = open(flnm, 0)) == -1) fail(1, "can't open %s", flnm); break; case 1: /* Real time data; one cycle */ realtime++; t = safe_strtoi(argv[optind], "invalid sampling interval"); n = 2; break; case 2: /* Real time data; specified cycles */ default: realtime++; t = safe_strtoi(argv[optind], "invalid sampling interval"); n = 1 + safe_strtoi(argv[optind+1], "invalid sample count"); break; } /* * "u" is the default option, which displays CPU utilization. */ if (strlen(options) == 0) (void) strcpy(options, "u"); /* * "A" means all data options. */ if (strchr(options, 'A') != NULL) (void) strcpy(options, "udqbwcayvmpgrk"); if (realtime) { /* * Get input data from sadc via pipe. */ if (t <= 0) fail(0, "sampling interval t <= 0 sec"); if (n < 2) fail(0, "number of sample intervals n <= 0"); (void) sprintf(arg1, "%d", t); (void) sprintf(arg2, "%d", n); if (pipe(pipedes) == -1) fail(1, "pipe failed"); if ((childid = fork()) == 0) { /* * Child: shift pipedes[write] to stdout, * and close the pipe entries. */ (void) dup2(pipedes[1], 1); if (pipedes[0] != 1) (void) close(pipedes[0]); if (pipedes[1] != 1) (void) close(pipedes[1]); if (execlp("/usr/lib/sa/sadc", "/usr/lib/sa/sadc", arg1, arg2, 0) == -1) fail(1, "exec of /usr/lib/sa/sadc failed"); } else if (childid == -1) { fail(1, "Could not fork to exec sadc"); } /* * Parent: close unused output. */ fin = pipedes[0]; (void) close(pipedes[1]); } if (oflg) { if (strcmp(ofile, flnm) == 0) fail(0, "output file name same as input file name"); fout = creat(ofile, 00644); } hz = sysconf(_SC_CLK_TCK); nxio = oxio = dxio = axio = NULL; if (realtime) { /* * Make single pass, processing all options. */ (void) strcpy(fopt, options); passno++; prpass(realtime); (void) kill(childid, SIGINT); (void) wait(NULL); } else { /* * Make multiple passes, one for each option. */ while (strlen(strncpy(fopt, &options[jj++], 1))) { if (lseek(fin, 0, SEEK_SET) == (off_t)-1) fail(0, "lseek failed"); passno++; prpass(realtime); } } return (0); } /* * Convert array of 32-bit uints to 64-bit uints */ static void convert_32to64(uint64_t *dst, uint_t *src, int size) { for (; size > 0; size--) *dst++ = (uint64_t)(*src++); } /* * Read records from input, classify, and decide on printing. */ static void prpass(int input_pipe) { size_t size; int i, j, state_change, recno = 0; kid_t kid; float trec, tnext = 0; ulong_t old_niodevs = 0, prev_niodevs = 0; iodevinfo_t *aio, *dio, *oio; struct stat in_stat; struct sa tx; uint64_t ts, te; /* time interval start and end */ do_disk = (strchr(fopt, 'd') != NULL); if (!input_pipe && fstat(fin, &in_stat) == -1) fail(1, "unable to stat data file"); if (sflg) tnext = start_time; while (safe_read(fin, &tx, sizeof (struct sa))) { /* * First, we convert 32bit tx to 64bit nx structure * which is used later. Conversion could be done * after initial operations, right before calculations, * but it would introduce additional juggling with vars. * Thus, we convert all data now, and don't care about * tx any further. */ nx.valid = tx.valid; nx.ts = tx.ts; convert_32to64((uint64_t *)&nx.csi, (uint_t *)&tx.csi, sizeof (tx.csi) / sizeof (uint_t)); convert_32to64((uint64_t *)&nx.cvmi, (uint_t *)&tx.cvmi, sizeof (tx.cvmi) / sizeof (uint_t)); convert_32to64((uint64_t *)&nx.si, (uint_t *)&tx.si, sizeof (tx.si) / sizeof (uint_t)); (void) memcpy(&nx.vmi, &tx.vmi, sizeof (tx) - (((char *)&tx.vmi) - ((char *)&tx))); /* * sadc is the only utility used to generate sar data * and it uses the valid field as follows: * 0 - dummy record * 1 - data record * We can use this fact to improve sar's ability to detect * bad data, since any value apart from 0 or 1 can be * interpreted as invalid data. */ if (nx.valid != 0 && nx.valid != 1) fail(2, "data file not in sar format"); state_change = 0; niodevs = nx.niodevs; /* * niodevs has the value of current number of devices * from nx structure. * * The following 'if' condition is to decide whether memory * has to be allocated or not if already allocated memory is * bigger or smaller than memory needed to store the current * niodevs details in memory. * * when first while loop starts, pre_niodevs has 0 and then * always get initialized to the current number of devices * from nx.niodevs if it is different from previously read * niodevs. * * if the current niodevs has the same value of previously * allocated memory i.e, for prev_niodevs, it skips the * following 'if' loop or otherwise it allocates memory for * current devises (niodevs) and stores that value in * prev_niodevs for next time when loop continues to read * from the file. */ if (niodevs != prev_niodevs) { off_t curr_pos; /* * The required buffer size must fit in a size_t. */ if (SIZE_MAX / sizeof (iodevinfo_t) < niodevs) fail(2, "insufficient address space to hold " "%lu device records", niodevs); size = niodevs * sizeof (iodevinfo_t); prev_niodevs = niodevs; /* * The data file must exceed this size to be valid. */ if (!input_pipe) { if ((curr_pos = lseek(fin, 0, SEEK_CUR)) == (off_t)-1) fail(1, "lseek failed"); if (in_stat.st_size < curr_pos || size > in_stat.st_size - curr_pos) fail(2, "data file corrupt; specified size" "exceeds actual"); } safe_zalloc((void **)&nxio, size, 1); } if (niodevs != old_niodevs) state_change = 1; for (i = 0; i < niodevs; i++) { if (safe_read(fin, &nxio[i], sizeof (iodevinfo_t)) == 0) fail(1, "premature end-of-file seen"); if (i < old_niodevs && nxio[i].ks.ks_kid != oxio[i].ks.ks_kid) state_change = 1; } curt = localtime(&nx.ts); trec = curt->tm_hour * 3600.0 + curt->tm_min * 60.0 + curt->tm_sec; if ((recno == 0) && (trec < start_time)) continue; if ((eflg) && (trec > end_time)) break; if ((oflg) && (passno == 1)) { safe_write(fout, &nx, sizeof (struct sa)); for (i = 0; i < niodevs; i++) safe_write(fout, &nxio[i], sizeof (iodevinfo_t)); } if (recno == 0) { if (passno == 1) prtmachid(); prthdg(); recno = 1; if ((iflg) && (tnext == 0)) tnext = trec; } if (nx.valid == 0) { /* * This dummy record signifies system restart * New initial values of counters follow in next * record. */ if (!realtime) { prttim(); (void) printf("\tunix restarts\n"); recno = 1; continue; } } if ((iflg) && (trec < tnext)) continue; if (state_change) { /* * Either the number of devices or the ordering of * the kstats has changed. We need to re-organise * the layout of our avg/delta arrays so that we * can cope with this in update_counters(). */ size = niodevs * sizeof (iodevinfo_t); safe_zalloc((void *)&aio, size, 0); safe_zalloc((void *)&dio, size, 0); safe_zalloc((void *)&oio, size, 0); /* * Loop through all the newly read iodev's, locate * the corresponding entry in the old arrays and * copy the entries into the same bucket of the * new arrays. */ for (i = 0; i < niodevs; i++) { kid = nxio[i].ks.ks_kid; for (j = 0; j < old_niodevs; j++) { if (oxio[j].ks.ks_kid == kid) { oio[i] = oxio[j]; aio[i] = axio[j]; dio[i] = dxio[j]; } } } free(axio); free(oxio); free(dxio); axio = aio; oxio = oio; dxio = dio; old_niodevs = niodevs; } if (recno++ > 1) { ts = ox.csi.cpu[0] + ox.csi.cpu[1] + ox.csi.cpu[2] + ox.csi.cpu[3]; te = nx.csi.cpu[0] + nx.csi.cpu[1] + nx.csi.cpu[2] + nx.csi.cpu[3]; tdiff = (float)(te - ts); sec_diff = tdiff / hz; percent = 100.0 / tdiff; /* * If the CPU stat counters have rolled * backward, this is our best indication that * a CPU has been offlined. We don't have * enough data to compute a sensible delta, so * toss out this interval, but compute the next * interval's delta from these values. */ if (tdiff <= 0) { ox = nx; continue; } update_counters(); prtopt(); lines++; if (passno == 1) totsec_diff += sec_diff; } ox = nx; /* Age the data */ (void) memcpy(oxio, nxio, niodevs * sizeof (iodevinfo_t)); if (isec > 0) while (tnext <= trec) tnext += isec; } /* * After this place, all functions are using niodevs to access the * memory for device details. Here, old_niodevs has the correct value * of memory allocated for storing device information. Since niodevs * doesn't have correct value, sometimes, it was corrupting memory. */ niodevs = old_niodevs; if (lines > 1) prtavg(); (void) memset(&ax, 0, sizeof (ax)); /* Zero out the accumulators. */ (void) memset(&kmi, 0, sizeof (kmi)); lines = 0; /* * axio will not be allocated if the user specified -e or -s, and * no records in the file fell inside the specified time range. */ if (axio) { (void) memset(axio, 0, niodevs * sizeof (iodevinfo_t)); } } /* * Print time label routine. */ static void prttim(void) { curt = localtime(&nx.ts); (void) printf("%.2d:%.2d:%.2d", curt->tm_hour, curt->tm_min, curt->tm_sec); tabflg = 1; } /* * Test if 8-spaces to be added routine. */ static void tsttab(void) { if (tabflg == 0) (void) printf(" "); else tabflg = 0; } /* * Print machine identification. */ static void prtmachid(void) { struct utsname name; (void) uname(&name); (void) printf("\n%s %s %s %s %s %.2d/%.2d/%.4d\n", name.sysname, name.nodename, name.release, name.version, name.machine, curt->tm_mon + 1, curt->tm_mday, curt->tm_year + 1900); } /* * Print report heading routine. */ static void prthdg(void) { int jj = 0; char ccc; (void) printf("\n"); prttim(); while ((ccc = fopt[jj++]) != NULL) { tsttab(); switch (ccc) { case 'u': (void) printf(" %7s %7s %7s %7s\n", "%usr", "%sys", "%wio", "%idle"); break; case 'b': (void) printf(" %7s %7s %7s %7s %7s %7s %7s %7s\n", "bread/s", "lread/s", "%rcache", "bwrit/s", "lwrit/s", "%wcache", "pread/s", "pwrit/s"); break; case 'd': (void) printf(" %-8.8s %7s %7s %7s %7s %7s %7s\n", "device", "%busy", "avque", "r+w/s", "blks/s", "avwait", "avserv"); break; case 'y': (void) printf(" %7s %7s %7s %7s %7s %7s\n", "rawch/s", "canch/s", "outch/s", "rcvin/s", "xmtin/s", "mdmin/s"); break; case 'c': (void) printf(" %7s %7s %7s %7s %7s %7s %7s\n", "scall/s", "sread/s", "swrit/s", "fork/s", "exec/s", "rchar/s", "wchar/s"); break; case 'w': (void) printf(" %7s %7s %7s %7s %7s\n", "swpin/s", "bswin/s", "swpot/s", "bswot/s", "pswch/s"); break; case 'a': (void) printf(" %7s %7s %7s\n", "iget/s", "namei/s", "dirbk/s"); break; case 'q': (void) printf(" %7s %7s %7s %7s\n", "runq-sz", "%runocc", "swpq-sz", "%swpocc"); break; case 'v': (void) printf(" %s %s %s %s\n", "proc-sz ov", "inod-sz ov", "file-sz ov", "lock-sz"); break; case 'm': (void) printf(" %7s %7s\n", "msg/s", "sema/s"); break; case 'p': (void) printf(" %7s %7s %7s %7s %7s %7s\n", "atch/s", "pgin/s", "ppgin/s", "pflt/s", "vflt/s", "slock/s"); break; case 'g': (void) printf(" %8s %8s %8s %8s %8s\n", "pgout/s", "ppgout/s", "pgfree/s", "pgscan/s", "%ufs_ipf"); break; case 'r': (void) printf(" %7s %8s\n", "freemem", "freeswap"); break; case 'k': (void) printf(" %7s %7s %5s %7s %7s %5s %11s %5s\n", "sml_mem", "alloc", "fail", "lg_mem", "alloc", "fail", "ovsz_alloc", "fail"); break; } } if (jj > 2 || do_disk) (void) printf("\n"); } /* * compute deltas and update accumulators */ static void update_counters(void) { int i; iodevinfo_t *nio, *oio, *aio, *dio; ulong_delta((uint64_t *)&nx.csi, (uint64_t *)&ox.csi, (uint64_t *)&dx.csi, (uint64_t *)&ax.csi, 0, sizeof (ax.csi)); ulong_delta((uint64_t *)&nx.si, (uint64_t *)&ox.si, (uint64_t *)&dx.si, (uint64_t *)&ax.si, 0, sizeof (ax.si)); ulong_delta((uint64_t *)&nx.cvmi, (uint64_t *)&ox.cvmi, (uint64_t *)&dx.cvmi, (uint64_t *)&ax.cvmi, 0, sizeof (ax.cvmi)); ax.vmi.freemem += dx.vmi.freemem = nx.vmi.freemem - ox.vmi.freemem; ax.vmi.swap_avail += dx.vmi.swap_avail = nx.vmi.swap_avail - ox.vmi.swap_avail; nio = nxio; oio = oxio; aio = axio; dio = dxio; for (i = 0; i < niodevs; i++) { aio->kios.wlastupdate += dio->kios.wlastupdate = nio->kios.wlastupdate - oio->kios.wlastupdate; aio->kios.reads += dio->kios.reads = nio->kios.reads - oio->kios.reads; aio->kios.writes += dio->kios.writes = nio->kios.writes - oio->kios.writes; aio->kios.nread += dio->kios.nread = nio->kios.nread - oio->kios.nread; aio->kios.nwritten += dio->kios.nwritten = nio->kios.nwritten - oio->kios.nwritten; aio->kios.wlentime += dio->kios.wlentime = nio->kios.wlentime - oio->kios.wlentime; aio->kios.rlentime += dio->kios.rlentime = nio->kios.rlentime - oio->kios.rlentime; aio->kios.wtime += dio->kios.wtime = nio->kios.wtime - oio->kios.wtime; aio->kios.rtime += dio->kios.rtime = nio->kios.rtime - oio->kios.rtime; nio++; oio++; aio++; dio++; } } static void prt_u_opt(struct sa64 *xx) { (void) printf(" %7.0f %7.0f %7.0f %7.0f\n", (float)xx->csi.cpu[1] * percent, (float)xx->csi.cpu[2] * percent, (float)xx->csi.cpu[3] * percent, (float)xx->csi.cpu[0] * percent); } static void prt_b_opt(struct sa64 *xx) { (void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n", (float)xx->csi.bread / sec_diff, (float)xx->csi.lread / sec_diff, freq((float)xx->csi.lread, (float)xx->csi.bread), (float)xx->csi.bwrite / sec_diff, (float)xx->csi.lwrite / sec_diff, freq((float)xx->csi.lwrite, (float)xx->csi.bwrite), (float)xx->csi.phread / sec_diff, (float)xx->csi.phwrite / sec_diff); } static void prt_d_opt(int ii, iodevinfo_t *xio) { double etime, hr_etime, tps, avq, avs; tsttab(); hr_etime = (double)xio[ii].kios.wlastupdate; if (hr_etime == 0.0) hr_etime = (double)NANOSEC; etime = hr_etime / (double)NANOSEC; tps = (double)(xio[ii].kios.reads + xio[ii].kios.writes) / etime; avq = (double)xio[ii].kios.wlentime / hr_etime; avs = (double)xio[ii].kios.rlentime / hr_etime; (void) printf(" %-8.8s ", nxio[ii].ks.ks_name); (void) printf("%7.0f %7.1f %7.0f %7.0f %7.1f %7.1f\n", (double)xio[ii].kios.rtime * 100.0 / hr_etime, avq + avs, tps, BLKS(xio[ii].kios.nread + xio[ii].kios.nwritten) / etime, (tps > 0 ? avq / tps * 1000.0 : 0.0), (tps > 0 ? avs / tps * 1000.0 : 0.0)); } static void prt_y_opt(struct sa64 *xx) { (void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n", (float)xx->csi.rawch / sec_diff, (float)xx->csi.canch / sec_diff, (float)xx->csi.outch / sec_diff, (float)xx->csi.rcvint / sec_diff, (float)xx->csi.xmtint / sec_diff, (float)xx->csi.mdmint / sec_diff); } static void prt_c_opt(struct sa64 *xx) { (void) printf(" %7.0f %7.0f %7.0f %7.2f %7.2f %7.0f %7.0f\n", (float)xx->csi.syscall / sec_diff, (float)xx->csi.sysread / sec_diff, (float)xx->csi.syswrite / sec_diff, (float)(xx->csi.sysfork + xx->csi.sysvfork) / sec_diff, (float)xx->csi.sysexec / sec_diff, (float)xx->csi.readch / sec_diff, (float)xx->csi.writech / sec_diff); } static void prt_w_opt(struct sa64 *xx) { (void) printf(" %7.2f %7.1f %7.2f %7.1f %7.0f\n", (float)xx->cvmi.swapin / sec_diff, (float)PGTOBLK(xx->cvmi.pgswapin) / sec_diff, (float)xx->cvmi.swapout / sec_diff, (float)PGTOBLK(xx->cvmi.pgswapout) / sec_diff, (float)xx->csi.pswitch / sec_diff); } static void prt_a_opt(struct sa64 *xx) { (void) printf(" %7.0f %7.0f %7.0f\n", (float)xx->csi.ufsiget / sec_diff, (float)xx->csi.namei / sec_diff, (float)xx->csi.ufsdirblk / sec_diff); } static void prt_q_opt(struct sa64 *xx) { if (xx->si.runocc == 0 || xx->si.updates == 0) (void) printf(" %7.1f %7.0f", 0., 0.); else { (void) printf(" %7.1f %7.0f", (float)xx->si.runque / (float)xx->si.runocc, (float)xx->si.runocc / (float)xx->si.updates * 100.0); } if (xx->si.swpocc == 0 || xx->si.updates == 0) (void) printf(" %7.1f %7.0f\n", 0., 0.); else { (void) printf(" %7.1f %7.0f\n", (float)xx->si.swpque / (float)xx->si.swpocc, (float)xx->si.swpocc / (float)xx->si.updates * 100.0); } } static void prt_v_opt(struct sa64 *xx) { (void) printf(" %4lu/%-4lu %4llu %4lu/%-4lu %4llu %4lu/%-4lu " "%4llu %4lu/%-4lu\n", nx.szproc, nx.mszproc, xx->csi.procovf, nx.szinode, nx.mszinode, xx->csi.inodeovf, nx.szfile, nx.mszfile, xx->csi.fileovf, nx.szlckr, nx.mszlckr); } static void prt_m_opt(struct sa64 *xx) { (void) printf(" %7.2f %7.2f\n", (float)xx->csi.msg / sec_diff, (float)xx->csi.sema / sec_diff); } static void prt_p_opt(struct sa64 *xx) { (void) printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", (float)xx->cvmi.pgfrec / sec_diff, (float)xx->cvmi.pgin / sec_diff, (float)xx->cvmi.pgpgin / sec_diff, (float)(xx->cvmi.prot_fault + xx->cvmi.cow_fault) / sec_diff, (float)(xx->cvmi.hat_fault + xx->cvmi.as_fault) / sec_diff, (float)xx->cvmi.softlock / sec_diff); } static void prt_g_opt(struct sa64 *xx) { (void) printf(" %8.2f %8.2f %8.2f %8.2f %8.2f\n", (float)xx->cvmi.pgout / sec_diff, (float)xx->cvmi.pgpgout / sec_diff, (float)xx->cvmi.dfree / sec_diff, (float)xx->cvmi.scan / sec_diff, (float)xx->csi.ufsipage * 100.0 / denom((float)xx->csi.ufsipage + (float)xx->csi.ufsinopage)); } static void prt_r_opt(struct sa64 *xx) { /* Avoid divide by Zero - Should never happen */ if (xx->si.updates == 0) (void) printf(" %7.0f %8.0f\n", 0., 0.); else { (void) printf(" %7.0f %8.0f\n", (double)xx->vmi.freemem / (float)xx->si.updates, (double)PGTOBLK(xx->vmi.swap_avail) / (float)xx->si.updates); } } static void prt_k_opt(struct sa64 *xx, int n) { if (n != 1) { (void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f" " %5.0f\n", (float)kmi.km_mem[KMEM_SMALL] / n, (float)kmi.km_alloc[KMEM_SMALL] / n, (float)kmi.km_fail[KMEM_SMALL] / n, (float)kmi.km_mem[KMEM_LARGE] / n, (float)kmi.km_alloc[KMEM_LARGE] / n, (float)kmi.km_fail[KMEM_LARGE] / n, (float)kmi.km_alloc[KMEM_OSIZE] / n, (float)kmi.km_fail[KMEM_OSIZE] / n); } else { /* * If we are not reporting averages, use the read values * directly. */ (void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f" " %5.0f\n", (float)xx->kmi.km_mem[KMEM_SMALL], (float)xx->kmi.km_alloc[KMEM_SMALL], (float)xx->kmi.km_fail[KMEM_SMALL], (float)xx->kmi.km_mem[KMEM_LARGE], (float)xx->kmi.km_alloc[KMEM_LARGE], (float)xx->kmi.km_fail[KMEM_LARGE], (float)xx->kmi.km_alloc[KMEM_OSIZE], (float)xx->kmi.km_fail[KMEM_OSIZE]); } } /* * Print options routine. */ static void prtopt(void) { int ii, jj = 0; char ccc; prttim(); while ((ccc = fopt[jj++]) != NULL) { if (ccc != 'd') tsttab(); switch (ccc) { case 'u': prt_u_opt(&dx); break; case 'b': prt_b_opt(&dx); break; case 'd': for (ii = 0; ii < niodevs; ii++) prt_d_opt(ii, dxio); break; case 'y': prt_y_opt(&dx); break; case 'c': prt_c_opt(&dx); break; case 'w': prt_w_opt(&dx); break; case 'a': prt_a_opt(&dx); break; case 'q': prt_q_opt(&dx); break; case 'v': prt_v_opt(&dx); break; case 'm': prt_m_opt(&dx); break; case 'p': prt_p_opt(&dx); break; case 'g': prt_g_opt(&dx); break; case 'r': prt_r_opt(&dx); break; case 'k': prt_k_opt(&nx, 1); /* * To avoid overflow, copy the data from the sa record * into a struct kmeminfo_l which has members with * larger data types. */ kmi.km_mem[KMEM_SMALL] += nx.kmi.km_mem[KMEM_SMALL]; kmi.km_alloc[KMEM_SMALL] += nx.kmi.km_alloc[KMEM_SMALL]; kmi.km_fail[KMEM_SMALL] += nx.kmi.km_fail[KMEM_SMALL]; kmi.km_mem[KMEM_LARGE] += nx.kmi.km_mem[KMEM_LARGE]; kmi.km_alloc[KMEM_LARGE] += nx.kmi.km_alloc[KMEM_LARGE]; kmi.km_fail[KMEM_LARGE] += nx.kmi.km_fail[KMEM_LARGE]; kmi.km_alloc[KMEM_OSIZE] += nx.kmi.km_alloc[KMEM_OSIZE]; kmi.km_fail[KMEM_OSIZE] += nx.kmi.km_fail[KMEM_OSIZE]; break; } } if (jj > 2 || do_disk) (void) printf("\n"); if (realtime) (void) fflush(stdout); } /* * Print average routine. */ static void prtavg(void) { int ii, jj = 0; char ccc; tdiff = ax.csi.cpu[0] + ax.csi.cpu[1] + ax.csi.cpu[2] + ax.csi.cpu[3]; if (tdiff <= 0.0) return; sec_diff = tdiff / hz; percent = 100.0 / tdiff; (void) printf("\n"); while ((ccc = fopt[jj++]) != NULL) { if (ccc != 'v') (void) printf("Average "); switch (ccc) { case 'u': prt_u_opt(&ax); break; case 'b': prt_b_opt(&ax); break; case 'd': tabflg = 1; for (ii = 0; ii < niodevs; ii++) prt_d_opt(ii, axio); break; case 'y': prt_y_opt(&ax); break; case 'c': prt_c_opt(&ax); break; case 'w': prt_w_opt(&ax); break; case 'a': prt_a_opt(&ax); break; case 'q': prt_q_opt(&ax); break; case 'v': break; case 'm': prt_m_opt(&ax); break; case 'p': prt_p_opt(&ax); break; case 'g': prt_g_opt(&ax); break; case 'r': prt_r_opt(&ax); break; case 'k': prt_k_opt(&ax, lines); break; } } } static void ulong_delta(uint64_t *new, uint64_t *old, uint64_t *delta, uint64_t *accum, int begin, int end) { int i; uint64_t n, o, d; for (i = begin; i < end; i += sizeof (uint64_t)) { n = *new++; o = *old++; if (o > n) { d = n + 0x100000000LL - o; } else { d = n - o; } *accum++ += *delta++ = d; } } /* * used to prevent zero denominators */ static float denom(float x) { return ((x > 0.5) ? x : 1.0); } /* * a little calculation that comes up often when computing frequency * of one operation relative to another */ static float freq(float x, float y) { return ((x < 0.5) ? 100.0 : (x - y) / x * 100.0); } static void usage(void) { (void) fprintf(stderr, "usage: sar [-ubdycwaqvmpgrkA][-o file] t [n]\n" "\tsar [-ubdycwaqvmpgrkA] [-s hh:mm][-e hh:mm][-i ss][-f file]\n"); } static void fail(int do_perror, char *message, ...) { va_list args; va_start(args, message); (void) fprintf(stderr, "sar: "); (void) vfprintf(stderr, message, args); va_end(args); (void) fprintf(stderr, "\n"); switch (do_perror) { case 0: /* usage message */ usage(); break; case 1: /* perror output */ perror(""); break; case 2: /* no further output */ break; default: /* error */ (void) fprintf(stderr, "unsupported failure mode\n"); break; } exit(2); } static int safe_strtoi(char const *val, char *errmsg) { char *end; long tmp; errno = 0; tmp = strtol(val, &end, 10); if (*end != '\0' || errno) fail(0, "%s %s", errmsg, val); return ((int)tmp); } static void safe_zalloc(void **ptr, int size, int free_first) { if (free_first && *ptr != NULL) free(*ptr); if ((*ptr = malloc(size)) == NULL) fail(1, "malloc failed"); (void) memset(*ptr, 0, size); } static int safe_read(int fd, void *buf, size_t size) { size_t rsize = read(fd, buf, size); if (rsize == 0) return (0); if (rsize != size) fail(1, "read failed"); return (1); } static void safe_write(int fd, void *buf, size_t size) { if (write(fd, buf, size) != size) fail(1, "write failed"); }