prstat.c revision 51de0612c34338935b2a778550614b1ace39ed56
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Portions Copyright 2009 Chad Mynhier
*/
#include <sys/resource.h>
#include <sys/vm_usage.h>
#include <zone.h>
#include <libzonecfg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include <project.h>
#include <langinfo.h>
#include <libintl.h>
#include <locale.h>
#include "prstat.h"
#include "prutil.h"
#include "prtable.h"
#include "prsort.h"
#include "prfile.h"
/*
* of this file, we care about the curses.h ERR so include that last.
*/
#if defined(ERR)
#endif
#ifndef TEXT_DOMAIN /* should be defined by cc -D */
#endif
#include <curses.h>
#include <term.h>
#define PSINFO_HEADER_PROC \
#define PSINFO_HEADER_PROC_LGRP \
#define PSINFO_HEADER_LWP \
#define PSINFO_HEADER_LWP_LGRP \
#define USAGE_HEADER_PROC \
#define USAGE_HEADER_LWP \
#define USER_HEADER_PROC \
" NPROC USERNAME SWAP RSS MEMORY TIME CPU "
#define USER_HEADER_LWP \
" NLWP USERNAME SWAP RSS MEMORY TIME CPU "
#define TASK_HEADER_PROC \
"TASKID NPROC SWAP RSS MEMORY TIME CPU PROJECT "
#define TASK_HEADER_LWP \
"TASKID NLWP SWAP RSS MEMORY TIME CPU PROJECT "
#define PROJECT_HEADER_PROC \
"PROJID NPROC SWAP RSS MEMORY TIME CPU PROJECT "
#define PROJECT_HEADER_LWP \
"PROJID NLWP SWAP RSS MEMORY TIME CPU PROJECT "
#define ZONE_HEADER_PROC \
"ZONEID NPROC SWAP RSS MEMORY TIME CPU ZONE "
#define ZONE_HEADER_LWP \
"ZONEID NLWP SWAP RSS MEMORY TIME CPU ZONE "
#define PSINFO_LINE \
"%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %-.16s/%d"
#define PSINFO_LINE_LGRP \
"%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %4d %-.16s/%d"
#define USAGE_LINE \
"%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
"%3.3s %-.12s/%d"
#define USER_LINE \
"%6d %-8s %5.5s %5.5s %3.3s%% %9s %3.3s%%"
#define TASK_LINE \
"%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
#define PROJECT_LINE \
"%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
#define ZONE_LINE \
"%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
#define TOTAL_LINE \
"Total: %d processes, %d lwps, load averages: %3.2f, %3.2f, %3.2f"
/* global variables */
static char *t_ulon; /* termcap: start underline */
static char *t_uloff; /* termcap: end underline */
static char *t_up; /* termcap: cursor 1 line up */
static char *t_eol; /* termcap: clear end of line */
static char *t_smcup; /* termcap: cursor mvcap on */
static char *t_rmcup; /* termcap: cursor mvcap off */
static char *t_home; /* termcap: move cursor home */
static float total_cpu; /* total cpu usage */
static float total_mem; /* total memory usage */
static long pagesize;
/* default settings */
5, /* interval between updates, seconds */
15, /* number of lines in top part */
5, /* number of lines in bottom part */
-1, /* number of iterations; infinitely */
-1 /* sort in decreasing order */
};
/*
* Print timestamp as decimal reprentation of time_t value (-d u was specified)
* or the standard date format (-d d was specified).
*/
static void
print_timestamp(void)
{
/* We only need to retrieve this once per invocation */
(void) printf("%ld", t);
char dstr[64];
int len;
if (len > 0)
}
(void) putchar('\n');
}
static void
{
double psetloadavg[3];
*loadavg++ += psetloadavg[0];
}
}
/*
* Queries the memory virtual and rss size for each member of a list.
* This will override the values computed by /proc aggregation.
*/
static void
{
size_t i;
int ret;
/*
* prune results returned to non-global zones automatically, so
* there is no need to pass different flags when calling from a
* non-global zone.
*
* Currently list_getsize() is only called with a single flag. This
* is because -Z, -J, -T, and -a are mutually exclusive. Regardless
* of this, we handle multiple flags.
*/
/*
* Gather rss for all users in all zones. Treat the same
* uid in different zones as the same user.
*/
/* Gather rss for all tasks in all zones */
/*
* Gather rss for all projects in all zones. Treat the same
* projid in diffrent zones as the same project.
*/
/* Gather rss for all zones */
} else {
"Cannot determine rss flags for output options %x\n"),
}
/*
* getvmusage() returns an array of result structures. One for
* each zone, project, task, or user on the system, depending on
* flags.
*
* If getvmusage() fails, prstat will use the size already gathered
* from psinfo
*/
return;
for (;;) {
if (ret == 0)
break;
continue;
}
/*
* Failure for some other reason. Prstat will use the size
* already gathered from psinfo.
*/
return;
}
switch (flags) {
case VMUSAGE_COL_RUSERS:
break;
case VMUSAGE_ALL_TASKS:
break;
case VMUSAGE_COL_PROJECTS:
break;
case VMUSAGE_ALL_ZONES:
break;
default:
"Unknown vmusage flags %d\n"), flags);
}
}
(float)physmem;
/* Output using data from getvmusage() */
}
/*
* If no match is found, prstat will use the size already
* gathered from psinfo.
*/
}
}
/*
* A routine to display the contents of the list on the screen
*/
static void
{
double loadavg[3] = {0, 0, 0};
int i, lwpid;
/*
* If processor sets aren't specified, we display system-wide
* load averages.
*/
}
(void) putchar('\r');
case LT_PROJECTS:
(void) printf(PROJECT_HEADER_LWP);
else
(void) printf(PROJECT_HEADER_PROC);
break;
case LT_TASKS:
(void) printf(TASK_HEADER_LWP);
else
(void) printf(TASK_HEADER_PROC);
break;
case LT_ZONES:
(void) printf(ZONE_HEADER_LWP);
else
(void) printf(ZONE_HEADER_PROC);
break;
case LT_USERS:
(void) printf(USER_HEADER_LWP);
else
(void) printf(USER_HEADER_PROC);
break;
case LT_LWPS:
(void) printf(PSINFO_HEADER_LWP_LGRP);
else
(void) printf(PSINFO_HEADER_LWP);
}
(void) printf(USAGE_HEADER_LWP);
} else {
(void) printf(PSINFO_HEADER_PROC_LGRP);
else
(void) printf(PSINFO_HEADER_PROC);
}
(void) printf(USAGE_HEADER_PROC);
}
break;
}
(void) putchar('\n');
case LT_PROJECTS:
case LT_TASKS:
case LT_USERS:
case LT_ZONES:
/*
* CPU usage and memory usage normalization
*/
if (total_cpu >= 100)
else
else
else
(void) putchar('\r');
else
(void) putchar('\n');
break;
case LT_LWPS:
else
"RT") == 0 ||
"SYS") == 0 ||
else
4);
10);
else
(void) putchar('\r');
(void) printf(PSINFO_LINE_LGRP,
} else {
(void) printf(PSINFO_LINE,
}
(void) putchar('\n');
}
(void) putchar('\r');
(void) printf(USAGE_LINE,
(void) putchar('\n');
}
break;
}
}
(void) putchar('\r');
case LT_PROJECTS:
case LT_USERS:
case LT_TASKS:
case LT_ZONES:
(void) putchar('\n');
}
break;
case LT_LWPS:
(void) putchar('\n');
}
}
}
(void) putchar('\r');
return;
(void) putchar('\n');
(void) putchar('\r');
}
static lwp_info_t *
{
} else {
}
return (lwp);
}
static void
{
else
else
}
static void
{
fd_closeall();
while (lwp) {
}
} else {
while (id) {
}
}
}
static void
{
goto update;
}
continue;
continue;
continue;
continue;
continue;
}
else
return;
}
}
else
}
static void
{
float period;
/*
* If we are reading cpu times for the first time then
* calculate average cpu times based on whole process
* execution time.
*/
if (period == 0) { /* zombie */
period = 1;
} else {
}
} else {
/*
* If this is not a first time we are reading a process's
* CPU times then recalculate CPU times based on fresh data
* obtained from procfs and previous CPU time usage values.
*/
if (period == 0) { /* zombie */
period = 1;
} else {
}
}
}
static int
{
char procfile[MAX_PROCFS_PATH];
return (1);
return (1);
}
return (0);
}
static void
{
}
static void
{
sizeof (psinfo_t) - sizeof (lwpsinfo_t));
}
static void
{
char *pidstr;
total_procs = 0;
total_lwps = 0;
total_cpu = 0;
total_mem = 0;
continue;
continue; /* skip sched, pageout and fsflush */
continue; /* check if we really want this pid */
continue;
continue;
}
int rep_lwp = 0;
&header, sizeof (prheader_t)) != 0) {
continue;
}
continue;
}
nlwps = 0;
/*LINTED ALIGNMENT*/
if (!has_element(&cpu_tbl,
lwpsinfo->pr_bindpset) ||
continue;
nlwps++;
== OPT_PSETS) {
/*
* If one of process's LWPs is bound
* to a given processor set, report the
* whole process. We may be doing this
* a few times but we'll get an accurate
* lwp count in return.
*/
} else {
if (rep_lwp == 0) {
rep_lwp = 1;
} else {
}
}
}
if (nlwps == 0) {
continue;
}
} else {
continue;
}
}
total_procs++;
total_lwps += nlwps;
continue;
}
/*
* If process has more than one lwp, then we may have to
*/
&header, sizeof (prheader_t)) != 0) {
continue;
}
continue;
}
/*LINTED ALIGNMENT*/
/*
* New LWPs created after we read lpsinfo
* will be ignored. Don't want to do
* everything all over again.
*/
continue;
}
} else {
continue;
}
continue;
}
total_procs++;
total_lwps += nlwps;
}
fd_update();
}
/*
* This procedure removes all dead lwps from the linked list of all lwps.
* It also creates linked list of ids if necessary.
*/
static void
{
return;
/*
* Process all live LWPs.
* When we're done, mark them as dead.
* They will be marked "alive" on the next
* /proc scan if they still exist.
*/
} else {
}
}
}
static void
{
(void) initscr();
(void) nonl();
is_curses_on = TRUE;
}
}
static void
{
(void) endwin();
}
}
static int
nlines()
{
char *envp;
int n;
}
return (n);
}
}
return (-1);
}
static void
{
int i, n;
return;
}
} else {
else
}
n++;
for (i = 0; i <= n; i++)
}
static int
setsize()
{
static int oldn = 0;
int n;
n = nlines();
if (n == oldn)
return (0);
oldn = n;
if (n == -1) {
setmovecur(); /* set default window size */
return (1);
}
n = n - 3; /* minus header, total and cursor lines */
n--; /* minus timestamp */
if (n < 1)
if (n < 8) {
} else {
}
} else {
else
}
}
setmovecur();
return (1);
}
static void
{
int err;
switch (err) {
case 0:
"defaulting to -c option\n"));
break;
case -1:
"defaulting to -c option\n"));
break;
default:
"defaulting to -c option\n"));
}
return;
}
return;
}
return;
}
}
}
}
static void
sig_handler(int sig)
{
switch (sig) {
break;
break;
case SIGINT:
break;
}
}
static void
{
}
static void
{
if (p == NULL)
}
static void
fill_prj_table(char *arg)
{
if (p == NULL)
}
}
static void
fill_set_table(char *arg)
{
if (p == NULL)
}
}
static void
Exit()
{
curses_off();
list_clear(&lwps);
list_clear(&users);
list_clear(&tasks);
list_clear(&zones);
fd_exit();
}
int
{
char *p;
int opt;
int timeout;
char key;
(void) textdomain(TEXT_DOMAIN);
lwpid_init();
"vcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJz:Z")) != (int)EOF) {
switch (opt) {
case 'r':
break;
case 'R':
break;
case 'c':
break;
case 'd':
if (optarg) {
if (*optarg == 'u')
else if (*optarg == 'd')
else
Usage();
} else {
Usage();
}
break;
case 'h':
break;
case 'H':
break;
case 'm':
case 'v':
break;
case 't':
break;
case 'a':
break;
case 'T':
break;
case 'J':
break;
case 'n':
break;
case 's':
break;
case 'S':
break;
case 'u':
break;
case 'U':
break;
case 'p':
break;
case 'C':
break;
case 'P':
break;
case 'k':
break;
case 'j':
break;
case 'L':
break;
case 'z':
break;
case 'Z':
break;
default:
Usage();
}
}
"-a, -J, -T or -Z\n"));
"-t, -J, -T or -Z\n"));
"-J, -T and -Z options are mutually exclusive\n"));
}
/*
* There is not enough space to combine microstate information and
* lgroup information and still fit in 80-column output.
*/
}
Usage();
Priocntl("RT");
}
ldtermcap(); /* can turn OPT_TERMCAP off */
(void) setsize();
curses_on();
(void) putchar('\n');
}
set_signals();
/*
* main program loop
*/
do {
if (sigterm == 1)
break;
if (sigtstp == 1) {
curses_off();
/*
* prstat stops here until it receives SIGCONT signal.
*/
sigtstp = 0;
curses_on();
sigwinch = 1;
}
if (sigwinch == 1) {
if (setsize() == 1) {
}
sigwinch = 0;
}
list_refresh(&lwps);
if (print_movecur)
list_print(&lwps);
}
list_print(&users);
list_clear(&users);
}
list_print(&tasks);
list_clear(&tasks);
}
}
list_print(&zones);
list_clear(&zones);
}
break;
/*
* If poll() returns -1 and sets errno to EINTR here because
* the process received a signal, it is Ok to abort this
* timeout and loop around because we check the signals at the
* top of the loop.
*/
break;
}
}
} else {
}
(void) putchar('\r');
return (0);
}