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]
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>
37 #include <sys/vm_usage.h>
39 #include <libzonecfg.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.
74 #ifndef TEXT_DOMAIN /* should be defined by cc -D */
75 #define TEXT_DOMAIN "SYS_TEST" /* use this only if it wasn't */
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"
118 "%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
121 "%6d %-8s %5.5s %5.5s %3.3s%% %9s %3.3s%%"
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"
127 "%6d %8d %5s %5s %3.3s%% %9s %3.3s%% %28s"
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 */
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).
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 */
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
) {
205 len
= strftime(dstr
, sizeof (dstr
), fmt
, localtime(&t
));
207 (void) printf("%s", dstr
);
210 (void) putchar('\n');
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.
231 list_getsize(list_t
*list
)
234 vmusage_t
*results
, *next
;
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
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
;
276 "Cannot determine rss flags for output options %x\n"),
281 * getvmusage() returns an array of result structures. One for
282 * each zone, project, task, or user on the system, depending on
285 * If getvmusage() fails, prstat will use the size already gathered
288 if (getvmusage(flags
, opts
.o_interval
, NULL
, &nres
) != 0)
291 results
= (vmusage_t
*)Malloc(sizeof (vmusage_t
) * nres
);
293 ret
= getvmusage(flags
, opts
.o_interval
, results
, &nres
);
296 if (errno
== EOVERFLOW
) {
297 results
= (vmusage_t
*)Realloc(results
,
298 sizeof (vmusage_t
) * nres
);
302 * Failure for some other reason. Prstat will use the size
303 * already gathered from psinfo.
308 for (id
= list
->l_head
; id
!= NULL
; id
= id
->id_next
) {
312 for (i
= 0; i
< nres
; i
++, next
++) {
314 case VMUSAGE_COL_RUSERS
:
315 if (next
->vmu_id
== id
->id_uid
)
318 case VMUSAGE_ALL_TASKS
:
319 if (next
->vmu_id
== id
->id_taskid
)
322 case VMUSAGE_COL_PROJECTS
:
323 if (next
->vmu_id
== id
->id_projid
)
326 case VMUSAGE_ALL_ZONES
:
327 if (next
->vmu_id
== id
->id_zoneid
)
332 "Unknown vmusage flags %d\n"), flags
);
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
) /
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.
352 * A routine to display the contents of the list on the screen
355 list_print(list_t
*list
)
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];
369 double loadavg
[3] = {0, 0, 0};
372 if (list
->l_size
== 0)
375 if (foreach_element(&set_tbl
, &loadavg
, psetloadavg
) == 0) {
377 * If processor sets aren't specified, we display system-wide
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
)))
386 if (opts
.o_outpmode
& OPT_TTY
)
387 (void) putchar('\r');
391 switch (list
->l_type
) {
393 if (opts
.o_outpmode
& OPT_LWPS
)
394 n
= printf(PROJECT_HEADER_LWP
);
396 n
= printf(PROJECT_HEADER_PROC
);
399 if (opts
.o_outpmode
& OPT_LWPS
)
400 n
= printf(TASK_HEADER_LWP
);
402 n
= printf(TASK_HEADER_PROC
);
405 if (opts
.o_outpmode
& OPT_LWPS
)
406 n
= printf(ZONE_HEADER_LWP
);
408 n
= printf(ZONE_HEADER_PROC
);
411 if (opts
.o_outpmode
& OPT_LWPS
)
412 n
= printf(USER_HEADER_LWP
);
414 n
= printf(USER_HEADER_PROC
);
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
);
422 n
= printf(PSINFO_HEADER_LWP
);
424 if (opts
.o_outpmode
& OPT_MSACCT
)
425 n
= printf(USAGE_HEADER_LWP
);
427 if (opts
.o_outpmode
& OPT_PSINFO
) {
428 if (opts
.o_outpmode
& OPT_LGRP
)
429 n
= printf(PSINFO_HEADER_PROC_LGRP
);
431 n
= printf(PSINFO_HEADER_PROC
);
433 if (opts
.o_outpmode
& OPT_MSACCT
)
434 n
= printf(USAGE_HEADER_PROC
);
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
);
445 (void) putchar('\n');
447 for (i
= 0; i
< list
->l_used
; i
++) {
448 switch (list
->l_type
) {
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
;
461 if (id
->id_sizematch
== B_FALSE
&& total_mem
>= 100)
462 mem
= (100 * id
->id_pctmem
) / total_mem
;
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
),
470 } else if (list
->l_type
== LT_ZONES
) {
471 getzonename(id
->id_zoneid
, zonename
,
473 opts
.o_outpmode
& (OPT_TERMCAP
|OPT_TRUNC
),
476 getprojname(id
->id_projid
, projname
,
478 opts
.o_outpmode
& OPT_NORESOLVE
,
479 opts
.o_outpmode
& (OPT_TERMCAP
|OPT_TRUNC
),
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
,
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
,
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
,
502 (void) printf(USER_LINE
, id
->id_nproc
, pname
,
503 psize
, prssize
, pmem
, ptime
, pcpu
);
505 (void) putchar('\n');
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
),
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);
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
,
525 strcmp(lwp
->li_info
.pr_lwp
.pr_clname
,
527 lwp
->li_info
.pr_lwp
.pr_sname
== 'Z')
528 (void) strcpy(pnice
, " -");
531 lwp
->li_info
.pr_lwp
.pr_nice
- NZERO
,
533 Format_num(ppri
, lwp
->li_info
.pr_lwp
.pr_pri
, 4);
535 FRC2PCT(lwp
->li_info
.pr_lwp
.pr_pctcpu
), 4);
536 if (opts
.o_outpmode
& OPT_LWPS
)
538 lwp
->li_info
.pr_lwp
.pr_time
.tv_sec
,
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
);
552 (void) printf(PSINFO_LINE
,
553 (int)lwp
->li_info
.pr_pid
, pname
,
554 psize
, prssize
, pstate
, ppri
, pnice
,
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
,
581 (void) putchar('\n');
587 if (opts
.o_outpmode
& OPT_TTY
)
588 (void) putchar('\r');
589 if (opts
.o_outpmode
& OPT_TERMCAP
) {
590 switch (list
->l_type
) {
595 while (i
++ < opts
.o_nbottom
) {
597 (void) putchar('\n');
601 while (i
++ < opts
.o_ntop
) {
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
)
614 (void) printf(TOTAL_LINE
, total_procs
, total_lwps
,
615 loadavg
[LOADAVG_1MIN
], loadavg
[LOADAVG_5MIN
],
616 loadavg
[LOADAVG_15MIN
]);
618 (void) putchar('\n');
619 if (opts
.o_outpmode
& OPT_TTY
)
620 (void) putchar('\r');
622 (void) fflush(stdout
);
626 list_add_lwp(list_t
*list
, pid_t pid
, id_t lwpid
)
630 if (list
->l_head
== NULL
) {
631 list
->l_head
= list
->l_tail
= lwp
= Zalloc(sizeof (lwp_info_t
));
633 lwp
= Zalloc(sizeof (lwp_info_t
));
634 lwp
->li_prev
= list
->l_tail
;
635 ((lwp_info_t
*)list
->l_tail
)->li_next
= lwp
;
638 lwp
->li_info
.pr_pid
= pid
;
639 lwp
->li_info
.pr_lwp
.pr_lwpid
= lwpid
;
640 lwpid_add(lwp
, pid
, lwpid
);
646 list_remove_lwp(list_t
*list
, lwp_info_t
*lwp
)
649 lwp
->li_prev
->li_next
= lwp
->li_next
;
651 list
->l_head
= lwp
->li_next
; /* removing the head */
653 lwp
->li_next
->li_prev
= lwp
->li_prev
;
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
);
664 list_clear(list_t
*list
)
666 if (list
->l_type
== LT_LWPS
) {
667 lwp_info_t
*lwp
= list
->l_tail
;
674 list_remove_lwp(&lwps
, lwp_tmp
);
677 id_info_t
*id
= list
->l_head
;
681 nextid
= id
->id_next
;
686 list
->l_head
= list
->l_tail
= NULL
;
691 list_update(list_t
*list
, lwp_info_t
*lwp
)
695 if (list
->l_head
== NULL
) { /* first element */
696 list
->l_head
= list
->l_tail
= id
= Zalloc(sizeof (id_info_t
));
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
))
704 if ((list
->l_type
== LT_TASKS
) &&
705 (id
->id_taskid
!= lwp
->li_info
.pr_taskid
))
707 if ((list
->l_type
== LT_PROJECTS
) &&
708 (id
->id_projid
!= lwp
->li_info
.pr_projid
))
710 if ((list
->l_type
== LT_ZONES
) &&
711 (id
->id_zoneid
!= lwp
->li_info
.pr_zoneid
))
713 if ((list
->l_type
== LT_LGRPS
) &&
714 (id
->id_lgroup
!= lwp
->li_info
.pr_lwp
.pr_lgrp
))
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
);
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
);
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
;
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
;
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
);
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
;
769 lwp_update(lwp_info_t
*lwp
, pid_t pid
, id_t lwpid
, struct prusage
*usage
)
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
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 */
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
);
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 */
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
));
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
)
858 if (pread(fd_getfd(*fd
), buf
, bufsize
, 0) != bufsize
) {
866 add_proc(psinfo_t
*psinfo
)
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
;
881 get_lwpname(pid_t pid
, id_t lwpid
, char *buf
, size_t bufsize
)
888 if (asprintf(&path
, "/proc/%d/lwp/%d/lwpname",
889 (int)pid
, (int)lwpid
) == -1)
892 if ((fd
= open(path
, O_RDONLY
)) != -1) {
893 (void) read(fd
, buf
, bufsize
);
894 buf
[bufsize
- 1] = '\0';
902 add_lwp(psinfo_t
*psinfo
, lwpsinfo_t
*lwpsinfo
, int flags
)
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
));
920 prstat_scandir(DIR *procdir
)
936 lwpsinfo_t
*lwpsinfo
;
944 convert_zone(&zone_tbl
);
945 for (rewinddir(procdir
); (direntp
= readdir(procdir
)); ) {
946 pidstr
= direntp
->d_name
;
947 if (pidstr
[0] == '.') /* skip "." and ".." */
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)
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
);
967 nlwps
= psinfo
.pr_nlwp
+ psinfo
.pr_nzomb
;
969 if (nlwps
> 1 && (opts
.o_outpmode
& (OPT_LWPS
| OPT_PSETS
))) {
972 if (read_procfile(&fds
->fds_lpsinfo
, pidstr
, "lpsinfo",
973 &header
, sizeof (prheader_t
)) != 0) {
974 fd_close(fds
->fds_psinfo
);
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
);
990 for (i
= 0; i
< nent
; i
++, ptr
+= header
.pr_entsize
) {
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
))
1000 if ((opts
.o_outpmode
& (OPT_PSETS
| OPT_LWPS
))
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.
1013 add_lwp(&psinfo
, lwpsinfo
,
1016 add_lwp(&psinfo
, lwpsinfo
, 0);
1022 fd_close(fds
->fds_lpsinfo
);
1023 fd_close(fds
->fds_psinfo
);
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
);
1035 if (!(opts
.o_outpmode
& OPT_MSACCT
)) {
1037 total_lwps
+= nlwps
;
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
);
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
);
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
)
1075 lwp_update(lwp
, pid
, lwpid
, lwpusage
);
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
);
1085 lwpid
= psinfo
.pr_lwp
.pr_lwpid
;
1086 if ((lwp
= lwpid_get(pid
, lwpid
)) == NULL
)
1088 lwp_update(lwp
, pid
, lwpid
, &usage
);
1091 total_lwps
+= nlwps
;
1097 * This procedure removes all dead lwps from the linked list of all lwps.
1098 * It also creates linked list of ids if necessary.
1101 list_refresh(list_t
*list
)
1103 lwp_info_t
*lwp
, *lwp_next
;
1105 if (!(list
->l_type
& LT_LWPS
))
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
;
1131 lwp_next
= lwp
->li_next
;
1132 list_remove_lwp(&lwps
, lwp
);
1141 if ((opts
.o_outpmode
& OPT_TERMCAP
) && (is_curses_on
== FALSE
)) {
1144 (void) putp(t_smcup
);
1145 is_curses_on
= TRUE
;
1152 if ((is_curses_on
== TRUE
) && (opts
.o_outpmode
& OPT_TERMCAP
)) {
1153 (void) putp(t_rmcup
);
1155 is_curses_on
= FALSE
;
1157 (void) fflush(stdout
);
1161 nlines(int *linesp
, int *colsp
)
1169 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) != -1) {
1171 *linesp
= ws
.ws_row
;
1174 if (ws
.ws_row
> 0 && ws
.ws_col
> 0)
1178 if ((envp
= getenv("LINES")) != NULL
) {
1179 if ((n
= Atoi(envp
)) > 0) {
1180 opts
.o_outpmode
&= ~OPT_USEHOME
;
1184 if ((envp
= getenv("COLUMNS")) != NULL
) {
1185 if ((n
= Atoi(envp
)) > 0) {
1190 return ((*linesp
> 0 && *colsp
> 0) ? 0 : -1);
1197 if ((opts
.o_outpmode
& OPT_FULLSCREEN
) &&
1198 (opts
.o_outpmode
& OPT_USEHOME
)) {
1202 if (opts
.o_outpmode
& OPT_SPLIT
) {
1203 if (opts
.o_ntop
== 0)
1204 n
= opts
.o_nbottom
+ 1;
1206 n
= opts
.o_ntop
+ opts
.o_nbottom
+ 2;
1208 if (opts
.o_outpmode
& OPT_USERS
)
1209 n
= opts
.o_nbottom
+ 1;
1211 n
= opts
.o_ntop
+ 1;
1213 if (((opts
.o_outpmode
& OPT_UDATE
) || (opts
.o_outpmode
& OPT_DDATE
)))
1216 if (movecur
!= NULL
&& movecur
!= empty_string
&& movecur
!= t_home
)
1218 movecur
= Zalloc(strlen(t_up
) * (n
+ 5));
1219 for (i
= 0; i
<= n
; i
++)
1220 (void) strcat(movecur
, t_up
);
1226 static int oldn
= 0;
1229 if (opts
.o_outpmode
& OPT_FULLSCREEN
) {
1230 ret
= nlines(&n
, &cols
);
1237 opts
.o_outpmode
&= ~OPT_USEHOME
;
1238 setmovecur(); /* set default window size */
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 */
1246 Die(gettext("window is too small (try -n)\n"));
1247 if (opts
.o_outpmode
& OPT_SPLIT
) {
1249 Die(gettext("window is too small (try -n)\n"));
1251 opts
.o_ntop
= (n
/ 4) * 3;
1252 opts
.o_nbottom
= n
- 1 - opts
.o_ntop
;
1255 if (opts
.o_outpmode
& OPT_USERS
)
1269 if (setupterm(NULL
, STDIN_FILENO
, &err
) == ERR
) {
1272 Warn(gettext("failed to load terminal info, "
1273 "defaulting to -c option\n"));
1276 Warn(gettext("terminfo database not found, "
1277 "defaulting to -c option\n"));
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
;
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
;
1301 if (t_up
== NULL
|| t_eol
== NULL
) {
1302 opts
.o_outpmode
&= ~OPT_TERMCAP
;
1303 t_eol
= t_up
= movecur
= empty_string
;
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
;
1319 sig_handler(int sig
)
1322 case SIGTSTP
: sigtstp
= 1;
1324 case SIGWINCH
: sigwinch
= 1;
1327 case SIGTERM
: sigterm
= 1;
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
);
1343 fill_table(table_t
*table
, char *arg
, char option
)
1345 char *p
= strtok(arg
, ", ");
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
));
1356 fill_prj_table(char *arg
)
1359 char *p
= strtok(arg
, ", ");
1362 Die(gettext("invalid argument for -j\n"));
1364 if ((projid
= getprojidbyname(p
)) == -1)
1366 add_element(&prj_tbl
, (long)projid
);
1368 while (p
= strtok(NULL
, ", ")) {
1369 if ((projid
= getprojidbyname(p
)) == -1)
1371 add_element(&prj_tbl
, (long)projid
);
1376 fill_set_table(char *arg
)
1378 char *p
= strtok(arg
, ", ");
1382 Die(gettext("invalid argument for -C\n"));
1384 if ((id
= Atoi(p
)) == 0)
1386 add_element(&set_tbl
, id
);
1387 while (p
= strtok(NULL
, ", ")) {
1388 if ((id
= Atoi(p
)) == 0)
1390 if (!has_element(&set_tbl
, id
))
1391 add_element(&set_tbl
, id
);
1402 list_clear(&projects
);
1409 main(int argc
, char **argv
)
1413 char *sortk
= "cpu"; /* default sort key */
1416 struct pollfd pollset
;
1419 (void) setlocale(LC_ALL
, "");
1420 (void) textdomain(TEXT_DOMAIN
);
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
) {
1431 opts
.o_outpmode
|= OPT_NORESOLVE
;
1434 opts
.o_outpmode
|= OPT_REALTIME
;
1437 opts
.o_outpmode
&= ~OPT_TERMCAP
;
1438 opts
.o_outpmode
&= ~OPT_FULLSCREEN
;
1443 opts
.o_outpmode
|= OPT_UDATE
;
1444 else if (*optarg
== 'd')
1445 opts
.o_outpmode
|= OPT_DDATE
;
1453 fill_table(&lgr_tbl
, optarg
, 'h');
1456 opts
.o_outpmode
|= OPT_LGRP
;
1460 opts
.o_outpmode
&= ~OPT_PSINFO
;
1461 opts
.o_outpmode
|= OPT_MSACCT
;
1464 opts
.o_outpmode
&= ~OPT_PSINFO
;
1465 opts
.o_outpmode
|= OPT_USERS
;
1468 opts
.o_outpmode
|= OPT_SPLIT
| OPT_USERS
;
1471 opts
.o_outpmode
|= OPT_SPLIT
| OPT_TASKS
;
1474 opts
.o_outpmode
|= OPT_SPLIT
| OPT_PROJECTS
;
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)
1484 opts
.o_outpmode
&= ~OPT_FULLSCREEN
;
1487 opts
.o_sortorder
= -1;
1491 opts
.o_sortorder
= 1;
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
);
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
);
1509 fill_table(&pid_tbl
, optarg
, 'p');
1512 fill_set_table(optarg
);
1513 opts
.o_outpmode
|= OPT_PSETS
;
1516 fill_table(&cpu_tbl
, optarg
, 'P');
1519 fill_table(&tsk_tbl
, optarg
, 'k');
1522 fill_prj_table(optarg
);
1525 opts
.o_outpmode
|= OPT_LWPS
;
1528 opts
.o_outpmode
|= OPT_TRUNC
;
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
);
1538 opts
.o_outpmode
|= OPT_SPLIT
| OPT_ZONES
;
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
)))) {
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"));
1584 opts
.o_interval
= Atoi(argv
[optind
++]);
1586 opts
.o_count
= Atoi(argv
[optind
++]);
1587 if (opts
.o_count
== 0)
1588 Die(gettext("invalid counter value\n"));
1591 if (opts
.o_outpmode
& OPT_REALTIME
)
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
)
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
)
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
);
1626 pollset
.fd
= STDIN_FILENO
;
1627 pollset
.events
= POLLIN
;
1628 timeout
= opts
.o_interval
* MILLISEC
;
1638 (void) signal(SIGTSTP
, SIG_DFL
);
1639 (void) kill(0, SIGTSTP
);
1641 * prstat stops here until it receives SIGCONT signal.
1644 (void) signal(SIGTSTP
, sig_handler
);
1646 print_movecur
= FALSE
;
1647 if (opts
.o_outpmode
& OPT_FULLSCREEN
)
1650 if (sigwinch
== 1) {
1651 if (setsize() == 1) {
1655 list_free(&projects
);
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
);
1664 (void) signal(SIGWINCH
, sig_handler
);
1666 prstat_scandir(procdir
);
1667 list_refresh(&lwps
);
1669 (void) putp(movecur
);
1670 print_movecur
= TRUE
;
1671 if ((opts
.o_outpmode
& OPT_PSINFO
) ||
1672 (opts
.o_outpmode
& OPT_MSACCT
)) {
1676 if (opts
.o_outpmode
& OPT_USERS
) {
1677 list_getsize(&users
);
1682 if (opts
.o_outpmode
& OPT_TASKS
) {
1683 list_getsize(&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
);
1700 if (opts
.o_count
== 1)
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
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')
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');