a program to see who consumes the most resources on a machine. written by Aki Nyrhinen Notes: Sat Oct 21 22:47:03 EDT 2006 geoff We'd prefer to see something like Unix's watch(1) which just updates a window with the output of an arbitrary command every time interval. Reference: /n/sources/patch/sorry/top Date: Mon Oct 16 21:53:38 CES 2006 Signed-off-by: andrey@lanl.gov Reviewed-by: geoff --- /sys/src/cmd/top.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/top.c Mon Oct 16 21:52:37 2006 @@ -0,0 +1,418 @@ +#include +#include +#include +#include + +typedef struct Proc Proc; +struct Proc { + char pid[32]; + char name[32]; + char user[32]; + long cpri; + long bpri; + long promil; + long usr; + long sys; + long mem; + long pusr; + long psys; + long pmem; +}; + +enum { + Mcpu, + Mmem, + Muser, + Mprio, + Mexit, +}; + +char *menustr[] = { + "cpu", + "mem", + "user", + "prio", + "exit", + nil +}; +Menu menu = { + menustr, + nil, + -1 +}; + +int (*cmp)(void *, void *); + +Image *normal; +Image *title; + +int procallo; +int nproc, sleeptime = 5000; +int sort = Mcpu; +Proc *procs; + +void updater(int fd); + +void +usage(void) +{ + fprint(2, "usage: %s [-f font]\n", argv0); + exits("usage"); +} + +int +cpucmp(void *aptr, void *bptr) +{ + Proc *a, *b; + a = aptr; + b = bptr; + return b->promil - a->promil; +} + +int +memcmpr(void *aptr, void *bptr) +{ + Proc *a, *b; + a = aptr; + b = bptr; + return b->mem - a->mem; +} + +int +usercmp(void *aptr, void *bptr) +{ + Proc *a, *b; + a = aptr; + b = bptr; + return strcmp(a->user, b->user); +} + +int +priocmp(void *aptr, void *bptr) +{ + Proc *a, *b; + a = aptr; + b = bptr; + return b->cpri - a->cpri; +} + +void +main(int argc, char **argv) +{ + Event e; + char path[64]; + char *font = nil; + int kupd, k, p[2], pid, fd; + + ARGBEGIN{ + case 'f': + font = EARGF(usage()); + break; + case 'T': + sleeptime = atoi(EARGF(usage())); + if(sleeptime <= 0) + sysfatal("bad sleep time: %d\n", sleeptime); + break; + default: + usage(); + }ARGEND + + if(font == nil) + font = getenv("font"); + + if(initdraw(nil, font, "top") == -1) + sysfatal("initdraw"); + + normal = allocimagemix(display, DPalebluegreen, DWhite); + title = allocimagemix(display, DPalegreyblue, DWhite); + + if(normal == nil || title == nil) + sysfatal("allocimagemix"); + + pipe(p); + pid = rfork(RFPROC|RFMEM); + if(!pid) + updater(p[0]); + + cmp = cpucmp; + + einit(Ekeyboard|Emouse); + kupd = estart(0, p[1], 1); + eresized(0); + for(;;) + switch(k = event(&e)){ + default: + if(k == kupd){ + eresized(0); + write(p[1], &k, 1); /* release */ + } + break; + case Ekeyboard: + switch(e.kbdc){ + default: + break; + case 0x7f: + case 'q': + goto casequit; + } + break; + case Emouse: + if(e.mouse.buttons & 4) + switch(emenuhit(3, &e.mouse, &menu)){ + default: + break; + case Mcpu: + cmp = cpucmp; + break; + case Mmem: + cmp = memcmpr; + break; + case Muser: + cmp = usercmp; + break; + case Mprio: + cmp = priocmp; + break; + case Mexit: + casequit: + snprint(path, sizeof path, "/proc/%d/note", pid); + fd = open(path, OWRITE); + write(fd, "die", 3); + close(fd); + exits(0); + } + } +} + +long +msec(void) +{ + vlong ms; + ms = nsec(); + ms /= 1000000LL; + return (long)ms; +} + +void +updater(int syncfd) +{ + static char buf[256]; + long prev, now, elaps; + + prev = msec(); + for(;;){ + int fd, proci; + long i, j, n, ndir; + Dir *dir; + fd = open("/proc", OREAD); + ndir = dirreadall(fd, &dir); + close(fd); + chdir("/proc"); + if(procallo < ndir){ + procallo = ndir; + /* + * allocate for worst case: all old processes died and as many + * new ones were created + */ + procs = realloc(procs, 2 * sizeof(Proc) * procallo); + } + now = msec(); + elaps = now - prev; + if(elaps == 0) + elaps = 1; + proci = 0; + + for(i = 0; i < nproc; i++) + *procs[i].name = 0; + for(i = 0; i < ndir; i++){ + char *tok[12]; + if(*dir[i].name < '0' || *dir[i].name > '9') + continue; + + chdir(dir[i].name); + fd = open("status", OREAD); + if(fd == -1) + continue; + n = read(fd, buf, sizeof buf - 1); + close(fd); + chdir(".."); + + buf[n] = 0; + tokenize(buf, tok, nelem(tok)); + for(j = 0; j < nproc; j++) { + if(!strcmp(procs[j].pid, dir[i].name)){ + long dif; + strncpy(procs[j].user, tok[1], 31); + procs[j].user[31] = '\0'; + procs[j].pusr = procs[j].usr; + procs[j].psys = procs[j].sys; + procs[j].pmem = procs[j].mem; + procs[j].usr = atol(tok[3]) + atol(tok[6]); + procs[j].sys = atol(tok[4]) + atol(tok[7]); + procs[j].mem = atol(tok[9]); + procs[j].bpri = atol(tok[10]); + procs[j].cpri = atol(tok[11]); + strncpy(procs[j].name, tok[0], 32); + dif = procs[j].usr + procs[j].sys; + dif -= procs[j].pusr + procs[j].psys; + procs[j].promil = (dif * 1000) / elaps; + break; + } + } + if(j == nproc){ + strncpy(procs[nproc].pid, dir[i].name, 32); + strncpy(procs[nproc].name, tok[0], 32); + strncpy(procs[j].user, tok[1], 31); + procs[j].user[31] = '\0'; + procs[nproc].promil = 0; + procs[nproc].usr = atol(tok[3]) + atol(tok[6]); + procs[nproc].sys = atol(tok[4]) + atol(tok[7]); + procs[nproc].mem = atol(tok[9]); + procs[nproc].pusr = atol(tok[3]) + atol(tok[6]); + procs[nproc].psys = atol(tok[4]) + atol(tok[7]); + procs[nproc].pmem = atol(tok[9]); + procs[nproc].bpri = atol(tok[10]); + procs[nproc].cpri = atol(tok[11]); + nproc++; + } + proci++; + } + j = 0; + for(i = 0; i < nproc; i++) + if(*procs[i].name != 0) + memmove(&procs[j++], &procs[i], sizeof procs[0]); + nproc = j; + + qsort(procs, nproc, sizeof(Proc), cmp); + nproc = proci; + free(dir); + write(syncfd, buf, 1); + read(syncfd, buf, 1); + prev = now; + sleep(sleeptime); + } +} + +void +eresized(int new) +{ + Point p, dp; + int i, wid, namewid, userwid, memwid, difwid, priwid; + long t; + char str[256]; + Image *bnc; + + if(new) + if(getwindow(display, Refmesg) == -1) + sysfatal("can't reattach window"); + + priwid = namewid = userwid = memwid = difwid = 0; + p = screen->r.min; + for(i = 0; i < nproc; i++){ + wid = stringwidth(font, procs[i].name); + if(wid > namewid) + namewid = wid; + + wid = stringwidth(font, procs[i].user); + if(wid > userwid) + userwid = wid; + + t = procs[i].promil; + snprint(str, sizeof str, "%ld.%01ld%%", t / 10, t % 10); + wid = stringwidth(font, str); + if(wid > difwid) + difwid = wid; + + t = procs[i].mem; + snprint(str, sizeof str, "%ld.%01ldM", t / 1000, (t % 1000) / 100); + wid = stringwidth(font, str); + if(wid > memwid) + memwid = wid; + + t = procs[i].cpri; + snprint(str, sizeof str, "%ld", t); + wid = stringwidth(font, str); + if(wid > priwid) + priwid = wid; + } + wid = stringwidth(font, "prio"); + if(priwid < wid) + priwid = wid; + priwid += 10; + namewid += 10; + userwid += 10; + memwid += 10; + difwid += 10; + + bnc = allocimage(display, Rect(0,0, Dx(screen->r), font->height), screen->chan, 0, DOpaque); + dp = screen->r.min; + p = bnc->r.min; + p.x += 10; + draw(bnc, bnc->r, title, nil, ZP); + string(bnc, p, display->black, ZP, font, "name"); + p.x += namewid; + + string(bnc, p, display->black, ZP, font, "user"); + p.x += userwid; + + + wid = stringwidth(font, "%cpu"); + p.x += difwid - wid; + string(bnc, p, display->black, ZP, font, "%cpu"); + p.x += wid; + + wid = stringwidth(font, "mem"); + p.x += memwid - wid; + string(bnc, p, display->black, ZP, font, "mem"); + p.x += wid; + + wid = stringwidth(font, "prio"); + p.x += priwid - wid; + string(bnc, p, display->black, ZP, font, "prio"); + p.x += wid; + + border(bnc, bnc->r, 1, display->black, ZP); + draw(screen, Rpt(dp, addpt(dp, Pt(Dx(bnc->r), font->height))), bnc, nil, ZP); + dp.y += Dy(bnc->r) + 2; + + for(i = 0; dp.y < screen->r.max.y && i < nproc; i++){ + p = bnc->r.min; + p.x += 10; + draw(bnc, bnc->r, normal, nil, ZP); + string(bnc, p, display->black, ZP, font, procs[i].name); + p.x += namewid; + + string(bnc, p, display->black, ZP, font, procs[i].user); + p.x += userwid; + + t = procs[i].promil; + snprint(str, sizeof str, "%ld.%01ld%%", t / 10, t % 10); + wid = stringwidth(font, str); + p.x += difwid - wid; + string(bnc, p, display->black, ZP, font, str); + p.x += wid; + + t = procs[i].mem; + snprint(str, sizeof str, "%ld.%01ldM", t / 1000, (t % 1000) / 100); + wid = stringwidth(font, str); + p.x += memwid - wid; + string(bnc, p, display->black, ZP, font, str); + p.x += wid; + + t = procs[i].cpri; + snprint(str, sizeof str, "%ld", t); + wid = stringwidth(font, str); + p.x += priwid - wid; + string(bnc, p, display->black, ZP, font, str); + p.x += wid; + + border(bnc, bnc->r, 1, display->black, ZP); + + draw(screen, Rpt(dp, addpt(dp, Pt(Dx(bnc->r), font->height))), bnc, nil, ZP); + + dp.y += Dy(bnc->r) + 2; + } + if(dp.y < screen->r.max.y) + draw(screen, Rpt(dp, screen->r.max), display->white, nil, ZP); + freeimage(bnc); +} --- /sys/man/8/top Thu Jan 1 00:00:00 1970 +++ /sys/man/8/top Mon Oct 16 21:52:53 2006 @@ -0,0 +1,35 @@ +.TH STATS 8 +.SH NAME +top \- display information about processes +.SH SYNOPSIS +.B top +[ +.BI - t µs +] +[ +.BI - f font +] +.SH DESCRIPTION +.I Top +displays a list of processes running on the +.I machine +it was started on. Each line contains the executable name of a process, the user runing it, percent of the cpu used in the last period, memory consumption and its priority. The processes are ordered based on the cpu time they have consumed in the last time interval. +.PP +The right mouse button presents a menu to change the order in which processes are displayed. Possible ordering is by cpu time, memory consumption, user and priority. +.PP +The +option +.TP +.BI -T " µs +specifies the duration between samples (default 5000µs). +.TP +.BI -f " font +specifies a font different than the default system font. +.PD +.SH FILES +.B /proc/pid/stats +.SH SOURCE +.B /sys/src/cmd/top.c +.SH BUGS +.I Top +is designed to show process information, not to control processes. Hence it omits the process id in its display.