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