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