2 /* Author: Ben Gras <beng@few.vu.nl> 17 march 2006 */
3 /* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */
7 #define _POSIX_SOURCE 1
27 #include <sys/ioc_tty.h>
28 #include <sys/times.h>
29 #include <sys/types.h>
31 #include <sys/select.h>
33 #include <minix/com.h>
34 #include <minix/config.h>
35 #include <minix/type.h>
36 #include <minix/endpoint.h>
37 #include <minix/const.h>
38 #include <minix/u64.h>
40 #include <minix/procfs.h>
42 #define TIMECYCLEKEY 't'
45 #define ORDER_MEMORY 1
46 #define ORDER_HIGHEST ORDER_MEMORY
47 int order
= ORDER_CPU
;
51 /* name of cpu cycle types, in the order they appear in /psinfo. */
52 char *cputimenames
[] = { "user", "ipc", "kernelcall" };
54 #define CPUTIMENAMES (sizeof(cputimenames)/sizeof(cputimenames[0]))
56 #define CPUTIME(m, i) (m & (1L << (i)))
58 unsigned int nr_procs
, nr_tasks
;
61 #define SLOT_NR(e) (_ENDPOINT_P(e) + nr_tasks)
63 #define TC_BUFFER 1024 /* Size of termcap(3) buffer */
64 #define TC_STRINGS 200 /* Enough room for cm,cl,so,se */
68 int blockedverbose
= 0;
77 endpoint_t p_endpoint
;
79 u64_t p_cpucycles
[CPUTIMENAMES
];
86 char p_name
[PROC_NAME_LEN
+1];
89 struct proc
*proc
= NULL
, *prev_proc
= NULL
;
91 void parse_file(pid_t pid
)
93 char path
[PATH_MAX
], name
[256], type
, state
;
94 int version
, endpt
, effuid
;
95 unsigned long cycles_hi
, cycles_lo
;
101 sprintf(path
, "%d/psinfo", pid
);
103 if ((fp
= fopen(path
, "r")) == NULL
)
106 if (fscanf(fp
, "%d", &version
) != 1) {
111 if (version
!= PSINFO_VERSION
) {
112 fputs("procfs version mismatch!\n", stderr
);
116 if (fscanf(fp
, " %c %d", &type
, &endpt
) != 2) {
121 slot
= SLOT_NR(endpt
);
123 if(slot
< 0 || slot
>= nr_total
) {
124 fprintf(stderr
, "top: unreasonable endpoint number %d\n", endpt
);
131 if (type
== TYPE_TASK
)
132 p
->p_flags
|= IS_TASK
;
133 else if (type
== TYPE_SYSTEM
)
134 p
->p_flags
|= IS_SYSTEM
;
136 p
->p_endpoint
= endpt
;
139 if (fscanf(fp
, " %255s %c %d %d %lu %*u %lu %lu",
140 name
, &state
, &p
->p_blocked
, &p
->p_priority
,
141 &p
->p_user_time
, &cycles_hi
, &cycles_lo
) != 7) {
147 strncpy(p
->p_name
, name
, sizeof(p
->p_name
)-1);
148 p
->p_name
[sizeof(p
->p_name
)-1] = 0;
150 if (state
!= STATE_RUN
)
151 p
->p_flags
|= BLOCKED
;
152 p
->p_cpucycles
[0] = make64(cycles_lo
, cycles_hi
);
155 if (!(p
->p_flags
& IS_TASK
)) {
157 if ((i
=fscanf(fp
, " %lu %*u %*u %*c %*d %*u %u %*u %d %*c %*d %*u",
158 &p
->p_memory
, &effuid
, &p
->p_nice
)) != 3) {
164 p
->p_effuid
= effuid
;
167 for(i
= 1; i
< CPUTIMENAMES
; i
++) {
168 if(fscanf(fp
, " %lu %lu",
169 &cycles_hi
, &cycles_lo
) == 2) {
170 p
->p_cpucycles
[i
] = make64(cycles_lo
, cycles_hi
);
172 p
->p_cpucycles
[i
] = make64(0, 0);
184 struct dirent
*p_ent
;
188 if ((p_dir
= opendir(".")) == NULL
) {
189 perror("opendir on " _PATH_PROC
);
193 for (p_ent
= readdir(p_dir
); p_ent
!= NULL
; p_ent
= readdir(p_dir
)) {
194 pid
= strtol(p_ent
->d_name
, &end
, 10);
196 if (!end
[0] && pid
!= 0)
213 proc
= malloc(nr_total
* sizeof(proc
[0]));
216 fprintf(stderr
, "Out of memory!\n");
221 for (i
= 0; i
< nr_total
; i
++)
227 int print_memory(void)
230 unsigned int pagesize
;
231 unsigned long total
, free
, largest
, cached
;
233 if ((fp
= fopen("meminfo", "r")) == NULL
)
236 if (fscanf(fp
, "%u %lu %lu %lu %lu", &pagesize
, &total
, &free
,
237 &largest
, &cached
) != 5) {
244 printf("main memory: %ldK total, %ldK free, %ldK contig free, "
246 (pagesize
* total
)/1024, (pagesize
* free
)/1024,
247 (pagesize
* largest
)/1024, (pagesize
* cached
)/1024);
252 int print_load(double *loads
, int nloads
)
255 printf("load averages: ");
256 for(i
= 0; i
< nloads
; i
++)
257 printf("%s %.2f", (i
> 0) ? "," : "", loads
[i
]);
262 int print_proc_summary(struct proc
*proc
)
264 int p
, alive
, running
, sleeping
;
266 alive
= running
= sleeping
= 0;
268 for(p
= 0; p
< nr_total
; p
++) {
269 if (proc
[p
].p_endpoint
== IDLE
)
271 if(!(proc
[p
].p_flags
& USED
))
274 if(proc
[p
].p_flags
& BLOCKED
)
279 printf("%d processes: %d running, %d sleeping\n",
280 alive
, running
, sleeping
);
289 int cmp_procs(const void *v1
, const void *v2
)
292 struct tp
*p1
= (struct tp
*) v1
, *p2
= (struct tp
*) v2
;
293 int p1blocked
, p2blocked
;
295 if(order
== ORDER_MEMORY
) {
296 if(p1
->p
->p_memory
< p2
->p
->p_memory
) return 1;
297 if(p1
->p
->p_memory
> p2
->p
->p_memory
) return -1;
301 p1blocked
= !!(p1
->p
->p_flags
& BLOCKED
);
302 p2blocked
= !!(p2
->p
->p_flags
& BLOCKED
);
304 /* Primarily order by used number of cpu cycles.
306 * Exception: if in blockedverbose mode, a blocked
307 * process is always printed after an unblocked
308 * process, and used cpu cycles don't matter.
310 * In both cases, process slot number is a tie breaker.
313 if(blockedverbose
&& (p1blocked
|| p2blocked
)) {
314 if(!p1blocked
&& p2blocked
)
316 if(!p2blocked
&& p1blocked
)
318 } else if((c
=cmp64(p1
->ticks
, p2
->ticks
)) != 0)
321 /* Process slot number is a tie breaker. */
322 return (int) (p1
->p
- p2
->p
);
325 struct tp
*lookup(endpoint_t who
, struct tp
*tptab
, int np
)
329 for(t
= 0; t
< np
; t
++)
330 if(who
== tptab
[t
].p
->p_endpoint
)
333 fprintf(stderr
, "lookup: tp %d (0x%x) not found.\n", who
, who
);
340 * since we don't have true div64(u64_t, u64_t) we scale the 64 bit counters to
341 * 32. Since the samplig happens every ~1s and the counters count CPU cycles
342 * during this period, unless we have extremely fast CPU, shifting the counters
343 * by 12 make them 32bit counters which we can use for normal integer
346 #define SCALE (1 << 12)
350 void print_proc(struct tp
*tp
, u32_t tcyc
)
353 static struct passwd
*who
= NULL
;
354 static int last_who
= -1;
358 struct proc
*pr
= tp
->p
;
360 printf("%5d ", pr
->p_pid
);
364 if(last_who
!= euid
|| !who
) {
365 who
= getpwuid(euid
);
369 if(who
&& who
->pw_name
) printf("%-8s ", who
->pw_name
);
370 else if(!(pr
->p_flags
& IS_TASK
)) printf("%8d ", pr
->p_effuid
);
373 printf(" %2d ", pr
->p_priority
);
374 if(!(pr
->p_flags
& IS_TASK
)) {
375 printf(" %3d ", pr
->p_nice
);
377 printf("%6ldK", (pr
->p_memory
+ 512) / 1024);
378 printf("%6s", (pr
->p_flags
& BLOCKED
) ? "" : "RUN");
379 ticks
= pr
->p_user_time
;
380 printf(" %3u:%02u ", (ticks
/system_hz
/60), (ticks
/system_hz
)%60);
382 pcyc
= div64u(tp
->ticks
, SCALE
);
384 printf("%6.2f%% %s", 100.0*pcyc
/tcyc
, name
);
387 char *cputimemodename(int cputimemode
)
389 static char name
[100];
394 for(i
= 0; i
< CPUTIMENAMES
; i
++) {
395 if(CPUTIME(cputimemode
, i
)) {
396 assert(strlen(name
) +
397 strlen(cputimenames
[i
]) < sizeof(name
));
398 strcat(name
, cputimenames
[i
]);
406 u64_t
cputicks(struct proc
*p1
, struct proc
*p2
, int timemode
)
409 u64_t t
= make64(0, 0);
410 for(i
= 0; i
< CPUTIMENAMES
; i
++) {
411 if(!CPUTIME(timemode
, i
))
413 if(p1
->p_endpoint
== p2
->p_endpoint
) {
414 t
= add64(t
, sub64(p2
->p_cpucycles
[i
],
415 p1
->p_cpucycles
[i
]));
417 t
= add64(t
, p2
->p_cpucycles
[i
]);
424 void print_procs(int maxlines
,
425 struct proc
*proc1
, struct proc
*proc2
, int cputimemode
)
428 u64_t idleticks
= cvu64(0);
429 u64_t kernelticks
= cvu64(0);
430 u64_t systemticks
= cvu64(0);
431 u64_t userticks
= cvu64(0);
432 u64_t total_ticks
= cvu64(0);
436 static struct tp
*tick_procs
= NULL
;
438 if (tick_procs
== NULL
) {
439 tick_procs
= malloc(nr_total
* sizeof(tick_procs
[0]));
441 if (tick_procs
== NULL
) {
442 fprintf(stderr
, "Out of memory!\n");
447 for(p
= nprocs
= 0; p
< nr_total
; p
++) {
449 if(!(proc2
[p
].p_flags
& USED
))
451 tick_procs
[nprocs
].p
= proc2
+ p
;
452 tick_procs
[nprocs
].ticks
= cputicks(&proc1
[p
], &proc2
[p
], cputimemode
);
453 uticks
= cputicks(&proc1
[p
], &proc2
[p
], 1);
454 total_ticks
= add64(total_ticks
, uticks
);
455 if(p
-NR_TASKS
== IDLE
) {
459 if(p
-NR_TASKS
== KERNEL
) {
460 kernelticks
= uticks
;
463 if(!(proc2
[p
].p_flags
& IS_TASK
)) {
464 if(proc2
[p
].p_flags
& IS_SYSTEM
)
465 systemticks
= add64(systemticks
,
466 tick_procs
[nprocs
].ticks
);
468 userticks
= add64(userticks
,
469 tick_procs
[nprocs
].ticks
);
475 if (!cmp64u(total_ticks
, 0))
478 qsort(tick_procs
, nprocs
, sizeof(tick_procs
[0]), cmp_procs
);
480 tcyc
= div64u(total_ticks
, SCALE
);
482 tmp
= div64u(userticks
, SCALE
);
483 printf("CPU states: %6.2f%% user, ", 100.0*(tmp
)/tcyc
);
485 tmp
= div64u(systemticks
, SCALE
);
486 printf("%6.2f%% system, ", 100.0*tmp
/tcyc
);
488 tmp
= div64u(kernelticks
, SCALE
);
489 printf("%6.2f%% kernel, ", 100.0*tmp
/tcyc
);
491 tmp
= div64u(idleticks
, SCALE
);
492 printf("%6.2f%% idle", 100.0*tmp
/tcyc
);
494 #define NEWLINE do { printf("\n"); if(--maxlines <= 0) { return; } } while(0)
497 printf("CPU time displayed (press '%c' to cycle): %s",
498 TIMECYCLEKEY
, cputimemodename(cputimemode
));
503 printf(" PID USERNAME PRI NICE SIZE STATE TIME CPU COMMAND");
505 for(p
= 0; p
< nprocs
; p
++) {
509 pr
= tick_procs
[p
].p
;
511 if(pr
->p_flags
& IS_TASK
) {
512 /* skip old kernel tasks as they don't run anymore */
516 /* If we're in blocked verbose mode, indicate start of
519 if(blockedverbose
&& (pr
->p_flags
& BLOCKED
) && !blockedseen
) {
521 printf("Blocked processes:");
526 print_proc(&tick_procs
[p
], tcyc
);
532 /* Traverse dependency chain if blocked. */
533 while(pr
->p_flags
& BLOCKED
) {
534 endpoint_t dep
= NONE
;
538 if((dep
= pr
->p_blocked
) == NONE
) {
539 printf("not blocked on a process");
547 tpdep
= lookup(dep
, tick_procs
, nprocs
);
549 printf("%*s> ", level
, "");
550 print_proc(tpdep
, tcyc
);
556 void showtop(int cputimemode
, int r
)
559 double loads
[NLOADS
];
560 int nloads
, lines
= 0;
561 struct winsize winsize
;
563 if(ioctl(STDIN_FILENO
, TIOCGWINSZ
, &winsize
) != 0) {
564 perror("TIOCGWINSZ");
565 fprintf(stderr
, "TIOCGWINSZ failed\n");
570 if (prev_proc
== NULL
)
573 if((nloads
= getloadavg(loads
, NLOADS
)) != NLOADS
) {
574 fprintf(stderr
, "getloadavg() failed - %d loads\n", nloads
);
579 printf("%s", Tclr_all
);
581 lines
+= print_load(loads
, NLOADS
);
582 lines
+= print_proc_summary(proc
);
583 lines
+= print_memory();
585 if(winsize
.ws_row
> 0) r
= winsize
.ws_row
;
587 print_procs(r
- lines
- 2, prev_proc
, proc
, cputimemode
);
594 static char buffer
[TC_BUFFER
], strings
[TC_STRINGS
];
595 char *s
= strings
, *v
;
599 if(!(term
= getenv("TERM"))) {
600 fprintf(stderr
, "No TERM set\n");
604 if ( tgetent( buffer
, term
) != 1 ) {
605 fprintf(stderr
, "tgetent failed for term %s\n", term
);
612 if ( (Tclr_all
= tgetstr( "cl", &s
)) == NULL
)
615 if((v
= tgetstr ("li", &s
)) != NULL
)
616 sscanf(v
, "%d", rows
);
617 if(*rows
< 1) *rows
= 24;
620 void sigwinch(int sig
) { }
626 if ((fp
= fopen("kinfo", "r")) == NULL
) {
627 fprintf(stderr
, "opening " _PATH_PROC
"kinfo failed\n");
631 if (fscanf(fp
, "%u %u", &nr_procs
, &nr_tasks
) != 2) {
632 fprintf(stderr
, "reading from " _PATH_PROC
"kinfo failed\n");
638 nr_total
= (int) (nr_procs
+ nr_tasks
);
641 int main(int argc
, char *argv
[])
644 int cputimemode
= 1; /* bitmap. */
646 if (chdir(_PATH_PROC
) != 0) {
647 perror("chdir to " _PATH_PROC
);
651 system_hz
= (u32_t
) sysconf(_SC_CLK_TCK
);
657 while((c
=getopt(argc
, argv
, "s:B")) != EOF
) {
667 "Usage: %s [-s<secdelay>] [-B]\n",
676 /* Catch window size changes so display is updated properly
679 signal(SIGWINCH
, sigwinch
);
685 showtop(cputimemode
, r
);
690 FD_SET(STDIN_FILENO
, &fds
);
692 if((ns
=select(STDIN_FILENO
+1, &fds
, NULL
, NULL
, &tv
)) < 0
698 if(ns
> 0 && FD_ISSET(STDIN_FILENO
, &fds
)) {
700 if(read(STDIN_FILENO
, &c
, 1) == 1) {
708 if(order
> ORDER_HIGHEST
)
713 if(cputimemode
>= (1L << CPUTIMENAMES
))