xref: /plan9/sys/src/cmd/venti/srv/hproc.c (revision 09911ebd3c39e055375075cbbf1b4a01ead82642)
1 #include "stdinc.h"
2 #include <bio.h>
3 #include <mach.h>
4 #include <ureg.h>
5 #include "/sys/src/libthread/threadimpl.h"
6 #include "dat.h"
7 #include "fns.h"
8 
9 typedef struct Ureg Ureg;
10 typedef struct Debug Debug;
11 
12 struct Debug
13 {
14 	int textfd;
15 	QLock lock;
16 	Fhdr fhdr;
17 	Map *map;
18 	Fmt *fmt;
19 	int pid;
20 	char *stkprefix;
21 	int pcoff;
22 	int spoff;
23 };
24 
25 static Debug debug = { -1 };
26 
27 static int
text(int pid)28 text(int pid)
29 {
30 	int fd;
31 	char buf[100];
32 
33 	if(debug.textfd >= 0){
34 		close(debug.textfd);
35 		debug.textfd = -1;
36 	}
37 	memset(&debug.fhdr, 0, sizeof debug.fhdr);
38 
39 	snprint(buf, sizeof buf, "#p/%d/text", pid);
40 	fd = open(buf, OREAD);
41 	if(fd < 0)
42 		return -1;
43 	if(crackhdr(fd, &debug.fhdr) < 0){
44 		close(fd);
45 		return -1;
46 	}
47 	if(syminit(fd, &debug.fhdr) < 0){
48 		memset(&debug.fhdr, 0, sizeof debug.fhdr);
49 		close(fd);
50 		return -1;
51 	}
52 	debug.textfd = fd;
53 	machbytype(debug.fhdr.type);
54 	return 0;
55 }
56 
57 static void
unmap(Map * m)58 unmap(Map *m)
59 {
60 	int i;
61 
62 	for(i=0; i<m->nsegs; i++)
63 		if(m->seg[i].inuse)
64 			close(m->seg[i].fd);
65 	free(m);
66 }
67 
68 static Map*
map(int pid)69 map(int pid)
70 {
71 	int mem;
72 	char buf[100];
73 	Map *m;
74 
75 	snprint(buf, sizeof buf, "#p/%d/mem", pid);
76 	mem = open(buf, OREAD);
77 	if(mem < 0)
78 		return nil;
79 
80 	m = attachproc(pid, 0, mem, &debug.fhdr);
81 	if(m == 0){
82 		close(mem);
83 		return nil;
84 	}
85 
86 	if(debug.map)
87 		unmap(debug.map);
88 	debug.map = m;
89 	debug.pid = pid;
90 	return m;
91 }
92 
93 static void
dprint(char * fmt,...)94 dprint(char *fmt, ...)
95 {
96 	va_list arg;
97 
98 	va_start(arg, fmt);
99 	fmtvprint(debug.fmt, fmt, arg);
100 	va_end(arg);
101 }
102 
103 static void
openfiles(void)104 openfiles(void)
105 {
106 	char buf[4096];
107 	int fd, n;
108 
109 	snprint(buf, sizeof buf, "#p/%d/fd", getpid());
110 	if((fd = open(buf, OREAD)) < 0){
111 		dprint("open %s: %r\n", buf);
112 		return;
113 	}
114 	n = readn(fd, buf, sizeof buf-1);
115 	close(fd);
116 	if(n >= 0){
117 		buf[n] = 0;
118 		fmtstrcpy(debug.fmt, buf);
119 	}
120 }
121 
122 /*
123  *	dump the raw symbol table
124  */
125 static void
printsym(void)126 printsym(void)
127 {
128 	int i;
129 	Sym *sp;
130 
131 	for (i = 0; sp = getsym(i); i++) {
132 		switch(sp->type) {
133 		case 't':
134 		case 'l':
135 			dprint("%16#llux t %s\n", sp->value, sp->name);
136 			break;
137 		case 'T':
138 		case 'L':
139 			dprint("%16#llux T %s\n", sp->value, sp->name);
140 			break;
141 		case 'D':
142 		case 'd':
143 		case 'B':
144 		case 'b':
145 		case 'a':
146 		case 'p':
147 		case 'm':
148 			dprint("%16#llux %c %s\n", sp->value, sp->type, sp->name);
149 			break;
150 		default:
151 			break;
152 		}
153 	}
154 }
155 
156 static void
printmap(char * s,Map * map)157 printmap(char *s, Map *map)
158 {
159 	int i;
160 
161 	if (!map)
162 		return;
163 	dprint("%s\n", s);
164 	for (i = 0; i < map->nsegs; i++) {
165 		if (map->seg[i].inuse)
166 			dprint("%-16s %-16#llux %-16#llux %-16#llux\n",
167 				map->seg[i].name, map->seg[i].b,
168 				map->seg[i].e, map->seg[i].f);
169 	}
170 }
171 
172 static void
printlocals(Map * map,Symbol * fn,uintptr fp)173 printlocals(Map *map, Symbol *fn, uintptr fp)
174 {
175 	int i;
176 	uintptr w;
177 	Symbol s;
178 	char buf[100];
179 
180 	s = *fn;
181 	for (i = 0; localsym(&s, i); i++) {
182 		if (s.class != CAUTO)
183 			continue;
184 		snprint(buf, sizeof buf, "%s%s/", debug.stkprefix, s.name);
185 		if (geta(map, fp - s.value, (uvlong*)&w) > 0)
186 			dprint("\t%-10s %10#p %ld\n", buf, w, w);
187 		else
188 			dprint("\t%-10s ?\n", buf);
189 	}
190 }
191 
192 static void
printparams(Map * map,Symbol * fn,uintptr fp)193 printparams(Map *map, Symbol *fn, uintptr fp)
194 {
195 	int i;
196 	Symbol s;
197 	uintptr w;
198 	int first = 0;
199 
200 	fp += mach->szaddr;			/* skip saved pc */
201 	s = *fn;
202 	for (i = 0; localsym(&s, i); i++) {
203 		if (s.class != CPARAM)
204 			continue;
205 		if (first++)
206 			dprint(", ");
207 		if (geta(map, fp + s.value, (uvlong *)&w) > 0)
208 			dprint("%s=%#p", s.name, w);
209 	}
210 }
211 
212 static void
printsource(uintptr dot)213 printsource(uintptr dot)
214 {
215 	char str[100];
216 
217 	if (fileline(str, sizeof str, dot))
218 		dprint("%s", str);
219 }
220 
221 
222 /*
223  *	callback on stack trace
224  */
225 static uintptr nextpc;
226 
227 static void
ptrace(Map * map,uvlong pc,uvlong sp,Symbol * sym)228 ptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
229 {
230 	if(nextpc == 0)
231 		nextpc = sym->value;
232 	if(debug.stkprefix == nil)
233 		debug.stkprefix = "";
234 	dprint("%s%s(", debug.stkprefix, sym->name);
235 	printparams(map, sym, sp);
236 	dprint(")");
237 	if(nextpc != sym->value)
238 		dprint("+%#llux ", nextpc - sym->value);
239 	printsource(nextpc);
240 	dprint("\n");
241 	printlocals(map, sym, sp);
242 	nextpc = pc;
243 }
244 
245 static void
stacktracepcsp(Map * m,uintptr pc,uintptr sp)246 stacktracepcsp(Map *m, uintptr pc, uintptr sp)
247 {
248 	nextpc = 0;
249 	if(machdata->ctrace==nil)
250 		dprint("no machdata->ctrace\n");
251 	else if(machdata->ctrace(m, pc, sp, 0, ptrace) <= 0)
252 		dprint("no stack frame: pc=%#p sp=%#p\n", pc, sp);
253 }
254 
255 static void
ureginit(void)256 ureginit(void)
257 {
258 	Reglist *r;
259 
260 	for(r = mach->reglist; r->rname; r++)
261 		if (strcmp(r->rname, "PC") == 0)
262 			debug.pcoff = r->roffs;
263 		else if (strcmp(r->rname, "SP") == 0)
264 			debug.spoff = r->roffs;
265 }
266 
267 static void
stacktrace(Map * m)268 stacktrace(Map *m)
269 {
270 	uintptr pc, sp;
271 
272 	if(geta(m, debug.pcoff, (uvlong *)&pc) < 0){
273 		dprint("geta pc: %r");
274 		return;
275 	}
276 	if(geta(m, debug.spoff, (uvlong *)&sp) < 0){
277 		dprint("geta sp: %r");
278 		return;
279 	}
280 	stacktracepcsp(m, pc, sp);
281 }
282 
283 static uintptr
star(uintptr addr)284 star(uintptr addr)
285 {
286 	uintptr x;
287 	static int warned;
288 
289 	if(addr == 0)
290 		return 0;
291 
292 	if(debug.map == nil){
293 		if(!warned++)
294 			dprint("no debug.map\n");
295 		return 0;
296 	}
297 	if(geta(debug.map, addr, (uvlong *)&x) < 0){
298 		dprint("geta %#p (pid=%d): %r\n", addr, debug.pid);
299 		return 0;
300 	}
301 	return x;
302 }
303 
304 static uintptr
resolvev(char * name)305 resolvev(char *name)
306 {
307 	Symbol s;
308 
309 	if(lookup(nil, name, &s) == 0)
310 		return 0;
311 	return s.value;
312 }
313 
314 static uintptr
resolvef(char * name)315 resolvef(char *name)
316 {
317 	Symbol s;
318 
319 	if(lookup(name, nil, &s) == 0)
320 		return 0;
321 	return s.value;
322 }
323 
324 #define FADDR(type, p, name) ((p) + offsetof(type, name))
325 #define FIELD(type, p, name) star(FADDR(type, p, name))
326 
327 static uintptr threadpc;
328 
329 static int
strprefix(char * big,char * pre)330 strprefix(char *big, char *pre)
331 {
332 	return strncmp(big, pre, strlen(pre));
333 }
334 static void
tptrace(Map * map,uvlong pc,uvlong sp,Symbol * sym)335 tptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
336 {
337 	char buf[512];
338 
339 	USED(map);
340 	USED(sym);
341 	USED(sp);
342 
343 	if(threadpc != 0)
344 		return;
345 	if(!fileline(buf, sizeof buf, pc))
346 		return;
347 	if(strprefix(buf, "/sys/src/libc/") == 0)
348 		return;
349 	if(strprefix(buf, "/sys/src/libthread/") == 0)
350 		return;
351 	threadpc = pc;
352 }
353 
354 static char*
threadstkline(uintptr t)355 threadstkline(uintptr t)
356 {
357 	uintptr pc, sp;
358 	static char buf[500];
359 
360 	if(FIELD(Thread, t, state) == Running){
361 		geta(debug.map, debug.pcoff, (uvlong *)&pc);
362 		geta(debug.map, debug.spoff, (uvlong *)&sp);
363 	}else{
364 		// pc = FIELD(Thread, t, sched[JMPBUFPC]);
365 		pc = resolvef("longjmp");
366 		sp = FIELD(Thread, t, sched[JMPBUFSP]);
367 	}
368 	if(machdata->ctrace == nil)
369 		return "";
370 	threadpc = 0;
371 	machdata->ctrace(debug.map, pc, sp, 0, tptrace);
372 	if(!fileline(buf, sizeof buf, threadpc))
373 		buf[0] = 0;
374 	return buf;
375 }
376 
377 static void
proc(uintptr p)378 proc(uintptr p)
379 {
380 	dprint("p=(Proc)%#p pid %d ", p, FIELD(Proc, p, pid));
381 	if(FIELD(Proc, p, thread) == 0)
382 		dprint(" Sched\n");
383 	else
384 		dprint(" Running\n");
385 }
386 
387 static void
fmtbufinit(Fmt * f,char * buf,int len)388 fmtbufinit(Fmt *f, char *buf, int len)
389 {
390 	memset(f, 0, sizeof *f);
391 	f->runes = 0;
392 	f->start = buf;
393 	f->to = buf;
394 	f->stop = buf + len - 1;
395 	f->flush = nil;
396 	f->farg = nil;
397 	f->nfmt = 0;
398 }
399 
400 static char*
fmtbufflush(Fmt * f)401 fmtbufflush(Fmt *f)
402 {
403 	*(char*)f->to = 0;
404 	return (char*)f->start;
405 }
406 
407 static char*
debugstr(uintptr s)408 debugstr(uintptr s)
409 {
410 	static char buf[4096];
411 	char *p, *e;
412 
413 	p = buf;
414 	e = buf+sizeof buf - 1;
415 	while(p < e){
416 		if(get1(debug.map, s++, (uchar*)p, 1) < 0)
417 			break;
418 		if(*p == 0)
419 			break;
420 		p++;
421 	}
422 	*p = 0;
423 	return buf;
424 }
425 
426 static char*
threadfmt(uintptr t)427 threadfmt(uintptr t)
428 {
429 	static char buf[4096];
430 	Fmt fmt;
431 	int s;
432 
433 	fmtbufinit(&fmt, buf, sizeof buf);
434 
435 	fmtprint(&fmt, "t=(Thread)%#p ", t);
436 	switch(s = FIELD(Thread, t, state)){
437 	case Running:
438 		fmtprint(&fmt, " Running   ");
439 		break;
440 	case Ready:
441 		fmtprint(&fmt, " Ready     ");
442 		break;
443 	case Rendezvous:
444 		fmtprint(&fmt, " Rendez    ");
445 		break;
446 	default:
447 		fmtprint(&fmt, " bad state %d ", s);
448 		break;
449 	}
450 
451 	fmtprint(&fmt, "%s", threadstkline(t));
452 
453 	if(FIELD(Thread, t, moribund) == 1)
454 		fmtprint(&fmt, " Moribund");
455 	if(s = FIELD(Thread, t, cmdname)){
456 		fmtprint(&fmt, " [%s]", debugstr(s));
457 	}
458 
459 	fmtbufflush(&fmt);
460 	return buf;
461 }
462 
463 
464 static void
thread(uintptr t)465 thread(uintptr t)
466 {
467 	dprint("%s\n", threadfmt(t));
468 }
469 
470 static void
threadapply(uintptr p,void (* fn)(uintptr))471 threadapply(uintptr p, void (*fn)(uintptr))
472 {
473 	int oldpid, pid;
474 	uintptr tq, t;
475 
476 	oldpid = debug.pid;
477 	pid = FIELD(Proc, p, pid);
478 	if(map(pid) == nil)
479 		return;
480 	tq = FADDR(Proc, p, threads);
481 	t = FIELD(Tqueue, tq, head);
482 	while(t != 0){
483 		fn(t);
484 		t = FIELD(Thread, t, nextt);
485 	}
486 	map(oldpid);
487 }
488 
489 static void
pthreads1(uintptr t)490 pthreads1(uintptr t)
491 {
492 	dprint("\t");
493 	thread(t);
494 }
495 
496 static void
pthreads(uintptr p)497 pthreads(uintptr p)
498 {
499 	threadapply(p, pthreads1);
500 }
501 
502 static void
lproc(uintptr p)503 lproc(uintptr p)
504 {
505 	proc(p);
506 	pthreads(p);
507 }
508 
509 static void
procapply(void (* fn)(uintptr))510 procapply(void (*fn)(uintptr))
511 {
512 	uintptr proc, pq;
513 
514 	pq = resolvev("_threadpq");
515 	if(pq == 0){
516 		dprint("no thread run queue\n");
517 		return;
518 	}
519 
520 	proc = FIELD(Pqueue, pq, head);
521 	while(proc){
522 		fn(proc);
523 		proc = FIELD(Proc, proc, next);
524 	}
525 }
526 
527 static void
threads(HConnect * c)528 threads(HConnect *c)
529 {
530 	USED(c);
531 	procapply(lproc);
532 }
533 
534 static void
procs(HConnect * c)535 procs(HConnect *c)
536 {
537 	USED(c);
538 	procapply(proc);
539 }
540 
541 static void
threadstack(uintptr t)542 threadstack(uintptr t)
543 {
544 	uintptr pc, sp;
545 
546 	if(FIELD(Thread, t, state) == Running){
547 		stacktrace(debug.map);
548 	}else{
549 		// pc = FIELD(Thread, t, sched[JMPBUFPC]);
550 		pc = resolvef("longjmp");
551 		sp = FIELD(Thread, t, sched[JMPBUFSP]);
552 		stacktracepcsp(debug.map, pc, sp);
553 	}
554 }
555 
556 
557 static void
tstacks(uintptr t)558 tstacks(uintptr t)
559 {
560 	dprint("\t");
561 	thread(t);
562 	threadstack(t);
563 	dprint("\n");
564 }
565 
566 static void
pstacks(uintptr p)567 pstacks(uintptr p)
568 {
569 	proc(p);
570 	threadapply(p, tstacks);
571 }
572 
573 static void
stacks(HConnect * c)574 stacks(HConnect *c)
575 {
576 	USED(c);
577 	debug.stkprefix = "\t\t";
578 	procapply(pstacks);
579 	debug.stkprefix = "";
580 }
581 
582 static void
symbols(HConnect * c)583 symbols(HConnect *c)
584 {
585 	USED(c);
586 	printsym();
587 }
588 
589 static void
segments(HConnect * c)590 segments(HConnect *c)
591 {
592 	USED(c);
593 	printmap("segments", debug.map);
594 }
595 
596 static void
fds(HConnect * c)597 fds(HConnect *c)
598 {
599 	USED(c);
600 	openfiles();
601 }
602 
603 static void
all(HConnect * c)604 all(HConnect *c)
605 {
606 	dprint("/proc/segment\n");
607 	segments(c);
608 	dprint("\n/proc/fd\n");
609 	fds(c);
610 	dprint("\n/proc/procs\n");
611 	procs(c);
612 	dprint("\n/proc/threads\n");
613 	threads(c);
614 	dprint("\n/proc/stacks\n");
615 	stacks(c);
616 	dprint("\n# /proc/symbols\n");
617 	// symbols(c);
618 }
619 
620 int
hproc(HConnect * c)621 hproc(HConnect *c)
622 {
623 	void (*fn)(HConnect*);
624 	Fmt fmt;
625 	static int beenhere;
626 	static char buf[65536];
627 
628 	if (!beenhere) {
629 		beenhere = 1;
630 		ureginit();
631 	}
632 	if(strcmp(c->req.uri, "/proc/all") == 0)
633 		fn = all;
634 	else if(strcmp(c->req.uri, "/proc/segment") == 0)
635 		fn = segments;
636 	else if(strcmp(c->req.uri, "/proc/fd") == 0)
637 		fn = fds;
638 	else if(strcmp(c->req.uri, "/proc/procs") == 0)
639 		fn = procs;
640 	else if(strcmp(c->req.uri, "/proc/threads") == 0)
641 		fn = threads;
642 	else if(strcmp(c->req.uri, "/proc/stacks") == 0)
643 		fn = stacks;
644 	else if(strcmp(c->req.uri, "/proc/symbols") == 0)
645 		fn = symbols;
646 	else
647 		return hnotfound(c);
648 
649 	if(hsettext(c) < 0)
650 		return -1;
651 	if(!canqlock(&debug.lock)){
652 		hprint(&c->hout, "debugger is busy\n");
653 		return 0;
654 	}
655 	if(debug.textfd < 0){
656 		if(text(getpid()) < 0){
657 			hprint(&c->hout, "cannot attach self text: %r\n");
658 			goto out;
659 		}
660 	}
661 	if(map(getpid()) == nil){
662 		hprint(&c->hout, "cannot map self: %r\n");
663 		goto out;
664 	}
665 
666 	fmtbufinit(&fmt, buf, sizeof buf);
667 	debug.fmt = &fmt;
668 	fn(c);
669 	hprint(&c->hout, "%s\n", fmtbufflush(&fmt));
670 	debug.fmt = nil;
671 out:
672 	qunlock(&debug.lock);
673 	return 0;
674 }
675