sar.c revision e83e7a19949561f70a103aeed99b082f16b5b791
/*
* 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
* 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 2008 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "sa.h"
#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 void safe_write(int, void *, size_t);
static int safe_strtoi(char const *, char *);
int, int);
static float denom(float);
static float freq(float, float);
static int t = 0, n = 0, lines = 0;
static int hz;
static int niodevs;
static int tabflg;
static int pipedes[2];
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
{
} kmi;
int
{
char ccc;
int i, jj = 0;
/*
* Process options with arguments and pack options
* without arguments.
*/
switch (ccc = (char)i) {
case 'o':
oflg++;
sizeof (ofile)) {
}
break;
case 's':
fail(0, "-%c %s -- illegal option argument",
else {
sflg++,
}
break;
case 'e':
fail(0, "-%c %s -- illegal option argument",
else {
eflg++;
}
break;
case 'i':
fail(0, "-%c %s -- illegal option argument",
else {
if (isec > 0.0)
iflg++;
}
break;
case 'f':
fflg++;
sizeof (ofile)) {
}
break;
case '?':
usage();
exit(1);
break;
default:
/*
* Check for repeated options. To make sure
* that options[30] does not overflow.
*/
break;
}
/*
* Are starting and ending times consistent?
*/
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.
*/
case 0: /* Get input data from file */
if (fflg == 0) {
}
break;
case 1: /* Real time data; one cycle */
realtime++;
n = 2;
break;
case 2: /* Real time data; specified cycles */
default:
realtime++;
break;
}
/*
* "u" is the default option, which displays CPU utilization.
*/
/*
* "A" means all data options.
*/
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");
/*
* Child: shift pipedes[write] to stdout,
* and close the pipe entries.
*/
if (pipedes[0] != 1)
} else if (childid == -1) {
}
/*
* Parent: close unused output.
*/
}
if (oflg) {
fail(0, "output file name same as input file name");
}
if (realtime) {
/*
* Make single pass, processing all options.
*/
passno++;
} else {
/*
* Make multiple passes, one for each option.
*/
fail(0, "lseek failed");
passno++;
}
}
return (0);
}
/*
* Convert array of 32-bit uints to 64-bit uints
*/
static void
{
}
/*
* Convert array of 64-bit uints to 32-bit uints
*/
static void
{
}
/*
* Read records from input, classify, and decide on printing.
*/
static void
prpass(int input_pipe)
{
int i, j, state_change, recno = 0;
if (sflg)
tnext = start_time;
/*
* First, we convert 32-bit tx to 64-bit 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.
*/
/*
* 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.
*/
state_change = 0;
/*
* 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) {
/*
* The required buffer size must fit in a size_t.
*/
"%lu device records", niodevs);
/*
* The data file must exceed this size to be valid.
*/
if (!input_pipe) {
(off_t)-1)
"exceeds actual");
}
}
if (niodevs != old_niodevs)
state_change = 1;
for (i = 0; i < niodevs; i++) {
if (i < old_niodevs &&
state_change = 1;
}
continue;
break;
/*
* The calculated values are stroed in nx strcuture.
* Convert 64-bit nx to 32-bit tx structure.
*/
for (i = 0; i < niodevs; i++)
sizeof (iodevinfo_t));
}
if (recno == 0) {
if (passno == 1)
prtmachid();
prthdg();
recno = 1;
}
/*
* 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;
}
}
continue;
if (state_change) {
/*
* Either the number of devices or the ordering of
* the kstats has changed. We need to re-organise
* can cope with this in update_counters().
*/
/*
* 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++) {
for (j = 0; j < old_niodevs; j++) {
}
}
}
}
if (recno++ > 1) {
/*
* 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) {
continue;
}
prtopt();
lines++;
if (passno == 1)
totsec_diff += sec_diff;
}
if (isec > 0)
}
/*
* 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.
*/
if (lines > 1)
prtavg();
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) {
}
}
/*
* Print time label routine.
*/
static void
prttim(void)
{
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)
{
(void) printf("\n%s %s %s %s %s %.2d/%.2d/%.4d\n",
}
/*
* Print report heading routine.
*/
static void
prthdg(void)
{
int jj = 0;
char ccc;
(void) printf("\n");
prttim();
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;
}
}
(void) printf("\n");
}
/*
* compute deltas and update accumulators
*/
static void
update_counters(void)
{
int i;
for (i = 0; i < niodevs; i++) {
nio++;
oio++;
aio++;
dio++;
}
}
static void
{
(void) printf(" %7.0f %7.0f %7.0f %7.0f\n",
}
static void
{
(void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n",
}
static void
{
tsttab();
if (hr_etime == 0.0)
(void) printf("%7.0f %7.1f %7.0f %7.0f %7.1f %7.1f\n",
tps,
}
static void
{
(void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n",
}
static void
{
(void) printf(" %7.0f %7.0f %7.0f %7.2f %7.2f %7.0f %7.0f\n",
}
static void
{
(void) printf(" %7.2f %7.1f %7.2f %7.1f %7.0f\n",
}
static void
{
(void) printf(" %7.0f %7.0f %7.0f\n",
}
static void
{
(void) printf(" %7.1f %7.0f", 0., 0.);
else {
(void) printf(" %7.1f %7.0f",
}
(void) printf(" %7.1f %7.0f\n", 0., 0.);
else {
(void) printf(" %7.1f %7.0f\n",
}
}
static void
{
(void) printf(" %4lu/%-4lu %4llu %4lu/%-4lu %4llu %4lu/%-4lu "
"%4llu %4lu/%-4lu\n",
}
static void
{
(void) printf(" %7.2f %7.2f\n",
}
static void
{
(void) printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
}
static void
{
(void) printf(" %8.2f %8.2f %8.2f %8.2f %8.2f\n",
}
static void
{
/* Avoid divide by Zero - Should never happen */
(void) printf(" %7.0f %8.0f\n", 0., 0.);
else {
(void) printf(" %7.0f %8.0f\n",
}
}
static void
{
if (n != 1) {
(void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f"
" %5.0f\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",
}
}
/*
* Print options routine.
*/
static void
prtopt(void)
{
char ccc;
prttim();
if (ccc != 'd')
tsttab();
switch (ccc) {
case 'u':
break;
case 'b':
break;
case 'd':
break;
case 'y':
break;
case 'c':
break;
case 'w':
break;
case 'a':
break;
case 'q':
break;
case 'v':
break;
case 'm':
break;
case 'p':
break;
case 'g':
break;
case 'r':
break;
case 'k':
/*
* To avoid overflow, copy the data from the sa record
* into a struct kmeminfo_l which has members with
* larger data types.
*/
break;
}
}
(void) printf("\n");
if (realtime)
}
/*
* Print average routine.
*/
static void
prtavg(void)
{
char ccc;
if (tdiff <= 0.0)
return;
(void) printf("\n");
if (ccc != 'v')
(void) printf("Average ");
switch (ccc) {
case 'u':
break;
case 'b':
break;
case 'd':
tabflg = 1;
break;
case 'y':
break;
case 'c':
break;
case 'w':
break;
case 'a':
break;
case 'q':
break;
case 'v':
break;
case 'm':
break;
case 'p':
break;
case 'g':
break;
case 'r':
break;
case 'k':
break;
}
}
}
static void
{
int i;
uint64_t n, o, d;
n = *new++;
o = *old++;
if (o > n) {
d = n + 0x100000000LL - o;
} else {
d = n - o;
}
}
}
/*
* 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)
{
"usage: sar [-ubdycwaqvmpgrkA][-o file] t [n]\n"
"\tsar [-ubdycwaqvmpgrkA] [-s hh:mm][-e hh:mm][-i ss][-f file]\n");
}
static void
{
switch (do_perror) {
case 0: /* usage message */
usage();
break;
case 1: /* perror output */
perror("");
break;
case 2: /* no further output */
break;
default: /* error */
break;
}
exit(2);
}
static int
{
char *end;
long tmp;
errno = 0;
return ((int)tmp);
}
static void
{
}
static int
{
if (rsize == 0)
return (0);
return (1);
}
static void
{
}