# # 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 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright (c) 2013 Andrew Stormont. All rights reserved. # # Copyright (c) 2018, Joyent, Inc. MANIFEST = sar.xml SVCMETHOD = svc-sar include ../Makefile.cmd ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) SMOFF += all_func_returns SADC= sadc SAR= sar TIMEX= timex SA1= sa1 SA2= sa2 sadc : LDLIBS += -lkstat # Executables produced BINPROG= $(TIMEX) SBINPROG= $(SAR) LIBPROG= $(SADC) LIBSHELL= $(SA1) $(SA2) PROGS= $(BINPROG) $(SBINPROG) $(LIBPROG) SHELLS= $(LIBSHELL) TXTS= README ALL= $(PROGS) $(SHELLS) # Source files SADC_OBJECTS= $(SADC).o srcs= $(TIMEX) $(SAR) $(SADC) SRCS= $(srcs:%=%.c) SHSRCS= $(SHELLS:%=%.sh) # Set of target install directories LIBSAD= $(ROOT)/usr/lib/sa CROND= $(ROOT)/var/spool/cron CRONTABSD= $(CROND)/crontabs # Set of target install files SYSCRONTAB= $(CRONTABSD)/sys ROOTPROG= $(BINPROG:%=$(ROOTBIN)/%) ROOTUSBINPROG= $(SBINPROG:%=$(ROOTUSRSBIN)/%) ROOTLIBPROG= $(LIBPROG:%=$(LIBSAD)/%) ROOTLIBSHELL= $(LIBSHELL:%=$(LIBSAD)/%) ROOTSYMLINKS= $(SBINPROG:%=$(ROOTBIN)/%) # Performance monitoring should not be enabled by default. Hence, these # entries are comments. ENTRY1= '$(POUND_SIGN) 0 * * * 0-6 /usr/lib/sa/sa1' ENTRY2= '$(POUND_SIGN) 20,40 8-17 * * 1-5 /usr/lib/sa/sa1' ENTRY3= '$(POUND_SIGN) 5 18 * * 1-5 /usr/lib/sa/sa2 -s 8:00 -e 18:01 -i 1200 -A' CLOBBERFILES= $(PROGS) $(SHELLS) # Conditionals $(LIBSAD)/$(SADC) : FILEMODE = 0555 .KEEP_STATE: all: $(ALL) $(TXTS) $(SADC): $(SADC_OBJECTS) $(LINK.c) -o $@ $(SADC_OBJECTS) $(LDLIBS) $(POST_PROCESS) # The edit of SYSCRONTAB must be done unconditionally because of the # creation of this file by a different component (Adm) and the possible # backdating. install: all $(ROOTPROG) $(ROOTUSBINPROG) \ $(ROOTLIBSHELL) $(ROOTSYMLINKS) \ $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTLIBPROG) @if [ -f $(SYSCRONTAB) ]; \ then \ if $(GREP) "sa1" $(SYSCRONTAB) >/dev/null 2>&1 ; then :; \ else \ echo $(ENTRY1) >> $(SYSCRONTAB); \ echo $(ENTRY2) >> $(SYSCRONTAB); \ echo "$(SYSCRONTAB) modified"; \ fi; \ if $(GREP) "sa2" $(SYSCRONTAB) >/dev/null 2>&1 ; then :; \ else \ echo $(ENTRY3) >> $(SYSCRONTAB); \ fi; \ fi $(LIBSAD)/%: % $(INS.file) $(ROOTSYMLINKS): -$(RM) $@; $(SYMLINK) ../sbin/`basename $@` $@ check: $(CHKMANIFEST) clean: $(RM) $(SADC_OBJECTS) $(PROGS) $(SHELLS) lint: lint_SRCS include ../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 2004 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. ident "%Z%%M% %I% %E% SMI" README 1.9 of 5/8/89 Instruction of daily report generation: 1. The line /usr/lib/sa/sadc /var/adm/sa/sa`date +%d` is executed by the start method for the system/sar:default service, if enabled sadc is executed once, such that a special record is written to the daily data file when UNIX restarts. 2. The execution of shell script sa1 [t n] causes data collection program to write system activity counters n times at every t-second interval on data file. If t and n are not specified, it writes data once. According to your installation's requirement, enter entries of executing sa1 [t n] in /var/spool/cron/crontabs/sys to collect system activity data. For example, entries 0 8-17 * * 1-5 su sys -c "/usr/lib/sa/sa1 1200 3 &" and 0 18-23 * * 1-5 su sys -c "/usr/lib/sa/sa1 &" cause data collection program to be activated at every hour on the hour from 8:00 to 23:00 on weekdays. Moreover, It writes data on data file 3 times at every 20 minutes interval from 8:00 to 17:00 and once at other times. 3. Shell procedure sa2 will invoke sar command to generate the daily report from the data file. Its usage is sa2 [-options] [-s hh:mm] [-e hh:mm] [-i ss] where -s and -e specify the report starting and ending times respectively, -i specifies the report data interval in seconds. If they are not specified, all data from the data file are to be reported. -options are report options, see manual page sar.1 for description. Make an entry to execute sa2 in /var/spool/cron/crontabs. For instance, entry 5 18 * * 1-5 su adm -c "/usr/lib/sa/sa2 -s 8:00 -e 18:01 -i 3600 -ubd &" causes the invocation of sar command at 18:05. It generates the daily report that includes the hourly cpu utilization, buffer usage and disk and tape activities from 8:00 to 18:01. /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SA_H #define _SA_H /* * sa.h contains struct sa and defines variables used in sadc.c and sar.c. * RESTRICTION: the data types defined in this file must not be changed. * sar writes these types to disk as binary data and to ensure version to * version compatibility they must not be changed. */ #include #ifdef __cplusplus extern "C" { #endif typedef struct iodevinfo { struct iodevinfo *next; kstat_t *ksp; kstat_t ks; kstat_io_t kios; } iodevinfo_t; #define KMEM_SMALL 0 /* small KMEM request index */ #define KMEM_LARGE 1 /* large KMEM request index */ #define KMEM_OSIZE 2 /* outsize KMEM request index */ #define KMEM_NCLASS 3 /* # of KMEM request classes */ typedef struct kmeminfo { ulong_t km_mem[KMEM_NCLASS]; /* amount of mem owned by KMEM */ ulong_t km_alloc[KMEM_NCLASS]; /* amount of mem allocated */ ulong_t km_fail[KMEM_NCLASS]; /* # of failed requests */ } kmeminfo_t; /* * structure sa defines the data structure of system activity data file */ struct sa { int valid; /* non-zero for valid data */ time_t ts; /* time stamp */ cpu_sysinfo_t csi; /* per-CPU system information */ cpu_vminfo_t cvmi; /* per-CPU vm information */ sysinfo_t si; /* global system information */ vminfo_t vmi; /* global vm information */ kmeminfo_t kmi; /* kernel mem allocation info */ ulong_t szinode; /* inode table size */ ulong_t szfile; /* file table size */ ulong_t szproc; /* proc table size */ ulong_t szlckr; /* file record lock table size */ ulong_t mszinode; /* max inode table size */ ulong_t mszfile; /* max file table size */ ulong_t mszproc; /* max proc table size */ ulong_t mszlckr; /* max file rec lock table size */ ulong_t niodevs; /* number of I/O devices */ /* An array of iodevinfo structs come next in the sadc files */ }; typedef struct cpu64_sysinfo { uint64_t cpu[CPU_STATES]; uint64_t wait[W_STATES]; uint64_t bread; uint64_t bwrite; uint64_t lread; uint64_t lwrite; uint64_t phread; uint64_t phwrite; uint64_t pswitch; uint64_t trap; uint64_t intr; uint64_t syscall; uint64_t sysread; uint64_t syswrite; uint64_t sysfork; uint64_t sysvfork; uint64_t sysexec; uint64_t readch; uint64_t writech; uint64_t rcvint; uint64_t xmtint; uint64_t mdmint; uint64_t rawch; uint64_t canch; uint64_t outch; uint64_t msg; uint64_t sema; uint64_t namei; uint64_t ufsiget; uint64_t ufsdirblk; uint64_t ufsipage; uint64_t ufsinopage; uint64_t inodeovf; uint64_t fileovf; uint64_t procovf; uint64_t intrthread; uint64_t intrblk; uint64_t idlethread; uint64_t inv_swtch; uint64_t nthreads; uint64_t cpumigrate; uint64_t xcalls; uint64_t mutex_adenters; uint64_t rw_rdfails; uint64_t rw_wrfails; uint64_t modload; uint64_t modunload; uint64_t bawrite; uint64_t rw_enters; uint64_t win_uo_cnt; uint64_t win_uu_cnt; uint64_t win_so_cnt; uint64_t win_su_cnt; uint64_t win_suo_cnt; } cpu64_sysinfo_t; typedef struct cpu64_vminfo { uint64_t pgrec; uint64_t pgfrec; uint64_t pgin; uint64_t pgpgin; uint64_t pgout; uint64_t pgpgout; uint64_t swapin; uint64_t pgswapin; uint64_t swapout; uint64_t pgswapout; uint64_t zfod; uint64_t dfree; uint64_t scan; uint64_t rev; uint64_t hat_fault; uint64_t as_fault; uint64_t maj_fault; uint64_t cow_fault; uint64_t prot_fault; uint64_t softlock; uint64_t kernel_asflt; uint64_t pgrrun; uint64_t execpgin; uint64_t execpgout; uint64_t execfree; uint64_t anonpgin; uint64_t anonpgout; uint64_t anonfree; uint64_t fspgin; uint64_t fspgout; uint64_t fsfree; } cpu64_vminfo_t; typedef struct sysinfo64 { uint64_t updates; uint64_t runque; uint64_t runocc; uint64_t swpque; uint64_t swpocc; uint64_t waiting; } sysinfo64_t; struct sa64 { int valid; time_t ts; cpu64_sysinfo_t csi; cpu64_vminfo_t cvmi; sysinfo64_t si; vminfo_t vmi; kmeminfo_t kmi; ulong_t szinode; ulong_t szfile; ulong_t szproc; ulong_t szlckr; ulong_t mszinode; ulong_t mszfile; ulong_t mszproc; ulong_t mszlckr; ulong_t niodevs; }; extern struct sa sa; #ifdef __cplusplus } #endif #endif /* _SA_H */ #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.5 */ # sa1.sh 1.5 of 5/8/89 DATE=`/bin/date +%d` ENDIR=/usr/lib/sa DFILE=/var/adm/sa/sa$DATE cd $ENDIR if [ $# = 0 ] then exec $ENDIR/sadc 1 1 $DFILE else exec $ENDIR/sadc $* $DFILE fi #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.4 */ # sa2.sh 1.4 of 5/8/89 DATE=`/bin/date +%d` RPT=/var/adm/sa/sar$DATE DFILE=/var/adm/sa/sa$DATE ENDIR=/usr/bin cd $ENDIR $ENDIR/sar $* -f $DFILE > $RPT /usr/bin/find /var/adm/sa \( -name 'sar*' -o -name 'sa*' \) -mtime +7 -exec /bin/rm {} \; /* * 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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * sadc.c writes system activity binary data to a file or stdout. * * Usage: sadc [t n] [file] * * if t and n are not specified, it writes a dummy record to data file. This * usage is particularly used at system booting. If t and n are specified, it * writes system data n times to file every t seconds. In both cases, if file * is not specified, it writes data to stdout. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sa.h" #define MAX(x1, x2) ((x1) >= (x2) ? (x1) : (x2)) static kstat_ctl_t *kc; /* libkstat cookie */ static int ncpus; static int oncpus; static kstat_t **cpu_stat_list = NULL; static kstat_t **ocpu_stat_list = NULL; static int ncaches; static kstat_t **kmem_cache_list = NULL; static kstat_t *sysinfo_ksp, *vminfo_ksp, *var_ksp; static kstat_t *system_misc_ksp, *ufs_inode_ksp, *kmem_oversize_ksp; static kstat_t *file_cache_ksp; static kstat_named_t *ufs_inode_size_knp, *nproc_knp; static kstat_named_t *file_total_knp, *file_avail_knp; static kstat_named_t *oversize_alloc_knp, *oversize_fail_knp; static int slab_create_index, slab_destroy_index, slab_size_index; static int buf_size_index, buf_avail_index, alloc_fail_index; static struct iodevinfo zeroiodev = { NULL, NULL }; static struct iodevinfo *firstiodev = NULL; static struct iodevinfo *lastiodev = NULL; static struct iodevinfo *snip = NULL; static ulong_t niodevs; static void all_stat_init(void); static int all_stat_load(void); static void fail(int, char *, ...); static void safe_zalloc(void **, int, int); static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *); static kstat_t *safe_kstat_lookup(kstat_ctl_t *, char *, int, char *); static void *safe_kstat_data_lookup(kstat_t *, char *); static int safe_kstat_data_index(kstat_t *, char *); static void init_iodevs(void); static int iodevinfo_load(void); static int kstat_copy(const kstat_t *, kstat_t *); static void diff_two_arrays(kstat_t ** const [], size_t, size_t, kstat_t ** const []); static void compute_cpu_stat_adj(void); static char *cmdname = "sadc"; static struct var var; static struct sa d; static int64_t cpu_stat_adj[CPU_STATES] = {0}; static long ninode; int caught_cont = 0; /* * Sleep until *wakeup + interval, keeping cadence where desired * * *wakeup - The time we last wanted to wake up. Updated. * interval - We want to sleep until *wakeup + interval * *caught_cont - Global set by signal handler if we got a SIGCONT */ void sleep_until(hrtime_t *wakeup, hrtime_t interval, int *caught_cont) { hrtime_t now, pause, pause_left; struct timespec pause_tv; int status; now = gethrtime(); pause = *wakeup + interval - now; if (pause <= 0 || pause < (interval / 4)) if (*caught_cont) { /* Reset our cadence (see comment below) */ *wakeup = now + interval; pause = interval; } else { /* * If we got here, then the time between the * output we just did, and the scheduled time * for the next output is < 1/4 of our requested * interval AND the number of intervals has been * requested AND we have never caught a SIGCONT * (so we have never been suspended). In this * case, we'll try to stay to the desired * cadence, and we will pause for 1/2 the normal * interval this time. */ pause = interval / 2; *wakeup += interval; } else *wakeup += interval; if (pause < 1000) /* Near enough */ return; /* Now do the actual sleep */ pause_left = pause; do { pause_tv.tv_sec = pause_left / NANOSEC; pause_tv.tv_nsec = pause_left % NANOSEC; status = nanosleep(&pause_tv, (struct timespec *)NULL); if (status < 0) if (errno == EINTR) { now = gethrtime(); pause_left = *wakeup - now; if (pause_left < 1000) /* Near enough */ return; } else { fail(1, "nanosleep failed"); } } while (status != 0); } /* * Signal handler - so we can be aware of SIGCONT */ void cont_handler(int sig_number) { /* Re-set the signal handler */ (void) signal(sig_number, cont_handler); caught_cont = 1; } int main(int argc, char *argv[]) { int ct; unsigned ti; int fp; time_t min; struct stat buf; char *fname; struct iodevinfo *iodev; off_t flength; hrtime_t start_n; hrtime_t period_n; ct = argc >= 3? atoi(argv[2]): 0; min = time((time_t *)0); ti = argc >= 3? atoi(argv[1]): 0; period_n = (hrtime_t)ti * NANOSEC; if ((kc = kstat_open()) == NULL) fail(1, "kstat_open(): can't open /dev/kstat"); /* Set up handler for SIGCONT */ if (signal(SIGCONT, cont_handler) == SIG_ERR) fail(1, "signal failed"); all_stat_init(); init_iodevs(); if (argc == 3 || argc == 1) { /* * no data file is specified, direct data to stdout. */ fp = 1; } else { struct flock lock; fname = (argc == 2) ? argv[1] : argv[3]; /* * Open or Create a data file. If the file doesn't exist, then * it will be created. */ if ((fp = open(fname, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) fail(1, "can't open data file"); /* * Lock the entire data file to prevent data corruption */ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fp, F_SETLK, &lock) == -1) fail(1, "can't lock data file"); /* * Get data file statistics for use in determining whether * truncation required and where rollback recovery should * be applied. */ if (fstat(fp, &buf) == -1) fail(1, "can't get data file information"); /* * If the data file was opened and is too old, truncate it */ if (min - buf.st_mtime > 86400) if (ftruncate(fp, 0) == -1) fail(1, "can't truncate data file"); /* * Remember filesize for rollback on error (bug #1223549) */ flength = buf.st_size; } memset(&d, 0, sizeof (d)); /* * If n == 0, write the additional dummy record. */ if (ct == 0) { d.valid = 0; d.ts = min; d.niodevs = niodevs; if (write(fp, &d, sizeof (struct sa)) != sizeof (struct sa)) ftruncate(fp, flength), fail(1, "write failed"); for (iodev = firstiodev; iodev; iodev = iodev->next) { if (write(fp, iodev, sizeof (struct iodevinfo)) != sizeof (struct iodevinfo)) ftruncate(fp, flength), fail(1, "write failed"); } } start_n = gethrtime(); for (;;) { do { (void) kstat_chain_update(kc); all_stat_init(); init_iodevs(); } while (all_stat_load() || iodevinfo_load()); d.ts = time((time_t *)0); d.valid = 1; d.niodevs = niodevs; if (write(fp, &d, sizeof (struct sa)) != sizeof (struct sa)) ftruncate(fp, flength), fail(1, "write failed"); for (iodev = firstiodev; iodev; iodev = iodev->next) { if (write(fp, iodev, sizeof (struct iodevinfo)) != sizeof (struct iodevinfo)) ftruncate(fp, flength), fail(1, "write failed"); } if (--ct > 0) { sleep_until(&start_n, period_n, &caught_cont); } else { close(fp); return (0); } } /*NOTREACHED*/ } /* * Get various KIDs for subsequent all_stat_load operations. */ static void all_stat_init(void) { kstat_t *ksp; /* * Initialize global statistics */ sysinfo_ksp = safe_kstat_lookup(kc, "unix", 0, "sysinfo"); vminfo_ksp = safe_kstat_lookup(kc, "unix", 0, "vminfo"); kmem_oversize_ksp = safe_kstat_lookup(kc, "vmem", -1, "kmem_oversize"); var_ksp = safe_kstat_lookup(kc, "unix", 0, "var"); system_misc_ksp = safe_kstat_lookup(kc, "unix", 0, "system_misc"); file_cache_ksp = safe_kstat_lookup(kc, "unix", 0, "file_cache"); ufs_inode_ksp = kstat_lookup(kc, "ufs", 0, "inode_cache"); safe_kstat_read(kc, system_misc_ksp, NULL); nproc_knp = safe_kstat_data_lookup(system_misc_ksp, "nproc"); safe_kstat_read(kc, file_cache_ksp, NULL); file_avail_knp = safe_kstat_data_lookup(file_cache_ksp, "buf_avail"); file_total_knp = safe_kstat_data_lookup(file_cache_ksp, "buf_total"); safe_kstat_read(kc, kmem_oversize_ksp, NULL); oversize_alloc_knp = safe_kstat_data_lookup(kmem_oversize_ksp, "mem_total"); oversize_fail_knp = safe_kstat_data_lookup(kmem_oversize_ksp, "fail"); if (ufs_inode_ksp != NULL) { safe_kstat_read(kc, ufs_inode_ksp, NULL); ufs_inode_size_knp = safe_kstat_data_lookup(ufs_inode_ksp, "size"); ninode = ((kstat_named_t *) safe_kstat_data_lookup(ufs_inode_ksp, "maxsize"))->value.l; } /* * Load constant values now -- no need to reread each time */ safe_kstat_read(kc, var_ksp, (void *) &var); /* * Initialize per-CPU and per-kmem-cache statistics */ ncpus = ncaches = 0; for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (strncmp(ksp->ks_name, "cpu_stat", 8) == 0) ncpus++; if (strcmp(ksp->ks_class, "kmem_cache") == 0) ncaches++; } safe_zalloc((void **)&cpu_stat_list, ncpus * sizeof (kstat_t *), 1); safe_zalloc((void **)&kmem_cache_list, ncaches * sizeof (kstat_t *), 1); ncpus = ncaches = 0; for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (strncmp(ksp->ks_name, "cpu_stat", 8) == 0 && kstat_read(kc, ksp, NULL) != -1) cpu_stat_list[ncpus++] = ksp; if (strcmp(ksp->ks_class, "kmem_cache") == 0 && kstat_read(kc, ksp, NULL) != -1) kmem_cache_list[ncaches++] = ksp; } if (ncpus == 0) fail(1, "can't find any cpu statistics"); if (ncaches == 0) fail(1, "can't find any kmem_cache statistics"); ksp = kmem_cache_list[0]; safe_kstat_read(kc, ksp, NULL); buf_size_index = safe_kstat_data_index(ksp, "buf_size"); slab_create_index = safe_kstat_data_index(ksp, "slab_create"); slab_destroy_index = safe_kstat_data_index(ksp, "slab_destroy"); slab_size_index = safe_kstat_data_index(ksp, "slab_size"); buf_avail_index = safe_kstat_data_index(ksp, "buf_avail"); alloc_fail_index = safe_kstat_data_index(ksp, "alloc_fail"); } /* * load statistics, summing across CPUs where needed */ static int all_stat_load(void) { int i, j; cpu_stat_t cs; ulong_t *np, *tp; uint64_t cpu_tick[4] = {0, 0, 0, 0}; memset(&d, 0, sizeof (d)); /* * Global statistics */ safe_kstat_read(kc, sysinfo_ksp, (void *) &d.si); safe_kstat_read(kc, vminfo_ksp, (void *) &d.vmi); safe_kstat_read(kc, system_misc_ksp, NULL); safe_kstat_read(kc, file_cache_ksp, NULL); if (ufs_inode_ksp != NULL) { safe_kstat_read(kc, ufs_inode_ksp, NULL); d.szinode = ufs_inode_size_knp->value.ul; } d.szfile = file_total_knp->value.ui64 - file_avail_knp->value.ui64; d.szproc = nproc_knp->value.ul; d.mszinode = (ninode > d.szinode) ? ninode : d.szinode; d.mszfile = d.szfile; d.mszproc = var.v_proc; /* * Per-CPU statistics. */ for (i = 0; i < ncpus; i++) { if (kstat_read(kc, cpu_stat_list[i], (void *) &cs) == -1) return (1); np = (ulong_t *)&d.csi; tp = (ulong_t *)&cs.cpu_sysinfo; /* * Accumulate cpu ticks for CPU_IDLE, CPU_USER, CPU_KERNEL and * CPU_WAIT with respect to each of the cpus. */ for (j = 0; j < CPU_STATES; j++) cpu_tick[j] += tp[j]; for (j = 0; j < sizeof (cpu_sysinfo_t); j += sizeof (ulong_t)) *np++ += *tp++; np = (ulong_t *)&d.cvmi; tp = (ulong_t *)&cs.cpu_vminfo; for (j = 0; j < sizeof (cpu_vminfo_t); j += sizeof (ulong_t)) *np++ += *tp++; } /* * Per-cache kmem statistics. */ for (i = 0; i < ncaches; i++) { kstat_named_t *knp; u_longlong_t slab_create, slab_destroy, slab_size, mem_total; u_longlong_t buf_size, buf_avail, alloc_fail; int kmi_index; if (kstat_read(kc, kmem_cache_list[i], NULL) == -1) return (1); knp = kmem_cache_list[i]->ks_data; slab_create = knp[slab_create_index].value.ui64; slab_destroy = knp[slab_destroy_index].value.ui64; slab_size = knp[slab_size_index].value.ui64; buf_size = knp[buf_size_index].value.ui64; buf_avail = knp[buf_avail_index].value.ui64; alloc_fail = knp[alloc_fail_index].value.ui64; if (buf_size <= 256) kmi_index = KMEM_SMALL; else kmi_index = KMEM_LARGE; mem_total = (slab_create - slab_destroy) * slab_size; d.kmi.km_mem[kmi_index] += (ulong_t)mem_total; d.kmi.km_alloc[kmi_index] += (ulong_t)mem_total - buf_size * buf_avail; d.kmi.km_fail[kmi_index] += (ulong_t)alloc_fail; } safe_kstat_read(kc, kmem_oversize_ksp, NULL); d.kmi.km_alloc[KMEM_OSIZE] = d.kmi.km_mem[KMEM_OSIZE] = oversize_alloc_knp->value.ui64; d.kmi.km_fail[KMEM_OSIZE] = oversize_fail_knp->value.ui64; /* * Adjust CPU statistics so the delta calculations in sar will * be correct when facing changes to the set of online CPUs. */ compute_cpu_stat_adj(); for (i = 0; i < CPU_STATES; i++) d.csi.cpu[i] = (cpu_tick[i] + cpu_stat_adj[i]) / ncpus; return (0); } static void fail(int do_perror, char *message, ...) { va_list args; va_start(args, message); fprintf(stderr, "%s: ", cmdname); vfprintf(stderr, message, args); va_end(args); if (do_perror) fprintf(stderr, ": %s", strerror(errno)); fprintf(stderr, "\n"); exit(2); } 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"); memset(*ptr, 0, size); } static kid_t safe_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data) { kid_t kstat_chain_id = kstat_read(kc, ksp, data); if (kstat_chain_id == -1) fail(1, "kstat_read(%x, '%s') failed", kc, ksp->ks_name); return (kstat_chain_id); } static kstat_t * safe_kstat_lookup(kstat_ctl_t *kc, char *ks_module, int ks_instance, char *ks_name) { kstat_t *ksp = kstat_lookup(kc, ks_module, ks_instance, ks_name); if (ksp == NULL) fail(0, "kstat_lookup('%s', %d, '%s') failed", ks_module == NULL ? "" : ks_module, ks_instance, ks_name == NULL ? "" : ks_name); return (ksp); } static void * safe_kstat_data_lookup(kstat_t *ksp, char *name) { void *fp = kstat_data_lookup(ksp, name); if (fp == NULL) fail(0, "kstat_data_lookup('%s', '%s') failed", ksp->ks_name, name); return (fp); } static int safe_kstat_data_index(kstat_t *ksp, char *name) { return ((int)((char *)safe_kstat_data_lookup(ksp, name) - (char *)ksp->ks_data) / (ksp->ks_data_size / ksp->ks_ndata)); } static int kscmp(kstat_t *ks1, kstat_t *ks2) { int cmp; cmp = strcmp(ks1->ks_module, ks2->ks_module); if (cmp != 0) return (cmp); cmp = ks1->ks_instance - ks2->ks_instance; if (cmp != 0) return (cmp); return (strcmp(ks1->ks_name, ks2->ks_name)); } static void init_iodevs(void) { struct iodevinfo *iodev, *previodev, *comp; kstat_t *ksp; iodev = &zeroiodev; niodevs = 0; /* * Patch the snip in the iodevinfo list (see below) */ if (snip) lastiodev->next = snip; for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (ksp->ks_type != KSTAT_TYPE_IO) continue; previodev = iodev; if (iodev->next) iodev = iodev->next; else { safe_zalloc((void **) &iodev->next, sizeof (struct iodevinfo), 0); iodev = iodev->next; iodev->next = NULL; } iodev->ksp = ksp; iodev->ks = *ksp; memset((void *)&iodev->kios, 0, sizeof (kstat_io_t)); iodev->kios.wlastupdate = iodev->ks.ks_crtime; iodev->kios.rlastupdate = iodev->ks.ks_crtime; /* * Insertion sort on (ks_module, ks_instance, ks_name) */ comp = &zeroiodev; while (kscmp(&iodev->ks, &comp->next->ks) > 0) comp = comp->next; if (previodev != comp) { previodev->next = iodev->next; iodev->next = comp->next; comp->next = iodev; iodev = previodev; } niodevs++; } /* * Put a snip in the linked list of iodevinfos. The idea: * If there was a state change such that now there are fewer * iodevs, we snip the list and retain the tail, rather than * freeing it. At the next state change, we clip the tail back on. * This prevents a lot of malloc/free activity, and it's simpler. */ lastiodev = iodev; snip = iodev->next; iodev->next = NULL; firstiodev = zeroiodev.next; } static int iodevinfo_load(void) { struct iodevinfo *iodev; for (iodev = firstiodev; iodev; iodev = iodev->next) { if (kstat_read(kc, iodev->ksp, (void *) &iodev->kios) == -1) return (1); } return (0); } static int kstat_copy(const kstat_t *src, kstat_t *dst) { *dst = *src; if (src->ks_data != NULL) { if ((dst->ks_data = malloc(src->ks_data_size)) == NULL) return (-1); bcopy(src->ks_data, dst->ks_data, src->ks_data_size); } else { dst->ks_data = NULL; dst->ks_data_size = 0; } return (0); } /* * Determine what is different between two sets of kstats; s[0] and s[1] * are arrays of kstats of size ns0 and ns1, respectively, and sorted by * instance number. u[0] and u[1] are two arrays which must be * caller-zallocated; each must be of size MAX(ns0, ns1). When the * function terminates, u[0] contains all s[0]-unique items and u[1] * contains all s[1]-unique items. Any unused entries in u[0] and u[1] * are left NULL. */ static void diff_two_arrays(kstat_t ** const s[], size_t ns0, size_t ns1, kstat_t ** const u[]) { kstat_t **s0p = s[0], **s1p = s[1]; kstat_t **u0p = u[0], **u1p = u[1]; int i = 0, j = 0; while (i < ns0 && j < ns1) { if ((*s0p)->ks_instance == (*s1p)->ks_instance) { if ((*s0p)->ks_kid != (*s1p)->ks_kid) { /* * The instance is the same, but this * CPU has been offline during the * interval, so we consider *u0p to * be s0p-unique, and similarly for * *u1p. */ *(u0p++) = *s0p; *(u1p++) = *s1p; } s0p++; i++; s1p++; j++; } else if ((*s0p)->ks_instance < (*s1p)->ks_instance) { *(u0p++) = *(s0p++); i++; } else { *(u1p++) = *(s1p++); j++; } } while (i < ns0) { *(u0p++) = *(s0p++); i++; } while (j < ns1) { *(u1p++) = *(s1p++); j++; } } static int cpuid_compare(const void *p1, const void *p2) { return ((*(kstat_t **)p1)->ks_instance - (*(kstat_t **)p2)->ks_instance); } /* * Identify those CPUs which were not present for the whole interval so * their statistics can be removed from the aggregate. */ static void compute_cpu_stat_adj(void) { int i, j; if (ocpu_stat_list) { kstat_t **s[2]; kstat_t **inarray[2]; int max_cpus = MAX(ncpus, oncpus); qsort(cpu_stat_list, ncpus, sizeof (*cpu_stat_list), cpuid_compare); qsort(ocpu_stat_list, oncpus, sizeof (*ocpu_stat_list), cpuid_compare); s[0] = ocpu_stat_list; s[1] = cpu_stat_list; safe_zalloc((void *)&inarray[0], sizeof (**inarray) * max_cpus, 0); safe_zalloc((void *)&inarray[1], sizeof (**inarray) * max_cpus, 0); diff_two_arrays(s, oncpus, ncpus, inarray); for (i = 0; i < max_cpus; i++) { if (inarray[0][i]) for (j = 0; j < CPU_STATES; j++) cpu_stat_adj[j] += ((cpu_stat_t *)inarray[0][i] ->ks_data)->cpu_sysinfo.cpu[j]; if (inarray[1][i]) for (j = 0; j < CPU_STATES; j++) cpu_stat_adj[j] -= ((cpu_stat_t *)inarray[1][i] ->ks_data)->cpu_sysinfo.cpu[j]; } free(inarray[0]); free(inarray[1]); } /* * Preserve the last interval's CPU stats. */ if (cpu_stat_list) { for (i = 0; i < oncpus; i++) free(ocpu_stat_list[i]->ks_data); oncpus = ncpus; safe_zalloc((void **)&ocpu_stat_list, oncpus * sizeof (*ocpu_stat_list), 1); for (i = 0; i < ncpus; i++) { safe_zalloc((void *)&ocpu_stat_list[i], sizeof (*ocpu_stat_list[0]), 0); if (kstat_copy(cpu_stat_list[i], ocpu_stat_list[i])) fail(1, "kstat_copy() failed"); } } } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * 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++); } /* * Convert array of 64-bit uints to 32-bit uints */ static void convert_64to32(uint_t *dst, uint64_t *src, int size) { for (; size > 0; size--) *dst++ = (uint32_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 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. */ 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)) { /* * The calculated values are stroed in nx strcuture. * Convert 64-bit nx to 32-bit tx structure. */ tx.valid = nx.valid; tx.ts = nx.ts; convert_64to32((uint_t *)&tx.csi, (uint64_t *)&nx.csi, sizeof (nx.csi) / sizeof (uint64_t)); convert_64to32((uint_t *)&tx.cvmi, (uint64_t *)&nx.cvmi, sizeof (nx.cvmi) / sizeof (uint64_t)); convert_64to32((uint_t *)&tx.si, (uint64_t *)&nx.si, sizeof (nx.si) / sizeof (uint64_t)); (void) memcpy(&tx.vmi, &nx.vmi, sizeof (nx) - (((char *)&nx.vmi) - ((char *)&nx))); if (tx.valid != 0 && tx.valid != 1) fail(2, "data file not in sar format"); safe_write(fout, &tx, 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++]) != '\0') { 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; aio->ks.ks_snaptime += dio->ks.ks_snaptime = nio->ks.ks_snaptime - oio->ks.ks_snaptime; 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, pbusy; tsttab(); hr_etime = (double)xio[ii].ks.ks_snaptime; if (hr_etime == 0.0) hr_etime = (double)NANOSEC; pbusy = (double)xio[ii].kios.rtime * 100.0 / hr_etime; if (pbusy > 100.0) pbusy = 100.0; 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", pbusy, 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++]) != '\0') { 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++]) != '\0') { 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"); } #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # #ident "%Z%%M% %I% %E% SMI" # # Start method script for the system activity reporting service. # # To enable periodic collection of data, you may need to uncomment # the entries in /var/spool/cron/crontabs/sys arg=$1 set -- `/bin/who -r` _INIT_RUN_NPREV="$8" # utmpx's runlevel entries are only set on reaching single-user, # so there might not be a who -r entry yet. [ -z "$_INIT_RUN_NPREV" ] && _INIT_RUN_NPREV=0 case "$arg" in 'start') if [ $_INIT_RUN_NPREV -eq 0 ]; then /usr/lib/sa/sadc /var/adm/sa/sa`date +%d` if [ ! -f /var/spool/cron/crontabs/sys ]; then cat << EOF > /var/spool/cron/crontabs/sys # # sys crontab # # The sys crontab should be used to do performance collection. # Please note that if the system/sar service is disabled this file # will be deleted. Please keep a backup copy if you change the # defaults. # 0 * * * 0-6 /usr/lib/sa/sa1 20,40 8-17 * * 1-5 /usr/lib/sa/sa1 5 18 * * 1-5 /usr/lib/sa/sa2 -s 8:00 -e 18:01 -i 1200 -A EOF fi fi ;; 'stop') rm /var/spool/cron/crontabs/sys ;; *) echo "Usage: $0 { start | stop }" exit 1 ;; esac exit 0 /* * 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 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define NSEC_TO_TICK(nsec) ((nsec) / nsec_per_tick) #define NSEC_TO_TICK_ROUNDUP(nsec) NSEC_TO_TICK((nsec) + \ nsec_per_tick/2) char fname[20]; static int hz; static int nsec_per_tick; void printt(char *, hrtime_t); void hmstime(char[]); void usage(); int main(int argc, char **argv) { struct tms buffer, obuffer; int status; register pid_t p; int c; hrtime_t before, after, timediff; char stime[9], etime[9]; char cmd[80]; int pflg = 0, sflg = 0, oflg = 0; char aopt[25]; FILE *pipin; char ttyid[12], line[150]; char eol; char fld[20][12]; int iline = 0, i, nfld; int ichar, iblok; long chars = 0, bloks = 0; aopt[0] = '\0'; hz = sysconf(_SC_CLK_TCK); nsec_per_tick = NANOSEC / hz; /* check options; */ while ((c = getopt(argc, argv, "sopfhkmrt")) != EOF) switch (c) { case 's': sflg++; break; case 'o': oflg++; break; case 'p': pflg++; break; case 'f': strcat(aopt, "-f "); break; case 'h': strcat(aopt, "-h "); break; case 'k': strcat(aopt, "-k "); break; case 'm': strcat(aopt, "-m "); break; case 'r': strcat(aopt, "-r "); break; case 't': strcat(aopt, "-t "); break; case '?': usage(); break; } if (optind >= argc) { fprintf(stderr, "timex: Missing command\n"); usage(); } /* * Check to see if accounting is installed and print a somewhat * meaninful message if not. */ if (((oflg+pflg) != 0) && (access("/usr/bin/acctcom", 01) == -1)) { oflg = 0; pflg = 0; fprintf(stderr, "Information from -p and -o options not available\n"); fprintf(stderr, " because process accounting is not operational.\n"); } if (sflg) { sprintf(fname, "/tmp/tmx%ld", getpid()); sprintf(cmd, "/usr/lib/sa/sadc 1 1 %s", fname); system(cmd); } if (pflg + oflg) hmstime(stime); before = gethrtime(); (void) times(&obuffer); if ((p = fork()) == (pid_t)-1) { perror("Fork Failed"); (void) unlink(fname); exit(EXIT_FAILURE); } if (p == 0) { setgid(getgid()); execvp(*(argv+optind), (argv+optind)); fprintf(stderr, "%s: %s\n", *(argv+optind), strerror(errno)); exit(EXIT_FAILURE); } signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); while (wait(&status) != p) ; if ((status&0377) != 0) fprintf(stderr, "Command terminated abnormally.\n"); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); (void) times(&buffer); after = gethrtime(); timediff = after - before; if (pflg + oflg) hmstime(etime); if (sflg) system(cmd); fprintf(stderr, "\n"); printt("real", NSEC_TO_TICK_ROUNDUP(timediff)); printt("user", buffer.tms_cutime - obuffer.tms_cutime); printt("sys ", buffer.tms_cstime - obuffer.tms_cstime); fprintf(stderr, "\n"); if (oflg+pflg) { if (isatty(0)) sprintf(ttyid, "-l %s", ttyname(0)+5); sprintf(cmd, "acctcom -S %s -E %s -u %s %s -i %s", stime, etime, getpwuid(getuid())->pw_name, ttyid, aopt); pipin = popen(cmd, "r"); while (fscanf(pipin, "%[^\n]%1c", line, &eol) > 1) { if (pflg) fprintf(stderr, "%s\n", line); if (oflg) { nfld = sscanf(line, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", fld[0], fld[1], fld[2], fld[3], fld[4], fld[5], fld[6], fld[7], fld[8], fld[9], fld[10], fld[11], fld[12], fld[13], fld[14], fld[15], fld[16], fld[17], fld[18], fld[19]); if (++iline == 3) for (i = 0; i < nfld; i++) { if (strcmp(fld[i], "CHARS") == 0) ichar = i+2; if (strcmp(fld[i], "BLOCKS") == 0) iblok = i+2; } if (iline > 4) { chars += atol(fld[ichar]); bloks += atol(fld[iblok]); } } } pclose(pipin); if (oflg) if (iline > 4) fprintf(stderr, "\nCHARS TRNSFD = %ld\n" "BLOCKS READ = %ld\n", chars, bloks); else fprintf(stderr, "\nNo process records found!\n"); } if (sflg) { sprintf(cmd, "/usr/bin/sar -ubdycwaqvmpgrk -f %s 1>&2", fname); system(cmd); unlink(fname); } exit(WEXITSTATUS(status)); } void printt(char *label, hrtime_t ticks) { long tk; /* number of leftover ticks */ long ss; /* number of seconds */ long mm; /* number of minutes */ long hh; /* number of hours */ longlong_t total = ticks; tk = total % hz; /* ticks % hz */ total /= hz; ss = total % 60; /* ticks / hz % 60 */ total /= 60; mm = total % 60; /* ticks / hz / 60 % 60 */ hh = total / 60; /* ticks / hz / 60 / 60 */ (void) fprintf(stderr, "%s ", label); /* Display either padding or the elapsed hours */ if (hh == 0L) { (void) fprintf(stderr, "%6c", ' '); } else { (void) fprintf(stderr, "%5ld:", hh); } /* * Display either nothing or the elapsed minutes, zero * padding (if hours > 0) or space padding (if not). */ if (mm == 0L && hh == 0L) { (void) fprintf(stderr, "%3c", ' '); } else if (mm != 0L && hh == 0L) { (void) fprintf(stderr, "%2ld:", mm); } else { (void) fprintf(stderr, "%02ld:", mm); } /* * Display the elapsed seconds; seconds are always * zero padded. */ if (hh == 0L && mm == 0L) { (void) fprintf(stderr, "%2ld.", ss); } else { (void) fprintf(stderr, "%02ld.", ss); } /* Display hundredths of a second. */ (void) fprintf(stderr, "%02ld\n", tk * 100/hz); } /* * hmstime() sets current time in hh:mm:ss string format in stime; */ void hmstime(char stime[]) { char *ltime; time_t tme; tme = time((time_t *)0); ltime = ctime(&tme); strncpy(stime, ltime+11, 8); stime[8] = '\0'; } void usage() { fprintf(stderr, "Usage: timex [-o] [-p [-fhkmrt]] [-s] command\n"); unlink(fname); exit(EXIT_FAILURE); }