xref: /plan9-contrib/sys/src/cmd/ptrace/ptrace.c (revision af198995f3f1f1d87a386835b2bcd15ca90a6fed)
1 #include <all.h>
2 
3 /*
4  * See st.c for a description of the data structures.
5  *
6  * Pos is the time and pixel slice into the time window shown
7  * and the rectangle plotted.
8  */
9 typedef struct Pos Pos;
10 
11 struct Pos
12 {
13 	vlong	t0;
14 	vlong	ival;
15 	int		p0;
16 	int		pend;
17 	vlong	npp;	/* ns per pix at this scale */
18 };
19 
20 static char *plumbfname = "ptrace.out";
21 static char *infname;
22 int what, verb, newwin;
23 Biobuf *bout;
24 static St *(*readst)(Biobuf*);
25 
26 int mainstacksize = Stack;
27 
28 static int piddx, graphdy;
29 static Rectangle plotr;
30 static Image *im, *bgcol, *rdycol, *waitcol, *runcol;
31 // These are aliases:
32 static Image *setcol, *sccol, *pfcol, *faultcol;
33 
34 static Channel *eventc;
35 static Mousectl *mousectl;
36 static Keyboardctl *keyboardctl;
37 static Proc *current;
38 
39 static Cursor crosscursor = {
40 	{-7, -7},
41 	{0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
42 	 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
43 	 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
44 	 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, },
45 	{0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
46 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
47 	 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
48 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, }
49 };
50 
51 Cursor busycursor ={
52 	{-1, -1},
53 	{0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
54 	 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
55 	 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
56 	 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
57 	{0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
58 	 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
59 	 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
60 	 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
61 };
62 
63 Cursor leftcursor = {
64 	{-8, -7},
65 	{0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58,
66 	 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02,
67 	 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58,
68 	 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
69 	{0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
70 	 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc,
71 	 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80,
72 	 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
73 };
74 
75 Cursor rightcursor = {
76 	{-7, -7},
77 	{0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20,
78 	 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20,
79 	 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20,
80 	 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
81 	{0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
82 	 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0,
83 	 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0,
84 	 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
85 };
86 
87 static void
busy(void)88 busy(void)
89 {
90 	setcursor(mousectl, &busycursor);
91 }
92 
93 enum{Browsing, Paused};
94 static void
ready(int state)95 ready(int state)
96 {
97 	if(state == Paused)
98 		setcursor(mousectl, nil);
99 	else
100 		setcursor(mousectl, &crosscursor);
101 }
102 
103 static void
clear(void)104 clear(void)
105 {
106 	if(im != nil)
107 		if(Dx(screen->r) != Dx(im->r) || Dy(screen->r) != Dy(im->r)){
108 			freeimage(im);
109 			im = nil;
110 		}
111 	if(im == nil)
112 			im = allocimage(display, screen->r, screen->chan, 0, -1);
113 	if(im == nil)
114 		sysfatal("allocimage: %r");
115 	draw(im, im->r, bgcol, nil, ZP);
116 	plotr = im->r;
117 	plotr.min.x += piddx;
118 	plotr.min.y += font->height+1;
119 }
120 
121 enum{ All, TimeAndPids, JustTime };
122 static void
flush(int justtime)123 flush(int justtime)
124 {
125 	Rectangle r;
126 
127 	r = screen->r;
128 	if(justtime)
129 		r.max.y = r.min.y + font->height;
130 	draw(screen, r, im, nil, im->r.min);
131 	if(justtime == TimeAndPids){
132 		r = screen->r;
133 		r.min.y += font->height;
134 		r.max.x = r.min.x + piddx;
135 		draw(screen, r, im, nil, r.min);
136 	}
137 	flushimage(display, 1);
138 }
139 
140 static int
proclocate(Proc * p,vlong t,int * ip)141 proclocate(Proc *p, vlong t, int *ip)
142 {
143 	int i, id;
144 
145 	for(i = 0; i < p->nstate; i++){
146 		id = p->state[i];
147 		if(graph[id]->time > t)
148 			break;
149 	}
150 	if(i == 0)
151 		*ip = i;
152 	else
153 		*ip = --i;
154 	return p->state[i];
155 }
156 
157 /*
158  * Fill up pp, after the user changes Pos.t0 and/or Pos.ival.
159  * Locate sets [s0:send] in procs, used later to plot them.
160  */
161 static void
locate(Pos * pp)162 locate(Pos *pp)
163 {
164 	int i, pmin, pmax, x;
165 
166 	pmin = 0;
167 	pmax = ngraph;
168 	for(i = 0; i < nproc; i++){
169 		Proc *p = &proc[i];
170 		x = proclocate(p, pp->t0, &p->s0);
171 		if(x < pmin)
172 			pmin = x;
173 		x = proclocate(p, pp->t0+pp->ival, &p->send);
174 		p->send++;
175 		if(x > pmax)
176 			pmax = x;
177 	}
178 	pp->p0 = pmin;
179 	pp->pend = pmax+1;
180 	if(Dx(plotr) > 0)
181 		pp->npp = pp->ival / (vlong)Dx(plotr);
182 }
183 
184 static int infominx, infomaxx;
185 
186 static void
drawtime(vlong t,vlong ival)187 drawtime(vlong t, vlong ival)
188 {
189 	Rectangle r;
190 	char s[80];
191 	Point p;
192 
193 	r = im->r;
194 	r.max.y = r.min.y + font->height;
195 	draw(im, r, bgcol, nil, ZP);
196 	seprint(s, s+sizeof s, "t: %#T", t);
197 	p = Pt(im->r.max.x - stringwidth(font, s) - 1, r.min.y);
198 	string(im, p, display->black, ZP, font, s);
199 	infomaxx = p.x-1;
200 	infominx = im->r.min.x;
201 	if(ival > 0){
202 		seprint(s, s+sizeof s, "ival: %#T", ival);
203 		infominx = im->r.min.x+1 + stringwidth(font, s)+1;
204 		if(infomaxx < infominx)
205 			return;
206 		p = Pt(im->r.min.x+1, r.min.y);
207 		string(im, p, display->black, ZP, font, s);
208 	}
209 }
210 
211 static void
drawpid(Proc * pp)212 drawpid(Proc *pp)
213 {
214 	Point p;
215 	St *g;
216 	char spid[80];
217 
218 	p.x = im->r.min.x;
219 	p.y = im->r.min.y + graphdy * (pp->row+1);
220 	if(p.y >= im->r.max.y)
221 		return;
222 	g = pgraph(pp, pp->s0);
223 	draw(im, Rpt(p, addpt(p, Pt(piddx, font->height))), bgcol, nil, ZP);
224 	if(g->pid > 0){
225 		seprint(spid, spid+sizeof spid, "%-4.4s% udm%ud", g->name, g->pid, g->machno);
226 		if(pp == current)
227 			string(im, p, setcol, ZP, font, spid);
228 		else
229 			string(im, p, display->black, ZP, font, spid);
230 	}
231 }
232 
233 static void
drawnotice(char * s)234 drawnotice(char *s)
235 {
236 	Point p;
237 	int dx, w, n;
238 	char buf[80];
239 	Rectangle r;
240 
241 	dx = infomaxx - infominx;
242 	r = Rect(infominx, im->r.min.y, infomaxx, im->r.min.y+graphdy);
243 	draw(im, r, bgcol, nil, ZP);
244 	w = stringwidth(font, s);
245 	if(w + 2 > dx){
246 		seprint(buf, buf+sizeof buf, s);
247 		s = buf;
248 		for(n = strlen(s); n > 0; n--){
249 			w = stringwidth(font, s);
250 			if(dx > w+2)
251 				break;
252 			s[n-1] = 0;
253 		}
254 		if(n == 0)
255 			return;
256 	}
257 	p = Pt(infominx + dx/2 - w/2, im->r.min.y);
258 	string(im, p, setcol, ZP, font, s);
259 }
260 
261 static void
drawinfo(St * g)262 drawinfo(St *g)
263 {
264 	char s[80];
265 
266 	seprint(s, s+sizeof s, "%#G", g);
267 	drawnotice(s);
268 }
269 
270 static int
dtwidth(vlong dt,vlong nsperpix)271 dtwidth(vlong dt, vlong nsperpix)
272 {
273 	if(dt < 0)
274 		return 0;
275 	if(nsperpix <= 0)
276 		return Dx(plotr);
277 	return (int)(dt / nsperpix);
278 }
279 
280 /*
281  * Try to draw events within the state represented by g
282  * ending in eg, if the scale permits.
283  */
284 static void
drawgraphs(St * g,St * eg,Point pt,Pos p)285 drawgraphs(St *g, St *eg, Point pt, Pos p)
286 {
287 	int x, faults, scs, pfs;
288 	vlong t, t0, dt;
289 	Rectangle r;
290 
291 	x = pt.x;
292 	t0 = g->time;
293 	t = t0;
294 	pfs = faults = scs = 0;
295 	while(g->pnext != 0 && graph[g->pnext] != eg){
296 		g = graph[g->pnext];
297 		g->x = x;
298 		if(g->etype == STrap)
299 			if(g->arg&(STrapRPF|STrapWPF))
300 				pfs++;
301 			else if(g->arg&STrapSC)
302 				scs++;
303 			else
304 				faults++;
305 		dt = g->time - t;
306 		if(dt < p.npp)
307 			continue;
308 		if(scs){
309 			r = Rect(x, pt.y-3*Wid/2, x+Wid, pt.y-Wid/2);
310 			draw(im, r, sccol, nil, ZP);
311 		}
312 		if(faults || pfs){
313 			r = Rect(x, pt.y+3*Wid/2, x+Wid, pt.y+5*Wid/2);
314 			draw(im, r, pfs?pfcol:faultcol, nil, ZP);
315 		}
316 		faults = pfs = scs = 0;
317 		t = g->time;
318 		x = plotr.min.x + dtwidth(t-p.t0, p.npp);
319 	}
320 }
321 
322 static void
drawproc(Proc * pp,Pos p)323 drawproc(Proc *pp, Pos p)
324 {
325 	St *g, *ng;
326 	int s;
327 	Image *c;
328 	vlong t0, tn, frac;
329 	int x0, xn;
330 	Rectangle r;
331 
332 	frac = 0;
333 	r = plotr;
334 	r.min.y += graphdy * pp->row + graphdy/2;
335 	r.max.y = r.min.y + Wid;
336 	for(s = pp->s0; s < pp->send; s++){
337 		g = pgraph(pp, s);
338 		switch(g->state){
339 			case SRun:
340 				c = runcol;
341 				break;
342 			case SDead:
343 				c = bgcol;
344 				break;
345 			case SReady:
346 				c = rdycol;
347 				break;
348 			default:
349 				c = waitcol;
350 		}
351 		t0 = g->time - p.t0;
352 		if(s < pp->nstate-1){
353 			ng = pgraph(pp, s+1);
354 			tn = ng->time - p.t0;
355 		}else
356 			break;
357 		x0 = dtwidth(t0, p.npp);
358 		xn = dtwidth(tn+frac, p.npp);
359 		g->x = plotr.min.x + x0;
360 		r.min.x = g->x;
361 		if(xn == x0){
362 			frac += tn-t0;
363 			continue;
364 		}
365 		frac = 0;
366 		if(xn - x0 < Wid)
367 			xn = x0 + Wid;
368 		r.max.x = plotr.min.x + xn;
369 		draw(im, r, c, nil, ZP);
370 		drawgraphs(g, ng, r.min, p);
371 	}
372 	if(s < pp->nstate)
373 		pgraph(pp, s)->x = r.max.x;
374 }
375 
376 /*
377  * Plot for the range indicated in pos.
378  * Processes must be located before.
379  * This updates St.x in the states shown for procs.
380  */
381 static void
plotallat(Pos p)382 plotallat(Pos p)
383 {
384 	int i;
385 
386 	clear();
387 	for(i = 0; i < nproc; i++){
388 		drawpid(&proc[i]);
389 		drawproc(&proc[i], p);
390 	}
391 	drawtime(p.t0, p.ival);
392 }
393 
394 static Point
ptinplot(Point pt)395 ptinplot(Point pt)
396 {
397 	if(pt.y < plotr.min.y)
398 		pt.y = plotr.min.y;
399 	if(pt.y > plotr.max.y-1)
400 		pt.y = plotr.max.y-1;
401 	if(pt.x < plotr.min.x)
402 		pt.x = plotr.min.x;
403 	if(pt.x > plotr.max.x-1)
404 		pt.x = plotr.max.x-1;
405 	return pt;
406 }
407 
408 static int
ptrow(Point pt)409 ptrow(Point pt)
410 {
411 	return (pt.y - plotr.min.y) / graphdy;
412 }
413 
414 static Proc*
procat(Point pt)415 procat(Point pt)
416 {
417 	int row, i;
418 	Proc *pp;
419 
420 	row = ptrow(pt);
421 	for(i = 0; i < nproc; i++){
422 		pp = &proc[i];
423 		if(pp->s0 < pp->send && pp->row == row)
424 			return pp;
425 	}
426 	return nil;
427 }
428 
429 static int
stateat(Proc * pp,Point pt)430 stateat(Proc *pp, Point pt)
431 {
432 	int s, last;
433 	St *g;
434 
435 	last = pp->s0;
436 	for(s = pp->s0; s < pp->send; s++){
437 		g = pgraph(pp, s);
438 		if(pt.x < g->x)
439 			break;
440 		last = s;
441 	}
442 	return last;
443 }
444 
445 static St*
graphat(Proc * pp,int s,Point pt,vlong npp)446 graphat(Proc *pp, int s, Point pt, vlong npp)
447 {
448 	St *g0, *g, *ng, *eg;
449 
450 	g0 = pgraph(pp, s);
451 
452 	/* If all's in a pixel, don't bother */
453 	if(s < pp->send-1){
454 		eg = pgraph(pp, s+1);
455 		if(eg->time - g0->time < npp)
456 			return g0;
457 	}else
458 		eg = nil;
459 	for(g = g0; g->pnext != 0 && graph[g->pnext] != eg; g = ng){
460 		ng = graph[g->pnext];
461 		if(ng->x > pt.x)
462 			break;
463 	}
464 	return g;
465 }
466 
467 static void
tplumb(vlong t)468 tplumb(vlong t)
469 {
470 	static int fd = -1;
471 	Plumbmsg *pm;
472 	static char wd[128];
473 	Plumbattr *attr;
474 
475 	if(fd < 0)
476 		fd = plumbopen("send", OWRITE);
477 	if(fd < 0){
478 		fprint(2, "%s: plumbopen: %r\n", argv0);
479 		return;
480 	}
481 	if(wd[0] == 0)
482 		getwd(wd, sizeof wd);
483 
484 	pm = mallocz(sizeof *pm, 1);
485 	attr = mallocz(sizeof *attr, 1);
486 	if(pm == nil || attr == nil){
487 		free(pm);
488 		fprint(2, "%s: no memory\n", argv0);
489 		return;
490 	}
491 	pm->src = strdup(argv0);
492 	pm->dst = strdup("edit");
493 	pm->wdir = strdup(wd);
494 	pm->type = strdup("text");
495 	pm->data = strdup(plumbfname);
496 	pm->ndata = strlen(pm->data);
497 	attr->name = strdup("addr");
498 	attr->value = smprint("/^%T .*\\n", t);
499 
500 	pm->attr = plumbaddattr(pm->attr, attr);
501 	plumbsend(fd, pm);
502 }
503 
504 static void
cursorat(Point pt,int doplumb,Pos p)505 cursorat(Point pt, int doplumb, Pos p)
506 {
507 	Proc *pp, *old;
508 	St *g;
509 	int gi, dx;
510 
511 	pt = ptinplot(pt);
512 	pp = procat(pt);
513 	if(pp == nil)
514 		return;
515 	gi = stateat(pp, pt);
516 	g = graphat(pp, gi, pt, p.npp);
517 
518 	dx = pt.x - plotr.min.x;
519 	drawtime(p.t0 + dx * p.npp, p.ival);
520 	drawinfo(g);
521 	old = current;
522 	current = pp;
523 	if(old != nil)
524 		drawpid(old);
525 	drawpid(current);
526 	flush(TimeAndPids);
527 	if(doplumb)
528 		tplumb(g->time);
529 }
530 
531 static void
printproc(Biobuf * b,Proc * pp,vlong t0,vlong ival)532 printproc(Biobuf *b, Proc *pp, vlong t0, vlong ival)
533 {
534 	St *g0, *ge, *g;
535 	vlong tend;
536 
537 	if(t0 < 0 || ival < 0){
538 		t0 = 0;
539 		tend = graph[ngraph-1]->time + 1;
540 	}else
541 		tend = t0+ival;
542 	ge = pgraph(pp, pp->send-1);
543 	g0 = pgraph(pp, pp->s0);
544 
545 	for(g = g0; g != ge; g = graph[g->pnext])
546 		if(g->time >= t0 && g->time < tend)
547 			Bprint(b, "%G\n", g);
548 }
549 
550 static void
printallat(Biobuf * b,vlong t0,vlong ival,int rmin,int rend)551 printallat(Biobuf *b, vlong t0, vlong ival, int rmin, int rend)
552 {
553 	Biobuf *bout;
554 	int i;
555 
556 	if(rmin < 0 || rend < 0){
557 		rmin = 0;
558 		rend = nproc;
559 	}else if(rend > nproc)
560 		rend = nproc;
561 
562 	if(b != nil)
563 		bout = b;
564 	else{
565 		bout = Bopen("ptrace.out", OWRITE);
566 		if(bout == nil){
567 			drawnotice("write failed");
568 			flush(JustTime);
569 			return;
570 		}
571 	}
572 	for(i = rmin; i < rend; i++)
573 		printproc(bout, &proc[i], t0, ival);
574 
575 	Bprint(bout, "\n");
576 
577 	if(b == nil){
578 		drawnotice("written ptrace.out");
579 		flush(JustTime);
580 		Bterm(bout);
581 	}else
582 		Bflush(b);
583 }
584 
585 static void
flagsweep(Point p0)586 flagsweep(Point p0)
587 {
588 	line(screen, Pt(p0.x, p0.y-8), Pt(p0.x, p0.y+8),
589 		ARROW(4, 4, 2), ARROW(4, 4, 2), 1, display->black, ZP);
590 	line(screen, Pt(p0.x-10, p0.y), Pt(p0.x+10, p0.y),
591 		ARROW(4, 4, 2), ARROW(4, 4, 2), 1, display->black, ZP);
592 	flushimage(display, 1);
593 }
594 
595 static void
sweep(int b,Point p0,Pos * p)596 sweep(int b, Point p0, Pos *p)
597 {
598 	enum{None, Left, Right};
599 	int where, rmin, rend;
600 	Point pt;
601 	Rectangle r;
602 	Mouse m;
603 	Pos np;
604 
605 	p0 = ptinplot(p0);
606 	flagsweep(p0);
607 	where = None;
608 	do{
609 		recv(mousectl->c, &m);
610 		pt = ptinplot(m.xy);
611 		if(pt.x > p0.x && where != Right){
612 			setcursor(mousectl, &rightcursor);
613 			where = Right;
614 		}else if(pt.x <= p0.x && where != Left){
615 			setcursor(mousectl, &leftcursor);
616 			where = Left;
617 		}
618 		r = canonrect(Rpt(p0, pt));
619 	}while(m.buttons);
620 
621 	np = *p;
622 	np.t0 += (r.min.x-plotr.min.x) * np.npp;
623 	if(np.t0 > graph[ngraph-1]->time)
624 		np.t0 = graph[ngraph-1]->time;
625 
626 	np.ival = np.npp * Dx(r);
627 	if(np.ival < US(1))
628 		np.ival = US(1);
629 	if(np.ival > graph[ngraph-1]->time + US(1))
630 		np.ival = graph[ngraph-1]->time + US(1);
631 
632 	rmin = ptrow(r.min);
633 	rend = ptrow(r.max) + 1;
634 	switch(b){
635 	case 1:
636 		*p = np;
637 		locate(p);
638 		break;
639 	case 2:
640 		printallat(nil, np.t0, np.ival, rmin, rend);
641 		break;
642 	case 4:
643 		printallat(bout, np.t0, np.ival, rmin, rend);
644 		break;
645 	}
646 }
647 
648 static void
setrows(void)649 setrows(void)
650 {
651 	int i;
652 
653 	for(i = 0; i < nproc; i++)
654 		proc[i].row = i;
655 }
656 
657 static void
debugdump(Pos p)658 debugdump(Pos p)
659 {
660 	Proc *pp;
661 	int i;
662 
663 	Bprint(bout, "pos p0 %d pend %d t0 %T ival %T\n",
664 		p.p0, p.pend, p.t0, p.ival);
665 	for(i = 0; i < nproc; i++){
666 		pp = &proc[i];
667 		Bprint(bout, "proc %d pid %d s0 %d send %d ns %d\n",
668 			i, pp->pid, pp->s0, pp->send, pp->nstate);
669 	}
670 	// XXX
671 	pp = &proc[13];
672 	Bprint(bout, "%R\n", plotr);
673 	for(i = pp->s0; i < pp->send; i++)
674 		Bprint(bout, "x %d %G\n", pgraph(pp, i)->x, pgraph(pp, i));
675 	Bflush(bout);
676 }
677 
678 static void
browse(void)679 browse(void)
680 {
681 	Rune r;
682 	Mouse m;
683 	Pos p;
684 	vlong tmax;
685 	int paused, b;
686 	Point lastxy;
687 	enum{Ptr, Rsz, Kbd};
688 	Alt a[] = {
689 		{mousectl->c, &m, CHANRCV},
690 		{mousectl->resizec, nil, CHANRCV},
691 		{keyboardctl->c, &r, CHANRCV},
692 		{nil, nil, CHANEND}
693 	};
694 
695 
696 	setrows();
697 restart:
698 	p.p0 = 0;
699 	p.pend = ngraph;
700 	p.ival = graph[ngraph-1]->time + US(1);
701 	p.t0 = 0;
702 	busy();
703 	clear();	/* set plotr; locate uses that */
704 	locate(&p);
705 	tmax = p.ival;
706 	paused = Browsing;
707 	plotallat(p);
708 	flush(All);
709 	for(;;){
710 		ready(paused);
711 		switch(alt(a)){
712 		case Rsz:
713 			if(getwindow(display, Refnone) < 0)
714 				sysfatal("getwindow: %r");
715 			if(im != nil)
716 				freeimage(im);
717 			im = nil;
718 		redraw:
719 			busy();
720 			plotallat(p);
721 			flush(All);
722 			while(nbrecv(keyboardctl->c, &r) != 0)
723 				;
724 			break;
725 		case Ptr:
726 			b = 0;
727 			if(m.buttons){
728 				b = m.buttons;
729 				lastxy = m.xy;
730 				recv(mousectl->c, &m);
731 				if(m.buttons == 0){
732 					if(b == 1 && lastxy.y < plotr.min.y)
733 						if(lastxy.x < plotr.min.x + Dx(plotr))
734 							goto right;
735 						else
736 							goto left;
737 				}else {
738 					busy();
739 					sweep(b, lastxy, &p);
740 					goto redraw;
741 				}
742 			}else
743 				while(nbrecv(mousectl->c, &m) != 0)
744 					;
745 			if(paused)
746 				break;
747 
748 			if(ptinrect(m.xy, plotr) && (b==4 || !eqpt(lastxy, m.xy))){
749 				cursorat(m.xy, b==4, p);
750 				lastxy = m.xy;
751 			}
752 			break;
753 		case Kbd:
754 			switch(r){
755 			case 'D':
756 				debugdump(p);
757 				break;
758 			case Kesc:
759 				busy();
760 				goto restart;
761 			case ' ':
762 				paused = !paused;
763 				break;
764 			case 'q':
765 				Bterm(bout);
766 				threadexitsall(nil);
767 			case '+':
768 			case Kup:
769 				busy();
770 				p.ival /= 2;
771 				if(p.ival < US(1))
772 					p.ival = US(1);
773 				locate(&p);
774 				goto redraw;
775 			case '-':
776 			case Kdown:
777 				if(p.ival > tmax)
778 					break;
779 				busy();
780 				p.ival *= 2;
781 				locate(&p);
782 				goto redraw;
783 			case Kright:
784 			right:
785 				busy();
786 				p.t0 += p.ival/Scroll;
787 				if(p.t0 > graph[ngraph-1]->time)
788 					p.t0 = graph[ngraph-1]->time;
789 				locate(&p);
790 				goto redraw;
791 				break;
792 			case Kleft:
793 			left:
794 				if(p.t0 < p.ival/Scroll)
795 					break;
796 				busy();
797 				p.t0 -= p.ival/Scroll;
798 				locate(&p);
799 				goto redraw;
800 				break;
801 			case 's':
802 				busy();
803 				printallat(nil, -1, -1, -1, -1);
804 				break;
805 			case 'p':
806 				busy();
807 				printallat(bout, -1, -1, -1, -1);
808 				break;
809 			}
810 			break;
811 		default:
812 			sysfatal("alt: %r");
813 		}
814 	}
815 }
816 
817 static void
mkwin(int dx,int dy)818 mkwin(int dx, int dy)
819 {
820 	char *wsys, line[256];
821 	int wfd;
822 
823 	if((wsys = getenv("wsys")) == nil)
824 		sysfatal("no wsys: %r");
825 	if((wfd = open(wsys, ORDWR)) < 0)
826 		sysfatal("open wsys: %r");
827 	snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d",
828 			getpid(), dx, dy);
829 	line[sizeof(line) - 1] = '\0';
830 	rfork(RFNAMEG);
831 	if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0)
832 		sysfatal("mount wsys: %r");
833 	if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
834 		sysfatal("mount wsys: %r");
835 }
836 
837 static void
colors(void)838 colors(void)
839 {
840 	piddx = stringwidth(font, "XXXXX" "XXXXXmXX");	/* 5 for name, 5 for pid */
841 	graphdy = font->height + 1;
842 	bgcol = allocimagemix(display, DPalebluegreen, DWhite);
843 	rdycol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreen);
844 	waitcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DRed);
845 	runcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DBlue);
846 	if(bgcol == nil || rdycol == nil || waitcol == nil || runcol == nil)
847 		sysfatal("color: %r");
848 	setcol = runcol;
849 	sccol = runcol;
850 	pfcol = waitcol;
851 	faultcol = display->black;
852 }
853 
854 static void
usage(void)855 usage(void)
856 {
857 	fprint(2, "Usage: %s [-dgpw] file\n", argv0);
858 	exits(nil);
859 }
860 
861 void
threadmain(int argc,char ** argv)862 threadmain(int argc, char **argv)
863 {
864 	int isdev, i;
865 
866 	isdev = 0;
867 
868 	ARGBEGIN {
869 	case 'd':
870 		isdev = 1;
871 		break;
872 	case 'g':
873 		what |= Plot;
874 		break;
875 	case 'p':
876 		what |= Dump;
877 		break;
878 	case 'v':
879 		verb++;
880 		break;
881 	case 'w':
882 		newwin++;
883 		break;
884 	default:
885 		usage();
886 	}
887 	ARGEND;
888 
889 	if(argc != 1)
890 		usage();
891 	infname = argv[0];
892 	if(!isdev)
893 		plumbfname = argv[0];
894 	if(what == 0)
895 		what = Dump;
896 
897 	bout = Bopen("/fd/1", OWRITE);
898 	if(bout == nil)
899 		sysfatal("stdout: Bopen: %r");
900 
901 	fmtinstall('T', Tfmt);
902 	fmtinstall('G', Gfmt);
903 
904 	readall(infname, isdev);
905 	if(ngraph == 0)
906 		sysfatal("nothing to plot");
907 
908 	makeprocs();
909 
910 	if(what&Dump)
911 		for(i = 0; i < ngraph; i++)
912 			if(Bprint(bout, "%G\n", graph[i]) < 0)
913 				sysfatal("out: %r");
914 
915 	if((what&Plot) == 0){
916 		Bterm(bout);
917 		exits(nil);
918 	}
919 
920 	if(Bflush(bout) < 0)
921 		sysfatal("out: %r");
922 	eventc = chancreate(sizeof(ulong), 10);
923 	if(eventc == nil)
924 		sysfatal("no memory");
925 	if(newwin)
926 		mkwin(Wx, Wy);
927 	if(initdraw(nil, "/lib/font/bit/pelm/unicode.8.font", argv0) < 0)
928 		if(initdraw(nil, nil, argv0) < 0)
929 			sysfatal("initdraw: %r");
930 	colors();
931 	if((mousectl = initmouse(nil, screen)) == nil)
932 		sysfatal("mouse: %r");
933 	if((keyboardctl = initkeyboard(nil)) == nil)
934 		sysfatal("keyboard: %r");
935 	browse();
936 	Bterm(bout);
937 }
938