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