# # 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 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # .KEEP_STATE: PROG = prstat OBJS = prstat.o prfile.o prtable.o prsort.o prutil.o include ../Makefile.cmd include ../Makefile.cmd.64 XGETFLAGS = -a -x prstat.xcl POFILE = pprstat.po POFILES = $(OBJS:.o=.po) # Hammerhead: ROOTLINK removed — ROOTBIN64 = ROOTBIN (path flattening) CLOBBERFILES += $(POFILE) $(POFILES) CSTD = $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) CERRWARN += -Wno-parentheses LDLIBS += -lcurses -lproject FILEMODE = 0555 .KEEP_STATE: .PARALLEL : $(OBJS) all: $(PROG) clean: $(RM) $(OBJS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) $(POFILE): $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ install: all $(ROOTPROG) include ../Makefile.targ /* * 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 (c) 1999 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include #include #include "prtable.h" #include "prutil.h" #include "prfile.h" #define FDS_TABLE_SIZE 1024 static fd_t *fd_tbl = NULL; static int fd_max; static int fd_cnt; static int fd_cnt_cur; static int fd_cnt_old; static fds_t *fds_tbl[FDS_TABLE_SIZE]; void fd_init(int n) { fd_max = n; fd_cnt = fd_cnt_cur = fd_cnt_old = 0; fd_tbl = Zalloc(sizeof (fd_t) * n); (void) memset(fds_tbl, 0, sizeof (fds_t *) * FDS_TABLE_SIZE); } void fd_exit() { if (fd_tbl) free(fd_tbl); } void fd_close(fd_t *fdp) { if (fdp) { if (fdp->fd_fd >= 0 && fdp->fd_name[0] != '\0') { (void) close(fdp->fd_fd); fd_cnt--; } (void) memset(fdp, 0, sizeof (fd_t)); fdp->fd_fd = -1; } } void fd_closeall() { fd_t *fdp = fd_tbl; int i; for (i = 0; i < fd_max; i++) { fd_close(fdp); fdp++; } } static void fd_recycle() { fd_t *fdp = fd_tbl; int counter; int i; counter = abs(fd_cnt_old - fd_cnt) + NUM_RESERVED_FD; for (i = 0; i < fd_max; i++, fdp++) { if (fdp->fd_fd == -1) continue; /* skip recycled ones */ if (fdp->fd_name[0] != '\0') { /* file has name */ (void) close(fdp->fd_fd); fd_cnt--; counter--; fdp->fd_fd = -1; } if (counter == 0) break; } } fd_t * fd_open(char *name, int flags, fd_t *fdp) { fd_t *fdp_new; int fd; if (fd_cnt > fd_max - NUM_RESERVED_FD) fd_recycle(); if (fdp != NULL) { if ((strcmp(fdp->fd_name, name) == 0) && (fdp->fd_fd >= 0)) { fd_cnt_cur++; return (fdp); } } again: fd = open(name, flags); if (fd == -1) { if ((errno == EMFILE) || (errno == ENFILE)) { fd_recycle(); goto again; } fdp_new = NULL; } else { fdp_new = &fd_tbl[fd]; fdp_new->fd_fd = fd; fdp_new->fd_flags = flags; (void) strcpy(fdp_new->fd_name, name); fd_cnt++; fd_cnt_cur++; } return (fdp_new); } int fd_getfd(fd_t *fdp) { return (fdp->fd_fd); } void fd_update() { fd_cnt_old = fd_cnt_cur; fd_cnt_cur = 0; } fds_t * fds_get(pid_t pid) { fds_t *fdsp; int hash = pid % FDS_TABLE_SIZE; for (fdsp = fds_tbl[hash]; fdsp; fdsp = fdsp->fds_next) if (fdsp->fds_pid == pid) /* searching for pid */ return (fdsp); fdsp = Zalloc(sizeof (fds_t)); /* adding new if pid was not found */ fdsp->fds_pid = pid; fdsp->fds_next = fds_tbl[hash]; fds_tbl[hash] = fdsp; return (fdsp); } void fds_rm(pid_t pid) { fds_t *fds; fds_t *fds_prev = NULL; int hash = pid % FDS_TABLE_SIZE; for (fds = fds_tbl[hash]; fds && fds->fds_pid != pid; fds = fds->fds_next) /* finding pid */ fds_prev = fds; if (fds) { /* if pid was found */ fd_close(fds->fds_psinfo); fd_close(fds->fds_usage); fd_close(fds->fds_lpsinfo); fd_close(fds->fds_lusage); if (fds_prev) fds_prev->fds_next = fds->fds_next; else fds_tbl[hash] = fds->fds_next; free(fds); } } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _PRFILE_H #define _PRFILE_H #include #ifdef __cplusplus extern "C" { #endif #define MAX_PROCFS_PATH 40 #define NUM_RESERVED_FD 10 typedef struct fd { int fd_fd; int fd_flags; char fd_name[MAX_PROCFS_PATH]; } fd_t; typedef struct fds { pid_t fds_pid; fd_t *fds_psinfo; fd_t *fds_usage; fd_t *fds_lpsinfo; fd_t *fds_lusage; struct fds *fds_next; } fds_t; extern void fd_init(int); extern void fd_exit(); extern fd_t *fd_open(char *, int, fd_t *); extern int fd_getfd(fd_t *); extern void fd_close(fd_t *); extern void fd_closeall(); extern void fd_update(); extern fds_t *fds_get(pid_t); extern void fds_rm(pid_t); #ifdef __cplusplus } #endif #endif /* _PRFILE_H */ /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Joyent, Inc. All rights reserved. */ #include #include #include #include #include "prstat.h" #include "prutil.h" #include "prsort.h" void list_alloc(list_t *list, int size) { list->l_size = size; if (size > 0) list->l_ptrs = Zalloc(sizeof (void *) * (size + 1)); else list->l_ptrs = NULL; } void list_free(list_t *list) { if (list && list->l_ptrs) { free(list->l_ptrs); list->l_ptrs = NULL; } } /* * Sorting routines */ static ulong_t get_cpu_from_psinfo(void *lwp) { return ((ulong_t) FRC2PCT((((lwp_info_t *)lwp)->li_info.pr_lwp.pr_pctcpu)*1000)); } static ulong_t get_cpu_from_usage(void *lwp) { lwp_info_t *p = (lwp_info_t *)lwp; float cpu = 0; cpu += p->li_usr; cpu += p->li_sys; cpu *= 1000; return ((ulong_t)cpu); } static ulong_t get_time(void *lwp) { return ((ulong_t)TIME2SEC(((lwp_info_t *)lwp)->li_info.pr_lwp.pr_time)); } static ulong_t get_size(void *lwp) { return ((ulong_t)((lwp_info_t *)lwp)->li_info.pr_size); } static ulong_t get_rssize(void *lwp) { return ((ulong_t)((lwp_info_t *)lwp)->li_info.pr_rssize); } static ulong_t get_pri(void *lwp) { return ((ulong_t)((lwp_info_t *)lwp)->li_info.pr_lwp.pr_pri); } static ulong_t get_idkey(void *id) { return (((id_info_t *)id)->id_key); } void list_setkeyfunc(char *arg, optdesc_t *opt, list_t *list, int type) { if (list == NULL) return; list->l_sortorder = opt->o_sortorder; list->l_type = type; if (arg == NULL) { /* special case for id_infos */ list->l_func = get_idkey; return; } if (strcmp("cpu", arg) == 0) { if (opt->o_outpmode & OPT_MSACCT) list->l_func = get_cpu_from_usage; else list->l_func = get_cpu_from_psinfo; return; } if (strcmp("time", arg) == 0) { list->l_func = get_time; return; } if (strcmp("size", arg) == 0) { list->l_func = get_size; return; } if (strcmp("rss", arg) == 0) { list->l_func = get_rssize; return; } if (strcmp("pri", arg) == 0) { list->l_func = get_pri; return; } Die(gettext("invalid sort key -- %s\n"), arg); } ulong_t list_getkeyval(list_t *list, void *ptr) { return (list->l_func(ptr)); } static int compare_keys(list_t *list, ulong_t key1, ulong_t key2) { if (key1 == key2) return (0); if (key1 < key2) return (1 * list->l_sortorder); else return (-1 * list->l_sortorder); } static void list_insert(list_t *list, void *ptr) { int i, j; long k1, k2; for (i = 0; i < list->l_used; i++) { /* insert in the middle */ k1 = list_getkeyval(list, ptr); k2 = list_getkeyval(list, list->l_ptrs[i]); if (compare_keys(list, k1, k2) >= 0) { for (j = list->l_used - 1; j >= i; j--) list->l_ptrs[j+1] = list->l_ptrs[j]; list->l_ptrs[i] = ptr; if (list->l_used < list->l_size) list->l_used++; return; } } if (i + 1 <= list->l_size) { /* insert at the tail */ list->l_ptrs[list->l_used] = ptr; list->l_used++; } } static void list_preinsert(list_t *list, void *ptr) { ulong_t k1, k2; if (list->l_used < list->l_size) { /* just add */ list_insert(list, ptr); return; } k1 = list_getkeyval(list, list->l_ptrs[list->l_used - 1]); k2 = list_getkeyval(list, ptr); if (compare_keys(list, k1, k2) >= 0) /* skip insertion */ return; k1 = list_getkeyval(list, list->l_ptrs[0]); if (compare_keys(list, k2, k1) >= 0) { /* add at the head */ list_insert(list, ptr); return; } list_insert(list, ptr); } void list_sort(list_t *list) { list->l_used = 0; if (list->l_size == 0) return; (void) memset(list->l_ptrs, 0, sizeof (void *) * list->l_size); if (list->l_type == LT_LWPS) { lwp_info_t *lwp = list->l_head; while (lwp) { list_preinsert(list, (void *)lwp); lwp = lwp->li_next; } } else { id_info_t *id = list->l_head; while (id) { list_preinsert(list, (void *)id); id = id->id_next; } } } /* * 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 (c) 1999-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _PRSORT_H #define _PRSORT_H #ifdef __cplusplus extern "C" { #endif #include "prstat.h" extern void list_alloc(list_t *, int); extern void list_free(list_t *); extern void list_setkeyfunc(char *, optdesc_t *, list_t *, int); extern ulong_t list_getkeyval(list_t *, void *); extern void list_sort(list_t *); #ifdef __cplusplus } #endif #endif /* _PRSORT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier * Copyright 2018 Joyent, Inc. All rights reserved. */ #include #include #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 LOGIN_WIDTH 8 #define ZONE_WIDTH 28 #define PROJECT_WIDTH 28 #define PSINFO_HEADER_PROC \ " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP " #define PSINFO_HEADER_PROC_LGRP \ " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/NLWP " #define PSINFO_HEADER_LWP \ " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/LWP " #define PSINFO_HEADER_LWP_LGRP \ " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/LWP " #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/LWP " #define USER_HEADER_PROC \ " NPROC USERNAME SWAP RSS MEMORY TIME CPU " #define USER_HEADER_LWP \ " NLWP USERNAME SWAP RSS MEMORY TIME CPU " #define TASK_HEADER_PROC \ "TASKID NPROC SWAP RSS MEMORY TIME CPU PROJECT " #define TASK_HEADER_LWP \ "TASKID NLWP SWAP RSS MEMORY TIME CPU PROJECT " #define PROJECT_HEADER_PROC \ "PROJID NPROC SWAP RSS MEMORY TIME CPU PROJECT " #define PROJECT_HEADER_LWP \ "PROJID NLWP SWAP RSS MEMORY TIME CPU PROJECT " #define ZONE_HEADER_PROC \ "ZONEID NPROC SWAP RSS MEMORY TIME CPU ZONE " #define ZONE_HEADER_LWP \ "ZONEID NLWP SWAP RSS MEMORY TIME CPU ZONE " #define PSINFO_LINE \ "%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %s" #define PSINFO_LINE_LGRP \ "%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %4d %s" #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 %s" #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 table_t lgr_tbl = {0, 0, NULL}; /* selected lgroups */ static zonetbl_t zone_tbl = {0, 0, NULL}; /* selected zones */ static uidtbl_t euid_tbl = {0, 0, NULL}; /* selected effective users */ static uidtbl_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 list_t lgroups; /* list of lgroups */ static volatile uint_t sigwinch = 0; static volatile uint_t sigtstp = 0; static volatile uint_t sigterm = 0; static long pagesize; /* default settings */ 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 */ }; /* * Print timestamp as decimal reprentation of time_t value (-d u was specified) * or the standard date format (-d d was specified). */ static void print_timestamp(void) { time_t t = time(NULL); static char *fmt = NULL; /* We only need to retrieve this once per invocation */ if (fmt == NULL) fmt = nl_langinfo(_DATE_FMT); if (opts.o_outpmode & OPT_UDATE) { (void) printf("%ld", t); } else if (opts.o_outpmode & OPT_DDATE) { char dstr[64]; int len; len = strftime(dstr, sizeof (dstr), fmt, localtime(&t)); if (len > 0) (void) printf("%s", dstr); } (void) putp(t_eol); (void) putchar('\n'); } 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]; } } /* * Queries the memory virtual and rss size for each member of a list. * This will override the values computed by /proc aggregation. */ static void list_getsize(list_t *list) { id_info_t *id; vmusage_t *results, *next; vmusage_t *match; size_t nres = 0; size_t i; uint_t flags = 0; int ret; size_t physmem = sysconf(_SC_PHYS_PAGES) * pagesize; /* * Determine what swap/rss results to calculate. getvmusage() will * prune results returned to non-global zones automatically, so * there is no need to pass different flags when calling from a * non-global zone. * * Currently list_getsize() is only called with a single flag. This * is because -Z, -J, -T, and -a are mutually exclusive. Regardless * of this, we handle multiple flags. */ if (opts.o_outpmode & OPT_USERS) { /* * Gather rss for all users in all zones. Treat the same * uid in different zones as the same user. */ flags |= VMUSAGE_COL_RUSERS; } else if (opts.o_outpmode & OPT_TASKS) { /* Gather rss for all tasks in all zones */ flags |= VMUSAGE_ALL_TASKS; } else if (opts.o_outpmode & OPT_PROJECTS) { /* * Gather rss for all projects in all zones. Treat the same * projid in diffrent zones as the same project. */ flags |= VMUSAGE_COL_PROJECTS; } else if (opts.o_outpmode & OPT_ZONES) { /* Gather rss for all zones */ flags |= VMUSAGE_ALL_ZONES; } else { Die(gettext( "Cannot determine rss flags for output options %x\n"), opts.o_outpmode); } /* * getvmusage() returns an array of result structures. One for * each zone, project, task, or user on the system, depending on * flags. * * If getvmusage() fails, prstat will use the size already gathered * from psinfo */ if (getvmusage(flags, opts.o_interval, NULL, &nres) != 0) return; results = (vmusage_t *)Malloc(sizeof (vmusage_t) * nres); for (;;) { ret = getvmusage(flags, opts.o_interval, results, &nres); if (ret == 0) break; if (errno == EOVERFLOW) { results = (vmusage_t *)Realloc(results, sizeof (vmusage_t) * nres); continue; } /* * Failure for some other reason. Prstat will use the size * already gathered from psinfo. */ free(results); return; } for (id = list->l_head; id != NULL; id = id->id_next) { match = NULL; next = results; for (i = 0; i < nres; i++, next++) { switch (flags) { case VMUSAGE_COL_RUSERS: if (next->vmu_id == id->id_uid) match = next; break; case VMUSAGE_ALL_TASKS: if (next->vmu_id == id->id_taskid) match = next; break; case VMUSAGE_COL_PROJECTS: if (next->vmu_id == id->id_projid) match = next; break; case VMUSAGE_ALL_ZONES: if (next->vmu_id == id->id_zoneid) match = next; break; default: Die(gettext( "Unknown vmusage flags %d\n"), flags); } } if (match != NULL) { id->id_size = match->vmu_swap_all / 1024; id->id_rssize = match->vmu_rss_all / 1024; id->id_pctmem = (100.0 * (float)match->vmu_rss_all) / (float)physmem; /* Output using data from getvmusage() */ id->id_sizematch = B_TRUE; } /* * If no match is found, prstat will use the size already * gathered from psinfo. */ } free(results); } /* * 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 name[PRFNSZ + THREAD_NAME_MAX + 2]; char projname[PROJNAME_MAX+1]; char zonename[ZONENAME_MAX+1]; float cpu, mem; double loadavg[3] = {0, 0, 0}; int i, n; if (list->l_size == 0) return; 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_UDATE) || (opts.o_outpmode & OPT_DDATE)) && ((list->l_type == LT_LWPS) || !(opts.o_outpmode & OPT_SPLIT))) print_timestamp(); if (opts.o_outpmode & OPT_TTY) (void) putchar('\r'); (void) putp(t_ulon); n = opts.o_cols; switch (list->l_type) { case LT_PROJECTS: if (opts.o_outpmode & OPT_LWPS) n = printf(PROJECT_HEADER_LWP); else n = printf(PROJECT_HEADER_PROC); break; case LT_TASKS: if (opts.o_outpmode & OPT_LWPS) n = printf(TASK_HEADER_LWP); else n = printf(TASK_HEADER_PROC); break; case LT_ZONES: if (opts.o_outpmode & OPT_LWPS) n = printf(ZONE_HEADER_LWP); else n = printf(ZONE_HEADER_PROC); break; case LT_USERS: if (opts.o_outpmode & OPT_LWPS) n = printf(USER_HEADER_LWP); else n = printf(USER_HEADER_PROC); break; case LT_LWPS: if (opts.o_outpmode & OPT_LWPS) { if (opts.o_outpmode & OPT_PSINFO) { if (opts.o_outpmode & OPT_LGRP) n = printf(PSINFO_HEADER_LWP_LGRP); else n = printf(PSINFO_HEADER_LWP); } if (opts.o_outpmode & OPT_MSACCT) n = printf(USAGE_HEADER_LWP); } else { if (opts.o_outpmode & OPT_PSINFO) { if (opts.o_outpmode & OPT_LGRP) n = printf(PSINFO_HEADER_PROC_LGRP); else n = printf(PSINFO_HEADER_PROC); } if (opts.o_outpmode & OPT_MSACCT) n = printf(USAGE_HEADER_PROC); } break; } /* Pad out the header line so the underline spans the whole width */ if ((opts.o_outpmode & OPT_TERMCAP) && n < opts.o_cols) (void) printf("%*s", (int)(opts.o_cols - n), ""); (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 (id->id_sizematch == B_FALSE && 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, sizeof (pname), opts.o_outpmode & OPT_NORESOLVE, opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC), LOGIN_WIDTH); } else if (list->l_type == LT_ZONES) { getzonename(id->id_zoneid, zonename, sizeof (zonename), opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC), ZONE_WIDTH); } else { getprojname(id->id_projid, projname, sizeof (projname), opts.o_outpmode & OPT_NORESOLVE, opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC), PROJECT_WIDTH); } 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]; format_name(lwp, name, sizeof (name)); pwd_getname(lwp->li_info.pr_uid, pname, sizeof (pname), opts.o_outpmode & OPT_NORESOLVE, opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC), LOGIN_WIDTH); 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'); if (opts.o_outpmode & OPT_LGRP) { (void) printf(PSINFO_LINE_LGRP, (int)lwp->li_info.pr_pid, pname, psize, prssize, pstate, ppri, pnice, ptime, pcpu, lwp->li_info.pr_lwp.pr_lgrp, name); } else { (void) printf(PSINFO_LINE, (int)lwp->li_info.pr_pid, pname, psize, prssize, pstate, ppri, pnice, ptime, pcpu, name); } (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'); (void) printf(USAGE_LINE, (int)lwp->li_info.pr_pid, pname, usr, sys, trp, tfl, dfl, lck, slp, lat, vcx, icx, scl, sig, name); (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; if ((list->l_type == LT_LGRPS) && (id->id_lgroup != lwp->li_info.pr_lwp.pr_lgrp)) 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; id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp; 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_lgroup = lwp->li_info.pr_lwp.pr_lgrp; id->id_nproc++; id->id_sizematch = B_FALSE; 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 get_lwpname(pid_t pid, id_t lwpid, char *buf, size_t bufsize) { char *path = NULL; int fd; buf[0] = '\0'; if (asprintf(&path, "/proc/%d/lwp/%d/lwpname", (int)pid, (int)lwpid) == -1) return; if ((fd = open(path, O_RDONLY)) != -1) { (void) read(fd, buf, bufsize); buf[bufsize - 1] = '\0'; (void) close(fd); } free(path); } 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)); get_lwpname(pid, lwpid, lwp->li_lwpname, sizeof (lwp->li_lwpname)); } 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) || !has_element(&lgr_tbl, lwpsinfo->pr_lgrp)) 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) || !has_element(&lgr_tbl, psinfo.pr_lwp.pr_lgrp)) { 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); if (opts.o_outpmode & OPT_LGRP) list_update(&lgroups, 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(void) { 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(void) { 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(int *linesp, int *colsp) { struct winsize ws; char *envp; int n; *linesp = -1; *colsp = -1; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) { if (ws.ws_row > 0) *linesp = ws.ws_row; if (ws.ws_col > 0) *colsp = ws.ws_col; if (ws.ws_row > 0 && ws.ws_col > 0) return (0); } if ((envp = getenv("LINES")) != NULL) { if ((n = Atoi(envp)) > 0) { opts.o_outpmode &= ~OPT_USEHOME; *linesp = n; } } if ((envp = getenv("COLUMNS")) != NULL) { if ((n = Atoi(envp)) > 0) { *colsp = n; } } return ((*linesp > 0 && *colsp > 0) ? 0 : -1); } static void setmovecur(void) { int i, n; if ((opts.o_outpmode & OPT_FULLSCREEN) && (opts.o_outpmode & OPT_USEHOME)) { movecur = t_home; return; } if (opts.o_outpmode & OPT_SPLIT) { if (opts.o_ntop == 0) n = opts.o_nbottom + 1; else 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 (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE))) n++; 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(void) { static int oldn = 0; int cols, n, ret; if (opts.o_outpmode & OPT_FULLSCREEN) { ret = nlines(&n, &cols); if (ret != -1) opts.o_cols = cols; if (n == oldn) return (0); oldn = n; if (ret == -1) { opts.o_outpmode &= ~OPT_USEHOME; setmovecur(); /* set default window size */ return (1); } n = n - 3; /* minus header, total and cursor lines */ if ((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)) n--; /* minus timestamp */ 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 option) { char *p = strtok(arg, ", "); if (p == NULL) Die(gettext("invalid argument for -%c\n"), option); add_element(table, (long)Atoi(p)); while (p = strtok(NULL, ", ")) add_element(table, (long)Atoi(p)); } static void fill_prj_table(char *arg) { projid_t projid; char *p = strtok(arg, ", "); if (p == NULL) Die(gettext("invalid argument for -j\n")); 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 (p == NULL) Die(gettext("invalid argument for -C\n")); 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()); pagesize = sysconf(_SC_PAGESIZE); while ((opt = getopt(argc, argv, "vcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJWz:Z")) != (int)EOF) { switch (opt) { case 'r': opts.o_outpmode |= OPT_NORESOLVE; break; case 'R': opts.o_outpmode |= OPT_REALTIME; break; case 'c': opts.o_outpmode &= ~OPT_TERMCAP; opts.o_outpmode &= ~OPT_FULLSCREEN; break; case 'd': if (optarg) { if (*optarg == 'u') opts.o_outpmode |= OPT_UDATE; else if (*optarg == 'd') opts.o_outpmode |= OPT_DDATE; else Usage(); } else { Usage(); } break; case 'h': fill_table(&lgr_tbl, optarg, 'h'); break; case 'H': opts.o_outpmode |= OPT_LGRP; 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': if ((p = strtok(optarg, ",")) == NULL) Die(gettext("invalid argument for -n\n")); opts.o_ntop = Atoi(p); if (p = strtok(NULL, ",")) opts.o_nbottom = Atoi(p); else if (opts.o_ntop == 0) opts.o_nbottom = 5; 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': if ((p = strtok(optarg, ", ")) == NULL) Die(gettext("invalid argument for -u\n")); add_uid(&euid_tbl, p); while (p = strtok(NULL, ", ")) add_uid(&euid_tbl, p); break; case 'U': if ((p = strtok(optarg, ", ")) == NULL) Die(gettext("invalid argument for -U\n")); add_uid(&ruid_tbl, p); while (p = strtok(NULL, ", ")) add_uid(&ruid_tbl, p); break; case 'p': fill_table(&pid_tbl, optarg, 'p'); break; case 'C': fill_set_table(optarg); opts.o_outpmode |= OPT_PSETS; break; case 'P': fill_table(&cpu_tbl, optarg, 'P'); break; case 'k': fill_table(&tsk_tbl, optarg, 'k'); break; case 'j': fill_prj_table(optarg); break; case 'L': opts.o_outpmode |= OPT_LWPS; break; case 'W': opts.o_outpmode |= OPT_TRUNC; break; case 'z': if ((p = strtok(optarg, ", ")) == NULL) Die(gettext("invalid argument for -z\n")); 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_outpmode & OPT_SPLIT) && opts.o_ntop == 0) Die(gettext("invalid argument for -n\n")); if (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")); } /* * There is not enough space to combine microstate information and * lgroup information and still fit in 80-column output. */ if ((opts.o_outpmode & OPT_LGRP) && (opts.o_outpmode & OPT_MSACCT)) { Die(gettext("-H and -m 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_alloc(&lgroups, 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); list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS); 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")); if (!(opts.o_outpmode & OPT_TERMCAP)) (void) putchar('\n'); (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_getsize(&users); list_sort(&users); list_print(&users); list_clear(&users); } if (opts.o_outpmode & OPT_TASKS) { list_getsize(&tasks); list_sort(&tasks); list_print(&tasks); list_clear(&tasks); } if (opts.o_outpmode & OPT_PROJECTS) { list_getsize(&projects); list_sort(&projects); list_print(&projects); list_clear(&projects); } if (opts.o_outpmode & OPT_ZONES) { list_getsize(&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); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier * Copyright 2018 Joyent, Inc. All rights reserved. */ #ifndef _PRSTAT_H #define _PRSTAT_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * FRC2PCT macro is used to convert 16-bit binary fractions in the range * 0.0 to 1.0 with binary point to the right of the high order bit * (i.e. 1.0 == 0x8000) to percentage value. */ #define FRC2PCT(pp) (((float)(pp))/0x8000*100) #define TIME2NSEC(__t)\ (hrtime_t)(((hrtime_t)__t.tv_sec * (hrtime_t)NANOSEC) + (hrtime_t)__t.tv_nsec) #define TIME2SEC(__t)\ (hrtime_t)(__t.tv_sec) /* * List of available output modes */ #define OPT_PSINFO 0x0001 /* read process's data from "psinfo" */ #define OPT_LWPS 0x0002 /* report about all lwps */ #define OPT_USERS 0x0004 /* report about most active users */ #define OPT_UNUSED 0x0008 /* reserved for future use */ #define OPT_REALTIME 0x0010 /* real-time scheduling class flag */ #define OPT_MSACCT 0x0020 /* microstate accounting flag */ #define OPT_TERMCAP 0x0040 /* use termcap data to move cursor */ #define OPT_SPLIT 0x0080 /* split-screen mode flag */ #define OPT_TTY 0x0100 /* report results to tty or file */ #define OPT_FULLSCREEN 0x0200 /* full-screen mode flag */ #define OPT_USEHOME 0x0400 /* use 'home' to move cursor up */ #define OPT_TASKS 0x0800 /* report about system tasks */ #define OPT_PROJECTS 0x1000 /* report about system projects */ #define OPT_ZONES 0x2000 /* report about zones */ #define OPT_PSETS 0x4000 /* report for specified psets */ #define OPT_LGRP 0x8000 /* report home lgroups */ #define OPT_UDATE 0x20000 /* print unix timestamp */ #define OPT_DDATE 0x40000 /* print timestamp in date(1) format */ #define OPT_NORESOLVE 0x80000 /* no nsswitch lookups */ #define OPT_TRUNC 0x100000 /* truncate long names */ /* * Flags to keep track of process or lwp status */ #define LWP_ALIVE 0x0008 /* this pid/lwp still exists */ #define LWP_REPRESENT 0x0010 /* this LWP represents the process */ /* * Possible list types */ #define LT_LWPS 0x0001 #define LT_USERS 0x0002 #define LT_TASKS 0x0004 #define LT_PROJECTS 0x0008 #define LT_ZONES 0x0010 #define LT_LGRPS 0x0020 /* * Linked list of per-process or per-lwp statistics */ typedef struct lwp_info { psinfo_t li_info; /* data read from psinfo file */ prusage_t li_usage; /* data read from usage file */ ulong_t li_key; /* value of the key for this lwp */ int li_flags; /* process/lwp flags */ float li_usr; /* user level CPU time */ float li_sys; /* system call CPU time */ float li_trp; /* other system trap CPU time */ float li_tfl; /* text page fault sleep time */ float li_dfl; /* data page fault sleep time */ float li_lck; /* user lock wait sleep time */ float li_slp; /* all other sleep time */ float li_lat; /* wait-cpu (latency) time */ ulong_t li_vcx; /* voluntary context switches */ ulong_t li_icx; /* involuntary context switches */ ulong_t li_scl; /* system calls */ ulong_t li_sig; /* received signals */ char li_lwpname[THREAD_NAME_MAX]; struct lwp_info *li_next; /* pointer to next lwp */ struct lwp_info *li_prev; /* pointer to previous lwp */ } lwp_info_t; /* * Linked list of collective per-uid, per-taskid, per-projid or per-lgroup * statistics */ typedef struct id_info { uid_t id_uid; /* user id */ taskid_t id_taskid; /* task id */ projid_t id_projid; /* project id */ zoneid_t id_zoneid; /* zone id */ int id_lgroup; /* lgroup id */ uint_t id_nproc; /* number of processes */ boolean_t id_sizematch; /* size/rssize from getvmusage() */ size_t id_size; /* memory usage */ size_t id_rssize; /* resident set size */ ulong_t id_time; /* cpu time (in secs) */ float id_pctcpu; /* percentage of cpu usage */ float id_pctmem; /* percentage of memory usage */ ulong_t id_key; /* sort key value */ struct id_info *id_next; /* pointer to next entry */ struct id_info *id_prev; /* pointer to previous entry */ } id_info_t; typedef ulong_t (*keyfunc_t)(void *); /* * Per-list structure */ typedef struct list { int l_type; /* list type */ int l_count; /* number of entries in the list */ void *l_head; /* pointer to the head of the list */ void *l_tail; /* pointer to the tail of the list */ int l_size; /* number of allocated pointers */ int l_used; /* number of used pointers */ int l_sortorder; /* sorting order for the list */ keyfunc_t l_func; /* pointer to key function */ void **l_ptrs; /* pointer to an array of pointers */ } list_t; /* * Command line options */ typedef struct optdesc { int o_interval; /* interval between updates */ int o_ntop; /* number of lines in top half */ int o_nbottom; /* number of lines in bottom half */ int o_count; /* number of iterations */ int o_outpmode; /* selected output mode */ int o_sortorder; /* +1 ascending, -1 descending */ int o_cols; /* number of columns */ } optdesc_t; extern optdesc_t opts; #ifdef __cplusplus } #endif #endif /* _PRSTAT_H */ # # 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 # msgid "cpu" msgid "time" msgid "size" msgid "rss" msgid "pri" msgid " - " msgid "RT" msgid "SYS" msgid " -" msgid "\n\r" msgid "/proc/%d/lwp/%d/lwpctl" msgid "/proc/%d/status" msgid "/proc/%d/ctl" msgid "/proc/%s/psinfo" msgid "/proc/%s/lpsinfo" msgid "/proc/%s/lusage" msgid "/proc/%s/usage" msgid "\0" msgid "smul" msgid "rmul" msgid "cuu1" msgid "el" msgid "smcup" msgid "rmcup" msgid "" msgid "vcmaRLtu:U:n:p:C:P:s:S:" msgid "," msgid ", " msgid "/proc" msgid "%d" msgid "%s" msgid "%s: " msgid ": %s\n" msgid "%4d%c" msgid "%3d:%2.2d.%2.2d" msgid "%1.1f" msgid "%.0f" msgid ".%1dM" msgid "%2dK" msgid "%3d" msgid "sleep" msgid "run" msgid "zombie" msgid "stop" msgid "idle" msgid "xbrk" msgid "cpu%-3d" msgid "?" msgid "home" msgid "LINES" /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier */ #include #include #include #include #include #include #include #include #include #include #include #include "prstat.h" #include "prutil.h" #include "prtable.h" static plwp_t *plwp_tbl[PLWP_TBL_SZ]; void lwpid_init() { (void) memset(&plwp_tbl, 0, sizeof (plwp_t *) * PLWP_TBL_SZ); } static uid_t pwd_getid(char *name) { struct passwd *pwd; if ((pwd = getpwnam(name)) == NULL) Die(gettext("invalid user name: %s\n"), name); return (pwd->pw_uid); } void pwd_getname(uid_t uid, char *name, size_t length, int noresolve, int trunc, size_t width) { struct passwd *pwd; size_t n; if (noresolve || (pwd = getpwuid(uid)) == NULL) { n = snprintf(NULL, 0, "%u", uid); if (trunc && n > width) (void) snprintf(name, length, "%.*u%c", width - 1, uid, '*'); else (void) snprintf(name, length, "%u", uid); } else { n = mbstowcs(NULL, pwd->pw_name, 0); if (n == (size_t)-1) (void) snprintf(name, length, "%s", "ERROR"); else if (trunc && n > width) (void) snprintf(name, length, "%.*s%c", width - 1, pwd->pw_name, '*'); else (void) snprintf(name, length, "%s", pwd->pw_name); } } void add_uid(uidtbl_t *tbl, char *name) { uid_t *uid; if (tbl->n_size == tbl->n_nent) { /* reallocation */ if ((tbl->n_size *= 2) == 0) tbl->n_size = 4; /* first time */ tbl->n_list = Realloc(tbl->n_list, tbl->n_size*sizeof (uid_t)); } uid = &tbl->n_list[tbl->n_nent++]; if (isdigit(name[0])) { *uid = Atoi(name); } else { *uid = pwd_getid(name); } } int has_uid(uidtbl_t *tbl, uid_t uid) { size_t i; if (tbl->n_nent) { /* do linear search if table is not empty */ for (i = 0; i < tbl->n_nent; i++) if (tbl->n_list[i] == uid) return (1); } else { return (1); /* if table is empty return true */ } return (0); /* nothing has been found */ } void add_element(table_t *table, long element) { if (table->t_size == table->t_nent) { if ((table->t_size *= 2) == 0) table->t_size = 4; table->t_list = Realloc(table->t_list, table->t_size * sizeof (long)); } table->t_list[table->t_nent++] = element; } int has_element(table_t *table, long element) { size_t i; if (table->t_nent) { /* do linear search if table is not empty */ for (i = 0; i < table->t_nent; i++) if (table->t_list[i] == element) return (1); } else { /* if table is empty then */ return (1); /* pretend that element was found */ } return (0); /* element was not found */ } int foreach_element(table_t *table, void *buf, void (*walker)(long, void *)) { size_t i; if (table->t_nent) { for (i = 0; i < table->t_nent; i++) walker(table->t_list[i], buf); } else { return (0); } return (1); } void add_zone(zonetbl_t *tbl, char *str) { zonename_t *entp; zoneid_t id; char *cp; /* * str should be either the name of a configured zone, or the * id of a running zone. If str is a zone name, store the name * in the table; otherwise, just store the id. */ if (zone_get_id(str, &id) != 0) { Die(gettext("unknown zone -- %s\n"), str); /*NOTREACHED*/ } /* was zone specified by name or id? */ errno = 0; if (id == (zoneid_t)strtol(str, &cp, 0) && errno == 0 && cp != str && *cp == '\0') { /* found it by id, don't store the name */ str = NULL; } if (tbl->z_size == tbl->z_nent) { /* reallocation */ if ((tbl->z_size *= 2) == 0) tbl->z_size = 4; /* first time */ tbl->z_list = Realloc(tbl->z_list, tbl->z_size * sizeof (zonename_t)); } entp = &tbl->z_list[tbl->z_nent++]; if (str) (void) strlcpy(entp->z_name, str, ZONENAME_MAX); else entp->z_name[0] = '\0'; entp->z_id = id; } int has_zone(zonetbl_t *tbl, zoneid_t id) { long i; if (tbl->z_nent) { /* do linear search if table is not empty */ for (i = 0; i < tbl->z_nent; i++) if (tbl->z_list[i].z_id == id) return (1); return (0); /* nothing has been found */ } return (1); /* if table is empty return true */ } /* * Lookup ids for each zone name; this is done once each time /proc * is scanned to avoid calling getzoneidbyname for each process. */ void convert_zone(zonetbl_t *tbl) { long i; zoneid_t id; char *name; for (i = 0; i < tbl->z_nent; i++) { name = tbl->z_list[i].z_name; if (name != NULL) { if ((id = getzoneidbyname(name)) != -1) tbl->z_list[i].z_id = id; } } } void lwpid_add(lwp_info_t *lwp, pid_t pid, id_t lwpid) { plwp_t *elm = Zalloc(sizeof (plwp_t)); int hash = pid % PLWP_TBL_SZ; elm->l_pid = pid; elm->l_lwpid = lwpid; elm->l_lwp = lwp; elm->l_next = plwp_tbl[hash]; /* add in front of chain */ plwp_tbl[hash] = elm; } void lwpid_del(pid_t pid, id_t lwpid) { plwp_t *elm, *elm_prev; int hash = pid % PLWP_TBL_SZ; elm = plwp_tbl[hash]; elm_prev = NULL; while (elm) { if ((elm->l_pid == pid) && (elm->l_lwpid == lwpid)) { if (!elm_prev) /* first chain element */ plwp_tbl[hash] = elm->l_next; else elm_prev->l_next = elm->l_next; free(elm); break; } else { elm_prev = elm; elm = elm->l_next; } } } static plwp_t * lwpid_getptr(pid_t pid, id_t lwpid) { plwp_t *elm = plwp_tbl[pid % PLWP_TBL_SZ]; while (elm) { if ((elm->l_pid == pid) && (elm->l_lwpid == lwpid)) return (elm); else elm = elm->l_next; } return (NULL); } lwp_info_t * lwpid_get(pid_t pid, id_t lwpid) { plwp_t *elm = lwpid_getptr(pid, lwpid); if (elm) return (elm->l_lwp); else return (NULL); } int lwpid_pidcheck(pid_t pid) { plwp_t *elm; elm = plwp_tbl[pid % PLWP_TBL_SZ]; while (elm) { if (elm->l_pid == pid) return (1); else elm = elm->l_next; } return (0); } int lwpid_is_active(pid_t pid, id_t lwpid) { plwp_t *elm = lwpid_getptr(pid, lwpid); if (elm) return (elm->l_active); else return (0); } void lwpid_set_active(pid_t pid, id_t lwpid) { plwp_t *elm = lwpid_getptr(pid, lwpid); if (elm) elm->l_active = LWP_ACTIVE; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier */ #ifndef _PRTABLE_H #define _PRTABLE_H #ifdef __cplusplus extern "C" { #endif #include #include #include "prstat.h" #define PLWP_TBL_SZ 4096 /* hash table of plwp_t structures */ #define LWP_ACTIVE 1 typedef struct { size_t t_size; size_t t_nent; long *t_list; } table_t; typedef struct { size_t n_size; size_t n_nent; uid_t *n_list; } uidtbl_t; typedef struct { zoneid_t z_id; char z_name[ZONENAME_MAX]; } zonename_t; typedef struct { size_t z_size; size_t z_nent; zonename_t *z_list; } zonetbl_t; typedef struct plwp { /* linked list of pointers to lwps */ pid_t l_pid; id_t l_lwpid; int l_active; lwp_info_t *l_lwp; struct plwp *l_next; } plwp_t; extern void pwd_getname(uid_t, char *, size_t, int, int, size_t); extern void add_uid(uidtbl_t *, char *); extern int has_uid(uidtbl_t *, uid_t); extern void add_element(table_t *, long); extern int has_element(table_t *, long); extern void add_zone(zonetbl_t *, char *); extern int has_zone(zonetbl_t *, zoneid_t); extern void convert_zone(zonetbl_t *); extern int foreach_element(table_t *, void *, void (*)(long, void *)); extern void lwpid_init(); extern void lwpid_add(lwp_info_t *, pid_t, id_t); extern lwp_info_t *lwpid_get(pid_t, id_t); extern int lwpid_pidcheck(pid_t); extern void lwpid_del(pid_t, id_t); extern void lwpid_set_active(pid_t, id_t); extern int lwpid_is_active(pid_t, id_t); #ifdef __cplusplus } #endif #endif /* _PRTABLE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier * Copyright 2018 Joyent, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "prfile.h" #include "prstat.h" #include "prutil.h" static char PRG_FMT[] = "%s: "; static char ERR_FMT[] = ": %s\n"; static char *progname; static char projbuf[PROJECT_BUFSZ]; #define RLIMIT_NOFILE_MAX 32767 /*PRINTFLIKE1*/ void Warn(char *format, ...) { int err = errno; va_list alist; if (progname != NULL) (void) fprintf(stderr, PRG_FMT, progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, gettext(ERR_FMT), strerror(err)); } /*PRINTFLIKE1*/ void Die(char *format, ...) { int err = errno; va_list alist; if (progname != NULL) (void) fprintf(stderr, PRG_FMT, progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, gettext(ERR_FMT), strerror(err)); exit(1); } void Progname(char *arg0) { char *p = strrchr(arg0, '/'); if (p == NULL) p = arg0; else p++; progname = p; } void Usage() { (void) fprintf(stderr, gettext( "Usage:\tprstat [-acHJLmrRtTvWZ] [-u euidlist] [-U uidlist]\n" "\t[-p pidlist] [-P cpulist] [-C psrsetlist] [-h lgrouplist]\n" "\t[-j projidlist] [-k taskidlist] [-z zoneidlist]\n" "\t[-s key | -S key] [-n nprocs[,nusers]] [-d d|u]\n" "\t[interval [counter]]\n")); exit(1); } int Atoi(char *p) { int i; char *q; errno = 0; i = (int)strtol(p, &q, 10); if (errno != 0 || q == p || i < 0 || *q != '\0') Die(gettext("illegal argument -- %s\n"), p); /*NOTREACHED*/ else return (i); return (0); /* keep gcc happy */ } void Format_size(char *str, size_t size, int length) { char tag = 'K'; if (size >= 10000) { size = (size + 512) / 1024; tag = 'M'; if (size >= 10000) { size = (size + 512) / 1024; tag = 'G'; } } (void) snprintf(str, length, "%4d%c", (int)size, tag); } void Format_time(char *str, ulong_t time, int length) { (void) snprintf(str, length, gettext("%3d:%2.2d:%2.2d"), /* hr:mm:ss */ (int)time/3600, (int)(time % 3600)/60, (int)time % 60); } void Format_pct(char *str, float val, int length) { if (val > (float)100) val = 100; if (val < 0) val = 0; if (val < (float)9.95) (void) snprintf(str, length, "%1.1f", val); else (void) snprintf(str, length, "%.0f", val); } void Format_num(char *str, int num, int length) { if (num >= 100000) { (void) snprintf(str, length, ".%1dM", num/100000); } else { if (num >= 1000) (void) snprintf(str, length, "%2dK", num/1000); else (void) snprintf(str, length, "%3d", num); } } void Format_state(char *str, char state, processorid_t pr_id, int length) { switch (state) { case 'S': (void) strncpy(str, "sleep", length); break; case 'R': (void) strncpy(str, "run", length); break; case 'Z': (void) strncpy(str, "zombie", length); break; case 'T': (void) strncpy(str, "stop", length); break; case 'I': (void) strncpy(str, "idle", length); break; case 'W': (void) strncpy(str, "wait", length); break; case 'O': (void) snprintf(str, length, "cpu%-3d", (int)pr_id); break; default: (void) strncpy(str, "?", length); break; } } void * Realloc(void *ptr, size_t size) { int cnt = 0; void *sav = ptr; eagain: if ((ptr = realloc(ptr, size))) return (ptr); if ((++cnt <= 3) && (errno == EAGAIN)) { Warn(gettext("realloc() failed, attempt %d"), cnt); (void) poll(NULL, 0, 5000); /* wait for 5 seconds */ ptr = sav; goto eagain; } ptr = sav; Die(gettext("not enough memory")); /*NOTREACHED*/ return (NULL); /* keep gcc happy */ } void * Malloc(size_t size) { return (Realloc(NULL, size)); } void * Zalloc(size_t size) { return (memset(Realloc(NULL, size), 0, size)); } int Setrlimit() { struct rlimit rlim; int fd_limit; if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) Die(gettext("getrlimit failed")); fd_limit = rlim.rlim_cur; rlim.rlim_max = MIN(rlim.rlim_max, RLIMIT_NOFILE_MAX); rlim.rlim_cur = rlim.rlim_max; (void) enable_extended_FILE_stdio(-1, -1); if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) return (fd_limit); else return (rlim.rlim_cur); } void Priocntl(char *class) { pcinfo_t pcinfo; pcparms_t pcparms; (void) strcpy(pcinfo.pc_clname, class); if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) { Warn(gettext("cannot get real time class parameters")); return; } pcparms.pc_cid = pcinfo.pc_cid; ((rtparms_t *)pcparms.pc_clparms)->rt_pri = 0; ((rtparms_t *)pcparms.pc_clparms)->rt_tqsecs = 0; ((rtparms_t *)pcparms.pc_clparms)->rt_tqnsecs = RT_NOCHANGE; if (priocntl(P_PID, getpid(), PC_SETPARMS, (caddr_t)&pcparms) == -1) Warn(gettext("cannot enter the real time class")); } void getprojname(projid_t projid, char *str, size_t len, int noresolve, int trunc, size_t width) { struct project proj; size_t n; if (noresolve || getprojbyid(projid, &proj, projbuf, PROJECT_BUFSZ) == NULL) { (void) snprintf(str, len, "%-6d", (int)projid); } else { n = mbstowcs(NULL, proj.pj_name, 0); if (n == (size_t)-1) (void) snprintf(str, len, "%-28s", "ERROR"); else if (trunc && n > width) (void) snprintf(str, len, "%.*s%c", width - 1, proj.pj_name, '*'); else (void) snprintf(str, len, "%-28s", proj.pj_name); } } void getzonename(zoneid_t zoneid, char *str, size_t len, int trunc, size_t width) { char zone_name[ZONENAME_MAX]; size_t n; if (getzonenamebyid(zoneid, zone_name, sizeof (zone_name)) < 0) { (void) snprintf(str, len, "%-6d", (int)zoneid); } else { n = mbstowcs(NULL, zone_name, 0); if (n == (size_t)-1) (void) snprintf(str, len, "%-28s", "ERROR"); else if (trunc && n > width) (void) snprintf(str, len, "%.*s%c", width - 1, zone_name, '*'); else (void) snprintf(str, len, "%-28s", zone_name); } } /* * Remove all unprintable characters from process name */ static void stripfname(char *buf, size_t bufsize, const char *pname) { int bytesleft = PRFNSZ; wchar_t wchar; int length; char *cp; (void) strlcpy(buf, pname, bufsize); buf[bytesleft - 1] = '\0'; for (cp = buf; *cp != '\0'; cp += length) { length = mbtowc(&wchar, cp, MB_LEN_MAX); if (length <= 0) { *cp = '\0'; break; } if (!iswprint(wchar)) { if (bytesleft <= length) { *cp = '\0'; break; } (void) memmove(cp, cp + length, bytesleft - length); length = 0; } bytesleft -= length; } } /* * prstat has always implicitly wanted a terminal width of at least 80 columns * (when a TTY is present). If run in a terminal narrower than 80 columns, * prstat output may wrap. For wider terminals, we allow the last column to use * the additional space. * * We never truncate if using -c, or not outputting to a TTY. */ static int format_namewidth(void) { int prefixlen = 0; if (opts.o_cols == 0 || !(opts.o_outpmode & (OPT_TERMCAP | OPT_TRUNC))) return (0); if (opts.o_outpmode & OPT_PSINFO) { if (opts.o_outpmode & OPT_LGRP) prefixlen = 64; else prefixlen = 59; } else if (opts.o_outpmode & OPT_MSACCT) { prefixlen = 64; } return (opts.o_cols - prefixlen); } void format_name(lwp_info_t *lwp, char *buf, size_t buflen) { int pname_width = PRFNSZ; char nr_suffix[20]; char pname[PRFNSZ]; int width; int n; stripfname(pname, sizeof (pname), lwp->li_info.pr_fname); if (opts.o_outpmode & OPT_LWPS) { n = snprintf(nr_suffix, sizeof (nr_suffix), "%d", lwp->li_info.pr_lwp.pr_lwpid); } else { n = snprintf(nr_suffix, sizeof (nr_suffix), "%d", lwp->li_info.pr_nlwp + lwp->li_info.pr_nzomb); } width = format_namewidth(); /* If we're over budget, truncate the process name not the LWP part. */ if (strlen(pname) > (width - n - 1)) { pname_width = width - n - 1; pname[pname_width - 1] = '*'; } if ((opts.o_outpmode & OPT_LWPS) && lwp->li_lwpname[0] != '\0') { n = snprintf(buf, buflen, "%.*s/%s [%s]", pname_width, pname, nr_suffix, lwp->li_lwpname); } else { n = snprintf(buf, buflen, "%.*s/%s", pname_width, pname, nr_suffix); } if (width > 0 && strlen(buf) > width) buf[width] = '\0'; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier * Copyright 2018, Joyent, Inc. */ #ifndef _PRUTIL_H #define _PRUTIL_H #include #include #ifdef __cplusplus extern "C" { #endif extern void Die(char *, ...); extern void Warn(char *, ...); extern void Progname(char *); extern void Usage(); extern int Atoi(char *); extern void Format_size(char *, size_t, int); extern void Format_pct(char *, float, int); extern void Format_num(char *, int, int); extern void Format_time(char *, ulong_t, int); extern void Format_state(char *, char, processorid_t, int); extern void *Realloc(void *, size_t); extern void *Malloc(size_t); extern void *Zalloc(size_t); extern int Setrlimit(); extern void Priocntl(char *); extern void getprojname(projid_t, char *, size_t, int, int, size_t); extern void getzonename(projid_t, char *, size_t, int, size_t); extern void format_name(lwp_info_t *, char *, size_t); #ifdef __cplusplus } #endif #endif /* _PRUTIL_H */