VM: full munmap
[minix.git] / usr.bin / top / top.c
blob296a0f3eadf585a6d08d7d7099ddfd135b2a780c
2 /* Author: Ben Gras <beng@few.vu.nl> 17 march 2006 */
3 /* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */
5 #define _MINIX 1
6 #ifndef __NBSD_LIBC
7 #define _POSIX_SOURCE 1
8 #endif
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <pwd.h>
13 #include <curses.h>
14 #include <timers.h>
15 #include <stdlib.h>
16 #include <limits.h>
17 #include <termcap.h>
18 #include <termios.h>
19 #include <time.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <dirent.h>
25 #include <assert.h>
27 #include <sys/ioc_tty.h>
28 #include <sys/times.h>
29 #include <sys/types.h>
30 #include <sys/time.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>
39 #include <paths.h>
40 #include <minix/procfs.h>
42 #define TIMECYCLEKEY 't'
44 #define ORDER_CPU 0
45 #define ORDER_MEMORY 1
46 #define ORDER_HIGHEST ORDER_MEMORY
47 int order = ORDER_CPU;
49 u32_t system_hz;
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;
59 int nr_total;
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 */
66 char *Tclr_all;
68 int blockedverbose = 0;
70 #define USED 0x1
71 #define IS_TASK 0x2
72 #define IS_SYSTEM 0x4
73 #define BLOCKED 0x8
75 struct proc {
76 int p_flags;
77 endpoint_t p_endpoint;
78 pid_t p_pid;
79 u64_t p_cpucycles[CPUTIMENAMES];
80 int p_priority;
81 endpoint_t p_blocked;
82 time_t p_user_time;
83 vir_bytes p_memory;
84 uid_t p_effuid;
85 int p_nice;
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;
96 FILE *fp;
97 struct proc *p;
98 int slot;
99 int i;
101 sprintf(path, "%d/psinfo", pid);
103 if ((fp = fopen(path, "r")) == NULL)
104 return;
106 if (fscanf(fp, "%d", &version) != 1) {
107 fclose(fp);
108 return;
111 if (version != PSINFO_VERSION) {
112 fputs("procfs version mismatch!\n", stderr);
113 exit(1);
116 if (fscanf(fp, " %c %d", &type, &endpt) != 2) {
117 fclose(fp);
118 return;
121 slot = SLOT_NR(endpt);
123 if(slot < 0 || slot >= nr_total) {
124 fprintf(stderr, "top: unreasonable endpoint number %d\n", endpt);
125 fclose(fp);
126 return;
129 p = &proc[slot];
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;
137 p->p_pid = pid;
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) {
143 fclose(fp);
144 return;
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);
153 p->p_memory = 0L;
155 if (!(p->p_flags & IS_TASK)) {
156 int i;
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) {
160 fclose(fp);
161 return;
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);
171 } else {
172 p->p_cpucycles[i] = make64(0, 0);
176 p->p_flags |= USED;
178 fclose(fp);
181 void parse_dir(void)
183 DIR *p_dir;
184 struct dirent *p_ent;
185 pid_t pid;
186 char *end;
188 if ((p_dir = opendir(".")) == NULL) {
189 perror("opendir on " _PATH_PROC);
190 exit(1);
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)
197 parse_file(pid);
200 closedir(p_dir);
203 void get_procs(void)
205 struct proc *p;
206 int i;
208 p = prev_proc;
209 prev_proc = proc;
210 proc = p;
212 if (proc == NULL) {
213 proc = malloc(nr_total * sizeof(proc[0]));
215 if (proc == NULL) {
216 fprintf(stderr, "Out of memory!\n");
217 exit(1);
221 for (i = 0; i < nr_total; i++)
222 proc[i].p_flags = 0;
224 parse_dir();
227 int print_memory(void)
229 FILE *fp;
230 unsigned int pagesize;
231 unsigned long total, free, largest, cached;
233 if ((fp = fopen("meminfo", "r")) == NULL)
234 return 0;
236 if (fscanf(fp, "%u %lu %lu %lu %lu", &pagesize, &total, &free,
237 &largest, &cached) != 5) {
238 fclose(fp);
239 return 0;
242 fclose(fp);
244 printf("main memory: %ldK total, %ldK free, %ldK contig free, "
245 "%ldK cached\n",
246 (pagesize * total)/1024, (pagesize * free)/1024,
247 (pagesize * largest)/1024, (pagesize * cached)/1024);
249 return 1;
252 int print_load(double *loads, int nloads)
254 int i;
255 printf("load averages: ");
256 for(i = 0; i < nloads; i++)
257 printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
258 printf("\n");
259 return 1;
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)
270 continue;
271 if(!(proc[p].p_flags & USED))
272 continue;
273 alive++;
274 if(proc[p].p_flags & BLOCKED)
275 sleeping++;
276 else
277 running++;
279 printf("%d processes: %d running, %d sleeping\n",
280 alive, running, sleeping);
281 return 1;
284 struct tp {
285 struct proc *p;
286 u64_t ticks;
289 int cmp_procs(const void *v1, const void *v2)
291 int c;
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;
298 return 0;
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)
315 return -1;
316 if(!p2blocked && p1blocked)
317 return 1;
318 } else if((c=cmp64(p1->ticks, p2->ticks)) != 0)
319 return -c;
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)
327 int t;
329 for(t = 0; t < np; t++)
330 if(who == tptab[t].p->p_endpoint)
331 return &tptab[t];
333 fprintf(stderr, "lookup: tp %d (0x%x) not found.\n", who, who);
334 abort();
336 return NULL;
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
344 * arithmetics
346 #define SCALE (1 << 12)
348 double ktotal = 0;
350 void print_proc(struct tp *tp, u32_t tcyc)
352 int euid = 0;
353 static struct passwd *who = NULL;
354 static int last_who = -1;
355 char *name = "";
356 unsigned long pcyc;
357 int ticks;
358 struct proc *pr = tp->p;
360 printf("%5d ", pr->p_pid);
361 euid = pr->p_effuid;
362 name = pr->p_name;
364 if(last_who != euid || !who) {
365 who = getpwuid(euid);
366 last_who = 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);
371 else printf(" ");
373 printf(" %2d ", pr->p_priority);
374 if(!(pr->p_flags & IS_TASK)) {
375 printf(" %3d ", pr->p_nice);
376 } else printf(" ");
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];
390 int i;
392 name[0] = '\0';
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]);
399 strcat(name, " ");
403 return name;
406 u64_t cputicks(struct proc *p1, struct proc *p2, int timemode)
408 int i;
409 u64_t t = make64(0, 0);
410 for(i = 0; i < CPUTIMENAMES; i++) {
411 if(!CPUTIME(timemode, i))
412 continue;
413 if(p1->p_endpoint == p2->p_endpoint) {
414 t = add64(t, sub64(p2->p_cpucycles[i],
415 p1->p_cpucycles[i]));
416 } else {
417 t = add64(t, p2->p_cpucycles[i]);
421 return t;
424 void print_procs(int maxlines,
425 struct proc *proc1, struct proc *proc2, int cputimemode)
427 int p, nprocs;
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);
433 unsigned long tcyc;
434 unsigned long tmp;
435 int blockedseen = 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");
443 exit(1);
447 for(p = nprocs = 0; p < nr_total; p++) {
448 u64_t uticks;
449 if(!(proc2[p].p_flags & USED))
450 continue;
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) {
456 idleticks = uticks;
457 continue;
459 if(p-NR_TASKS == KERNEL) {
460 kernelticks = uticks;
461 continue;
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);
467 else
468 userticks = add64(userticks,
469 tick_procs[nprocs].ticks);
472 nprocs++;
475 if (!cmp64u(total_ticks, 0))
476 return;
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)
495 NEWLINE;
497 printf("CPU time displayed (press '%c' to cycle): %s",
498 TIMECYCLEKEY, cputimemodename(cputimemode));
499 NEWLINE;
501 NEWLINE;
503 printf(" PID USERNAME PRI NICE SIZE STATE TIME CPU COMMAND");
504 NEWLINE;
505 for(p = 0; p < nprocs; p++) {
506 struct proc *pr;
507 int level = 0;
509 pr = tick_procs[p].p;
511 if(pr->p_flags & IS_TASK) {
512 /* skip old kernel tasks as they don't run anymore */
513 continue;
516 /* If we're in blocked verbose mode, indicate start of
517 * blocked processes.
519 if(blockedverbose && (pr->p_flags & BLOCKED) && !blockedseen) {
520 NEWLINE;
521 printf("Blocked processes:");
522 NEWLINE;
523 blockedseen = 1;
526 print_proc(&tick_procs[p], tcyc);
527 NEWLINE;
529 if(!blockedverbose)
530 continue;
532 /* Traverse dependency chain if blocked. */
533 while(pr->p_flags & BLOCKED) {
534 endpoint_t dep = NONE;
535 struct tp *tpdep;
536 level += 5;
538 if((dep = pr->p_blocked) == NONE) {
539 printf("not blocked on a process");
540 NEWLINE;
541 break;
544 if(dep == ANY)
545 break;
547 tpdep = lookup(dep, tick_procs, nprocs);
548 pr = tpdep->p;
549 printf("%*s> ", level, "");
550 print_proc(tpdep, tcyc);
551 NEWLINE;
556 void showtop(int cputimemode, int r)
558 #define NLOADS 3
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");
566 exit(1);
569 get_procs();
570 if (prev_proc == NULL)
571 get_procs();
573 if((nloads = getloadavg(loads, NLOADS)) != NLOADS) {
574 fprintf(stderr, "getloadavg() failed - %d loads\n", nloads);
575 exit(1);
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);
588 fflush(NULL);
591 void init(int *rows)
593 char *term;
594 static char buffer[TC_BUFFER], strings[TC_STRINGS];
595 char *s = strings, *v;
597 *rows = 0;
599 if(!(term = getenv("TERM"))) {
600 fprintf(stderr, "No TERM set\n");
601 exit(1);
604 if ( tgetent( buffer, term ) != 1 ) {
605 fprintf(stderr, "tgetent failed for term %s\n", term);
606 exit(1);
609 initscr();
610 cbreak();
612 if ( (Tclr_all = tgetstr( "cl", &s )) == NULL )
613 Tclr_all = "\f";
615 if((v = tgetstr ("li", &s)) != NULL)
616 sscanf(v, "%d", rows);
617 if(*rows < 1) *rows = 24;
620 void sigwinch(int sig) { }
622 void getkinfo(void)
624 FILE *fp;
626 if ((fp = fopen("kinfo", "r")) == NULL) {
627 fprintf(stderr, "opening " _PATH_PROC "kinfo failed\n");
628 exit(1);
631 if (fscanf(fp, "%u %u", &nr_procs, &nr_tasks) != 2) {
632 fprintf(stderr, "reading from " _PATH_PROC "kinfo failed\n");
633 exit(1);
636 fclose(fp);
638 nr_total = (int) (nr_procs + nr_tasks);
641 int main(int argc, char *argv[])
643 int r, c, s = 0;
644 int cputimemode = 1; /* bitmap. */
646 if (chdir(_PATH_PROC) != 0) {
647 perror("chdir to " _PATH_PROC);
648 return 1;
651 system_hz = (u32_t) sysconf(_SC_CLK_TCK);
653 getkinfo();
655 init(&r);
657 while((c=getopt(argc, argv, "s:B")) != EOF) {
658 switch(c) {
659 case 's':
660 s = atoi(optarg);
661 break;
662 case 'B':
663 blockedverbose = 1;
664 break;
665 default:
666 fprintf(stderr,
667 "Usage: %s [-s<secdelay>] [-B]\n",
668 argv[0]);
669 return 1;
673 if(s < 1)
674 s = 2;
676 /* Catch window size changes so display is updated properly
677 * right away.
679 signal(SIGWINCH, sigwinch);
681 while(1) {
682 fd_set fds;
683 int ns;
684 struct timeval tv;
685 showtop(cputimemode, r);
686 tv.tv_sec = s;
687 tv.tv_usec = 0;
689 FD_ZERO(&fds);
690 FD_SET(STDIN_FILENO, &fds);
692 if((ns=select(STDIN_FILENO+1, &fds, NULL, NULL, &tv)) < 0
693 && errno != EINTR) {
694 perror("select");
695 sleep(1);
698 if(ns > 0 && FD_ISSET(STDIN_FILENO, &fds)) {
699 char c;
700 if(read(STDIN_FILENO, &c, 1) == 1) {
701 switch(c) {
702 case 'q':
703 putchar('\r');
704 return 0;
705 break;
706 case 'o':
707 order++;
708 if(order > ORDER_HIGHEST)
709 order = 0;
710 break;
711 case TIMECYCLEKEY:
712 cputimemode++;
713 if(cputimemode >= (1L << CPUTIMENAMES))
714 cputimemode = 1;
715 break;
721 return 0;