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