diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/prstat/prstat.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/prstat/prstat.c')
-rw-r--r-- | usr/src/cmd/prstat/prstat.c | 1399 |
1 files changed, 1399 insertions, 0 deletions
diff --git a/usr/src/cmd/prstat/prstat.c b/usr/src/cmd/prstat/prstat.c new file mode 100644 index 0000000000..626e175af3 --- /dev/null +++ b/usr/src/cmd/prstat/prstat.c @@ -0,0 +1,1399 @@ +/* + * 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); +} |