prstat.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/loadavg.h>
#include <sys/time.h>
#include <sys/pset.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 <libintl.h>
#include <locale.h>
#include "prstat.h"
#include "prutil.h"
#include "prtable.h"
#include "prsort.h"
#include "prfile.h"
/*
* x86 <sys/regs.h> ERR conflicts with <curses.h> ERR. For the purposes
* of this file, we care about the curses.h ERR so include that last.
*/
#if defined(ERR)
#undef ERR
#endif
#ifndef TEXT_DOMAIN /* should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */
#endif
#include <curses.h>
#include <term.h>
#define PSINFO_HEADER_PROC \
" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP "
#define PSINFO_HEADER_LWP \
" PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/LWPID "
#define USAGE_HEADER_PROC \
" PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP "
#define USAGE_HEADER_LWP \
" PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWPID "
#define USER_HEADER_PROC \
" NPROC USERNAME SIZE RSS MEMORY TIME CPU "
#define USER_HEADER_LWP \
" NLWP USERNAME SIZE RSS MEMORY TIME CPU "
#define TASK_HEADER_PROC \
"TASKID NPROC SIZE RSS MEMORY TIME CPU PROJECT "
#define TASK_HEADER_LWP \
"TASKID NLWP SIZE RSS MEMORY TIME CPU PROJECT "
#define PROJECT_HEADER_PROC \
"PROJID NPROC SIZE RSS MEMORY TIME CPU PROJECT "
#define PROJECT_HEADER_LWP \
"PROJID NLWP SIZE RSS MEMORY TIME CPU PROJECT "
#define ZONE_HEADER_PROC \
"ZONEID NPROC SIZE RSS MEMORY TIME CPU ZONE "
#define ZONE_HEADER_LWP \
"ZONEID NLWP SIZE RSS MEMORY TIME CPU ZONE "
#define PSINFO_LINE \
"%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %-.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 char *movecur = NULL; /* termcap: move up string */
static char *empty_string = "\0"; /* termcap: empty string */
static uint_t print_movecur = FALSE; /* print movecur or not */
static int is_curses_on = FALSE; /* current curses state */
static table_t pid_tbl = {0, 0, NULL}; /* selected processes */
static table_t cpu_tbl = {0, 0, NULL}; /* selected processors */
static table_t set_tbl = {0, 0, NULL}; /* selected processor sets */
static table_t prj_tbl = {0, 0, NULL}; /* selected projects */
static table_t tsk_tbl = {0, 0, NULL}; /* selected tasks */
static zonetbl_t zone_tbl = {0, 0, NULL}; /* selected zones */
static nametbl_t euid_tbl = {0, 0, NULL}; /* selected effective users */
static nametbl_t ruid_tbl = {0, 0, NULL}; /* selected real users */
static uint_t total_procs; /* total number of procs */
static uint_t total_lwps; /* total number of lwps */
static float total_cpu; /* total cpu usage */
static float total_mem; /* total memory usage */
static list_t lwps; /* list of lwps/processes */
static list_t users; /* list of users */
static list_t tasks; /* list of tasks */
static list_t projects; /* list of projects */
static list_t zones; /* list of zones */
static volatile uint_t sigwinch = 0;
static volatile uint_t sigtstp = 0;
static volatile uint_t sigterm = 0;
/* default settings */
static optdesc_t opts = {
5, /* interval between updates, seconds */
15, /* number of lines in top part */
5, /* number of lines in bottom part */
-1, /* number of iterations; infinitely */
OPT_PSINFO | OPT_FULLSCREEN | OPT_USEHOME | OPT_TERMCAP,
-1 /* sort in decreasing order */
};
static void
psetloadavg(long psetid, void *ptr)
{
double psetloadavg[3];
double *loadavg = ptr;
if (pset_getloadavg((psetid_t)psetid, psetloadavg, 3) != -1) {
*loadavg++ += psetloadavg[0];
*loadavg++ += psetloadavg[1];
*loadavg += psetloadavg[2];
}
}
/*
* A routine to display the contents of the list on the screen
*/
static void
list_print(list_t *list)
{
lwp_info_t *lwp;
id_info_t *id;
char usr[4], sys[4], trp[4], tfl[4];
char dfl[4], lck[4], slp[4], lat[4];
char vcx[4], icx[4], scl[4], sig[4];
char psize[6], prssize[6], pmem[6], pcpu[6], ptime[12];
char pstate[7], pnice[4], ppri[4];
char pname[LOGNAME_MAX+1];
char projname[PROJNAME_MAX+1];
char zonename[ZONENAME_MAX+1];
float cpu, mem;
double loadavg[3] = {0, 0, 0};
int i, lwpid;
if (foreach_element(&set_tbl, &loadavg, psetloadavg) == 0) {
/*
* If processor sets aren't specified, we display system-wide
* load averages.
*/
(void) getloadavg(loadavg, 3);
}
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
(void) putp(t_ulon);
switch (list->l_type) {
case LT_PROJECTS:
if (opts.o_outpmode & OPT_LWPS)
(void) printf(PROJECT_HEADER_LWP);
else
(void) printf(PROJECT_HEADER_PROC);
break;
case LT_TASKS:
if (opts.o_outpmode & OPT_LWPS)
(void) printf(TASK_HEADER_LWP);
else
(void) printf(TASK_HEADER_PROC);
break;
case LT_ZONES:
if (opts.o_outpmode & OPT_LWPS)
(void) printf(ZONE_HEADER_LWP);
else
(void) printf(ZONE_HEADER_PROC);
break;
case LT_USERS:
if (opts.o_outpmode & OPT_LWPS)
(void) printf(USER_HEADER_LWP);
else
(void) printf(USER_HEADER_PROC);
break;
case LT_LWPS:
if (opts.o_outpmode & OPT_LWPS) {
if (opts.o_outpmode & OPT_PSINFO)
(void) printf(PSINFO_HEADER_LWP);
if (opts.o_outpmode & OPT_MSACCT)
(void) printf(USAGE_HEADER_LWP);
} else {
if (opts.o_outpmode & OPT_PSINFO)
(void) printf(PSINFO_HEADER_PROC);
if (opts.o_outpmode & OPT_MSACCT)
(void) printf(USAGE_HEADER_PROC);
}
break;
}
(void) putp(t_uloff);
(void) putp(t_eol);
(void) putchar('\n');
for (i = 0; i < list->l_used; i++) {
switch (list->l_type) {
case LT_PROJECTS:
case LT_TASKS:
case LT_USERS:
case LT_ZONES:
id = list->l_ptrs[i];
/*
* CPU usage and memory usage normalization
*/
if (total_cpu >= 100)
cpu = (100 * id->id_pctcpu) / total_cpu;
else
cpu = id->id_pctcpu;
if (total_mem >= 100)
mem = (100 * id->id_pctmem) / total_mem;
else
mem = id->id_pctmem;
if (list->l_type == LT_USERS)
pwd_getname(id->id_uid, pname, LOGNAME_MAX + 1);
else if (list->l_type == LT_ZONES)
getzonename(id->id_zoneid, zonename,
ZONENAME_MAX);
else
getprojname(id->id_projid, projname,
PROJNAME_MAX);
Format_size(psize, id->id_size, 6);
Format_size(prssize, id->id_rssize, 6);
Format_pct(pmem, mem, 4);
Format_pct(pcpu, cpu, 4);
Format_time(ptime, id->id_time, 10);
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
if (list->l_type == LT_PROJECTS)
(void) printf(PROJECT_LINE, (int)id->id_projid,
id->id_nproc, psize, prssize, pmem, ptime,
pcpu, projname);
else if (list->l_type == LT_TASKS)
(void) printf(TASK_LINE, (int)id->id_taskid,
id->id_nproc, psize, prssize, pmem, ptime,
pcpu, projname);
else if (list->l_type == LT_ZONES)
(void) printf(ZONE_LINE, (int)id->id_zoneid,
id->id_nproc, psize, prssize, pmem, ptime,
pcpu, zonename);
else
(void) printf(USER_LINE, id->id_nproc, pname,
psize, prssize, pmem, ptime, pcpu);
(void) putp(t_eol);
(void) putchar('\n');
break;
case LT_LWPS:
lwp = list->l_ptrs[i];
if (opts.o_outpmode & OPT_LWPS)
lwpid = lwp->li_info.pr_lwp.pr_lwpid;
else
lwpid = lwp->li_info.pr_nlwp +
lwp->li_info.pr_nzomb;
pwd_getname(lwp->li_info.pr_uid, pname,
LOGNAME_MAX + 1);
if (opts.o_outpmode & OPT_PSINFO) {
Format_size(psize, lwp->li_info.pr_size, 6);
Format_size(prssize, lwp->li_info.pr_rssize, 6);
Format_state(pstate,
lwp->li_info.pr_lwp.pr_sname,
lwp->li_info.pr_lwp.pr_onpro, 7);
if (strcmp(lwp->li_info.pr_lwp.pr_clname,
"RT") == 0 ||
strcmp(lwp->li_info.pr_lwp.pr_clname,
"SYS") == 0 ||
lwp->li_info.pr_lwp.pr_sname == 'Z')
(void) strcpy(pnice, " -");
else
Format_num(pnice,
lwp->li_info.pr_lwp.pr_nice - NZERO,
4);
Format_num(ppri, lwp->li_info.pr_lwp.pr_pri, 4);
Format_pct(pcpu,
FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu), 4);
if (opts.o_outpmode & OPT_LWPS)
Format_time(ptime,
lwp->li_info.pr_lwp.pr_time.tv_sec,
10);
else
Format_time(ptime,
lwp->li_info.pr_time.tv_sec, 10);
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
stripfname(lwp->li_info.pr_fname);
(void) printf(PSINFO_LINE,
(int)lwp->li_info.pr_pid, pname,
psize, prssize, pstate, ppri, pnice,
ptime, pcpu, lwp->li_info.pr_fname, lwpid);
(void) putp(t_eol);
(void) putchar('\n');
}
if (opts.o_outpmode & OPT_MSACCT) {
Format_pct(usr, lwp->li_usr, 4);
Format_pct(sys, lwp->li_sys, 4);
Format_pct(slp, lwp->li_slp, 4);
Format_num(vcx, lwp->li_vcx, 4);
Format_num(icx, lwp->li_icx, 4);
Format_num(scl, lwp->li_scl, 4);
Format_num(sig, lwp->li_sig, 4);
Format_pct(trp, lwp->li_trp, 4);
Format_pct(tfl, lwp->li_tfl, 4);
Format_pct(dfl, lwp->li_dfl, 4);
Format_pct(lck, lwp->li_lck, 4);
Format_pct(lat, lwp->li_lat, 4);
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
stripfname(lwp->li_info.pr_fname);
(void) printf(USAGE_LINE,
(int)lwp->li_info.pr_pid, pname,
usr, sys, trp, tfl, dfl, lck,
slp, lat, vcx, icx, scl, sig,
lwp->li_info.pr_fname, lwpid);
(void) putp(t_eol);
(void) putchar('\n');
}
break;
}
}
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
if (opts.o_outpmode & OPT_TERMCAP) {
switch (list->l_type) {
case LT_PROJECTS:
case LT_USERS:
case LT_TASKS:
case LT_ZONES:
while (i++ < opts.o_nbottom) {
(void) putp(t_eol);
(void) putchar('\n');
}
break;
case LT_LWPS:
while (i++ < opts.o_ntop) {
(void) putp(t_eol);
(void) putchar('\n');
}
}
}
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
if ((opts.o_outpmode & OPT_SPLIT) && list->l_type == LT_LWPS)
return;
(void) printf(TOTAL_LINE, total_procs, total_lwps,
loadavg[LOADAVG_1MIN], loadavg[LOADAVG_5MIN],
loadavg[LOADAVG_15MIN]);
(void) putp(t_eol);
(void) putchar('\n');
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
(void) putp(t_eol);
(void) fflush(stdout);
}
static lwp_info_t *
list_add_lwp(list_t *list, pid_t pid, id_t lwpid)
{
lwp_info_t *lwp;
if (list->l_head == NULL) {
list->l_head = list->l_tail = lwp = Zalloc(sizeof (lwp_info_t));
} else {
lwp = Zalloc(sizeof (lwp_info_t));
lwp->li_prev = list->l_tail;
((lwp_info_t *)list->l_tail)->li_next = lwp;
list->l_tail = lwp;
}
lwp->li_info.pr_pid = pid;
lwp->li_info.pr_lwp.pr_lwpid = lwpid;
lwpid_add(lwp, pid, lwpid);
list->l_count++;
return (lwp);
}
static void
list_remove_lwp(list_t *list, lwp_info_t *lwp)
{
if (lwp->li_prev)
lwp->li_prev->li_next = lwp->li_next;
else
list->l_head = lwp->li_next; /* removing the head */
if (lwp->li_next)
lwp->li_next->li_prev = lwp->li_prev;
else
list->l_tail = lwp->li_prev; /* removing the tail */
lwpid_del(lwp->li_info.pr_pid, lwp->li_info.pr_lwp.pr_lwpid);
if (lwpid_pidcheck(lwp->li_info.pr_pid) == 0)
fds_rm(lwp->li_info.pr_pid);
list->l_count--;
free(lwp);
}
static void
list_clear(list_t *list)
{
if (list->l_type == LT_LWPS) {
lwp_info_t *lwp = list->l_tail;
lwp_info_t *lwp_tmp;
fd_closeall();
while (lwp) {
lwp_tmp = lwp;
lwp = lwp->li_prev;
list_remove_lwp(&lwps, lwp_tmp);
}
} else {
id_info_t *id = list->l_head;
id_info_t *nextid;
while (id) {
nextid = id->id_next;
free(id);
id = nextid;
}
list->l_count = 0;
list->l_head = list->l_tail = NULL;
}
}
static void
list_update(list_t *list, lwp_info_t *lwp)
{
id_info_t *id;
if (list->l_head == NULL) { /* first element */
list->l_head = list->l_tail = id = Zalloc(sizeof (id_info_t));
goto update;
}
for (id = list->l_head; id; id = id->id_next) {
if ((list->l_type == LT_USERS) &&
(id->id_uid != lwp->li_info.pr_uid))
continue;
if ((list->l_type == LT_TASKS) &&
(id->id_taskid != lwp->li_info.pr_taskid))
continue;
if ((list->l_type == LT_PROJECTS) &&
(id->id_projid != lwp->li_info.pr_projid))
continue;
if ((list->l_type == LT_ZONES) &&
(id->id_zoneid != lwp->li_info.pr_zoneid))
continue;
id->id_nproc++;
id->id_taskid = lwp->li_info.pr_taskid;
id->id_projid = lwp->li_info.pr_projid;
id->id_zoneid = lwp->li_info.pr_zoneid;
if (lwp->li_flags & LWP_REPRESENT) {
id->id_size += lwp->li_info.pr_size;
id->id_rssize += lwp->li_info.pr_rssize;
}
id->id_pctcpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
if (opts.o_outpmode & OPT_LWPS)
id->id_time += TIME2SEC(lwp->li_info.pr_lwp.pr_time);
else
id->id_time += TIME2SEC(lwp->li_info.pr_time);
id->id_pctmem += FRC2PCT(lwp->li_info.pr_pctmem);
id->id_key += lwp->li_key;
total_cpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
total_mem += FRC2PCT(lwp->li_info.pr_pctmem);
return;
}
id = list->l_tail;
id->id_next = Zalloc(sizeof (id_info_t));
id->id_next->id_prev = list->l_tail;
id->id_next->id_next = NULL;
list->l_tail = id->id_next;
id = list->l_tail;
update:
id->id_uid = lwp->li_info.pr_uid;
id->id_projid = lwp->li_info.pr_projid;
id->id_taskid = lwp->li_info.pr_taskid;
id->id_zoneid = lwp->li_info.pr_zoneid;
id->id_nproc++;
if (lwp->li_flags & LWP_REPRESENT) {
id->id_size = lwp->li_info.pr_size;
id->id_rssize = lwp->li_info.pr_rssize;
}
id->id_pctcpu = FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
if (opts.o_outpmode & OPT_LWPS)
id->id_time = TIME2SEC(lwp->li_info.pr_lwp.pr_time);
else
id->id_time = TIME2SEC(lwp->li_info.pr_time);
id->id_pctmem = FRC2PCT(lwp->li_info.pr_pctmem);
id->id_key = lwp->li_key;
total_cpu += id->id_pctcpu;
total_mem += id->id_pctmem;
list->l_count++;
}
static void
lwp_update(lwp_info_t *lwp, pid_t pid, id_t lwpid, struct prusage *usage)
{
float period;
if (!lwpid_is_active(pid, lwpid)) {
/*
* If we are reading cpu times for the first time then
* calculate average cpu times based on whole process
* execution time.
*/
(void) memcpy(&lwp->li_usage, usage, sizeof (prusage_t));
period = TIME2NSEC(usage->pr_rtime);
period = period/(float)100;
if (period == 0) { /* zombie */
period = 1;
lwp->li_usr = 0;
lwp->li_sys = 0;
lwp->li_slp = 0;
} else {
lwp->li_usr = TIME2NSEC(usage->pr_utime)/period;
lwp->li_sys = TIME2NSEC(usage->pr_stime)/period;
lwp->li_slp = TIME2NSEC(usage->pr_slptime)/period;
}
lwp->li_trp = TIME2NSEC(usage->pr_ttime)/period;
lwp->li_tfl = TIME2NSEC(usage->pr_tftime)/period;
lwp->li_dfl = TIME2NSEC(usage->pr_dftime)/period;
lwp->li_lck = TIME2NSEC(usage->pr_ltime)/period;
lwp->li_lat = TIME2NSEC(usage->pr_wtime)/period;
period = (period / NANOSEC)*(float)100; /* now in seconds */
lwp->li_vcx = (ulong_t)
(opts.o_interval * (usage->pr_vctx/period));
lwp->li_icx = (ulong_t)
(opts.o_interval * (usage->pr_ictx/period));
lwp->li_scl = (ulong_t)
(opts.o_interval * (usage->pr_sysc/period));
lwp->li_sig = (ulong_t)
(opts.o_interval * (usage->pr_sigs/period));
(void) lwpid_set_active(pid, lwpid);
} 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.
*/
period = TIME2NSEC(usage->pr_rtime)-
TIME2NSEC(lwp->li_usage.pr_rtime);
period = period/(float)100;
if (period == 0) { /* zombie */
period = 1;
lwp->li_usr = 0;
lwp->li_sys = 0;
lwp->li_slp = 0;
} else {
lwp->li_usr = (TIME2NSEC(usage->pr_utime)-
TIME2NSEC(lwp->li_usage.pr_utime))/period;
lwp->li_sys = (TIME2NSEC(usage->pr_stime) -
TIME2NSEC(lwp->li_usage.pr_stime))/period;
lwp->li_slp = (TIME2NSEC(usage->pr_slptime) -
TIME2NSEC(lwp->li_usage.pr_slptime))/period;
}
lwp->li_trp = (TIME2NSEC(usage->pr_ttime) -
TIME2NSEC(lwp->li_usage.pr_ttime))/period;
lwp->li_tfl = (TIME2NSEC(usage->pr_tftime) -
TIME2NSEC(lwp->li_usage.pr_tftime))/period;
lwp->li_dfl = (TIME2NSEC(usage->pr_dftime) -
TIME2NSEC(lwp->li_usage.pr_dftime))/period;
lwp->li_lck = (TIME2NSEC(usage->pr_ltime) -
TIME2NSEC(lwp->li_usage.pr_ltime))/period;
lwp->li_lat = (TIME2NSEC(usage->pr_wtime) -
TIME2NSEC(lwp->li_usage.pr_wtime))/period;
lwp->li_vcx = usage->pr_vctx - lwp->li_usage.pr_vctx;
lwp->li_icx = usage->pr_ictx - lwp->li_usage.pr_ictx;
lwp->li_scl = usage->pr_sysc - lwp->li_usage.pr_sysc;
lwp->li_sig = usage->pr_sigs - lwp->li_usage.pr_sigs;
(void) memcpy(&lwp->li_usage, usage, sizeof (prusage_t));
}
}
static int
read_procfile(fd_t **fd, char *pidstr, char *file, void *buf, size_t bufsize)
{
char procfile[MAX_PROCFS_PATH];
(void) snprintf(procfile, MAX_PROCFS_PATH,
"/proc/%s/%s", pidstr, file);
if ((*fd = fd_open(procfile, O_RDONLY, *fd)) == NULL)
return (1);
if (pread(fd_getfd(*fd), buf, bufsize, 0) != bufsize) {
fd_close(*fd);
return (1);
}
return (0);
}
static void
add_proc(psinfo_t *psinfo)
{
lwp_info_t *lwp;
id_t lwpid;
pid_t pid = psinfo->pr_pid;
lwpid = psinfo->pr_lwp.pr_lwpid;
if ((lwp = lwpid_get(pid, lwpid)) == NULL)
lwp = list_add_lwp(&lwps, pid, lwpid);
lwp->li_flags |= LWP_ALIVE | LWP_REPRESENT;
(void) memcpy(&lwp->li_info, psinfo, sizeof (psinfo_t));
lwp->li_info.pr_lwp.pr_pctcpu = lwp->li_info.pr_pctcpu;
}
static void
add_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, int flags)
{
lwp_info_t *lwp;
pid_t pid = psinfo->pr_pid;
id_t lwpid = lwpsinfo->pr_lwpid;
if ((lwp = lwpid_get(pid, lwpid)) == NULL)
lwp = list_add_lwp(&lwps, pid, lwpid);
lwp->li_flags &= ~LWP_REPRESENT;
lwp->li_flags |= LWP_ALIVE;
lwp->li_flags |= flags;
(void) memcpy(&lwp->li_info, psinfo,
sizeof (psinfo_t) - sizeof (lwpsinfo_t));
(void) memcpy(&lwp->li_info.pr_lwp, lwpsinfo, sizeof (lwpsinfo_t));
}
static void
prstat_scandir(DIR *procdir)
{
char *pidstr;
pid_t pid;
id_t lwpid;
size_t entsz;
long nlwps, nent, i;
char *buf, *ptr;
fds_t *fds;
lwp_info_t *lwp;
dirent_t *direntp;
prheader_t header;
psinfo_t psinfo;
prusage_t usage;
lwpsinfo_t *lwpsinfo;
prusage_t *lwpusage;
total_procs = 0;
total_lwps = 0;
total_cpu = 0;
total_mem = 0;
convert_zone(&zone_tbl);
for (rewinddir(procdir); (direntp = readdir(procdir)); ) {
pidstr = direntp->d_name;
if (pidstr[0] == '.') /* skip "." and ".." */
continue;
pid = atoi(pidstr);
if (pid == 0 || pid == 2 || pid == 3)
continue; /* skip sched, pageout and fsflush */
if (has_element(&pid_tbl, pid) == 0)
continue; /* check if we really want this pid */
fds = fds_get(pid); /* get ptr to file descriptors */
if (read_procfile(&fds->fds_psinfo, pidstr,
"psinfo", &psinfo, sizeof (psinfo_t)) != 0)
continue;
if (!has_uid(&ruid_tbl, psinfo.pr_uid) ||
!has_uid(&euid_tbl, psinfo.pr_euid) ||
!has_element(&prj_tbl, psinfo.pr_projid) ||
!has_element(&tsk_tbl, psinfo.pr_taskid) ||
!has_zone(&zone_tbl, psinfo.pr_zoneid)) {
fd_close(fds->fds_psinfo);
continue;
}
nlwps = psinfo.pr_nlwp + psinfo.pr_nzomb;
if (nlwps > 1 && (opts.o_outpmode & (OPT_LWPS | OPT_PSETS))) {
int rep_lwp = 0;
if (read_procfile(&fds->fds_lpsinfo, pidstr, "lpsinfo",
&header, sizeof (prheader_t)) != 0) {
fd_close(fds->fds_psinfo);
continue;
}
nent = header.pr_nent;
entsz = header.pr_entsize * nent;
ptr = buf = Malloc(entsz);
if (pread(fd_getfd(fds->fds_lpsinfo), buf,
entsz, sizeof (struct prheader)) != entsz) {
fd_close(fds->fds_lpsinfo);
fd_close(fds->fds_psinfo);
free(buf);
continue;
}
nlwps = 0;
for (i = 0; i < nent; i++, ptr += header.pr_entsize) {
/*LINTED ALIGNMENT*/
lwpsinfo = (lwpsinfo_t *)ptr;
if (!has_element(&cpu_tbl,
lwpsinfo->pr_onpro) ||
!has_element(&set_tbl,
lwpsinfo->pr_bindpset))
continue;
nlwps++;
if ((opts.o_outpmode & (OPT_PSETS | OPT_LWPS))
== 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.
*/
add_proc(&psinfo);
} else {
if (rep_lwp == 0) {
rep_lwp = 1;
add_lwp(&psinfo, lwpsinfo,
LWP_REPRESENT);
} else {
add_lwp(&psinfo, lwpsinfo, 0);
}
}
}
free(buf);
if (nlwps == 0) {
fd_close(fds->fds_lpsinfo);
fd_close(fds->fds_psinfo);
continue;
}
} else {
if (!has_element(&cpu_tbl, psinfo.pr_lwp.pr_onpro) ||
!has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset)) {
fd_close(fds->fds_psinfo);
continue;
}
add_proc(&psinfo);
}
if (!(opts.o_outpmode & OPT_MSACCT)) {
total_procs++;
total_lwps += nlwps;
continue;
}
/*
* Get more information about processes from /proc/pid/usage.
* If process has more than one lwp, then we may have to
* also look at the /proc/pid/lusage file.
*/
if ((opts.o_outpmode & OPT_LWPS) && (nlwps > 1)) {
if (read_procfile(&fds->fds_lusage, pidstr, "lusage",
&header, sizeof (prheader_t)) != 0) {
fd_close(fds->fds_lpsinfo);
fd_close(fds->fds_psinfo);
continue;
}
nent = header.pr_nent;
entsz = header.pr_entsize * nent;
buf = Malloc(entsz);
if (pread(fd_getfd(fds->fds_lusage), buf,
entsz, sizeof (struct prheader)) != entsz) {
fd_close(fds->fds_lusage);
fd_close(fds->fds_lpsinfo);
fd_close(fds->fds_psinfo);
free(buf);
continue;
}
for (i = 1, ptr = buf + header.pr_entsize; i < nent;
i++, ptr += header.pr_entsize) {
/*LINTED ALIGNMENT*/
lwpusage = (prusage_t *)ptr;
lwpid = lwpusage->pr_lwpid;
/*
* New LWPs created after we read lpsinfo
* will be ignored. Don't want to do
* everything all over again.
*/
if ((lwp = lwpid_get(pid, lwpid)) == NULL)
continue;
lwp_update(lwp, pid, lwpid, lwpusage);
}
free(buf);
} else {
if (read_procfile(&fds->fds_usage, pidstr, "usage",
&usage, sizeof (prusage_t)) != 0) {
fd_close(fds->fds_lpsinfo);
fd_close(fds->fds_psinfo);
continue;
}
lwpid = psinfo.pr_lwp.pr_lwpid;
if ((lwp = lwpid_get(pid, lwpid)) == NULL)
continue;
lwp_update(lwp, pid, lwpid, &usage);
}
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
list_refresh(list_t *list)
{
lwp_info_t *lwp, *lwp_next;
if (!(list->l_type & LT_LWPS))
return;
for (lwp = list->l_head; lwp != NULL; ) {
if (lwp->li_flags & LWP_ALIVE) {
/*
* 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.
*/
lwp->li_key = list_getkeyval(list, lwp);
if (opts.o_outpmode & OPT_USERS)
list_update(&users, lwp);
if (opts.o_outpmode & OPT_TASKS)
list_update(&tasks, lwp);
if (opts.o_outpmode & OPT_PROJECTS)
list_update(&projects, lwp);
if (opts.o_outpmode & OPT_ZONES)
list_update(&zones, lwp);
lwp->li_flags &= ~LWP_ALIVE;
lwp = lwp->li_next;
} else {
lwp_next = lwp->li_next;
list_remove_lwp(&lwps, lwp);
lwp = lwp_next;
}
}
}
static void
curses_on()
{
if ((opts.o_outpmode & OPT_TERMCAP) && (is_curses_on == FALSE)) {
(void) initscr();
(void) nonl();
(void) putp(t_smcup);
is_curses_on = TRUE;
}
}
static void
curses_off()
{
if ((is_curses_on == TRUE) && (opts.o_outpmode & OPT_TERMCAP)) {
(void) putp(t_rmcup);
(void) endwin();
is_curses_on = FALSE;
}
(void) fflush(stdout);
}
static int
nlines()
{
struct winsize ws;
char *envp;
int n;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
if (ws.ws_row > 0)
return (ws.ws_row);
}
if (envp = getenv("LINES")) {
if ((n = Atoi(envp)) > 0) {
opts.o_outpmode &= ~OPT_USEHOME;
return (n);
}
}
return (-1);
}
static void
setmovecur()
{
int i, n;
if ((opts.o_outpmode & OPT_FULLSCREEN) &&
(opts.o_outpmode & OPT_USEHOME)) {
movecur = t_home;
return;
}
if (opts.o_outpmode & OPT_SPLIT) {
n = opts.o_ntop + opts.o_nbottom + 2;
} else {
if (opts.o_outpmode & OPT_USERS)
n = opts.o_nbottom + 1;
else
n = opts.o_ntop + 1;
}
if (movecur != NULL && movecur != empty_string && movecur != t_home)
free(movecur);
movecur = Zalloc(strlen(t_up) * (n + 5));
for (i = 0; i <= n; i++)
(void) strcat(movecur, t_up);
}
static int
setsize()
{
static int oldn = 0;
int n;
if (opts.o_outpmode & OPT_FULLSCREEN) {
n = nlines();
if (n == oldn)
return (0);
oldn = n;
if (n == -1) {
opts.o_outpmode &= ~OPT_USEHOME;
setmovecur(); /* set default window size */
return (1);
}
n = n - 3; /* minus header, total and cursor lines */
if (n < 1)
Die(gettext("window is too small (try -n)\n"));
if (opts.o_outpmode & OPT_SPLIT) {
if (n < 8) {
Die(gettext("window is too small (try -n)\n"));
} else {
opts.o_ntop = (n / 4) * 3;
opts.o_nbottom = n - 1 - opts.o_ntop;
}
} else {
if (opts.o_outpmode & OPT_USERS)
opts.o_nbottom = n;
else
opts.o_ntop = n;
}
}
setmovecur();
return (1);
}
static void
ldtermcap()
{
int err;
if (setupterm(NULL, STDIN_FILENO, &err) == ERR) {
switch (err) {
case 0:
Warn(gettext("failed to load terminal info, "
"defaulting to -c option\n"));
break;
case -1:
Warn(gettext("terminfo database not found, "
"defaulting to -c option\n"));
break;
default:
Warn(gettext("failed to initialize terminal, "
"defaulting to -c option\n"));
}
opts.o_outpmode &= ~OPT_TERMCAP;
t_up = t_eol = t_smcup = t_rmcup = movecur = empty_string;
t_ulon = t_uloff = empty_string;
return;
}
t_ulon = tigetstr("smul");
t_uloff = tigetstr("rmul");
t_up = tigetstr("cuu1");
t_eol = tigetstr("el");
t_smcup = tigetstr("smcup");
t_rmcup = tigetstr("rmcup");
t_home = tigetstr("home");
if ((t_up == (char *)-1) || (t_eol == (char *)-1) ||
(t_smcup == (char *)-1) || (t_rmcup == (char *)-1)) {
opts.o_outpmode &= ~OPT_TERMCAP;
t_up = t_eol = t_smcup = t_rmcup = movecur = empty_string;
return;
}
if (t_up == NULL || t_eol == NULL) {
opts.o_outpmode &= ~OPT_TERMCAP;
t_eol = t_up = movecur = empty_string;
return;
}
if (t_ulon == (char *)-1 || t_uloff == (char *)-1 ||
t_ulon == NULL || t_uloff == NULL) {
t_ulon = t_uloff = empty_string; /* can live without it */
}
if (t_smcup == NULL || t_rmcup == NULL)
t_smcup = t_rmcup = empty_string;
if (t_home == (char *)-1 || t_home == NULL) {
opts.o_outpmode &= ~OPT_USEHOME;
t_home = empty_string;
}
}
static void
sig_handler(int sig)
{
switch (sig) {
case SIGTSTP: sigtstp = 1;
break;
case SIGWINCH: sigwinch = 1;
break;
case SIGINT:
case SIGTERM: sigterm = 1;
break;
}
}
static void
set_signals()
{
(void) signal(SIGTSTP, sig_handler);
(void) signal(SIGINT, sig_handler);
(void) signal(SIGTERM, sig_handler);
if (opts.o_outpmode & OPT_FULLSCREEN)
(void) signal(SIGWINCH, sig_handler);
}
static void
fill_table(table_t *table, char *arg)
{
char *p = strtok(arg, ", ");
long l = Atoi(p);
add_element(table, l);
while (p = strtok(NULL, ", ")) {
l = Atoi(p);
add_element(table, l);
}
}
static void
fill_prj_table(char *arg)
{
projid_t projid;
char *p = strtok(arg, ", ");
if ((projid = getprojidbyname(p)) == -1)
projid = Atoi(p);
add_element(&prj_tbl, (long)projid);
while (p = strtok(NULL, ", ")) {
if ((projid = getprojidbyname(p)) == -1)
projid = Atoi(p);
add_element(&prj_tbl, (long)projid);
}
}
static void
fill_set_table(char *arg)
{
char *p = strtok(arg, ", ");
psetid_t id;
if ((id = Atoi(p)) == 0)
id = PS_NONE;
add_element(&set_tbl, id);
while (p = strtok(NULL, ", ")) {
if ((id = Atoi(p)) == 0)
id = PS_NONE;
if (!has_element(&set_tbl, id))
add_element(&set_tbl, id);
}
}
static void
Exit()
{
curses_off();
list_clear(&lwps);
list_clear(&users);
list_clear(&tasks);
list_clear(&projects);
list_clear(&zones);
fd_exit();
}
int
main(int argc, char **argv)
{
DIR *procdir;
char *p;
char *sortk = "cpu"; /* default sort key */
int opt;
int timeout;
struct pollfd pollset;
char key;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
Progname(argv[0]);
lwpid_init();
fd_init(Setrlimit());
while ((opt = getopt(argc, argv, "vcmaRLtu:U:n:p:C:P:s:S:j:k:TJz:Z"))
!= (int)EOF) {
switch (opt) {
case 'R':
opts.o_outpmode |= OPT_REALTIME;
break;
case 'c':
opts.o_outpmode &= ~OPT_TERMCAP;
opts.o_outpmode &= ~OPT_FULLSCREEN;
break;
case 'm':
case 'v':
opts.o_outpmode &= ~OPT_PSINFO;
opts.o_outpmode |= OPT_MSACCT;
break;
case 't':
opts.o_outpmode &= ~OPT_PSINFO;
opts.o_outpmode |= OPT_USERS;
break;
case 'a':
opts.o_outpmode |= OPT_SPLIT | OPT_USERS;
break;
case 'T':
opts.o_outpmode |= OPT_SPLIT | OPT_TASKS;
break;
case 'J':
opts.o_outpmode |= OPT_SPLIT | OPT_PROJECTS;
break;
case 'n':
p = strtok(optarg, ",");
opts.o_ntop = Atoi(p);
if (p = strtok(NULL, ","))
opts.o_nbottom = Atoi(p);
opts.o_outpmode &= ~OPT_FULLSCREEN;
break;
case 's':
opts.o_sortorder = -1;
sortk = optarg;
break;
case 'S':
opts.o_sortorder = 1;
sortk = optarg;
break;
case 'u':
p = strtok(optarg, ", ");
add_uid(&euid_tbl, p);
while (p = strtok(NULL, ", "))
add_uid(&euid_tbl, p);
break;
case 'U':
p = strtok(optarg, ", ");
add_uid(&ruid_tbl, p);
while (p = strtok(NULL, ", "))
add_uid(&ruid_tbl, p);
break;
case 'p':
fill_table(&pid_tbl, optarg);
break;
case 'C':
fill_set_table(optarg);
opts.o_outpmode |= OPT_PSETS;
break;
case 'P':
fill_table(&cpu_tbl, optarg);
break;
case 'k':
fill_table(&tsk_tbl, optarg);
break;
case 'j':
fill_prj_table(optarg);
break;
case 'L':
opts.o_outpmode |= OPT_LWPS;
break;
case 'z':
p = strtok(optarg, ", ");
add_zone(&zone_tbl, p);
while (p = strtok(NULL, ", "))
add_zone(&zone_tbl, p);
break;
case 'Z':
opts.o_outpmode |= OPT_SPLIT | OPT_ZONES;
break;
default:
Usage();
}
}
(void) atexit(Exit);
if ((opts.o_outpmode & OPT_USERS) &&
!(opts.o_outpmode & OPT_SPLIT))
opts.o_nbottom = opts.o_ntop;
if (opts.o_ntop == 0 || opts.o_nbottom == 0)
Die(gettext("invalid argument for -n\n"));
if (!(opts.o_outpmode & OPT_SPLIT) && (opts.o_outpmode & OPT_USERS) &&
((opts.o_outpmode & (OPT_PSINFO | OPT_MSACCT))))
Die(gettext("-t option cannot be used with -v or -m\n"));
if ((opts.o_outpmode & OPT_SPLIT) && (opts.o_outpmode && OPT_USERS) &&
!((opts.o_outpmode & (OPT_PSINFO | OPT_MSACCT))))
Die(gettext("-t option cannot be used with "
"-a, -J, -T or -Z\n"));
if ((opts.o_outpmode & OPT_USERS) &&
(opts.o_outpmode & (OPT_TASKS | OPT_PROJECTS | OPT_ZONES)))
Die(gettext("-a option cannot be used with "
"-t, -J, -T or -Z\n"));
if (((opts.o_outpmode & OPT_TASKS) &&
(opts.o_outpmode & (OPT_PROJECTS|OPT_ZONES))) ||
((opts.o_outpmode & OPT_PROJECTS) &&
(opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) {
Die(gettext("-J, -T and -Z options are mutually exclusive\n"));
}
if (argc > optind)
opts.o_interval = Atoi(argv[optind++]);
if (argc > optind)
opts.o_count = Atoi(argv[optind++]);
if (opts.o_count == 0)
Die(gettext("invalid counter value\n"));
if (argc > optind)
Usage();
if (opts.o_outpmode & OPT_REALTIME)
Priocntl("RT");
if (isatty(STDOUT_FILENO) == 1 && isatty(STDIN_FILENO))
opts.o_outpmode |= OPT_TTY; /* interactive */
if (!(opts.o_outpmode & OPT_TTY)) {
opts.o_outpmode &= ~OPT_TERMCAP; /* no termcap for pipes */
opts.o_outpmode &= ~OPT_FULLSCREEN;
}
if (opts.o_outpmode & OPT_TERMCAP)
ldtermcap(); /* can turn OPT_TERMCAP off */
if (opts.o_outpmode & OPT_TERMCAP)
(void) setsize();
list_alloc(&lwps, opts.o_ntop);
list_alloc(&users, opts.o_nbottom);
list_alloc(&tasks, opts.o_nbottom);
list_alloc(&projects, opts.o_nbottom);
list_alloc(&zones, opts.o_nbottom);
list_setkeyfunc(sortk, &opts, &lwps, LT_LWPS);
list_setkeyfunc(NULL, &opts, &users, LT_USERS);
list_setkeyfunc(NULL, &opts, &tasks, LT_TASKS);
list_setkeyfunc(NULL, &opts, &projects, LT_PROJECTS);
list_setkeyfunc(NULL, &opts, &zones, LT_ZONES);
if (opts.o_outpmode & OPT_TERMCAP)
curses_on();
if ((procdir = opendir("/proc")) == NULL)
Die(gettext("cannot open /proc directory\n"));
if (opts.o_outpmode & OPT_TTY) {
(void) printf(gettext("Please wait...\r"));
(void) fflush(stdout);
}
set_signals();
pollset.fd = STDIN_FILENO;
pollset.events = POLLIN;
timeout = opts.o_interval * MILLISEC;
/*
* main program loop
*/
do {
if (sigterm == 1)
break;
if (sigtstp == 1) {
curses_off();
(void) signal(SIGTSTP, SIG_DFL);
(void) kill(0, SIGTSTP);
/*
* prstat stops here until it receives SIGCONT signal.
*/
sigtstp = 0;
(void) signal(SIGTSTP, sig_handler);
curses_on();
print_movecur = FALSE;
if (opts.o_outpmode & OPT_FULLSCREEN)
sigwinch = 1;
}
if (sigwinch == 1) {
if (setsize() == 1) {
list_free(&lwps);
list_free(&users);
list_free(&tasks);
list_free(&projects);
list_free(&zones);
list_alloc(&lwps, opts.o_ntop);
list_alloc(&users, opts.o_nbottom);
list_alloc(&tasks, opts.o_nbottom);
list_alloc(&projects, opts.o_nbottom);
list_alloc(&zones, opts.o_nbottom);
}
sigwinch = 0;
(void) signal(SIGWINCH, sig_handler);
}
prstat_scandir(procdir);
list_refresh(&lwps);
if (print_movecur)
(void) putp(movecur);
print_movecur = TRUE;
if ((opts.o_outpmode & OPT_PSINFO) ||
(opts.o_outpmode & OPT_MSACCT)) {
list_sort(&lwps);
list_print(&lwps);
}
if (opts.o_outpmode & OPT_USERS) {
list_sort(&users);
list_print(&users);
list_clear(&users);
}
if (opts.o_outpmode & OPT_TASKS) {
list_sort(&tasks);
list_print(&tasks);
list_clear(&tasks);
}
if (opts.o_outpmode & OPT_PROJECTS) {
list_sort(&projects);
list_print(&projects);
list_clear(&projects);
}
if (opts.o_outpmode & OPT_ZONES) {
list_sort(&zones);
list_print(&zones);
list_clear(&zones);
}
if (opts.o_count == 1)
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.
*/
if (opts.o_outpmode & OPT_TTY) {
if (poll(&pollset, (nfds_t)1, timeout) > 0) {
if (read(STDIN_FILENO, &key, 1) == 1) {
if (tolower(key) == 'q')
break;
}
}
} else {
(void) sleep(opts.o_interval);
}
} while (opts.o_count == (-1) || --opts.o_count);
if (opts.o_outpmode & OPT_TTY)
(void) putchar('\r');
return (0);
}