/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "prstat.h" #include "prutil.h" #include "prtable.h" #include "prsort.h" #include "prfile.h" /* * x86 ERR conflicts with 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 #include #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); }