summaryrefslogtreecommitdiff
path: root/sys/src/cmd/trace.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/trace.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/trace.c')
-rwxr-xr-xsys/src/cmd/trace.c759
1 files changed, 759 insertions, 0 deletions
diff --git a/sys/src/cmd/trace.c b/sys/src/cmd/trace.c
new file mode 100755
index 000000000..72262da40
--- /dev/null
+++ b/sys/src/cmd/trace.c
@@ -0,0 +1,759 @@
+#include <u.h>
+#include <tos.h>
+#include <libc.h>
+#include <thread.h>
+#include <ip.h>
+#include <bio.h>
+#include <draw.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <keyboard.h>
+#include "trace.h"
+
+#pragma varargck type "t" vlong
+#pragma varargck type "U" uvlong
+
+#define NS(x) ((vlong)x)
+#define US(x) (NS(x) * 1000ULL)
+#define MS(x) (US(x) * 1000ULL)
+#define S(x) (MS(x) * 1000ULL)
+
+#define numblocks(a, b) (((a) + (b) - 1) / (b))
+#define roundup(a, b) (numblocks((a), (b)) * (b))
+
+enum {
+ OneRound = MS(1)/2LL,
+ MilliRound = US(1)/2LL,
+};
+
+typedef struct Event Event;
+typedef struct Task Task;
+struct Event {
+ Traceevent;
+ vlong etime; /* length of block to draw */
+};
+
+struct Task {
+ int pid;
+ char *name;
+ int nevents;
+ Event *events;
+ vlong tstart;
+ vlong total;
+ vlong runtime;
+ vlong runmax;
+ vlong runthis;
+ long runs;
+ ulong tevents[Nevent];
+};
+
+enum {
+ Nevents = 1024,
+ Ncolor = 6,
+ K = 1024,
+};
+
+vlong now, prevts;
+
+int newwin;
+int Width = 1000;
+int Height = 100; // Per task
+int topmargin = 8;
+int bottommargin = 4;
+int lineht = 12;
+int wctlfd;
+int nevents;
+Traceevent *eventbuf;
+Event *event;
+
+void drawtrace(void);
+int schedparse(char*, char*, char*);
+int timeconv(Fmt*);
+
+char *schedstatename[] = {
+ [SAdmit] = "Admit",
+ [SSleep] = "Sleep",
+ [SDead] = "Dead",
+ [SDeadline] = "Deadline",
+ [SEdf] = "Edf",
+ [SExpel] = "Expel",
+ [SReady] = "Ready",
+ [SRelease] = "Release",
+ [SRun] = "Run",
+ [SSlice] = "Slice",
+ [SInts] = "Ints",
+ [SInte] = "Inte",
+ [SUser] = "User",
+ [SYield] = "Yield",
+};
+
+struct {
+ vlong scale;
+ vlong bigtics;
+ vlong littletics;
+ int sleep;
+} scales[] = {
+ { US(500), US(100), US(50), 0},
+ { US(1000), US(500), US(100), 0},
+ { US(2000), US(1000), US(200), 0},
+ { US(5000), US(1000), US(500), 0},
+ { MS(10), MS(5), MS(1), 20},
+ { MS(20), MS(10), MS(2), 20},
+ { MS(50), MS(10), MS(5), 20},
+ { MS(100), MS(50), MS(10), 20}, /* starting scaleno */
+ { MS(200), MS(100), MS(20), 20},
+ { MS(500), MS(100), MS(50), 50},
+ { MS(1000), MS(500), MS(100), 100},
+ { MS(2000), MS(1000), MS(200), 100},
+ { MS(5000), MS(1000), MS(500), 100},
+ { S(10), S(50), S(1), 100},
+ { S(20), S(10), S(2), 100},
+ { S(50), S(10), S(5), 100},
+ { S(100), S(50), S(10), 100},
+ { S(200), S(100), S(20), 100},
+ { S(500), S(100), S(50), 100},
+ { S(1000), S(500), S(100), 100},
+};
+
+int ntasks, verbose, triggerproc, paused;
+Task *tasks;
+Image *cols[Ncolor][4];
+Font *mediumfont, *tinyfont;
+Image *grey, *red, *green, *blue, *bg, *fg;
+char*profdev = "/proc/trace";
+
+static void
+usage(void)
+{
+ fprint(2, "Usage: %s [-d profdev] [-w] [-v] [-t triggerproc] [processes]\n", argv0);
+ exits(nil);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ int fd, i;
+ char fname[80];
+
+ fmtinstall('t', timeconv);
+ ARGBEGIN {
+ case 'd':
+ profdev = EARGF(usage());
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ newwin++;
+ break;
+ case 't':
+ triggerproc = (int)strtol(EARGF(usage()), nil, 0);
+ break;
+ default:
+ usage();
+ }
+ ARGEND;
+
+ fname[sizeof fname - 1] = 0;
+ for(i = 0; i < argc; i++){
+ snprint(fname, sizeof fname - 2, "/proc/%s/ctl",
+ argv[i]);
+ if((fd = open(fname, OWRITE)) < 0){
+ fprint(2, "%s: cannot open %s: %r\n",
+ argv[0], fname);
+ continue;
+ }
+
+ if(fprint(fd, "trace 1") < 0)
+ fprint(2, "%s: cannot enable tracing on %s: %r\n",
+ argv[0], fname);
+ close(fd);
+ }
+
+ drawtrace();
+}
+
+static void
+mkcol(int i, int c0, int c1, int c2)
+{
+ cols[i][0] = allocimagemix(display, c0, DWhite);
+ cols[i][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c1);
+ cols[i][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c2);
+ cols[i][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c0);
+}
+
+static void
+colinit(void)
+{
+ mediumfont = openfont(display, "/lib/font/bit/lucidasans/unicode.10.font");
+ if(mediumfont == nil)
+ mediumfont = font;
+ tinyfont = openfont(display, "/lib/font/bit/lucidasans/unicode.7.font");
+ if(tinyfont == nil)
+ tinyfont = font;
+ topmargin = mediumfont->height+2;
+ bottommargin = tinyfont->height+2;
+
+ /* Peach */
+ mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
+ /* Aqua */
+ mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
+ /* Yellow */
+ mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
+ /* Green */
+ mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
+ /* Blue */
+ mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
+ /* Grey */
+ cols[5][0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEEEEEEFF);
+ cols[5][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
+ cols[5][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x888888FF);
+ cols[5][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF);
+ grey = cols[5][2];
+ red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFF0000FF);
+ green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x00FF00FF);
+ blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x0000FFFF);
+ bg = display->white;
+ fg = display->black;
+}
+
+static void
+redraw(int scaleno)
+{
+ int n, i, j, x;
+ char buf[256];
+ Point p, q;
+ Rectangle r, rtime;
+ Task *t;
+ vlong ts, oldestts, newestts, period, ppp, scale, s, ss;
+
+# define time2x(t) ((int)(((t) - oldestts) / ppp))
+
+ scale = scales[scaleno].scale;
+ period = scale + scales[scaleno].littletics;
+ ppp = period / Width; // period per pixel.
+
+ /* Round `now' to a nice number */
+ newestts = now - (now % scales[scaleno].bigtics) +
+ (scales[scaleno].littletics>>1);
+
+ oldestts = newestts - period;
+
+//print("newestts %t, period %t, %d-%d\n", newestts, period, time2x(oldestts), time2x(newestts));
+ if (prevts < oldestts){
+ oldestts = newestts - period;
+
+ prevts = oldestts;
+ draw(screen, screen->r, bg, nil, ZP);
+ }else{
+ /* just white out time */
+ rtime = screen->r;
+ rtime.min.x = rtime.max.x - stringwidth(mediumfont, "00000000000.000s");
+ rtime.max.y = rtime.min.y + mediumfont->height;
+ draw(screen, rtime, bg, nil, ZP);
+ }
+ p = screen->r.min;
+ for (n = 0; n != ntasks; n++) {
+ t = &tasks[n];
+ /* p is upper left corner for this task */
+ rtime = Rpt(p, addpt(p, Pt(500, mediumfont->height)));
+ draw(screen, rtime, bg, nil, ZP);
+ snprint(buf, sizeof(buf), "%d %s", t->pid, t->name);
+ q = string(screen, p, fg, ZP, mediumfont, buf);
+ s = now - t->tstart;
+ if(t->tevents[SRelease])
+ snprint(buf, sizeof(buf), " per %t — avg: %t max: %t",
+ (vlong)(s/t->tevents[SRelease]),
+ (vlong)(t->runtime/t->tevents[SRelease]),
+ t->runmax);
+ else if((s /=1000000000LL) != 0)
+ snprint(buf, sizeof(buf), " per 1s — avg: %t total: %t",
+ t->total/s,
+ t->total);
+ else
+ snprint(buf, sizeof(buf), " total: %t", t->total);
+ string(screen, q, fg, ZP, tinyfont, buf);
+ p.y += Height;
+ }
+ x = time2x(prevts);
+
+ p = screen->r.min;
+ for (n = 0; n != ntasks; n++) {
+ t = &tasks[n];
+
+ /* p is upper left corner for this task */
+
+ /* Move part already drawn */
+ r = Rect(p.x, p.y + topmargin, p.x + x, p.y+Height);
+ draw(screen, r, screen, nil, Pt(p.x + Width - x, p.y + topmargin));
+
+ r.max.x = screen->r.max.x;
+ r.min.x += x;
+ draw(screen, r, bg, nil, ZP);
+
+ line(screen, addpt(p, Pt(x, Height - lineht)), Pt(screen->r.max.x, p.y + Height - lineht),
+ Endsquare, Endsquare, 0, cols[n % Ncolor][1], ZP);
+
+ for (i = 0; i < t->nevents-1; i++)
+ if (prevts < t->events[i + 1].time)
+ break;
+
+ if (i > 0) {
+ memmove(t->events, t->events + i, (t->nevents - i) * sizeof(Event));
+ t->nevents -= i;
+ }
+
+ for (i = 0; i != t->nevents; i++) {
+ Event *e = &t->events[i], *_e;
+ int sx, ex;
+
+ switch (e->etype & 0xffff) {
+ case SAdmit:
+ if (e->time > prevts && e->time <= newestts) {
+ sx = time2x(e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endarrow, Endsquare, 1, green, ZP);
+ }
+ break;
+ case SExpel:
+ if (e->time > prevts && e->time <= newestts) {
+ sx = time2x(e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endsquare, Endarrow, 1, red, ZP);
+ }
+ break;
+ case SRelease:
+ if (e->time > prevts && e->time <= newestts) {
+ sx = time2x(e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endarrow, Endsquare, 1, fg, ZP);
+ }
+ break;
+ case SDeadline:
+ if (e->time > prevts && e->time <= newestts) {
+ sx = time2x(e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endsquare, Endarrow, 1, fg, ZP);
+ }
+ break;
+
+ case SYield:
+ case SUser:
+ if (e->time > prevts && e->time <= newestts) {
+ sx = time2x(e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endsquare, Endarrow, 0,
+ (e->etype == SYield)? green: blue, ZP);
+ }
+ break;
+ case SSlice:
+ if (e->time > prevts && e->time <= newestts) {
+ sx = time2x(e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endsquare, Endarrow, 0, red, ZP);
+ }
+ break;
+
+ case SRun:
+ case SEdf:
+ sx = time2x(e->time);
+ ex = time2x(e->etime);
+ if(ex == sx)
+ ex++;
+
+ r = Rect(sx, topmargin + 8, ex, Height - lineht);
+ r = rectaddpt(r, p);
+
+ draw(screen, r, cols[n % Ncolor][e->etype==SRun?1:3], nil, ZP);
+
+ if(t->pid == triggerproc && ex < Width)
+ paused ^= 1;
+
+ for(j = 0; j < t->nevents; j++){
+ _e = &t->events[j];
+ switch(_e->etype & 0xffff){
+ case SInts:
+ if (_e->time > prevts && _e->time <= newestts){
+ sx = time2x(_e->time);
+ line(screen, addpt(p, Pt(sx, topmargin)),
+ addpt(p, Pt(sx, Height / 2 - bottommargin)),
+ Endsquare, Endsquare, 0,
+ green, ZP);
+ }
+ break;
+ case SInte:
+ if (_e->time > prevts && _e->time <= newestts) {
+ sx = time2x(_e->time);
+ line(screen, addpt(p, Pt(sx, Height / 2 - bottommargin)),
+ addpt(p, Pt(sx, Height - bottommargin)),
+ Endsquare, Endsquare, 0,
+ blue, ZP);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ p.y += Height;
+ }
+
+ ts = prevts + scales[scaleno].littletics - (prevts % scales[scaleno].littletics);
+ x = time2x(ts);
+
+ while(x < Width){
+ p = screen->r.min;
+ for(n = 0; n < ntasks; n++){
+ int height, width;
+
+ /* p is upper left corner for this task */
+ if ((ts % scales[scaleno].scale) == 0){
+ height = 10 * Height;
+ width = 1;
+ }else if ((ts % scales[scaleno].bigtics) == 0){
+ height = 12 * Height;
+ width = 0;
+ }else{
+ height = 13 * Height;
+ width = 0;
+ }
+ height >>= 4;
+
+ line(screen, addpt(p, Pt(x, height)), addpt(p, Pt(x, Height - lineht)),
+ Endsquare, Endsquare, width, cols[n % Ncolor][2], ZP);
+
+ p.y += Height;
+ }
+ ts += scales[scaleno].littletics;
+ x = time2x(ts);
+ }
+
+ rtime = screen->r;
+ rtime.min.y = rtime.max.y - tinyfont->height + 2;
+ draw(screen, rtime, bg, nil, ZP);
+ ts = oldestts + scales[scaleno].bigtics - (oldestts % scales[scaleno].bigtics);
+ x = time2x(ts);
+ ss = 0;
+ while(x < Width){
+ snprint(buf, sizeof(buf), "%t", ss);
+ string(screen, addpt(p, Pt(x - stringwidth(tinyfont, buf)/2, - tinyfont->height - 1)),
+ fg, ZP, tinyfont, buf);
+ ts += scales[scaleno].bigtics;
+ ss += scales[scaleno].bigtics;
+ x = time2x(ts);
+ }
+
+ snprint(buf, sizeof(buf), "%t", now);
+ string(screen, Pt(screen->r.max.x - stringwidth(mediumfont, buf), screen->r.min.y),
+ fg, ZP, mediumfont, buf);
+
+ flushimage(display, 1);
+ prevts = newestts;
+}
+
+Task*
+newtask(ulong pid)
+{
+ Task *t;
+ char buf[64], *p;
+ int fd,n;
+
+ tasks = realloc(tasks, (ntasks + 1) * sizeof(Task));
+ assert(tasks);
+
+ t = &tasks[ntasks++];
+ memset(t, 0, sizeof(Task));
+ t->events = nil;
+ snprint(buf, sizeof buf, "/proc/%ld/status", pid);
+ t->name = nil;
+ fd = open(buf, OREAD);
+ if (fd >= 0){
+ n = read(fd, buf, sizeof buf);
+ if(n > 0){
+ p = buf + sizeof buf - 1;
+ *p = 0;
+ p = strchr(buf, ' ');
+ if (p) *p = 0;
+ t->name = strdup(buf);
+ }else
+ print("%s: %r\n", buf);
+ close(fd);
+ }else
+ print("%s: %r\n", buf);
+ t->pid = pid;
+ prevts = 0;
+ if (newwin){
+ fprint(wctlfd, "resize -dx %d -dy %d\n",
+ Width + 20, (ntasks * Height) + 5);
+ }else
+ Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
+ return t;
+}
+
+void
+doevent(Task *t, Traceevent *ep)
+{
+ int i, n;
+ Event *event;
+ vlong runt;
+
+ t->tevents[ep->etype & 0xffff]++;
+ n = t->nevents++;
+ t->events = realloc(t->events, t->nevents*sizeof(Event));
+ assert(t->events);
+ event = &t->events[n];
+ memmove(event, ep, sizeof(Traceevent));
+ event->etime = 0;
+
+ switch(event->etype & 0xffff){
+ case SRelease:
+ if (t->runthis > t->runmax)
+ t->runmax = t->runthis;
+ t->runthis = 0;
+ break;
+
+ case SSleep:
+ case SYield:
+ case SReady:
+ case SSlice:
+ for(i = n-1; i >= 0; i--)
+ if (t->events[i].etype == SRun ||
+ t->events[i].etype == SEdf)
+ break;
+ if(i < 0 || t->events[i].etime != 0)
+ break;
+ runt = event->time - t->events[i].time;
+ if(runt > 0){
+ t->events[i].etime = event->time;
+ t->runtime += runt;
+ t->total += runt;
+ t->runthis += runt;
+ t->runs++;
+ }
+ break;
+ case SDead:
+print("task died %ld %t %s\n", event->pid, event->time, schedstatename[event->etype & 0xffff]);
+ free(t->events);
+ free(t->name);
+ ntasks--;
+ memmove(t, t+1, sizeof(Task)*(&tasks[ntasks]-t));
+ if (newwin)
+ fprint(wctlfd, "resize -dx %d -dy %d\n",
+ Width + 20, (ntasks * Height) + 5);
+ else
+ Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
+ prevts = 0;
+ }
+}
+
+void
+drawtrace(void)
+{
+ char *wsys, line[256];
+ int wfd, logfd;
+ Mousectl *mousectl;
+ Keyboardctl *keyboardctl;
+ int scaleno;
+ Rune r;
+ int i, n;
+ Task *t;
+ Traceevent *ep;
+
+ eventbuf = malloc(Nevents*sizeof(Traceevent));
+ assert(eventbuf);
+
+ if((logfd = open(profdev, OREAD)) < 0)
+ sysfatal("%s: Cannot open %s: %r", argv0, profdev);
+
+ if(newwin){
+ if((wsys = getenv("wsys")) == nil)
+ sysfatal("%s: Cannot find windowing system: %r",
+ argv0);
+
+ if((wfd = open(wsys, ORDWR)) < 0)
+ sysfatal("%s: Cannot open windowing system: %r",
+ argv0);
+
+ snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d",
+ getpid(), Width + 20, Height + 5);
+ line[sizeof(line) - 1] = '\0';
+ rfork(RFNAMEG);
+
+ if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0)
+ sysfatal("%s: Cannot mount %s under /mnt/wsys: %r",
+ argv0, line);
+
+ if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
+ sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r",
+ argv0);
+
+ }
+ if((wctlfd = open("/dev/wctl", OWRITE)) < 0)
+ sysfatal("%s: Cannot open /dev/wctl: %r", argv0);
+ if(initdraw(nil, nil, "trace") < 0)
+ sysfatal("%s: initdraw failure: %r", argv0);
+
+ Width = Dx(screen->r);
+ Height = Dy(screen->r);
+
+ if((mousectl = initmouse(nil, screen)) == nil)
+ sysfatal("%s: cannot initialize mouse: %r", argv0);
+
+ if((keyboardctl = initkeyboard(nil)) == nil)
+ sysfatal("%s: cannot initialize keyboard: %r", argv0);
+
+ colinit();
+
+ paused = 0;
+ scaleno = 7; /* 100 milliseconds */
+ now = nsec();
+ for(;;) {
+ Alt a[] = {
+ { mousectl->c, nil, CHANRCV },
+ { mousectl->resizec, nil, CHANRCV },
+ { keyboardctl->c, &r, CHANRCV },
+ { nil, nil, CHANNOBLK },
+ };
+
+ switch (alt(a)) {
+ case 0:
+ continue;
+
+ case 1:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("drawrt: Cannot re-attach window");
+ if(newwin){
+ if(Dx(screen->r) != Width ||
+ Dy(screen->r) != (ntasks * Height)){
+ fprint(2, "resize: x: have %d, need %d; y: have %d, need %d\n",
+ Dx(screen->r), Width + 8, Dy(screen->r), (ntasks * Height) + 8);
+ fprint(wctlfd, "resize -dx %d -dy %d\n",
+ Width + 8, (ntasks * Height) + 8);
+ }
+ }
+ else{
+ Width = Dx(screen->r);
+ Height = ntasks? Dy(screen->r)/ntasks:
+ Dy(screen->r);
+ }
+ break;
+
+ case 2:
+
+ switch(r){
+ case 'r':
+ for(i = 0; i < ntasks; i++){
+ tasks[i].tstart = now;
+ tasks[i].total = 0;
+ tasks[i].runtime = 0;
+ tasks[i].runmax = 0;
+ tasks[i].runthis = 0;
+ tasks[i].runs = 0;
+ memset(tasks[i].tevents, 0, Nevent*sizeof(ulong));
+
+ }
+ break;
+
+ case 'p':
+ paused ^= 1;
+ prevts = 0;
+ break;
+
+ case '-':
+ if (scaleno < nelem(scales) - 1)
+ scaleno++;
+ prevts = 0;
+ break;
+
+ case '+':
+ if (scaleno > 0)
+ scaleno--;
+ prevts = 0;
+ break;
+
+ case 'q':
+ threadexitsall(nil);
+
+ case 'v':
+ verbose ^= 1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3:
+ now = nsec();
+ while((n = read(logfd, eventbuf, Nevents*sizeof(Traceevent))) > 0){
+ assert((n % sizeof(Traceevent)) == 0);
+ nevents = n / sizeof(Traceevent);
+ for (ep = eventbuf; ep < eventbuf + nevents; ep++){
+ if ((ep->etype & 0xffff) >= Nevent){
+ print("%ld %t Illegal event %ld\n",
+ ep->pid, ep->time, ep->etype & 0xffff);
+ continue;
+ }
+ if (verbose)
+ print("%ld %t %s\n",
+ ep->pid, ep->time, schedstatename[ep->etype & 0xffff]);
+
+ for(i = 0; i < ntasks; i++)
+ if(tasks[i].pid == ep->pid)
+ break;
+
+ if(i == ntasks){
+ t = newtask(ep->pid);
+ t->tstart = ep->time;
+ }else
+ t = &tasks[i];
+
+ doevent(t, ep);
+ }
+ }
+ if(!paused)
+ redraw(scaleno);
+ }
+ sleep(scales[scaleno].sleep);
+ }
+}
+
+int
+timeconv(Fmt *f)
+{
+ char buf[128], *sign;
+ vlong t;
+
+ buf[0] = 0;
+ switch(f->r) {
+ case 'U':
+ t = va_arg(f->args, vlong);
+ break;
+ case 't': // vlong in nanoseconds
+ t = va_arg(f->args, vlong);
+ break;
+ default:
+ return fmtstrcpy(f, "(timeconv)");
+ }
+ if (t < 0) {
+ sign = "-";
+ t = -t;
+ }else
+ sign = "";
+ if (t > S(1)){
+ t += OneRound;
+ sprint(buf, "%s%d.%.3ds", sign, (int)(t / S(1)), (int)(t % S(1))/1000000);
+ }else if (t > MS(1)){
+ t += MilliRound;
+ sprint(buf, "%s%d.%.3dms", sign, (int)(t / MS(1)), (int)(t % MS(1))/1000);
+ }else if (t > US(1))
+ sprint(buf, "%s%d.%.3dµs", sign, (int)(t / US(1)), (int)(t % US(1)));
+ else
+ sprint(buf, "%s%dns", sign, (int)t);
+ return fmtstrcpy(f, buf);
+}