8158 Want named threads API
[unleashed.git] / usr / src / cmd / prstat / prstat.c
blob0ff4f51bcdad6b84e7e20933916b2cb0cd4a8160
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2013 Gary Mills
25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
28 * Portions Copyright 2009 Chad Mynhier
29 * Copyright 2018 Joyent, Inc. All rights reserved.
32 #include <sys/types.h>
33 #include <sys/resource.h>
34 #include <sys/loadavg.h>
35 #include <sys/time.h>
36 #include <sys/pset.h>
37 #include <sys/vm_usage.h>
38 #include <zone.h>
39 #include <libzonecfg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <dirent.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <poll.h>
48 #include <ctype.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include <signal.h>
52 #include <time.h>
53 #include <project.h>
55 #include <langinfo.h>
56 #include <libintl.h>
57 #include <locale.h>
59 #include "prstat.h"
60 #include "prutil.h"
61 #include "prtable.h"
62 #include "prsort.h"
63 #include "prfile.h"
66 * x86 <sys/regs.h> ERR conflicts with <curses.h> ERR. For the purposes
67 * of this file, we care about the curses.h ERR so include that last.
70 #if defined(ERR)
71 #undef ERR
72 #endif
74 #ifndef TEXT_DOMAIN /* should be defined by cc -D */
75 #define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */
76 #endif
78 #include <curses.h>
79 #include <term.h>
81 #define LOGIN_WIDTH 8
82 #define ZONE_WIDTH 28
83 #define PROJECT_WIDTH 28
85 #define PSINFO_HEADER_PROC \
86 " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP "
87 #define PSINFO_HEADER_PROC_LGRP \
88 " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/NLWP "
89 #define PSINFO_HEADER_LWP \
90 " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/LWP "
91 #define PSINFO_HEADER_LWP_LGRP \
92 " PID USERNAME SIZE RSS STATE PRI NICE TIME CPU LGRP PROCESS/LWP "
93 #define USAGE_HEADER_PROC \
94 " PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP "
95 #define USAGE_HEADER_LWP \
96 " PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWP "
97 #define USER_HEADER_PROC \
98 " NPROC USERNAME SWAP RSS MEMORY TIME CPU "
99 #define USER_HEADER_LWP \
100 " NLWP USERNAME SWAP RSS MEMORY TIME CPU "
101 #define TASK_HEADER_PROC \
102 "TASKID NPROC SWAP RSS MEMORY TIME CPU PROJECT "
103 #define TASK_HEADER_LWP \
104 "TASKID NLWP SWAP RSS MEMORY TIME CPU PROJECT "
105 #define PROJECT_HEADER_PROC \
106 "PROJID NPROC SWAP RSS MEMORY TIME CPU PROJECT "
107 #define PROJECT_HEADER_LWP \
108 "PROJID NLWP SWAP RSS MEMORY TIME CPU PROJECT "
109 #define ZONE_HEADER_PROC \
110 "ZONEID NPROC SWAP RSS MEMORY TIME CPU ZONE "
111 #define ZONE_HEADER_LWP \
112 "ZONEID NLWP SWAP RSS MEMORY TIME CPU ZONE "
113 #define PSINFO_LINE \
114 "%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %s"
115 #define PSINFO_LINE_LGRP \
116 "%6d %-8s %5s %5s %-6s %3s %3s %9s %3.3s%% %4d %s"
117 #define USAGE_LINE \
118 "%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
119 "%3.3s %3.3s %s"
120 #define USER_LINE \
121 "%6d %-8s %5.5s %5.5s %3.3s%% %9s %3.3s%%"
122 #define TASK_LINE \
123 "%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
124 #define PROJECT_LINE \
125 "%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
126 #define ZONE_LINE \
127 "%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
129 #define TOTAL_LINE \
130 "Total: %d processes, %d lwps, load averages: %3.2f, %3.2f, %3.2f"
132 /* global variables */
134 static char *t_ulon; /* termcap: start underline */
135 static char *t_uloff; /* termcap: end underline */
136 static char *t_up; /* termcap: cursor 1 line up */
137 static char *t_eol; /* termcap: clear end of line */
138 static char *t_smcup; /* termcap: cursor mvcap on */
139 static char *t_rmcup; /* termcap: cursor mvcap off */
140 static char *t_home; /* termcap: move cursor home */
141 static char *movecur = NULL; /* termcap: move up string */
142 static char *empty_string = "\0"; /* termcap: empty string */
143 static uint_t print_movecur = FALSE; /* print movecur or not */
144 static int is_curses_on = FALSE; /* current curses state */
146 static table_t pid_tbl = {0, 0, NULL}; /* selected processes */
147 static table_t cpu_tbl = {0, 0, NULL}; /* selected processors */
148 static table_t set_tbl = {0, 0, NULL}; /* selected processor sets */
149 static table_t prj_tbl = {0, 0, NULL}; /* selected projects */
150 static table_t tsk_tbl = {0, 0, NULL}; /* selected tasks */
151 static table_t lgr_tbl = {0, 0, NULL}; /* selected lgroups */
152 static zonetbl_t zone_tbl = {0, 0, NULL}; /* selected zones */
153 static uidtbl_t euid_tbl = {0, 0, NULL}; /* selected effective users */
154 static uidtbl_t ruid_tbl = {0, 0, NULL}; /* selected real users */
156 static uint_t total_procs; /* total number of procs */
157 static uint_t total_lwps; /* total number of lwps */
158 static float total_cpu; /* total cpu usage */
159 static float total_mem; /* total memory usage */
161 static list_t lwps; /* list of lwps/processes */
162 static list_t users; /* list of users */
163 static list_t tasks; /* list of tasks */
164 static list_t projects; /* list of projects */
165 static list_t zones; /* list of zones */
166 static list_t lgroups; /* list of lgroups */
168 static volatile uint_t sigwinch = 0;
169 static volatile uint_t sigtstp = 0;
170 static volatile uint_t sigterm = 0;
172 static long pagesize;
174 /* default settings */
176 optdesc_t opts = {
177 5, /* interval between updates, seconds */
178 15, /* number of lines in top part */
179 5, /* number of lines in bottom part */
180 -1, /* number of iterations; infinitely */
181 OPT_PSINFO | OPT_FULLSCREEN | OPT_USEHOME | OPT_TERMCAP,
182 -1 /* sort in decreasing order */
186 * Print timestamp as decimal reprentation of time_t value (-d u was specified)
187 * or the standard date format (-d d was specified).
189 static void
190 print_timestamp(void)
192 time_t t = time(NULL);
193 static char *fmt = NULL;
195 /* We only need to retrieve this once per invocation */
196 if (fmt == NULL)
197 fmt = nl_langinfo(_DATE_FMT);
199 if (opts.o_outpmode & OPT_UDATE) {
200 (void) printf("%ld", t);
201 } else if (opts.o_outpmode & OPT_DDATE) {
202 char dstr[64];
203 int len;
205 len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
206 if (len > 0)
207 (void) printf("%s", dstr);
209 (void) putp(t_eol);
210 (void) putchar('\n');
213 static void
214 psetloadavg(long psetid, void *ptr)
216 double psetloadavg[3];
217 double *loadavg = ptr;
219 if (pset_getloadavg((psetid_t)psetid, psetloadavg, 3) != -1) {
220 *loadavg++ += psetloadavg[0];
221 *loadavg++ += psetloadavg[1];
222 *loadavg += psetloadavg[2];
227 * Queries the memory virtual and rss size for each member of a list.
228 * This will override the values computed by /proc aggregation.
230 static void
231 list_getsize(list_t *list)
233 id_info_t *id;
234 vmusage_t *results, *next;
235 vmusage_t *match;
236 size_t nres = 0;
237 size_t i;
238 uint_t flags = 0;
239 int ret;
240 size_t physmem = sysconf(_SC_PHYS_PAGES) * pagesize;
243 * Determine what swap/rss results to calculate. getvmusage() will
244 * prune results returned to non-global zones automatically, so
245 * there is no need to pass different flags when calling from a
246 * non-global zone.
248 * Currently list_getsize() is only called with a single flag. This
249 * is because -Z, -J, -T, and -a are mutually exclusive. Regardless
250 * of this, we handle multiple flags.
252 if (opts.o_outpmode & OPT_USERS) {
254 * Gather rss for all users in all zones. Treat the same
255 * uid in different zones as the same user.
257 flags |= VMUSAGE_COL_RUSERS;
259 } else if (opts.o_outpmode & OPT_TASKS) {
260 /* Gather rss for all tasks in all zones */
261 flags |= VMUSAGE_ALL_TASKS;
263 } else if (opts.o_outpmode & OPT_PROJECTS) {
265 * Gather rss for all projects in all zones. Treat the same
266 * projid in diffrent zones as the same project.
268 flags |= VMUSAGE_COL_PROJECTS;
270 } else if (opts.o_outpmode & OPT_ZONES) {
271 /* Gather rss for all zones */
272 flags |= VMUSAGE_ALL_ZONES;
274 } else {
275 Die(gettext(
276 "Cannot determine rss flags for output options %x\n"),
277 opts.o_outpmode);
281 * getvmusage() returns an array of result structures. One for
282 * each zone, project, task, or user on the system, depending on
283 * flags.
285 * If getvmusage() fails, prstat will use the size already gathered
286 * from psinfo
288 if (getvmusage(flags, opts.o_interval, NULL, &nres) != 0)
289 return;
291 results = (vmusage_t *)Malloc(sizeof (vmusage_t) * nres);
292 for (;;) {
293 ret = getvmusage(flags, opts.o_interval, results, &nres);
294 if (ret == 0)
295 break;
296 if (errno == EOVERFLOW) {
297 results = (vmusage_t *)Realloc(results,
298 sizeof (vmusage_t) * nres);
299 continue;
302 * Failure for some other reason. Prstat will use the size
303 * already gathered from psinfo.
305 free(results);
306 return;
308 for (id = list->l_head; id != NULL; id = id->id_next) {
310 match = NULL;
311 next = results;
312 for (i = 0; i < nres; i++, next++) {
313 switch (flags) {
314 case VMUSAGE_COL_RUSERS:
315 if (next->vmu_id == id->id_uid)
316 match = next;
317 break;
318 case VMUSAGE_ALL_TASKS:
319 if (next->vmu_id == id->id_taskid)
320 match = next;
321 break;
322 case VMUSAGE_COL_PROJECTS:
323 if (next->vmu_id == id->id_projid)
324 match = next;
325 break;
326 case VMUSAGE_ALL_ZONES:
327 if (next->vmu_id == id->id_zoneid)
328 match = next;
329 break;
330 default:
331 Die(gettext(
332 "Unknown vmusage flags %d\n"), flags);
335 if (match != NULL) {
336 id->id_size = match->vmu_swap_all / 1024;
337 id->id_rssize = match->vmu_rss_all / 1024;
338 id->id_pctmem = (100.0 * (float)match->vmu_rss_all) /
339 (float)physmem;
340 /* Output using data from getvmusage() */
341 id->id_sizematch = B_TRUE;
344 * If no match is found, prstat will use the size already
345 * gathered from psinfo.
348 free(results);
352 * A routine to display the contents of the list on the screen
354 static void
355 list_print(list_t *list)
357 lwp_info_t *lwp;
358 id_info_t *id;
359 char usr[4], sys[4], trp[4], tfl[4];
360 char dfl[4], lck[4], slp[4], lat[4];
361 char vcx[4], icx[4], scl[4], sig[4];
362 char psize[6], prssize[6], pmem[6], pcpu[6], ptime[12];
363 char pstate[7], pnice[4], ppri[4];
364 char pname[LOGNAME_MAX+1];
365 char name[PRFNSZ + THREAD_NAME_MAX + 2];
366 char projname[PROJNAME_MAX+1];
367 char zonename[ZONENAME_MAX+1];
368 float cpu, mem;
369 double loadavg[3] = {0, 0, 0};
370 int i, n;
372 if (list->l_size == 0)
373 return;
375 if (foreach_element(&set_tbl, &loadavg, psetloadavg) == 0) {
377 * If processor sets aren't specified, we display system-wide
378 * load averages.
380 (void) getloadavg(loadavg, 3);
383 if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)) &&
384 ((list->l_type == LT_LWPS) || !(opts.o_outpmode & OPT_SPLIT)))
385 print_timestamp();
386 if (opts.o_outpmode & OPT_TTY)
387 (void) putchar('\r');
388 (void) putp(t_ulon);
390 n = opts.o_cols;
391 switch (list->l_type) {
392 case LT_PROJECTS:
393 if (opts.o_outpmode & OPT_LWPS)
394 n = printf(PROJECT_HEADER_LWP);
395 else
396 n = printf(PROJECT_HEADER_PROC);
397 break;
398 case LT_TASKS:
399 if (opts.o_outpmode & OPT_LWPS)
400 n = printf(TASK_HEADER_LWP);
401 else
402 n = printf(TASK_HEADER_PROC);
403 break;
404 case LT_ZONES:
405 if (opts.o_outpmode & OPT_LWPS)
406 n = printf(ZONE_HEADER_LWP);
407 else
408 n = printf(ZONE_HEADER_PROC);
409 break;
410 case LT_USERS:
411 if (opts.o_outpmode & OPT_LWPS)
412 n = printf(USER_HEADER_LWP);
413 else
414 n = printf(USER_HEADER_PROC);
415 break;
416 case LT_LWPS:
417 if (opts.o_outpmode & OPT_LWPS) {
418 if (opts.o_outpmode & OPT_PSINFO) {
419 if (opts.o_outpmode & OPT_LGRP)
420 n = printf(PSINFO_HEADER_LWP_LGRP);
421 else
422 n = printf(PSINFO_HEADER_LWP);
424 if (opts.o_outpmode & OPT_MSACCT)
425 n = printf(USAGE_HEADER_LWP);
426 } else {
427 if (opts.o_outpmode & OPT_PSINFO) {
428 if (opts.o_outpmode & OPT_LGRP)
429 n = printf(PSINFO_HEADER_PROC_LGRP);
430 else
431 n = printf(PSINFO_HEADER_PROC);
433 if (opts.o_outpmode & OPT_MSACCT)
434 n = printf(USAGE_HEADER_PROC);
436 break;
439 /* Pad out the header line so the underline spans the whole width */
440 if ((opts.o_outpmode & OPT_TERMCAP) && n < opts.o_cols)
441 (void) printf("%*s", (int)(opts.o_cols - n), "");
443 (void) putp(t_uloff);
444 (void) putp(t_eol);
445 (void) putchar('\n');
447 for (i = 0; i < list->l_used; i++) {
448 switch (list->l_type) {
449 case LT_PROJECTS:
450 case LT_TASKS:
451 case LT_USERS:
452 case LT_ZONES:
453 id = list->l_ptrs[i];
455 * CPU usage and memory usage normalization
457 if (total_cpu >= 100)
458 cpu = (100 * id->id_pctcpu) / total_cpu;
459 else
460 cpu = id->id_pctcpu;
461 if (id->id_sizematch == B_FALSE && total_mem >= 100)
462 mem = (100 * id->id_pctmem) / total_mem;
463 else
464 mem = id->id_pctmem;
465 if (list->l_type == LT_USERS) {
466 pwd_getname(id->id_uid, pname, sizeof (pname),
467 opts.o_outpmode & OPT_NORESOLVE,
468 opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC),
469 LOGIN_WIDTH);
470 } else if (list->l_type == LT_ZONES) {
471 getzonename(id->id_zoneid, zonename,
472 sizeof (zonename),
473 opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC),
474 ZONE_WIDTH);
475 } else {
476 getprojname(id->id_projid, projname,
477 sizeof (projname),
478 opts.o_outpmode & OPT_NORESOLVE,
479 opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC),
480 PROJECT_WIDTH);
482 Format_size(psize, id->id_size, 6);
483 Format_size(prssize, id->id_rssize, 6);
484 Format_pct(pmem, mem, 4);
485 Format_pct(pcpu, cpu, 4);
486 Format_time(ptime, id->id_time, 10);
487 if (opts.o_outpmode & OPT_TTY)
488 (void) putchar('\r');
489 if (list->l_type == LT_PROJECTS)
490 (void) printf(PROJECT_LINE, (int)id->id_projid,
491 id->id_nproc, psize, prssize, pmem, ptime,
492 pcpu, projname);
493 else if (list->l_type == LT_TASKS)
494 (void) printf(TASK_LINE, (int)id->id_taskid,
495 id->id_nproc, psize, prssize, pmem, ptime,
496 pcpu, projname);
497 else if (list->l_type == LT_ZONES)
498 (void) printf(ZONE_LINE, (int)id->id_zoneid,
499 id->id_nproc, psize, prssize, pmem, ptime,
500 pcpu, zonename);
501 else
502 (void) printf(USER_LINE, id->id_nproc, pname,
503 psize, prssize, pmem, ptime, pcpu);
504 (void) putp(t_eol);
505 (void) putchar('\n');
506 break;
507 case LT_LWPS:
508 lwp = list->l_ptrs[i];
510 format_name(lwp, name, sizeof (name));
512 pwd_getname(lwp->li_info.pr_uid, pname, sizeof (pname),
513 opts.o_outpmode & OPT_NORESOLVE,
514 opts.o_outpmode & (OPT_TERMCAP|OPT_TRUNC),
515 LOGIN_WIDTH);
517 if (opts.o_outpmode & OPT_PSINFO) {
518 Format_size(psize, lwp->li_info.pr_size, 6);
519 Format_size(prssize, lwp->li_info.pr_rssize, 6);
520 Format_state(pstate,
521 lwp->li_info.pr_lwp.pr_sname,
522 lwp->li_info.pr_lwp.pr_onpro, 7);
523 if (strcmp(lwp->li_info.pr_lwp.pr_clname,
524 "RT") == 0 ||
525 strcmp(lwp->li_info.pr_lwp.pr_clname,
526 "SYS") == 0 ||
527 lwp->li_info.pr_lwp.pr_sname == 'Z')
528 (void) strcpy(pnice, " -");
529 else
530 Format_num(pnice,
531 lwp->li_info.pr_lwp.pr_nice - NZERO,
533 Format_num(ppri, lwp->li_info.pr_lwp.pr_pri, 4);
534 Format_pct(pcpu,
535 FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu), 4);
536 if (opts.o_outpmode & OPT_LWPS)
537 Format_time(ptime,
538 lwp->li_info.pr_lwp.pr_time.tv_sec,
539 10);
540 else
541 Format_time(ptime,
542 lwp->li_info.pr_time.tv_sec, 10);
543 if (opts.o_outpmode & OPT_TTY)
544 (void) putchar('\r');
545 if (opts.o_outpmode & OPT_LGRP) {
546 (void) printf(PSINFO_LINE_LGRP,
547 (int)lwp->li_info.pr_pid, pname,
548 psize, prssize, pstate,
549 ppri, pnice, ptime, pcpu,
550 lwp->li_info.pr_lwp.pr_lgrp, name);
551 } else {
552 (void) printf(PSINFO_LINE,
553 (int)lwp->li_info.pr_pid, pname,
554 psize, prssize, pstate, ppri, pnice,
555 ptime, pcpu, name);
557 (void) putp(t_eol);
558 (void) putchar('\n');
560 if (opts.o_outpmode & OPT_MSACCT) {
561 Format_pct(usr, lwp->li_usr, 4);
562 Format_pct(sys, lwp->li_sys, 4);
563 Format_pct(slp, lwp->li_slp, 4);
564 Format_num(vcx, lwp->li_vcx, 4);
565 Format_num(icx, lwp->li_icx, 4);
566 Format_num(scl, lwp->li_scl, 4);
567 Format_num(sig, lwp->li_sig, 4);
568 Format_pct(trp, lwp->li_trp, 4);
569 Format_pct(tfl, lwp->li_tfl, 4);
570 Format_pct(dfl, lwp->li_dfl, 4);
571 Format_pct(lck, lwp->li_lck, 4);
572 Format_pct(lat, lwp->li_lat, 4);
573 if (opts.o_outpmode & OPT_TTY)
574 (void) putchar('\r');
575 (void) printf(USAGE_LINE,
576 (int)lwp->li_info.pr_pid, pname,
577 usr, sys, trp, tfl, dfl, lck,
578 slp, lat, vcx, icx, scl, sig,
579 name);
580 (void) putp(t_eol);
581 (void) putchar('\n');
583 break;
587 if (opts.o_outpmode & OPT_TTY)
588 (void) putchar('\r');
589 if (opts.o_outpmode & OPT_TERMCAP) {
590 switch (list->l_type) {
591 case LT_PROJECTS:
592 case LT_USERS:
593 case LT_TASKS:
594 case LT_ZONES:
595 while (i++ < opts.o_nbottom) {
596 (void) putp(t_eol);
597 (void) putchar('\n');
599 break;
600 case LT_LWPS:
601 while (i++ < opts.o_ntop) {
602 (void) putp(t_eol);
603 (void) putchar('\n');
608 if (opts.o_outpmode & OPT_TTY)
609 (void) putchar('\r');
611 if ((opts.o_outpmode & OPT_SPLIT) && list->l_type == LT_LWPS)
612 return;
614 (void) printf(TOTAL_LINE, total_procs, total_lwps,
615 loadavg[LOADAVG_1MIN], loadavg[LOADAVG_5MIN],
616 loadavg[LOADAVG_15MIN]);
617 (void) putp(t_eol);
618 (void) putchar('\n');
619 if (opts.o_outpmode & OPT_TTY)
620 (void) putchar('\r');
621 (void) putp(t_eol);
622 (void) fflush(stdout);
625 static lwp_info_t *
626 list_add_lwp(list_t *list, pid_t pid, id_t lwpid)
628 lwp_info_t *lwp;
630 if (list->l_head == NULL) {
631 list->l_head = list->l_tail = lwp = Zalloc(sizeof (lwp_info_t));
632 } else {
633 lwp = Zalloc(sizeof (lwp_info_t));
634 lwp->li_prev = list->l_tail;
635 ((lwp_info_t *)list->l_tail)->li_next = lwp;
636 list->l_tail = lwp;
638 lwp->li_info.pr_pid = pid;
639 lwp->li_info.pr_lwp.pr_lwpid = lwpid;
640 lwpid_add(lwp, pid, lwpid);
641 list->l_count++;
642 return (lwp);
645 static void
646 list_remove_lwp(list_t *list, lwp_info_t *lwp)
648 if (lwp->li_prev)
649 lwp->li_prev->li_next = lwp->li_next;
650 else
651 list->l_head = lwp->li_next; /* removing the head */
652 if (lwp->li_next)
653 lwp->li_next->li_prev = lwp->li_prev;
654 else
655 list->l_tail = lwp->li_prev; /* removing the tail */
656 lwpid_del(lwp->li_info.pr_pid, lwp->li_info.pr_lwp.pr_lwpid);
657 if (lwpid_pidcheck(lwp->li_info.pr_pid) == 0)
658 fds_rm(lwp->li_info.pr_pid);
659 list->l_count--;
660 free(lwp);
663 static void
664 list_clear(list_t *list)
666 if (list->l_type == LT_LWPS) {
667 lwp_info_t *lwp = list->l_tail;
668 lwp_info_t *lwp_tmp;
670 fd_closeall();
671 while (lwp) {
672 lwp_tmp = lwp;
673 lwp = lwp->li_prev;
674 list_remove_lwp(&lwps, lwp_tmp);
676 } else {
677 id_info_t *id = list->l_head;
678 id_info_t *nextid;
680 while (id) {
681 nextid = id->id_next;
682 free(id);
683 id = nextid;
685 list->l_count = 0;
686 list->l_head = list->l_tail = NULL;
690 static void
691 list_update(list_t *list, lwp_info_t *lwp)
693 id_info_t *id;
695 if (list->l_head == NULL) { /* first element */
696 list->l_head = list->l_tail = id = Zalloc(sizeof (id_info_t));
697 goto update;
700 for (id = list->l_head; id; id = id->id_next) {
701 if ((list->l_type == LT_USERS) &&
702 (id->id_uid != lwp->li_info.pr_uid))
703 continue;
704 if ((list->l_type == LT_TASKS) &&
705 (id->id_taskid != lwp->li_info.pr_taskid))
706 continue;
707 if ((list->l_type == LT_PROJECTS) &&
708 (id->id_projid != lwp->li_info.pr_projid))
709 continue;
710 if ((list->l_type == LT_ZONES) &&
711 (id->id_zoneid != lwp->li_info.pr_zoneid))
712 continue;
713 if ((list->l_type == LT_LGRPS) &&
714 (id->id_lgroup != lwp->li_info.pr_lwp.pr_lgrp))
715 continue;
716 id->id_nproc++;
717 id->id_taskid = lwp->li_info.pr_taskid;
718 id->id_projid = lwp->li_info.pr_projid;
719 id->id_zoneid = lwp->li_info.pr_zoneid;
720 id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp;
722 if (lwp->li_flags & LWP_REPRESENT) {
723 id->id_size += lwp->li_info.pr_size;
724 id->id_rssize += lwp->li_info.pr_rssize;
726 id->id_pctcpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
727 if (opts.o_outpmode & OPT_LWPS)
728 id->id_time += TIME2SEC(lwp->li_info.pr_lwp.pr_time);
729 else
730 id->id_time += TIME2SEC(lwp->li_info.pr_time);
731 id->id_pctmem += FRC2PCT(lwp->li_info.pr_pctmem);
732 id->id_key += lwp->li_key;
733 total_cpu += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
734 total_mem += FRC2PCT(lwp->li_info.pr_pctmem);
735 return;
738 id = list->l_tail;
739 id->id_next = Zalloc(sizeof (id_info_t));
740 id->id_next->id_prev = list->l_tail;
741 id->id_next->id_next = NULL;
742 list->l_tail = id->id_next;
743 id = list->l_tail;
744 update:
745 id->id_uid = lwp->li_info.pr_uid;
746 id->id_projid = lwp->li_info.pr_projid;
747 id->id_taskid = lwp->li_info.pr_taskid;
748 id->id_zoneid = lwp->li_info.pr_zoneid;
749 id->id_lgroup = lwp->li_info.pr_lwp.pr_lgrp;
750 id->id_nproc++;
751 id->id_sizematch = B_FALSE;
752 if (lwp->li_flags & LWP_REPRESENT) {
753 id->id_size = lwp->li_info.pr_size;
754 id->id_rssize = lwp->li_info.pr_rssize;
756 id->id_pctcpu = FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
757 if (opts.o_outpmode & OPT_LWPS)
758 id->id_time = TIME2SEC(lwp->li_info.pr_lwp.pr_time);
759 else
760 id->id_time = TIME2SEC(lwp->li_info.pr_time);
761 id->id_pctmem = FRC2PCT(lwp->li_info.pr_pctmem);
762 id->id_key = lwp->li_key;
763 total_cpu += id->id_pctcpu;
764 total_mem += id->id_pctmem;
765 list->l_count++;
768 static void
769 lwp_update(lwp_info_t *lwp, pid_t pid, id_t lwpid, struct prusage *usage)
771 float period;
773 if (!lwpid_is_active(pid, lwpid)) {
775 * If we are reading cpu times for the first time then
776 * calculate average cpu times based on whole process
777 * execution time.
779 (void) memcpy(&lwp->li_usage, usage, sizeof (prusage_t));
780 period = TIME2NSEC(usage->pr_rtime);
781 period = period/(float)100;
783 if (period == 0) { /* zombie */
784 period = 1;
785 lwp->li_usr = 0;
786 lwp->li_sys = 0;
787 lwp->li_slp = 0;
788 } else {
789 lwp->li_usr = TIME2NSEC(usage->pr_utime)/period;
790 lwp->li_sys = TIME2NSEC(usage->pr_stime)/period;
791 lwp->li_slp = TIME2NSEC(usage->pr_slptime)/period;
793 lwp->li_trp = TIME2NSEC(usage->pr_ttime)/period;
794 lwp->li_tfl = TIME2NSEC(usage->pr_tftime)/period;
795 lwp->li_dfl = TIME2NSEC(usage->pr_dftime)/period;
796 lwp->li_lck = TIME2NSEC(usage->pr_ltime)/period;
797 lwp->li_lat = TIME2NSEC(usage->pr_wtime)/period;
798 period = (period / NANOSEC)*(float)100; /* now in seconds */
799 lwp->li_vcx = (ulong_t)
800 (opts.o_interval * (usage->pr_vctx/period));
801 lwp->li_icx = (ulong_t)
802 (opts.o_interval * (usage->pr_ictx/period));
803 lwp->li_scl = (ulong_t)
804 (opts.o_interval * (usage->pr_sysc/period));
805 lwp->li_sig = (ulong_t)
806 (opts.o_interval * (usage->pr_sigs/period));
807 (void) lwpid_set_active(pid, lwpid);
808 } else {
810 * If this is not a first time we are reading a process's
811 * CPU times then recalculate CPU times based on fresh data
812 * obtained from procfs and previous CPU time usage values.
814 period = TIME2NSEC(usage->pr_rtime)-
815 TIME2NSEC(lwp->li_usage.pr_rtime);
816 period = period/(float)100;
818 if (period == 0) { /* zombie */
819 period = 1;
820 lwp->li_usr = 0;
821 lwp->li_sys = 0;
822 lwp->li_slp = 0;
823 } else {
824 lwp->li_usr = (TIME2NSEC(usage->pr_utime)-
825 TIME2NSEC(lwp->li_usage.pr_utime))/period;
826 lwp->li_sys = (TIME2NSEC(usage->pr_stime) -
827 TIME2NSEC(lwp->li_usage.pr_stime))/period;
828 lwp->li_slp = (TIME2NSEC(usage->pr_slptime) -
829 TIME2NSEC(lwp->li_usage.pr_slptime))/period;
831 lwp->li_trp = (TIME2NSEC(usage->pr_ttime) -
832 TIME2NSEC(lwp->li_usage.pr_ttime))/period;
833 lwp->li_tfl = (TIME2NSEC(usage->pr_tftime) -
834 TIME2NSEC(lwp->li_usage.pr_tftime))/period;
835 lwp->li_dfl = (TIME2NSEC(usage->pr_dftime) -
836 TIME2NSEC(lwp->li_usage.pr_dftime))/period;
837 lwp->li_lck = (TIME2NSEC(usage->pr_ltime) -
838 TIME2NSEC(lwp->li_usage.pr_ltime))/period;
839 lwp->li_lat = (TIME2NSEC(usage->pr_wtime) -
840 TIME2NSEC(lwp->li_usage.pr_wtime))/period;
841 lwp->li_vcx = usage->pr_vctx - lwp->li_usage.pr_vctx;
842 lwp->li_icx = usage->pr_ictx - lwp->li_usage.pr_ictx;
843 lwp->li_scl = usage->pr_sysc - lwp->li_usage.pr_sysc;
844 lwp->li_sig = usage->pr_sigs - lwp->li_usage.pr_sigs;
845 (void) memcpy(&lwp->li_usage, usage, sizeof (prusage_t));
849 static int
850 read_procfile(fd_t **fd, char *pidstr, char *file, void *buf, size_t bufsize)
852 char procfile[MAX_PROCFS_PATH];
854 (void) snprintf(procfile, MAX_PROCFS_PATH,
855 "/proc/%s/%s", pidstr, file);
856 if ((*fd = fd_open(procfile, O_RDONLY, *fd)) == NULL)
857 return (1);
858 if (pread(fd_getfd(*fd), buf, bufsize, 0) != bufsize) {
859 fd_close(*fd);
860 return (1);
862 return (0);
865 static void
866 add_proc(psinfo_t *psinfo)
868 lwp_info_t *lwp;
869 id_t lwpid;
870 pid_t pid = psinfo->pr_pid;
872 lwpid = psinfo->pr_lwp.pr_lwpid;
873 if ((lwp = lwpid_get(pid, lwpid)) == NULL)
874 lwp = list_add_lwp(&lwps, pid, lwpid);
875 lwp->li_flags |= LWP_ALIVE | LWP_REPRESENT;
876 (void) memcpy(&lwp->li_info, psinfo, sizeof (psinfo_t));
877 lwp->li_info.pr_lwp.pr_pctcpu = lwp->li_info.pr_pctcpu;
880 static void
881 get_lwpname(pid_t pid, id_t lwpid, char *buf, size_t bufsize)
883 char *path = NULL;
884 int fd;
886 buf[0] = '\0';
888 if (asprintf(&path, "/proc/%d/lwp/%d/lwpname",
889 (int)pid, (int)lwpid) == -1)
890 return;
892 if ((fd = open(path, O_RDONLY)) != -1) {
893 (void) read(fd, buf, bufsize);
894 buf[bufsize - 1] = '\0';
895 (void) close(fd);
898 free(path);
901 static void
902 add_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, int flags)
904 lwp_info_t *lwp;
905 pid_t pid = psinfo->pr_pid;
906 id_t lwpid = lwpsinfo->pr_lwpid;
908 if ((lwp = lwpid_get(pid, lwpid)) == NULL)
909 lwp = list_add_lwp(&lwps, pid, lwpid);
910 lwp->li_flags &= ~LWP_REPRESENT;
911 lwp->li_flags |= LWP_ALIVE;
912 lwp->li_flags |= flags;
913 (void) memcpy(&lwp->li_info, psinfo,
914 sizeof (psinfo_t) - sizeof (lwpsinfo_t));
915 (void) memcpy(&lwp->li_info.pr_lwp, lwpsinfo, sizeof (lwpsinfo_t));
916 get_lwpname(pid, lwpid, lwp->li_lwpname, sizeof (lwp->li_lwpname));
919 static void
920 prstat_scandir(DIR *procdir)
922 char *pidstr;
923 pid_t pid;
924 id_t lwpid;
925 size_t entsz;
926 long nlwps, nent, i;
927 char *buf, *ptr;
929 fds_t *fds;
930 lwp_info_t *lwp;
931 dirent_t *direntp;
933 prheader_t header;
934 psinfo_t psinfo;
935 prusage_t usage;
936 lwpsinfo_t *lwpsinfo;
937 prusage_t *lwpusage;
939 total_procs = 0;
940 total_lwps = 0;
941 total_cpu = 0;
942 total_mem = 0;
944 convert_zone(&zone_tbl);
945 for (rewinddir(procdir); (direntp = readdir(procdir)); ) {
946 pidstr = direntp->d_name;
947 if (pidstr[0] == '.') /* skip "." and ".." */
948 continue;
949 pid = atoi(pidstr);
950 if (pid == 0 || pid == 2 || pid == 3)
951 continue; /* skip sched, pageout and fsflush */
952 if (has_element(&pid_tbl, pid) == 0)
953 continue; /* check if we really want this pid */
954 fds = fds_get(pid); /* get ptr to file descriptors */
956 if (read_procfile(&fds->fds_psinfo, pidstr,
957 "psinfo", &psinfo, sizeof (psinfo_t)) != 0)
958 continue;
959 if (!has_uid(&ruid_tbl, psinfo.pr_uid) ||
960 !has_uid(&euid_tbl, psinfo.pr_euid) ||
961 !has_element(&prj_tbl, psinfo.pr_projid) ||
962 !has_element(&tsk_tbl, psinfo.pr_taskid) ||
963 !has_zone(&zone_tbl, psinfo.pr_zoneid)) {
964 fd_close(fds->fds_psinfo);
965 continue;
967 nlwps = psinfo.pr_nlwp + psinfo.pr_nzomb;
969 if (nlwps > 1 && (opts.o_outpmode & (OPT_LWPS | OPT_PSETS))) {
970 int rep_lwp = 0;
972 if (read_procfile(&fds->fds_lpsinfo, pidstr, "lpsinfo",
973 &header, sizeof (prheader_t)) != 0) {
974 fd_close(fds->fds_psinfo);
975 continue;
978 nent = header.pr_nent;
979 entsz = header.pr_entsize * nent;
980 ptr = buf = Malloc(entsz);
981 if (pread(fd_getfd(fds->fds_lpsinfo), buf,
982 entsz, sizeof (struct prheader)) != entsz) {
983 fd_close(fds->fds_lpsinfo);
984 fd_close(fds->fds_psinfo);
985 free(buf);
986 continue;
989 nlwps = 0;
990 for (i = 0; i < nent; i++, ptr += header.pr_entsize) {
991 /*LINTED ALIGNMENT*/
992 lwpsinfo = (lwpsinfo_t *)ptr;
993 if (!has_element(&cpu_tbl,
994 lwpsinfo->pr_onpro) ||
995 !has_element(&set_tbl,
996 lwpsinfo->pr_bindpset) ||
997 !has_element(&lgr_tbl, lwpsinfo->pr_lgrp))
998 continue;
999 nlwps++;
1000 if ((opts.o_outpmode & (OPT_PSETS | OPT_LWPS))
1001 == OPT_PSETS) {
1003 * If one of process's LWPs is bound
1004 * to a given processor set, report the
1005 * whole process. We may be doing this
1006 * a few times but we'll get an accurate
1007 * lwp count in return.
1009 add_proc(&psinfo);
1010 } else {
1011 if (rep_lwp == 0) {
1012 rep_lwp = 1;
1013 add_lwp(&psinfo, lwpsinfo,
1014 LWP_REPRESENT);
1015 } else {
1016 add_lwp(&psinfo, lwpsinfo, 0);
1020 free(buf);
1021 if (nlwps == 0) {
1022 fd_close(fds->fds_lpsinfo);
1023 fd_close(fds->fds_psinfo);
1024 continue;
1026 } else {
1027 if (!has_element(&cpu_tbl, psinfo.pr_lwp.pr_onpro) ||
1028 !has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset) ||
1029 !has_element(&lgr_tbl, psinfo.pr_lwp.pr_lgrp)) {
1030 fd_close(fds->fds_psinfo);
1031 continue;
1033 add_proc(&psinfo);
1035 if (!(opts.o_outpmode & OPT_MSACCT)) {
1036 total_procs++;
1037 total_lwps += nlwps;
1038 continue;
1041 * Get more information about processes from /proc/pid/usage.
1042 * If process has more than one lwp, then we may have to
1043 * also look at the /proc/pid/lusage file.
1045 if ((opts.o_outpmode & OPT_LWPS) && (nlwps > 1)) {
1046 if (read_procfile(&fds->fds_lusage, pidstr, "lusage",
1047 &header, sizeof (prheader_t)) != 0) {
1048 fd_close(fds->fds_lpsinfo);
1049 fd_close(fds->fds_psinfo);
1050 continue;
1052 nent = header.pr_nent;
1053 entsz = header.pr_entsize * nent;
1054 buf = Malloc(entsz);
1055 if (pread(fd_getfd(fds->fds_lusage), buf,
1056 entsz, sizeof (struct prheader)) != entsz) {
1057 fd_close(fds->fds_lusage);
1058 fd_close(fds->fds_lpsinfo);
1059 fd_close(fds->fds_psinfo);
1060 free(buf);
1061 continue;
1063 for (i = 1, ptr = buf + header.pr_entsize; i < nent;
1064 i++, ptr += header.pr_entsize) {
1065 /*LINTED ALIGNMENT*/
1066 lwpusage = (prusage_t *)ptr;
1067 lwpid = lwpusage->pr_lwpid;
1069 * New LWPs created after we read lpsinfo
1070 * will be ignored. Don't want to do
1071 * everything all over again.
1073 if ((lwp = lwpid_get(pid, lwpid)) == NULL)
1074 continue;
1075 lwp_update(lwp, pid, lwpid, lwpusage);
1077 free(buf);
1078 } else {
1079 if (read_procfile(&fds->fds_usage, pidstr, "usage",
1080 &usage, sizeof (prusage_t)) != 0) {
1081 fd_close(fds->fds_lpsinfo);
1082 fd_close(fds->fds_psinfo);
1083 continue;
1085 lwpid = psinfo.pr_lwp.pr_lwpid;
1086 if ((lwp = lwpid_get(pid, lwpid)) == NULL)
1087 continue;
1088 lwp_update(lwp, pid, lwpid, &usage);
1090 total_procs++;
1091 total_lwps += nlwps;
1093 fd_update();
1097 * This procedure removes all dead lwps from the linked list of all lwps.
1098 * It also creates linked list of ids if necessary.
1100 static void
1101 list_refresh(list_t *list)
1103 lwp_info_t *lwp, *lwp_next;
1105 if (!(list->l_type & LT_LWPS))
1106 return;
1108 for (lwp = list->l_head; lwp != NULL; ) {
1109 if (lwp->li_flags & LWP_ALIVE) {
1111 * Process all live LWPs.
1112 * When we're done, mark them as dead.
1113 * They will be marked "alive" on the next
1114 * /proc scan if they still exist.
1116 lwp->li_key = list_getkeyval(list, lwp);
1117 if (opts.o_outpmode & OPT_USERS)
1118 list_update(&users, lwp);
1119 if (opts.o_outpmode & OPT_TASKS)
1120 list_update(&tasks, lwp);
1121 if (opts.o_outpmode & OPT_PROJECTS)
1122 list_update(&projects, lwp);
1123 if (opts.o_outpmode & OPT_ZONES)
1124 list_update(&zones, lwp);
1125 if (opts.o_outpmode & OPT_LGRP)
1126 list_update(&lgroups, lwp);
1127 lwp->li_flags &= ~LWP_ALIVE;
1128 lwp = lwp->li_next;
1130 } else {
1131 lwp_next = lwp->li_next;
1132 list_remove_lwp(&lwps, lwp);
1133 lwp = lwp_next;
1138 static void
1139 curses_on(void)
1141 if ((opts.o_outpmode & OPT_TERMCAP) && (is_curses_on == FALSE)) {
1142 (void) initscr();
1143 (void) nonl();
1144 (void) putp(t_smcup);
1145 is_curses_on = TRUE;
1149 static void
1150 curses_off(void)
1152 if ((is_curses_on == TRUE) && (opts.o_outpmode & OPT_TERMCAP)) {
1153 (void) putp(t_rmcup);
1154 (void) endwin();
1155 is_curses_on = FALSE;
1157 (void) fflush(stdout);
1160 static int
1161 nlines(int *linesp, int *colsp)
1163 struct winsize ws;
1164 char *envp;
1165 int n;
1167 *linesp = -1;
1168 *colsp = -1;
1169 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
1170 if (ws.ws_row > 0)
1171 *linesp = ws.ws_row;
1172 if (ws.ws_col > 0)
1173 *colsp = ws.ws_col;
1174 if (ws.ws_row > 0 && ws.ws_col > 0)
1175 return (0);
1178 if ((envp = getenv("LINES")) != NULL) {
1179 if ((n = Atoi(envp)) > 0) {
1180 opts.o_outpmode &= ~OPT_USEHOME;
1181 *linesp = n;
1184 if ((envp = getenv("COLUMNS")) != NULL) {
1185 if ((n = Atoi(envp)) > 0) {
1186 *colsp = n;
1190 return ((*linesp > 0 && *colsp > 0) ? 0 : -1);
1193 static void
1194 setmovecur(void)
1196 int i, n;
1197 if ((opts.o_outpmode & OPT_FULLSCREEN) &&
1198 (opts.o_outpmode & OPT_USEHOME)) {
1199 movecur = t_home;
1200 return;
1202 if (opts.o_outpmode & OPT_SPLIT) {
1203 if (opts.o_ntop == 0)
1204 n = opts.o_nbottom + 1;
1205 else
1206 n = opts.o_ntop + opts.o_nbottom + 2;
1207 } else {
1208 if (opts.o_outpmode & OPT_USERS)
1209 n = opts.o_nbottom + 1;
1210 else
1211 n = opts.o_ntop + 1;
1213 if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)))
1214 n++;
1216 if (movecur != NULL && movecur != empty_string && movecur != t_home)
1217 free(movecur);
1218 movecur = Zalloc(strlen(t_up) * (n + 5));
1219 for (i = 0; i <= n; i++)
1220 (void) strcat(movecur, t_up);
1223 static int
1224 setsize(void)
1226 static int oldn = 0;
1227 int cols, n, ret;
1229 if (opts.o_outpmode & OPT_FULLSCREEN) {
1230 ret = nlines(&n, &cols);
1231 if (ret != -1)
1232 opts.o_cols = cols;
1233 if (n == oldn)
1234 return (0);
1235 oldn = n;
1236 if (ret == -1) {
1237 opts.o_outpmode &= ~OPT_USEHOME;
1238 setmovecur(); /* set default window size */
1239 return (1);
1241 n = n - 3; /* minus header, total and cursor lines */
1242 if ((opts.o_outpmode & OPT_UDATE) ||
1243 (opts.o_outpmode & OPT_DDATE))
1244 n--; /* minus timestamp */
1245 if (n < 1)
1246 Die(gettext("window is too small (try -n)\n"));
1247 if (opts.o_outpmode & OPT_SPLIT) {
1248 if (n < 8) {
1249 Die(gettext("window is too small (try -n)\n"));
1250 } else {
1251 opts.o_ntop = (n / 4) * 3;
1252 opts.o_nbottom = n - 1 - opts.o_ntop;
1254 } else {
1255 if (opts.o_outpmode & OPT_USERS)
1256 opts.o_nbottom = n;
1257 else
1258 opts.o_ntop = n;
1261 setmovecur();
1262 return (1);
1265 static void
1266 ldtermcap()
1268 int err;
1269 if (setupterm(NULL, STDIN_FILENO, &err) == ERR) {
1270 switch (err) {
1271 case 0:
1272 Warn(gettext("failed to load terminal info, "
1273 "defaulting to -c option\n"));
1274 break;
1275 case -1:
1276 Warn(gettext("terminfo database not found, "
1277 "defaulting to -c option\n"));
1278 break;
1279 default:
1280 Warn(gettext("failed to initialize terminal, "
1281 "defaulting to -c option\n"));
1283 opts.o_outpmode &= ~OPT_TERMCAP;
1284 t_up = t_eol = t_smcup = t_rmcup = movecur = empty_string;
1285 t_ulon = t_uloff = empty_string;
1286 return;
1288 t_ulon = tigetstr("smul");
1289 t_uloff = tigetstr("rmul");
1290 t_up = tigetstr("cuu1");
1291 t_eol = tigetstr("el");
1292 t_smcup = tigetstr("smcup");
1293 t_rmcup = tigetstr("rmcup");
1294 t_home = tigetstr("home");
1295 if ((t_up == (char *)-1) || (t_eol == (char *)-1) ||
1296 (t_smcup == (char *)-1) || (t_rmcup == (char *)-1)) {
1297 opts.o_outpmode &= ~OPT_TERMCAP;
1298 t_up = t_eol = t_smcup = t_rmcup = movecur = empty_string;
1299 return;
1301 if (t_up == NULL || t_eol == NULL) {
1302 opts.o_outpmode &= ~OPT_TERMCAP;
1303 t_eol = t_up = movecur = empty_string;
1304 return;
1306 if (t_ulon == (char *)-1 || t_uloff == (char *)-1 ||
1307 t_ulon == NULL || t_uloff == NULL) {
1308 t_ulon = t_uloff = empty_string; /* can live without it */
1310 if (t_smcup == NULL || t_rmcup == NULL)
1311 t_smcup = t_rmcup = empty_string;
1312 if (t_home == (char *)-1 || t_home == NULL) {
1313 opts.o_outpmode &= ~OPT_USEHOME;
1314 t_home = empty_string;
1318 static void
1319 sig_handler(int sig)
1321 switch (sig) {
1322 case SIGTSTP: sigtstp = 1;
1323 break;
1324 case SIGWINCH: sigwinch = 1;
1325 break;
1326 case SIGINT:
1327 case SIGTERM: sigterm = 1;
1328 break;
1332 static void
1333 set_signals()
1335 (void) signal(SIGTSTP, sig_handler);
1336 (void) signal(SIGINT, sig_handler);
1337 (void) signal(SIGTERM, sig_handler);
1338 if (opts.o_outpmode & OPT_FULLSCREEN)
1339 (void) signal(SIGWINCH, sig_handler);
1342 static void
1343 fill_table(table_t *table, char *arg, char option)
1345 char *p = strtok(arg, ", ");
1347 if (p == NULL)
1348 Die(gettext("invalid argument for -%c\n"), option);
1350 add_element(table, (long)Atoi(p));
1351 while (p = strtok(NULL, ", "))
1352 add_element(table, (long)Atoi(p));
1355 static void
1356 fill_prj_table(char *arg)
1358 projid_t projid;
1359 char *p = strtok(arg, ", ");
1361 if (p == NULL)
1362 Die(gettext("invalid argument for -j\n"));
1364 if ((projid = getprojidbyname(p)) == -1)
1365 projid = Atoi(p);
1366 add_element(&prj_tbl, (long)projid);
1368 while (p = strtok(NULL, ", ")) {
1369 if ((projid = getprojidbyname(p)) == -1)
1370 projid = Atoi(p);
1371 add_element(&prj_tbl, (long)projid);
1375 static void
1376 fill_set_table(char *arg)
1378 char *p = strtok(arg, ", ");
1379 psetid_t id;
1381 if (p == NULL)
1382 Die(gettext("invalid argument for -C\n"));
1384 if ((id = Atoi(p)) == 0)
1385 id = PS_NONE;
1386 add_element(&set_tbl, id);
1387 while (p = strtok(NULL, ", ")) {
1388 if ((id = Atoi(p)) == 0)
1389 id = PS_NONE;
1390 if (!has_element(&set_tbl, id))
1391 add_element(&set_tbl, id);
1395 static void
1396 Exit()
1398 curses_off();
1399 list_clear(&lwps);
1400 list_clear(&users);
1401 list_clear(&tasks);
1402 list_clear(&projects);
1403 list_clear(&zones);
1404 fd_exit();
1409 main(int argc, char **argv)
1411 DIR *procdir;
1412 char *p;
1413 char *sortk = "cpu"; /* default sort key */
1414 int opt;
1415 int timeout;
1416 struct pollfd pollset;
1417 char key;
1419 (void) setlocale(LC_ALL, "");
1420 (void) textdomain(TEXT_DOMAIN);
1421 Progname(argv[0]);
1422 lwpid_init();
1423 fd_init(Setrlimit());
1425 pagesize = sysconf(_SC_PAGESIZE);
1427 while ((opt = getopt(argc, argv,
1428 "vcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJWz:Z")) != (int)EOF) {
1429 switch (opt) {
1430 case 'r':
1431 opts.o_outpmode |= OPT_NORESOLVE;
1432 break;
1433 case 'R':
1434 opts.o_outpmode |= OPT_REALTIME;
1435 break;
1436 case 'c':
1437 opts.o_outpmode &= ~OPT_TERMCAP;
1438 opts.o_outpmode &= ~OPT_FULLSCREEN;
1439 break;
1440 case 'd':
1441 if (optarg) {
1442 if (*optarg == 'u')
1443 opts.o_outpmode |= OPT_UDATE;
1444 else if (*optarg == 'd')
1445 opts.o_outpmode |= OPT_DDATE;
1446 else
1447 Usage();
1448 } else {
1449 Usage();
1451 break;
1452 case 'h':
1453 fill_table(&lgr_tbl, optarg, 'h');
1454 break;
1455 case 'H':
1456 opts.o_outpmode |= OPT_LGRP;
1457 break;
1458 case 'm':
1459 case 'v':
1460 opts.o_outpmode &= ~OPT_PSINFO;
1461 opts.o_outpmode |= OPT_MSACCT;
1462 break;
1463 case 't':
1464 opts.o_outpmode &= ~OPT_PSINFO;
1465 opts.o_outpmode |= OPT_USERS;
1466 break;
1467 case 'a':
1468 opts.o_outpmode |= OPT_SPLIT | OPT_USERS;
1469 break;
1470 case 'T':
1471 opts.o_outpmode |= OPT_SPLIT | OPT_TASKS;
1472 break;
1473 case 'J':
1474 opts.o_outpmode |= OPT_SPLIT | OPT_PROJECTS;
1475 break;
1476 case 'n':
1477 if ((p = strtok(optarg, ",")) == NULL)
1478 Die(gettext("invalid argument for -n\n"));
1479 opts.o_ntop = Atoi(p);
1480 if (p = strtok(NULL, ","))
1481 opts.o_nbottom = Atoi(p);
1482 else if (opts.o_ntop == 0)
1483 opts.o_nbottom = 5;
1484 opts.o_outpmode &= ~OPT_FULLSCREEN;
1485 break;
1486 case 's':
1487 opts.o_sortorder = -1;
1488 sortk = optarg;
1489 break;
1490 case 'S':
1491 opts.o_sortorder = 1;
1492 sortk = optarg;
1493 break;
1494 case 'u':
1495 if ((p = strtok(optarg, ", ")) == NULL)
1496 Die(gettext("invalid argument for -u\n"));
1497 add_uid(&euid_tbl, p);
1498 while (p = strtok(NULL, ", "))
1499 add_uid(&euid_tbl, p);
1500 break;
1501 case 'U':
1502 if ((p = strtok(optarg, ", ")) == NULL)
1503 Die(gettext("invalid argument for -U\n"));
1504 add_uid(&ruid_tbl, p);
1505 while (p = strtok(NULL, ", "))
1506 add_uid(&ruid_tbl, p);
1507 break;
1508 case 'p':
1509 fill_table(&pid_tbl, optarg, 'p');
1510 break;
1511 case 'C':
1512 fill_set_table(optarg);
1513 opts.o_outpmode |= OPT_PSETS;
1514 break;
1515 case 'P':
1516 fill_table(&cpu_tbl, optarg, 'P');
1517 break;
1518 case 'k':
1519 fill_table(&tsk_tbl, optarg, 'k');
1520 break;
1521 case 'j':
1522 fill_prj_table(optarg);
1523 break;
1524 case 'L':
1525 opts.o_outpmode |= OPT_LWPS;
1526 break;
1527 case 'W':
1528 opts.o_outpmode |= OPT_TRUNC;
1529 break;
1530 case 'z':
1531 if ((p = strtok(optarg, ", ")) == NULL)
1532 Die(gettext("invalid argument for -z\n"));
1533 add_zone(&zone_tbl, p);
1534 while (p = strtok(NULL, ", "))
1535 add_zone(&zone_tbl, p);
1536 break;
1537 case 'Z':
1538 opts.o_outpmode |= OPT_SPLIT | OPT_ZONES;
1539 break;
1540 default:
1541 Usage();
1545 (void) atexit(Exit);
1546 if ((opts.o_outpmode & OPT_USERS) &&
1547 !(opts.o_outpmode & OPT_SPLIT))
1548 opts.o_nbottom = opts.o_ntop;
1549 if (!(opts.o_outpmode & OPT_SPLIT) && opts.o_ntop == 0)
1550 Die(gettext("invalid argument for -n\n"));
1551 if (opts.o_nbottom == 0)
1552 Die(gettext("invalid argument for -n\n"));
1553 if (!(opts.o_outpmode & OPT_SPLIT) && (opts.o_outpmode & OPT_USERS) &&
1554 ((opts.o_outpmode & (OPT_PSINFO | OPT_MSACCT))))
1555 Die(gettext("-t option cannot be used with -v or -m\n"));
1557 if ((opts.o_outpmode & OPT_SPLIT) && (opts.o_outpmode & OPT_USERS) &&
1558 !((opts.o_outpmode & (OPT_PSINFO | OPT_MSACCT))))
1559 Die(gettext("-t option cannot be used with "
1560 "-a, -J, -T or -Z\n"));
1562 if ((opts.o_outpmode & OPT_USERS) &&
1563 (opts.o_outpmode & (OPT_TASKS | OPT_PROJECTS | OPT_ZONES)))
1564 Die(gettext("-a option cannot be used with "
1565 "-t, -J, -T or -Z\n"));
1567 if (((opts.o_outpmode & OPT_TASKS) &&
1568 (opts.o_outpmode & (OPT_PROJECTS|OPT_ZONES))) ||
1569 ((opts.o_outpmode & OPT_PROJECTS) &&
1570 (opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) {
1571 Die(gettext(
1572 "-J, -T and -Z options are mutually exclusive\n"));
1576 * There is not enough space to combine microstate information and
1577 * lgroup information and still fit in 80-column output.
1579 if ((opts.o_outpmode & OPT_LGRP) && (opts.o_outpmode & OPT_MSACCT)) {
1580 Die(gettext("-H and -m options are mutually exclusive\n"));
1583 if (argc > optind)
1584 opts.o_interval = Atoi(argv[optind++]);
1585 if (argc > optind)
1586 opts.o_count = Atoi(argv[optind++]);
1587 if (opts.o_count == 0)
1588 Die(gettext("invalid counter value\n"));
1589 if (argc > optind)
1590 Usage();
1591 if (opts.o_outpmode & OPT_REALTIME)
1592 Priocntl("RT");
1593 if (isatty(STDOUT_FILENO) == 1 && isatty(STDIN_FILENO))
1594 opts.o_outpmode |= OPT_TTY; /* interactive */
1595 if (!(opts.o_outpmode & OPT_TTY)) {
1596 opts.o_outpmode &= ~OPT_TERMCAP; /* no termcap for pipes */
1597 opts.o_outpmode &= ~OPT_FULLSCREEN;
1599 if (opts.o_outpmode & OPT_TERMCAP)
1600 ldtermcap(); /* can turn OPT_TERMCAP off */
1601 if (opts.o_outpmode & OPT_TERMCAP)
1602 (void) setsize();
1603 list_alloc(&lwps, opts.o_ntop);
1604 list_alloc(&users, opts.o_nbottom);
1605 list_alloc(&tasks, opts.o_nbottom);
1606 list_alloc(&projects, opts.o_nbottom);
1607 list_alloc(&zones, opts.o_nbottom);
1608 list_alloc(&lgroups, opts.o_nbottom);
1609 list_setkeyfunc(sortk, &opts, &lwps, LT_LWPS);
1610 list_setkeyfunc(NULL, &opts, &users, LT_USERS);
1611 list_setkeyfunc(NULL, &opts, &tasks, LT_TASKS);
1612 list_setkeyfunc(NULL, &opts, &projects, LT_PROJECTS);
1613 list_setkeyfunc(NULL, &opts, &zones, LT_ZONES);
1614 list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS);
1615 if (opts.o_outpmode & OPT_TERMCAP)
1616 curses_on();
1617 if ((procdir = opendir("/proc")) == NULL)
1618 Die(gettext("cannot open /proc directory\n"));
1619 if (opts.o_outpmode & OPT_TTY) {
1620 (void) printf(gettext("Please wait...\r"));
1621 if (!(opts.o_outpmode & OPT_TERMCAP))
1622 (void) putchar('\n');
1623 (void) fflush(stdout);
1625 set_signals();
1626 pollset.fd = STDIN_FILENO;
1627 pollset.events = POLLIN;
1628 timeout = opts.o_interval * MILLISEC;
1631 * main program loop
1633 do {
1634 if (sigterm == 1)
1635 break;
1636 if (sigtstp == 1) {
1637 curses_off();
1638 (void) signal(SIGTSTP, SIG_DFL);
1639 (void) kill(0, SIGTSTP);
1641 * prstat stops here until it receives SIGCONT signal.
1643 sigtstp = 0;
1644 (void) signal(SIGTSTP, sig_handler);
1645 curses_on();
1646 print_movecur = FALSE;
1647 if (opts.o_outpmode & OPT_FULLSCREEN)
1648 sigwinch = 1;
1650 if (sigwinch == 1) {
1651 if (setsize() == 1) {
1652 list_free(&lwps);
1653 list_free(&users);
1654 list_free(&tasks);
1655 list_free(&projects);
1656 list_free(&zones);
1657 list_alloc(&lwps, opts.o_ntop);
1658 list_alloc(&users, opts.o_nbottom);
1659 list_alloc(&tasks, opts.o_nbottom);
1660 list_alloc(&projects, opts.o_nbottom);
1661 list_alloc(&zones, opts.o_nbottom);
1663 sigwinch = 0;
1664 (void) signal(SIGWINCH, sig_handler);
1666 prstat_scandir(procdir);
1667 list_refresh(&lwps);
1668 if (print_movecur)
1669 (void) putp(movecur);
1670 print_movecur = TRUE;
1671 if ((opts.o_outpmode & OPT_PSINFO) ||
1672 (opts.o_outpmode & OPT_MSACCT)) {
1673 list_sort(&lwps);
1674 list_print(&lwps);
1676 if (opts.o_outpmode & OPT_USERS) {
1677 list_getsize(&users);
1678 list_sort(&users);
1679 list_print(&users);
1680 list_clear(&users);
1682 if (opts.o_outpmode & OPT_TASKS) {
1683 list_getsize(&tasks);
1684 list_sort(&tasks);
1685 list_print(&tasks);
1686 list_clear(&tasks);
1688 if (opts.o_outpmode & OPT_PROJECTS) {
1689 list_getsize(&projects);
1690 list_sort(&projects);
1691 list_print(&projects);
1692 list_clear(&projects);
1694 if (opts.o_outpmode & OPT_ZONES) {
1695 list_getsize(&zones);
1696 list_sort(&zones);
1697 list_print(&zones);
1698 list_clear(&zones);
1700 if (opts.o_count == 1)
1701 break;
1703 * If poll() returns -1 and sets errno to EINTR here because
1704 * the process received a signal, it is Ok to abort this
1705 * timeout and loop around because we check the signals at the
1706 * top of the loop.
1708 if (opts.o_outpmode & OPT_TTY) {
1709 if (poll(&pollset, (nfds_t)1, timeout) > 0) {
1710 if (read(STDIN_FILENO, &key, 1) == 1) {
1711 if (tolower(key) == 'q')
1712 break;
1715 } else {
1716 (void) sleep(opts.o_interval);
1718 } while (opts.o_count == (-1) || --opts.o_count);
1720 if (opts.o_outpmode & OPT_TTY)
1721 (void) putchar('\r');
1722 return (0);