2 /* Author: Ben Gras <beng@few.vu.nl> 17 march 2006 */
4 #define _MINIX_SYSTEM 1
10 #include <minix/timers.h>
23 #include <sys/ttycom.h>
24 #include <sys/ioctl.h>
25 #include <sys/times.h>
26 #include <sys/types.h>
28 #include <sys/select.h>
30 #include <minix/com.h>
31 #include <minix/config.h>
32 #include <minix/type.h>
33 #include <minix/endpoint.h>
34 #include <minix/const.h>
35 #include <minix/u64.h>
36 #include <minix/paths.h>
37 #include <minix/procfs.h>
39 #define TIMECYCLEKEY 't'
43 #define ORDER_MEMORY 1
44 #define ORDER_HIGHEST ORDER_MEMORY
45 int order
= ORDER_CPU
;
49 /* name of cpu cycle types, in the order they appear in /psinfo. */
50 const char *cputimenames
[] = { "user", "ipc", "kernelcall" };
52 #define CPUTIMENAMES ((int) (sizeof(cputimenames)/sizeof(cputimenames[0])))
54 #define CPUTIME(m, i) (m & (1L << (i)))
56 unsigned int nr_procs
, nr_tasks
;
59 #define SLOT_NR(e) (_ENDPOINT_P(e) + nr_tasks)
61 #define TC_BUFFER 1024 /* Size of termcap(3) buffer */
62 #define TC_STRINGS 200 /* Enough room for cm,cl,so,se */
66 int blockedverbose
= 0;
75 endpoint_t p_endpoint
;
77 u64_t p_cpucycles
[CPUTIMENAMES
];
84 char p_name
[PROC_NAME_LEN
+1];
87 struct proc
*proc
= NULL
, *prev_proc
= NULL
;
92 char path
[PATH_MAX
], name
[256], type
, state
;
98 sprintf(path
, "%d/psinfo", pid
);
100 if ((fp
= fopen(path
, "r")) == NULL
)
103 if (fscanf(fp
, "%d", &version
) != 1) {
108 if (version
!= PSINFO_VERSION
) {
109 fputs("procfs version mismatch!\n", stderr
);
113 if (fscanf(fp
, " %c %d", &type
, &endpt
) != 2) {
118 slot
= SLOT_NR(endpt
);
120 if (slot
< 0 || slot
>= nr_total
) {
121 fprintf(stderr
, "top: unreasonable endpoint number %d\n",
129 if (type
== TYPE_TASK
)
130 p
->p_flags
|= IS_TASK
;
131 else if (type
== TYPE_SYSTEM
)
132 p
->p_flags
|= IS_SYSTEM
;
134 p
->p_endpoint
= endpt
;
137 if (fscanf(fp
, " %255s %c %d %d %u %*u %"PRIu64
" %"PRIu64
" %"PRIu64
139 name
, &state
, &p
->p_blocked
, &p
->p_priority
, &p
->p_user_time
,
140 &p
->p_cpucycles
[0], &p
->p_cpucycles
[1], &p
->p_cpucycles
[2],
141 &p
->p_memory
, &p
->p_nice
, &p
->p_effuid
) != 11) {
146 strlcpy(p
->p_name
, name
, sizeof(p
->p_name
));
148 if (state
!= STATE_RUN
)
149 p
->p_flags
|= BLOCKED
;
156 static void parse_dir(void)
159 struct dirent
*p_ent
;
163 if ((p_dir
= opendir(".")) == NULL
) {
164 perror("opendir on " _PATH_PROC
);
168 for (p_ent
= readdir(p_dir
); p_ent
!= NULL
; p_ent
= readdir(p_dir
)) {
169 pid
= strtol(p_ent
->d_name
, &end
, 10);
171 if (!end
[0] && pid
!= 0)
178 static void get_procs(void)
188 proc
= malloc(nr_total
* sizeof(proc
[0]));
191 fprintf(stderr
, "Out of memory!\n");
196 for (i
= 0; i
< nr_total
; i
++)
202 static int print_memory(void)
205 unsigned int pagesize
;
206 unsigned long total
, freemem
, largest
, cached
;
208 if ((fp
= fopen("meminfo", "r")) == NULL
)
211 if (fscanf(fp
, "%u %lu %lu %lu %lu", &pagesize
, &total
, &freemem
,
212 &largest
, &cached
) != 5) {
219 printf("main memory: %ldK total, %ldK free, %ldK contig free, "
221 (pagesize
* total
)/1024, (pagesize
* freemem
)/1024,
222 (pagesize
* largest
)/1024, (pagesize
* cached
)/1024);
227 static int print_load(double *loads
, int nloads
)
230 printf("load averages: ");
231 for(i
= 0; i
< nloads
; i
++)
232 printf("%s %.2f", (i
> 0) ? "," : "", loads
[i
]);
237 static int print_proc_summary(struct proc
*pproc
)
239 int p
, alive
, running
, sleeping
;
241 alive
= running
= sleeping
= 0;
243 for(p
= 0; p
< nr_total
; p
++) {
244 if (pproc
[p
].p_endpoint
== IDLE
)
246 if(!(pproc
[p
].p_flags
& USED
))
249 if(pproc
[p
].p_flags
& BLOCKED
)
254 printf("%d processes: %d running, %d sleeping\n",
255 alive
, running
, sleeping
);
264 static int cmp_procs(const void *v1
, const void *v2
)
266 const struct tp
*p1
= (const struct tp
*) v1
,
267 *p2
= (const struct tp
*) v2
;
268 int p1blocked
, p2blocked
;
270 if(order
== ORDER_MEMORY
) {
271 if(p1
->p
->p_memory
< p2
->p
->p_memory
) return 1;
272 if(p1
->p
->p_memory
> p2
->p
->p_memory
) return -1;
276 p1blocked
= !!(p1
->p
->p_flags
& BLOCKED
);
277 p2blocked
= !!(p2
->p
->p_flags
& BLOCKED
);
279 /* Primarily order by used number of cpu cycles.
281 * Exception: if in blockedverbose mode, a blocked
282 * process is always printed after an unblocked
283 * process, and used cpu cycles don't matter.
285 * In both cases, process slot number is a tie breaker.
288 if(blockedverbose
&& (p1blocked
|| p2blocked
)) {
289 if(!p1blocked
&& p2blocked
)
291 if(!p2blocked
&& p1blocked
)
293 } else if(p1
->ticks
!= p2
->ticks
) {
294 if(p1
->ticks
> p2
->ticks
) return -1;
295 assert(p1
->ticks
< p2
->ticks
);
299 /* Process slot number is a tie breaker. */
300 return (int) (p1
->p
- p2
->p
);
303 static struct tp
*lookup(endpoint_t who
, struct tp
*tptab
, int np
)
307 for(t
= 0; t
< np
; t
++)
308 if(who
== tptab
[t
].p
->p_endpoint
)
311 fprintf(stderr
, "lookup: tp %d (0x%x) not found.\n", who
, who
);
319 static void print_proc(struct tp
*tp
, u64_t total_ticks
)
322 static struct passwd
*who
= NULL
;
323 static int last_who
= -1;
324 const char *name
= "";
326 struct proc
*pr
= tp
->p
;
328 printf("%5d ", pr
->p_pid
);
332 if(last_who
!= euid
|| !who
) {
333 who
= getpwuid(euid
);
337 if(who
&& who
->pw_name
) printf("%-8s ", who
->pw_name
);
338 else if(!(pr
->p_flags
& IS_TASK
)) printf("%8d ", pr
->p_effuid
);
341 printf(" %2d ", pr
->p_priority
);
342 if(!(pr
->p_flags
& IS_TASK
)) {
343 printf(" %3d ", pr
->p_nice
);
345 printf("%6ldK", (pr
->p_memory
+ 512) / 1024);
346 printf("%6s", (pr
->p_flags
& BLOCKED
) ? "" : "RUN");
347 ticks
= pr
->p_user_time
;
348 printf(" %3u:%02u ", (ticks
/system_hz
/60), (ticks
/system_hz
)%60);
350 printf("%6.2f%% %s", 100.0 * tp
->ticks
/ total_ticks
, name
);
353 static char *cputimemodename(int cputimemode
)
355 static char name
[100];
360 for(i
= 0; i
< CPUTIMENAMES
; i
++) {
361 if(CPUTIME(cputimemode
, i
)) {
362 assert(strlen(name
) +
363 strlen(cputimenames
[i
]) < sizeof(name
));
364 strcat(name
, cputimenames
[i
]);
372 static u64_t
cputicks(struct proc
*p1
, struct proc
*p2
, int timemode
)
376 for(i
= 0; i
< CPUTIMENAMES
; i
++) {
377 if(!CPUTIME(timemode
, i
))
379 if(p1
->p_endpoint
== p2
->p_endpoint
) {
380 t
= t
+ p2
->p_cpucycles
[i
] - p1
->p_cpucycles
[i
];
382 t
= t
+ p2
->p_cpucycles
[i
];
389 static const char *ordername(int orderno
)
392 case ORDER_CPU
: return "cpu";
393 case ORDER_MEMORY
: return "memory";
395 return "invalid order";
398 static void print_procs(int maxlines
,
399 struct proc
*proc1
, struct proc
*proc2
, int cputimemode
)
403 u64_t kernelticks
= 0;
404 u64_t systemticks
= 0;
406 u64_t total_ticks
= 0;
408 static struct tp
*tick_procs
= NULL
;
410 if (tick_procs
== NULL
) {
411 tick_procs
= malloc(nr_total
* sizeof(tick_procs
[0]));
413 if (tick_procs
== NULL
) {
414 fprintf(stderr
, "Out of memory!\n");
419 for(p
= nprocs
= 0; p
< nr_total
; p
++) {
421 if(!(proc2
[p
].p_flags
& USED
))
423 tick_procs
[nprocs
].p
= proc2
+ p
;
424 tick_procs
[nprocs
].ticks
= cputicks(&proc1
[p
], &proc2
[p
], cputimemode
);
425 uticks
= cputicks(&proc1
[p
], &proc2
[p
], 1);
426 total_ticks
= total_ticks
+ uticks
;
427 if(p
-NR_TASKS
== IDLE
) {
431 if(p
-NR_TASKS
== KERNEL
) {
432 kernelticks
= uticks
;
434 if(!(proc2
[p
].p_flags
& IS_TASK
)) {
435 if(proc2
[p
].p_flags
& IS_SYSTEM
)
436 systemticks
= systemticks
+ tick_procs
[nprocs
].ticks
;
438 userticks
= userticks
+ tick_procs
[nprocs
].ticks
;
444 if (total_ticks
== 0)
447 qsort(tick_procs
, nprocs
, sizeof(tick_procs
[0]), cmp_procs
);
449 printf("CPU states: %6.2f%% user, ", 100.0 * userticks
/ total_ticks
);
450 printf("%6.2f%% system, ", 100.0 * systemticks
/ total_ticks
);
451 printf("%6.2f%% kernel, ", 100.0 * kernelticks
/ total_ticks
);
452 printf("%6.2f%% idle", 100.0 * idleticks
/ total_ticks
);
454 #define NEWLINE do { printf("\n"); if(--maxlines <= 0) { return; } } while(0)
457 printf("CPU time displayed ('%c' to cycle): %s; ",
458 TIMECYCLEKEY
, cputimemodename(cputimemode
));
459 printf(" sort order ('%c' to cycle): %s", ORDERKEY
, ordername(order
));
464 printf(" PID USERNAME PRI NICE SIZE STATE TIME CPU COMMAND");
466 for(p
= 0; p
< nprocs
; p
++) {
470 pr
= tick_procs
[p
].p
;
472 if((pr
->p_flags
& IS_TASK
) && pr
->p_pid
!= KERNEL
) {
473 /* skip old kernel tasks as they don't run anymore */
477 /* If we're in blocked verbose mode, indicate start of
480 if(blockedverbose
&& (pr
->p_flags
& BLOCKED
) && !blockedseen
) {
482 printf("Blocked processes:");
487 print_proc(&tick_procs
[p
], total_ticks
);
493 /* Traverse dependency chain if blocked. */
494 while(pr
->p_flags
& BLOCKED
) {
495 endpoint_t dep
= NONE
;
499 if((dep
= pr
->p_blocked
) == NONE
) {
500 printf("not blocked on a process");
508 tpdep
= lookup(dep
, tick_procs
, nprocs
);
510 printf("%*s> ", level
, "");
511 print_proc(tpdep
, total_ticks
);
517 static void showtop(int cputimemode
, int r
)
520 double loads
[NLOADS
];
521 int nloads
, lines
= 0;
522 struct winsize winsize
;
524 if(ioctl(STDIN_FILENO
, TIOCGWINSZ
, &winsize
) != 0) {
525 perror("TIOCGWINSZ");
526 fprintf(stderr
, "TIOCGWINSZ failed\n");
531 if (prev_proc
== NULL
) {
533 * A delay short enough to be unnoticable but long enough to
534 * allow for accumulation of sufficient data for the initial
535 * display not to show wildly inaccurate numbers.
541 if((nloads
= getloadavg(loads
, NLOADS
)) != NLOADS
) {
542 fprintf(stderr
, "getloadavg() failed - %d loads\n", nloads
);
547 printf("%s", Tclr_all
);
549 lines
+= print_load(loads
, NLOADS
);
550 lines
+= print_proc_summary(proc
);
551 lines
+= print_memory();
553 if(winsize
.ws_row
> 0) r
= winsize
.ws_row
;
555 print_procs(r
- lines
- 2, prev_proc
, proc
, cputimemode
);
559 static void init(int *rows
)
562 static char buffer
[TC_BUFFER
], strings
[TC_STRINGS
];
563 char *s
= strings
, *v
;
567 if(!(term
= getenv("TERM"))) {
568 fprintf(stderr
, "No TERM set\n");
572 if ( tgetent( buffer
, term
) != 1 ) {
573 fprintf(stderr
, "tgetent failed for term %s\n", term
);
580 if ( (Tclr_all
= tgetstr( "cl", &s
)) == NULL
)
583 if((v
= tgetstr ("li", &s
)) != NULL
)
584 sscanf(v
, "%d", rows
);
585 if(*rows
< 1) *rows
= 24;
588 static void sigwinch(int sig
) { }
590 static void getkinfo(void)
594 if ((fp
= fopen("kinfo", "r")) == NULL
) {
595 fprintf(stderr
, "opening " _PATH_PROC
"kinfo failed\n");
599 if (fscanf(fp
, "%u %u", &nr_procs
, &nr_tasks
) != 2) {
600 fprintf(stderr
, "reading from " _PATH_PROC
"kinfo failed\n");
606 nr_total
= (int) (nr_procs
+ nr_tasks
);
609 int main(int argc
, char *argv
[])
612 int cputimemode
= 1; /* bitmap. */
614 if (chdir(_PATH_PROC
) != 0) {
615 perror("chdir to " _PATH_PROC
);
619 system_hz
= (u32_t
) sysconf(_SC_CLK_TCK
);
625 while((optc
=getopt(argc
, argv
, "s:B")) != EOF
) {
635 "Usage: %s [-s<secdelay>] [-B]\n",
644 /* Catch window size changes so display is updated properly
647 signal(SIGWINCH
, sigwinch
);
653 showtop(cputimemode
, r
);
658 FD_SET(STDIN_FILENO
, &fds
);
660 if((ns
=select(STDIN_FILENO
+1, &fds
, NULL
, NULL
, &tv
)) < 0
666 if(ns
> 0 && FD_ISSET(STDIN_FILENO
, &fds
)) {
668 if(read(STDIN_FILENO
, &inc
, 1) == 1) {
676 if(order
> ORDER_HIGHEST
)
681 if(cputimemode
>= (1L << CPUTIMENAMES
))