1 #include "dat.h" 2 #include "fns.h" 3 #include "error.h" 4 #include "interp.h" 5 #include <isa.h> 6 #include "runt.h" 7 8 extern Pool* imagmem; 9 extern void (*memmonitor)(int, ulong, ulong, ulong); 10 11 static void cpxec(Prog *); 12 static void memprof(int, void*, ulong); 13 static void memprofmi(int, ulong, ulong, ulong); 14 15 extern Inst* pc2dispc(Inst*, Module*); 16 17 static int interval = 100; /* Sampling interval in milliseconds */ 18 19 enum 20 { 21 HSIZE = 32, 22 }; 23 24 #define HASH(m) ((m)%HSIZE) 25 26 /* cope with multiple profilers some day */ 27 28 typedef struct Record Record; 29 struct Record 30 { 31 int id; 32 char* name; 33 char* path; 34 Inst* base; 35 int size; 36 /*Module* m; */ 37 ulong mtime; 38 Qid qid; 39 Record* hash; 40 Record* link; 41 ulong bucket[1]; 42 }; 43 44 struct 45 { 46 Lock l; 47 vlong time; 48 Record* hash[HSIZE]; 49 Record* list; 50 } profile; 51 52 typedef struct Pmod Pmod; 53 struct Pmod 54 { 55 char* name; 56 Pmod* link; 57 } *pmods; 58 59 #define QSHIFT 4 60 #define QID(q) ((ulong)(q).path&0xf) 61 #define QPID(pid) ((pid)<<QSHIFT) 62 #define PID(q) ((q).vers) 63 #define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) 64 65 enum 66 { 67 Qdir, 68 Qname, 69 Qpath, 70 Qhist, 71 Qpctl, 72 Qctl, 73 }; 74 75 Dirtab profdir[] = 76 { 77 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 78 "name", {Qname}, 0, 0444, 79 "path", {Qpath}, 0, 0444, 80 "histogram", {Qhist}, 0, 0444, 81 "pctl", {Qpctl}, 0, 0222, 82 "ctl", {Qctl}, 0, 0222, 83 }; 84 85 enum{ 86 Pnil, /* null profiler */ 87 Psam, /* sampling profiler */ 88 Pcov, /* coverage profiler */ 89 Pmem, /* heap memory profiler */ 90 }; 91 92 enum{ 93 Mnone = 0, 94 Mmain = 1, 95 Mheap = 2, 96 Mimage = 4, 97 }; 98 99 static int profiler = Pnil; 100 static int mprofiler = Mnone; 101 102 static int ids; 103 static int samplefn; 104 105 static void sampler(void*); 106 107 static Record* 108 getrec(int id) 109 { 110 Record *r; 111 112 for(r = profile.list; r != nil; r = r->link) 113 if(r->id == id) 114 break; 115 return r; 116 } 117 118 static void 119 addpmod(char *m) 120 { 121 Pmod *p = malloc(sizeof(Pmod)); 122 123 if(p == nil) 124 return; 125 p->name = malloc(strlen(m)+1); 126 if(p->name == nil){ 127 free(p); 128 return; 129 } 130 strcpy(p->name, m); 131 p->link = pmods; 132 pmods = p; 133 } 134 135 static void 136 freepmods(void) 137 { 138 Pmod *p, *np; 139 140 for(p = pmods; p != nil; p = np){ 141 free(p->name); 142 np = p->link; 143 free(p); 144 } 145 pmods = nil; 146 } 147 148 static int 149 inpmods(char *m) 150 { 151 Pmod *p; 152 153 for(p = pmods; p != nil; p = p->link) 154 if(strcmp(p->name, m) == 0) 155 return 1; 156 return 0; 157 } 158 159 static void 160 freeprof(void) 161 { 162 int i; 163 Record *r, *nr; 164 165 ids = 0; 166 profiler = Pnil; 167 mprofiler = Mnone; 168 freepmods(); 169 for(r = profile.list; r != nil; r = nr){ 170 free(r->name); 171 free(r->path); 172 nr = r->link; 173 free(r); 174 } 175 profile.list = nil; 176 profile.time = 0; 177 for(i = 0; i < HSIZE; i++) 178 profile.hash[i] = nil; 179 } 180 181 static int 182 profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) 183 { 184 Qid qid; 185 Record *r; 186 ulong path, perm, len; 187 Dirtab *tab; 188 189 USED(name); 190 USED(d); 191 USED(nd); 192 193 if(s == DEVDOTDOT) { 194 mkqid(&qid, Qdir, 0, QTDIR); 195 devdir(c, qid, "#P", 0, eve, 0555, dp); 196 return 1; 197 } 198 199 if(c->qid.path == Qdir && c->qid.type & QTDIR) { 200 acquire(); 201 if(s-- == 0){ 202 tab = &profdir[Qctl]; 203 mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE); 204 devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); 205 release(); 206 return 1; 207 } 208 r = profile.list; 209 while(s-- && r != nil) 210 r = r->link; 211 if(r == nil) { 212 release(); 213 return -1; 214 } 215 sprint(up->genbuf, "%.8lux", (ulong)r->id); 216 mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR); 217 devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp); 218 release(); 219 return 1; 220 } 221 if(s >= nelem(profdir)-1) 222 error(Enonexist); /* was return -1; */ 223 tab = &profdir[s]; 224 path = PATH(c->qid); 225 226 acquire(); 227 r = getrec(PID(c->qid)); 228 if(r == nil) { 229 release(); 230 error(Enonexist); /* was return -1; */ 231 } 232 233 perm = tab->perm; 234 len = tab->length; 235 mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); 236 devdir(c, qid, tab->name, len, eve, perm, dp); 237 release(); 238 return 1; 239 } 240 241 static Chan* 242 profattach(char *spec) 243 { 244 return devattach('P', spec); 245 } 246 247 static Walkqid* 248 profwalk(Chan *c, Chan *nc, char **name, int nname) 249 { 250 return devwalk(c, nc, name, nname, 0, 0, profgen); 251 } 252 253 static int 254 profstat(Chan *c, uchar *db, int n) 255 { 256 return devstat(c, db, n, 0, 0, profgen); 257 } 258 259 static Chan* 260 profopen(Chan *c, int omode) 261 { 262 int qid; 263 Record *r; 264 265 if(c->qid.type & QTDIR) { 266 if(omode != OREAD) 267 error(Eisdir); 268 c->mode = openmode(omode); 269 c->flag |= COPEN; 270 c->offset = 0; 271 return c; 272 } 273 274 if(omode&OTRUNC) 275 error(Eperm); 276 277 qid = QID(c->qid); 278 if(qid == Qctl || qid == Qpctl){ 279 if (omode != OWRITE) 280 error(Eperm); 281 } 282 else{ 283 if(omode != OREAD) 284 error(Eperm); 285 } 286 287 if(qid != Qctl){ 288 acquire(); 289 r = getrec(PID(c->qid)); 290 release(); 291 if(r == nil) 292 error(Ethread); 293 } 294 295 c->offset = 0; 296 c->flag |= COPEN; 297 c->mode = openmode(omode); 298 if(QID(c->qid) == Qhist) 299 c->aux = nil; 300 return c; 301 } 302 303 static int 304 profwstat(Chan *c, uchar *dp, int n) 305 { 306 Dir d; 307 Record *r; 308 309 if(strcmp(up->env->user, eve)) 310 error(Eperm); 311 if(c->qid.type & QTDIR) 312 error(Eperm); 313 acquire(); 314 r = getrec(PID(c->qid)); 315 release(); 316 if(r == nil) 317 error(Ethread); 318 n = convM2D(dp, n, &d, nil); 319 if(n == 0) 320 error(Eshortstat); 321 d.mode &= 0777; 322 /* TO DO: copy to c->aux->perm, once that exists */ 323 return n; 324 } 325 326 static void 327 profclose(Chan *c) 328 { 329 USED(c); 330 } 331 332 static long 333 profread(Chan *c, void *va, long n, vlong offset) 334 { 335 int i; 336 Record *r; 337 char *a = va; 338 339 if(c->qid.type & QTDIR) 340 return devdirread(c, a, n, 0, 0, profgen); 341 acquire(); 342 r = getrec(PID(c->qid)); 343 release(); 344 if(r == nil) 345 error(Ethread); 346 switch(QID(c->qid)){ 347 case Qname: 348 return readstr(offset, va, n, r->name); 349 case Qpath: 350 return readstr(offset, va, n, r->path); 351 case Qhist: 352 i = (int)c->aux; 353 while(i < r->size && r->bucket[i] == 0) 354 i++; 355 if(i >= r->size) 356 return 0; 357 c->aux = (void*)(i+1); 358 if(n < 20) 359 error(Etoosmall); 360 return sprint(a, "%d %lud", i, r->bucket[i]); 361 case Qctl: 362 error(Eperm); 363 } 364 return 0; 365 } 366 367 static long 368 profwrite(Chan *c, void *va, long n, vlong offset) 369 { 370 int i; 371 char *a = va; 372 char buf[128], *fields[128]; 373 void (*f)(int, ulong, ulong, ulong); 374 375 USED(va); 376 USED(n); 377 USED(offset); 378 379 if(c->qid.type & QTDIR) 380 error(Eisdir); 381 switch(QID(c->qid)){ 382 case Qctl: 383 if(n > sizeof(buf)-1) 384 n = sizeof(buf)-1; 385 memmove(buf, a, n); 386 buf[n] = 0; 387 i = getfields(buf, fields, nelem(fields), 1, " \t\n"); 388 if(i > 0 && strcmp(fields[0], "module") == 0){ 389 f = memmonitor; 390 memmonitor = nil; 391 freepmods(); 392 while(--i > 0) 393 addpmod(fields[i]); 394 memmonitor = f; 395 return n; 396 } 397 if(i == 1){ 398 if(strcmp(fields[0], "start") == 0){ 399 if(profiler == Pnil) { 400 profiler = Psam; 401 if(!samplefn){ 402 samplefn = 1; 403 kproc("prof", sampler, 0, 0); 404 } 405 } 406 } 407 else if(strncmp(fields[0], "startmp", 7) == 0){ 408 if(profiler == Pnil){ 409 profiler = Pmem; 410 for(a = &fields[0][7]; *a != '\0'; a++){ 411 if(*a == '1'){ 412 memmonitor = memprofmi; 413 mprofiler |= Mmain; 414 } 415 else if(*a == '2'){ 416 heapmonitor = memprof; 417 mprofiler |= Mheap; 418 } 419 else if(*a == '3'){ 420 memmonitor = memprofmi; 421 mprofiler |= Mimage; 422 } 423 }; 424 } 425 } 426 else if(strcmp(fields[0], "stop") == 0){ 427 profiler = Pnil; 428 mprofiler = Mnone; 429 } 430 else if(strcmp(fields[0], "end") == 0){ 431 profiler = Pnil; 432 mprofiler = Mnone; 433 memmonitor = nil; 434 freeprof(); 435 interval = 100; 436 } 437 else 438 error(Ebadarg); 439 } 440 else if (i == 2){ 441 if(strcmp(fields[0], "interval") == 0) 442 interval = strtoul(fields[1], nil, 0); 443 else if(strcmp(fields[0], "startcp") == 0){ 444 Prog *p; 445 446 acquire(); 447 p = progpid(strtoul(fields[1], nil, 0)); 448 if(p == nil){ 449 release(); 450 return -1; 451 } 452 if(profiler == Pnil){ 453 profiler = Pcov; 454 p->xec = cpxec; 455 } 456 release(); 457 } 458 else 459 error(Ebadarg); 460 } 461 else 462 error(Ebadarg); 463 return n; 464 default: 465 error(Eperm); 466 } 467 return 0; 468 } 469 470 static Record* 471 newmodule(Module *m, int vm, int scale, int origin) 472 { 473 int dsize; 474 Record *r, **l; 475 476 if(!vm) 477 acquire(); 478 if((m->compiled && m->pctab == nil) || m->prog == nil) { 479 if(!vm) 480 release(); 481 return nil; 482 } 483 if(m->compiled) 484 dsize = m->nprog * sizeof(r->bucket[0]); 485 else 486 dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]); 487 dsize *= scale; 488 dsize += origin; 489 r = malloc(sizeof(Record)+dsize); 490 if(r == nil) { 491 if(!vm) 492 release(); 493 return nil; 494 } 495 496 r->id = ++ids; 497 if(ids == (1<<8)-1) 498 ids = 0; 499 kstrdup(&r->name, m->name); 500 kstrdup(&r->path, m->path); 501 r->base = m->prog; 502 r->size = dsize/sizeof(r->bucket[0]); 503 /* r->m = m; */ 504 r->mtime = m->mtime; 505 r->qid.path = m->qid.path; 506 r->qid.vers = m->qid.vers; 507 memset(r->bucket, 0, dsize); 508 r->link = profile.list; 509 profile.list = r; 510 511 l = &profile.hash[HASH(m->mtime)]; 512 r->hash = *l; 513 *l = r; 514 515 if(!vm) 516 release(); 517 return r; 518 } 519 520 #define LIMBO(m) ((m)->path[0] != '$') 521 522 Module* 523 limbomodule(void) 524 { 525 Frame *f; 526 uchar *fp; 527 Module *m; 528 529 m = R.M->m; 530 if(LIMBO(m)) 531 return m; 532 for(fp = R.FP ; fp != nil; fp = f->fp){ 533 f = (Frame*)fp; 534 if(f->mr != nil){ 535 m = f->mr->m; 536 if(LIMBO(m)) 537 return m; 538 } 539 } 540 return nil; 541 } 542 543 static Record* 544 mlook(Module *m, int limbo, int vm, int scale, int origin) 545 { 546 Record *r; 547 void (*f)(int, ulong, ulong, ulong); 548 549 if(limbo) 550 m = limbomodule(); 551 if(m == nil) 552 return nil; 553 for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){ 554 if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){ 555 r->base = m->prog; 556 return r; 557 } 558 } 559 if(pmods == nil || inpmods(m->name) || inpmods(m->path)){ 560 f = memmonitor; 561 memmonitor = nil; /* prevent monitoring of our memory usage */ 562 r = newmodule(m, vm, scale, origin); 563 memmonitor = f; 564 return r; 565 } 566 return nil; 567 } 568 569 static void 570 sampler(void* a) 571 { 572 int i; 573 Module *m; 574 Record *r; 575 Inst *p; 576 577 USED(a); 578 for(;;) { 579 osmillisleep(interval); 580 if(profiler != Psam) 581 break; 582 lock(&profile.l); 583 profile.time += interval; 584 if(R.M == H || (m = R.M->m) == nil){ 585 unlock(&profile.l); 586 continue; 587 } 588 p = R.PC; 589 r = mlook(m, 0, 0, 1, 0); 590 if(r == nil){ 591 unlock(&profile.l); 592 continue; 593 } 594 if(m->compiled && m->pctab != nil) 595 p = pc2dispc(p, m); 596 if((i = p-r->base) >= 0 && i < r->size) 597 r->bucket[i]++; 598 unlock(&profile.l); 599 } 600 samplefn = 0; 601 pexit("", 0); 602 } 603 604 /* 605 * coverage profiling 606 */ 607 608 static void 609 cpxec(Prog *p) 610 { 611 int op, i; 612 Module *m; 613 Record *r; 614 Prog *n; 615 616 R = p->R; 617 R.MP = R.M->MP; 618 R.IC = p->quanta; 619 620 if(p->kill != nil){ 621 char *m; 622 m = p->kill; 623 p->kill = nil; 624 error(m); 625 } 626 627 if(R.M->compiled) 628 comvec(); 629 else{ 630 m = R.M->m; 631 r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil; 632 do{ 633 dec[R.PC->add](); 634 op = R.PC->op; 635 if(r != nil){ 636 i = R.PC-r->base; 637 if(i >= 0 && i < r->size) 638 r->bucket[i]++; 639 } 640 R.PC++; 641 optab[op](); 642 if(op == ISPAWN || op == IMSPAWN){ 643 n = delruntail(Pdebug); /* any state will do */ 644 n->xec = cpxec; 645 addrun(n); 646 } 647 if(m != R.M->m){ 648 m = R.M->m; 649 r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil; 650 } 651 }while(--R.IC != 0); 652 } 653 654 p->R = R; 655 } 656 657 /* memory profiling */ 658 659 enum{ 660 Mhalloc, 661 Mhfree, 662 Mgcfree, 663 Mmfree, 664 Mmalloc, 665 Mifree, 666 Mialloc, 667 }; 668 669 static void 670 memprof(int c, void *v, ulong n) 671 { 672 int i, j, k; 673 ulong kk, *b; 674 Module *m; 675 Record *r; 676 Inst *p; 677 Heap *h; 678 679 USED(v); 680 USED(n); 681 if(profiler != Pmem){ 682 memmonitor = nil; 683 heapmonitor = nil; 684 return; 685 } 686 lock(&profile.l); 687 m = nil; 688 if(c != Mgcfree && (R.M == H || (m = R.M->m) == nil)){ 689 unlock(&profile.l); 690 return; 691 } 692 h = v; 693 if(c == Mhalloc || c == Mmalloc || c == Mialloc){ 694 p = R.PC; 695 if(m->compiled && m->pctab != nil) 696 p = pc2dispc(p, m); 697 if((r = mlook(m, 1, 1, 2, 2)) == nil){ 698 unlock(&profile.l); 699 return; 700 } 701 i = p-r->base; 702 k = (r->id<<24) | i; 703 if(c == Mhalloc){ 704 h->hprof = k; 705 j = hmsize(h)-sizeof(Heap); 706 } 707 else if(c == Mmalloc){ 708 setmalloctag(v, k); 709 j = msize(v); 710 } 711 else{ 712 ((ulong*)v)[1] = k; 713 j = poolmsize(imagmem, v)-sizeof(ulong); 714 } 715 } 716 else{ 717 if(c == Mmfree) 718 k = getmalloctag(v); 719 else if(c == Mifree) 720 k = ((ulong*)v)[1]; 721 else 722 k = h->hprof; 723 if((r = getrec(k>>24)) == nil){ 724 unlock(&profile.l); 725 return; 726 } 727 i = k&0xffffff; 728 if(c == Mmfree) 729 j = msize(v); 730 else if(c == Mifree) 731 j = poolmsize(imagmem, v)-sizeof(ulong); 732 else 733 j = hmsize(h)-sizeof(Heap); 734 j = -j; 735 } 736 i = 2*(i+1); 737 b = r->bucket; 738 if(i >= 0 && i < r->size){ 739 if(0){ 740 if(c == 1){ 741 b[0] -= j; 742 b[i] -= j; 743 } 744 else if(c == 2){ 745 b[1] -= j; 746 b[i+1] -= j; 747 } 748 } 749 else{ 750 b[0] += j; 751 if((int)b[0] < 0) 752 b[0] = 0; 753 b[i] += j; 754 if((int)b[i] < 0) 755 b[i] = 0; 756 if(j > 0){ 757 if((kk = b[0]) > b[1]) 758 b[1] = kk; 759 if((kk = b[i]) > b[i+1]) 760 b[i+1] = kk; 761 } 762 } 763 } 764 unlock(&profile.l); 765 } 766 767 /* main and image memory */ 768 static void 769 memprofmi(int c, ulong pc, ulong v, ulong n) 770 { 771 USED(pc); 772 773 if(c&2){ 774 if(!(mprofiler&Mimage)) 775 return; 776 } 777 else{ 778 if(!(mprofiler&Mmain)) 779 return; 780 } 781 switch(c){ 782 case 0: 783 c = Mmalloc; 784 break; 785 case 2: 786 c = Mialloc; 787 break; 788 case 0 | 1<<8: 789 c = Mmfree; 790 break; 791 case 2 | 1<<8: 792 c = Mifree; 793 break; 794 default: 795 print("bad profile code %d\n", c); 796 } 797 memprof(c, (void*)v, n); 798 } 799 800 Dev profdevtab = { 801 'P', 802 "prof", 803 804 devinit, 805 profattach, 806 profwalk, 807 profstat, 808 profopen, 809 devcreate, 810 profclose, 811 profread, 812 devbread, 813 profwrite, 814 devbwrite, 815 devremove, 816 profwstat 817 }; 818