rcapstat.c revision 0209230bf1261579beab4f55226bb509e6b850cb
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <libintl.h>
#include <locale.h>
#include "rcapd.h"
#include "utils.h"
#include "rcapd_stat.h"
static char mode[RC_MODE_LEN];
static rcapd_stat_hdr_t hdr;
static int global;
static int unformatted;
static time_t stat_mod = 0;
typedef struct col {
rcid_t col_id;
char col_name[LC_NAME_LEN];
uint64_t col_nproc;
uint64_t col_vmsize;
uint64_t col_rsssize;
uint64_t col_rsslimit;
uint64_t col_paged_eff;
uint64_t col_paged_eff_old;
uint64_t col_paged_eff_avg;
uint64_t col_paged_att;
uint64_t col_paged_att_old;
uint64_t col_paged_att_avg;
uint64_t col_count;
int col_fresh;
struct col *col_next;
struct col *col_prev;
lcollection_stat_t col_src_stat;
lcollection_stat_t col_old_stat;
} col_t;
static col_t *col_head;
static int ncol;
static col_t *
col_find(rcid_t id)
{
col_t *col;
for (col = col_head; col != NULL; col = col->col_next)
if (col->col_id.rcid_type == id.rcid_type &&
col->col_id.rcid_val == id.rcid_val)
return (col);
return (NULL);
}
static col_t *
col_insert(rcid_t id)
{
col_t *new_col;
new_col = malloc(sizeof (col_t));
if (new_col == NULL) {
(void) fprintf(stderr, gettext("rcapstat: malloc() failed\n"));
exit(E_ERROR);
}
(void) memset(new_col, 0, sizeof (col_t));
new_col->col_next = col_head;
new_col->col_id = id;
if (col_head != NULL)
col_head->col_prev = new_col;
col_head = new_col;
ncol++;
return (new_col);
}
static void
col_remove(col_t *col)
{
if (col->col_prev != NULL)
col->col_prev->col_next = col->col_next;
if (col->col_next != NULL)
col->col_next->col_prev = col->col_prev;
if (col_head == col)
col_head = col->col_next;
ncol--;
free(col);
}
static void
usage()
{
(void) fprintf(stderr,
gettext("usage: rcapstat [-g] [-p | -z] [interval [count]]\n"));
exit(E_USAGE);
}
static void
format_size(char *str, uint64_t size, int length)
{
char tag = 'K';
if (size >= 10000) {
size = (size + 512) / 1024;
tag = 'M';
if (size >= 10000) {
size = (size + 512) / 1024;
tag = 'G';
}
}
(void) snprintf(str, length, "%4lld%c", size, tag);
}
static int
read_stats(rcid_type_t stat_type)
{
int fd;
int proc_fd;
char procfile[20];
uint64_t pid;
col_t *col, *col_next;
lcollection_report_t report;
struct stat st;
if ((fd = open(STAT_FILE_DEFAULT, O_RDONLY)) < 0) {
warn(gettext("rcapd is not active\n"));
return (E_ERROR);
}
if (fstat(fd, &st) == 0)
stat_mod = st.st_mtime;
if (read(fd, &hdr, sizeof (hdr)) != sizeof (hdr)) {
(void) fprintf(stderr,
gettext("rcapstat: can't read stat file header: %s\n"),
strerror(errno));
(void) close(fd);
return (E_ERROR);
}
/*
* Check if rcapd is running
*/
pid = hdr.rs_pid;
(void) snprintf(procfile, 20, "/proc/%lld/psinfo", pid);
if ((proc_fd = open(procfile, O_RDONLY)) < 0) {
warn(gettext("rcapd is not active\n"));
(void) close(fd);
return (E_ERROR);
}
(void) close(proc_fd);
(void) strncpy(mode, hdr.rs_mode, RC_MODE_LEN);
for (col = col_head; col != NULL; col = col->col_next) {
col->col_fresh = 0;
col->col_paged_eff = 0;
col->col_paged_att = 0;
}
while (read(fd, &report, sizeof (report)) == sizeof (report)) {
if (report.lcol_id.rcid_type != stat_type)
continue;
col = col_find(report.lcol_id);
if (col == NULL) {
col = col_insert(report.lcol_id);
col->col_paged_eff_old = col->col_paged_eff =
report.lcol_stat.lcols_pg_eff;
col->col_paged_att_old = col->col_paged_att =
report.lcol_stat.lcols_pg_att;
col->col_count = 0;
}
(void) strncpy(col->col_name, report.lcol_name, LC_NAME_LEN);
col->col_vmsize = report.lcol_image_size;
col->col_rsssize = report.lcol_rss;
col->col_rsslimit = report.lcol_rss_cap;
col->col_fresh = 1;
if (report.lcol_stat.lcols_pg_eff > col->col_paged_eff_old) {
col->col_paged_eff =
report.lcol_stat.lcols_pg_eff -
col->col_paged_eff_old;
if (report.lcol_stat.lcols_scan_count > col->col_count)
col->col_paged_eff_avg =
col->col_paged_eff /
(report.lcol_stat.lcols_scan_count -
col->col_count);
} else {
col->col_paged_eff_avg = 0;
}
if (report.lcol_stat.lcols_pg_att > col->col_paged_att_old) {
col->col_paged_att =
report.lcol_stat.lcols_pg_att -
col->col_paged_att_old;
if (report.lcol_stat.lcols_scan_count > col->col_count)
col->col_paged_att_avg =
col->col_paged_att /
(report.lcol_stat.lcols_scan_count -
col->col_count);
} else {
col->col_paged_att_avg = 0;
}
col->col_paged_eff_old = report.lcol_stat.lcols_pg_eff;
col->col_paged_att_old = report.lcol_stat.lcols_pg_att;
col->col_nproc =
report.lcol_stat.lcols_proc_in -
report.lcol_stat.lcols_proc_out;
col->col_count = report.lcol_stat.lcols_scan_count;
col->col_src_stat = report.lcol_stat;
}
/*
* Remove stale data
*/
col = col_head;
while (col != NULL) {
col_next = col->col_next;
if (col->col_fresh == 0)
col_remove(col);
col = col_next;
}
(void) close(fd);
return (E_SUCCESS);
}
/*
* Print each collection's interval statistics.
*/
/*ARGSUSED*/
static void
print_unformatted_stats(void)
{
col_t *col;
#define DELTA(field) \
(col->col_src_stat.field - col->col_old_stat.field)
col = col_head;
while (col != NULL) {
if (bcmp(&col->col_src_stat, &col->col_old_stat,
sizeof (col->col_src_stat)) == 0) {
col = col->col_next;
continue;
}
(void) printf("%s %s status: succeeded/attempted (k): "
"%llu/%llu, ineffective/scans/unenforced/samplings: "
"%llu/%llu/%llu/%llu, RSS min/max (k): %llu/%llu, cap %llu "
"kB, processes/thpt: %llu/%llu, %llu scans over %lld ms\n",
mode, col->col_name, DELTA(lcols_pg_eff),
DELTA(lcols_pg_att), DELTA(lcols_scan_ineffective),
DELTA(lcols_scan), DELTA(lcols_unenforced_cap),
DELTA(lcols_rss_sample), col->col_src_stat.lcols_min_rss,
col->col_src_stat.lcols_max_rss, col->col_rsslimit,
(col->col_src_stat.lcols_proc_in -
col->col_old_stat.lcols_proc_out), DELTA(lcols_proc_out),
DELTA(lcols_scan_count), DELTA(lcols_scan_time_complete) /
(NANOSEC / MILLISEC));
col->col_old_stat = col->col_src_stat;
col = col->col_next;
}
if (global)
(void) printf(gettext("physical memory utilization: %3u%% "
"cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur,
hdr.rs_pressure_cap);
#undef DELTA
}
static void
print_stats(rcid_type_t stat_type)
{
col_t *col;
char size[6];
char limit[6];
char rss[6];
char nproc[6];
char paged_att[6];
char paged_eff[6];
char paged_att_avg[6];
char paged_eff_avg[6];
static int count = 0;
/*
* Print a header once every 20 times if we're only displaying reports
* for one collection (10 times if -g is used). Print a header every
* interval otherwise.
*/
if (count == 0 || ncol != 1)
(void) printf("%6s %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n",
"id", (stat_type == RCIDT_PROJECT ? "project" : "zone"),
"nproc", "vm", "rss", "cap",
"at", "avgat", "pg", "avgpg");
if (++count >= 20 || (count >= 10 && global != 0) || ncol != 1)
count = 0;
for (col = col_head; col != NULL; col = col->col_next) {
if (col->col_id.rcid_type != stat_type)
continue;
if (col->col_paged_att == 0)
strlcpy(nproc, "-", sizeof (nproc));
else
(void) snprintf(nproc, sizeof (nproc), "%lld",
col->col_nproc);
format_size(size, col->col_vmsize, 6);
format_size(rss, col->col_rsssize, 6);
format_size(limit, col->col_rsslimit, 6);
format_size(paged_att, col->col_paged_att, 6);
format_size(paged_eff, col->col_paged_eff, 6);
format_size(paged_att_avg, col->col_paged_att_avg, 6);
format_size(paged_eff_avg, col->col_paged_eff_avg, 6);
(void) printf("%6lld %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n",
col->col_id.rcid_val, col->col_name,
nproc,
size, rss, limit,
paged_att, paged_att_avg,
paged_eff, paged_eff_avg);
}
if (global)
(void) printf(gettext("physical memory utilization: %3u%% "
"cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur,
hdr.rs_pressure_cap);
}
int
main(int argc, char *argv[])
{
int interval = 5;
int count;
int always = 1;
int opt;
int projects = 0;
int zones = 0;
/* project reporting is the default if no option is specified */
rcid_type_t stat_type = RCIDT_PROJECT;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
(void) setprogname("rcapstat");
global = unformatted = 0;
while ((opt = getopt(argc, argv, "gpuz")) != (int)EOF) {
switch (opt) {
case 'g':
global = 1;
break;
case 'p':
projects = 1;
stat_type = RCIDT_PROJECT;
break;
case 'u':
unformatted = 1;
break;
case 'z':
stat_type = RCIDT_ZONE;
zones = 1;
break;
default:
usage();
}
}
if (argc > optind)
if ((interval = xatoi(argv[optind++])) <= 0)
die(gettext("invalid interval specified\n"));
if (argc > optind) {
if ((count = xatoi(argv[optind++])) <= 0)
die(gettext("invalid count specified\n"));
always = 0;
}
if (argc > optind || (projects > 0 && zones > 0))
usage();
while (always || count-- > 0) {
if (read_stats(stat_type) != E_SUCCESS)
return (E_ERROR);
if (!unformatted) {
print_stats(stat_type);
(void) fflush(stdout);
if (count || always)
(void) sleep(interval);
} else {
struct stat st;
print_unformatted_stats();
(void) fflush(stdout);
while (stat(STAT_FILE_DEFAULT, &st) == 0 &&
st.st_mtime == stat_mod)
usleep((useconds_t)(0.2 * MICROSEC));
}
}
return (E_SUCCESS);
}