re-establish kernel assert()s.
[minix.git] / commands / simple / top.c
blob9fa434b1daecdcef696bc0bba0d0beb8cd2d9e1e
2 /* Author: Ben Gras <beng@few.vu.nl> 17 march 2006 */
4 #define _MINIX 1
5 #define _POSIX_SOURCE 1
7 #include <stdio.h>
8 #include <pwd.h>
9 #include <curses.h>
10 #include <timers.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <limits.h>
14 #include <termcap.h>
15 #include <termios.h>
16 #include <time.h>
17 #include <string.h>
18 #include <signal.h>
19 #include <fcntl.h>
20 #include <errno.h>
22 #include <sys/ioc_tty.h>
23 #include <sys/times.h>
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <sys/select.h>
28 #include <minix/ipc.h>
29 #include <minix/com.h>
30 #include <minix/sysinfo.h>
31 #include <minix/config.h>
32 #include <minix/type.h>
33 #include <minix/const.h>
35 #include <minix/u64.h>
37 #include <machine/archtypes.h>
38 #include "../../servers/pm/mproc.h"
39 #include "../../kernel/const.h"
40 #include "../../kernel/proc.h"
42 u32_t system_hz;
44 #define TC_BUFFER 1024 /* Size of termcap(3) buffer */
45 #define TC_STRINGS 200 /* Enough room for cm,cl,so,se */
47 char *Tclr_all;
49 int blockedverbose = 0;
51 #if 0
52 int print_memory(struct pm_mem_info *pmi)
54 int h;
55 int largest_bytes = 0, total_bytes = 0;
56 for(h = 0; h < _NR_HOLES; h++) {
57 if(pmi->pmi_holes[h].h_base && pmi->pmi_holes[h].h_len) {
58 int bytes;
59 bytes = pmi->pmi_holes[h].h_len << CLICK_SHIFT;
60 if(bytes > largest_bytes) largest_bytes = bytes;
61 total_bytes += bytes;
65 printf("Mem: %dK Free, %dK Contiguous Free\n",
66 total_bytes/1024, largest_bytes/1024);
68 return 1;
70 #endif
72 int print_load(double *loads, int nloads)
74 int i;
75 printf("load averages: ");
76 for(i = 0; i < nloads; i++)
77 printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
78 printf("\n");
79 return 1;
82 #define PROCS (NR_PROCS+NR_TASKS)
84 int print_proc_summary(struct proc *proc)
86 int p, alive, running, sleeping;
88 alive = running = sleeping = 0;
90 for(p = 0; p < PROCS; p++) {
91 if(p - NR_TASKS == IDLE)
92 continue;
93 if(isemptyp(&proc[p]))
94 continue;
95 alive++;
96 if(!proc_is_runnable(&proc[p]))
97 sleeping++;
98 else
99 running++;
101 printf("%d processes: %d running, %d sleeping\n",
102 alive, running, sleeping);
103 return 1;
106 static struct tp {
107 struct proc *p;
108 u64_t ticks;
111 int cmp_ticks(const void *v1, const void *v2)
113 int c;
114 struct tp *p1 = (struct tp *) v1, *p2 = (struct tp *) v2;
115 int p1blocked, p2blocked;
117 p1blocked = !!p1->p->p_rts_flags;
118 p2blocked = !!p2->p->p_rts_flags;
120 /* Primarily order by used number of cpu cycles.
122 * Exception: if in blockedverbose mode, a blocked
123 * process is always printed after an unblocked
124 * process, and used cpu cycles don't matter.
126 * In both cases, process slot number is a tie breaker.
129 if(blockedverbose && (p1blocked || p2blocked)) {
130 if(!p1blocked && p2blocked)
131 return -1;
132 if( p2blocked && !p1blocked)
133 return 1;
134 } else if((c=cmp64(p1->ticks, p2->ticks)) != 0)
135 return -c;
137 /* Process slot number is a tie breaker. */
139 if(p1->p->p_nr < p2->p->p_nr)
140 return -1;
141 if(p1->p->p_nr > p2->p->p_nr)
142 return 1;
144 fprintf(stderr, "unreachable.\n");
145 abort();
148 struct tp *lookup(endpoint_t who, struct tp *tptab, int np)
150 int t;
152 for(t = 0; t < np; t++)
153 if(who == tptab[t].p->p_endpoint)
154 return &tptab[t];
156 fprintf(stderr, "lookup: tp %d (0x%x) not found.\n", who, who);
157 abort();
159 return NULL;
163 * since we don't have true div64(u64_t, u64_t) we scale the 64 bit counters to
164 * 32. Since the samplig happens every ~1s and the counters count CPU cycles
165 * during this period, unless we have extremely fast CPU, shifting the counters
166 * by 12 make them 32bit counters which we can use for normal integer
167 * arithmetics
169 #define SCALE (1 << 12)
171 void print_proc(struct tp *tp, struct mproc *mpr, u32_t tcyc)
173 int euid = 0;
174 static struct passwd *who = NULL;
175 static int last_who = -1;
176 char *name = "";
177 unsigned long pcyc;
178 int ticks;
179 struct proc *pr = tp->p;
181 printf("%5d ", mpr->mp_pid);
182 euid = mpr->mp_effuid;
183 name = mpr->mp_name;
185 if(last_who != euid || !who) {
186 who = getpwuid(euid);
187 last_who = euid;
190 if(who && who->pw_name) printf("%-8s ", who->pw_name);
191 else if(pr->p_nr >= 0) printf("%8d ", mpr->mp_effuid);
192 else printf(" ");
194 printf(" %2d ", pr->p_priority);
195 if(pr->p_nr >= 0) {
196 printf(" %3d ", mpr->mp_nice);
197 } else printf(" ");
198 printf("%5dK",
199 ((pr->p_memmap[T].mem_len +
200 pr->p_memmap[D].mem_len) << CLICK_SHIFT)/1024);
201 printf("%6s", pr->p_rts_flags ? "" : "RUN");
202 ticks = pr->p_user_time;
203 printf(" %3d:%02d ", (ticks/system_hz/60), (ticks/system_hz)%60);
205 pcyc = div64u(tp->ticks, SCALE);
207 printf("%6.2f%% %s", 100.0*pcyc/tcyc, name);
210 void print_procs(int maxlines,
211 struct proc *proc1, struct proc *proc2,
212 struct mproc *mproc)
214 int p, nprocs, tot=0;
215 u64_t idleticks = cvu64(0);
216 u64_t kernelticks = cvu64(0);
217 u64_t systemticks = cvu64(0);
218 u64_t userticks = cvu64(0);
219 u64_t total_ticks = cvu64(0);
220 unsigned long tcyc;
221 unsigned long tmp;
222 int blockedseen = 0;
223 struct tp tick_procs[PROCS];
225 for(p = nprocs = 0; p < PROCS; p++) {
226 if(isemptyp(&proc2[p]))
227 continue;
228 tick_procs[nprocs].p = proc2 + p;
229 if(proc1[p].p_endpoint == proc2[p].p_endpoint) {
230 tick_procs[nprocs].ticks =
231 sub64(proc2[p].p_cycles, proc1[p].p_cycles);
232 } else {
233 tick_procs[nprocs].ticks =
234 proc2[p].p_cycles;
236 total_ticks = add64(total_ticks, tick_procs[nprocs].ticks);
237 if(p-NR_TASKS == IDLE) {
238 idleticks = tick_procs[nprocs].ticks;
239 continue;
241 if(p-NR_TASKS == KERNEL) {
242 kernelticks = tick_procs[nprocs].ticks;
243 continue;
245 if(mproc[proc2[p].p_nr].mp_procgrp == 0)
246 systemticks = add64(systemticks, tick_procs[nprocs].ticks);
247 else if (p > NR_TASKS)
248 userticks = add64(userticks, tick_procs[nprocs].ticks);
250 nprocs++;
253 if (!cmp64u(total_ticks, 0))
254 return;
256 qsort(tick_procs, nprocs, sizeof(tick_procs[0]), cmp_ticks);
258 tcyc = div64u(total_ticks, SCALE);
260 tmp = div64u(userticks, SCALE);
261 printf("CPU states: %6.2f%% user, ", 100.0*(tmp)/tcyc);
263 tmp = div64u(systemticks, SCALE);
264 printf("%6.2f%% system, ", 100.0*tmp/tcyc);
266 tmp = div64u(kernelticks, SCALE);
267 printf("%6.2f%% kernel, ", 100.0*tmp/tcyc);
269 tmp = div64u(idleticks, SCALE);
270 printf("%6.2f%% idle", 100.0*tmp/tcyc);
272 #define NEWLINE do { printf("\n"); if(--maxlines <= 0) { return; } } while(0)
273 NEWLINE;
274 NEWLINE;
276 printf(" PID USERNAME PRI NICE SIZE STATE TIME CPU COMMAND");
277 NEWLINE;
278 for(p = 0; p < nprocs; p++) {
279 struct proc *pr;
280 int pnr;
281 int level = 0;
283 pnr = tick_procs[p].p->p_nr;
285 if(pnr < 0) {
286 /* skip old kernel tasks as they don't run anymore */
287 continue;
290 pr = tick_procs[p].p;
292 /* If we're in blocked verbose mode, indicate start of
293 * blocked processes.
295 if(blockedverbose && pr->p_rts_flags && !blockedseen) {
296 NEWLINE;
297 printf("Blocked processes:");
298 NEWLINE;
299 blockedseen = 1;
302 print_proc(&tick_procs[p], &mproc[pnr], tcyc);
303 NEWLINE;
305 if(!blockedverbose)
306 continue;
308 /* Traverse dependency chain if blocked. */
309 while(pr->p_rts_flags) {
310 endpoint_t dep = NONE;
311 struct tp *tpdep;
312 level += 5;
314 if((dep = P_BLOCKEDON(pr)) == NONE) {
315 printf("not blocked on a process");
316 NEWLINE;
317 break;
320 if(dep == ANY)
321 break;
323 tpdep = lookup(dep, tick_procs, nprocs);
324 pr = tpdep->p;
325 printf("%*s> ", level, "");
326 print_proc(tpdep, &mproc[pr->p_nr], tcyc);
327 NEWLINE;
332 void showtop(int r)
334 #define NLOADS 3
335 double loads[NLOADS];
336 int nloads, i, p, lines = 0;
337 static struct proc prev_proc[PROCS], proc[PROCS];
338 static int preheated = 0;
339 struct winsize winsize;
341 static struct pm_mem_info pmi;
343 static struct mproc mproc[NR_PROCS];
344 int mem = 0;
346 if(ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) != 0) {
347 perror("TIOCGWINSZ");
348 fprintf(stderr, "TIOCGWINSZ failed\n");
349 exit(1);
352 #if 0
353 if(getsysinfo(PM_PROC_NR, SI_MEM_ALLOC, &pmi) < 0) {
354 fprintf(stderr, "getsysinfo() for SI_MEM_ALLOC failed.\n");
355 mem = 0;
356 exit(1);;
357 } else mem = 1;
358 #endif
360 retry:
361 if(getsysinfo(PM_PROC_NR, SI_KPROC_TAB, proc) < 0) {
362 fprintf(stderr, "getsysinfo() for SI_KPROC_TAB failed.\n");
363 exit(1);
365 if (!preheated) {
366 preheated = 1;
367 memcpy(prev_proc, proc, sizeof(prev_proc));
368 goto retry;;
371 if(getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc) < 0) {
372 fprintf(stderr, "getsysinfo() for SI_PROC_TAB failed.\n");
373 exit(1);
376 if((nloads = getloadavg(loads, NLOADS)) != NLOADS) {
377 fprintf(stderr, "getloadavg() failed - %d loads\n", nloads);
378 exit(1);
382 printf("%s", Tclr_all);
384 lines += print_load(loads, NLOADS);
385 lines += print_proc_summary(proc);
386 #if 0
387 if(mem) { lines += print_memory(&pmi); }
388 #endif
390 if(winsize.ws_row > 0) r = winsize.ws_row;
392 print_procs(r - lines - 2, prev_proc,
393 proc, mproc);
395 memcpy(prev_proc, proc, sizeof(prev_proc));
398 void init(int *rows)
400 char *term;
401 static char buffer[TC_BUFFER], strings[TC_STRINGS];
402 char *s = strings, *v;
404 *rows = 0;
406 if(!(term = getenv("TERM"))) {
407 fprintf(stderr, "No TERM set\n");
408 exit(1);
411 if ( tgetent( buffer, term ) != 1 ) {
412 fprintf(stderr, "tgetent failed for term %s\n", term);
413 exit(1);
416 if ( (Tclr_all = tgetstr( "cl", &s )) == NULL )
417 Tclr_all = "\f";
419 if((v = tgetstr ("li", &s)) != NULL)
420 sscanf(v, "%d", rows);
421 if(*rows < 1) *rows = 24;
422 if(!initscr()) {
423 fprintf(stderr, "initscr() failed\n");
424 exit(1);
426 cbreak();
427 nl();
430 void sigwinch(int sig) { }
432 int main(int argc, char *argv[])
434 int r, c, s = 0, orig;
436 getsysinfo_up(PM_PROC_NR, SIU_SYSTEMHZ, sizeof(system_hz), &system_hz);
438 init(&r);
440 while((c=getopt(argc, argv, "s:B")) != EOF) {
441 switch(c) {
442 case 's':
443 s = atoi(optarg);
444 break;
445 case 'B':
446 blockedverbose = 1;
447 break;
448 default:
449 fprintf(stderr,
450 "Usage: %s [-s<secdelay>] [-B]\n",
451 argv[0]);
452 return 1;
456 if(s < 1)
457 s = 2;
459 /* Catch window size changes so display is updated properly
460 * right away.
462 signal(SIGWINCH, sigwinch);
464 while(1) {
465 fd_set fds;
466 int ns;
467 struct timeval tv;
468 showtop(r);
469 tv.tv_sec = s;
470 tv.tv_usec = 0;
472 FD_ZERO(&fds);
473 FD_SET(STDIN_FILENO, &fds);
474 if((ns=select(STDIN_FILENO+1, &fds, NULL, NULL, &tv)) < 0
475 && errno != EINTR) {
476 perror("select");
477 sleep(1);
480 if(ns > 0 && FD_ISSET(STDIN_FILENO, &fds)) {
481 char c;
482 if(read(STDIN_FILENO, &c, 1) == 1) {
483 switch(c) {
484 case 'q':
485 return 0;
486 break;
492 return 0;