1 /* $NetBSD: profile.c,v 1.5 2002/10/23 09:12:03 jdolecek Exp $ */ 2 3 /* 4 * Copyright 1997 5 * Digital Equipment Corporation. All rights reserved. 6 * 7 * This software is furnished under license and may be used and 8 * copied only in accordance with the following terms and conditions. 9 * Subject to these conditions, you may download, copy, install, 10 * use, modify and distribute this software in source and/or binary 11 * form. No title or ownership is transferred hereby. 12 * 13 * 1) Any source code used, modified or distributed must reproduce 14 * and retain this copyright notice and list of conditions as 15 * they appear in the source file. 16 * 17 * 2) No right is granted to use any trade name, trademark, or logo of 18 * Digital Equipment Corporation. Neither the "Digital Equipment 19 * Corporation" name nor any trademark or logo of Digital Equipment 20 * Corporation may be used to endorse or promote products derived 21 * from this software without the prior written permission of 22 * Digital Equipment Corporation. 23 * 24 * 3) This software is provided "AS-IS" and any express or implied 25 * warranties, including but not limited to, any implied warranties 26 * of merchantability, fitness for a particular purpose, or 27 * non-infringement are disclaimed. In no event shall DIGITAL be 28 * liable for any damages whatsoever, and in particular, DIGITAL 29 * shall not be liable for special, indirect, consequential, or 30 * incidental damages or damages for lost profits, loss of 31 * revenue or loss of use, whether such damages arise in contract, 32 * negligence, tort, under statute, in equity, at law or otherwise, 33 * even if advised of the possibility of such damage. 34 */ 35 36 /* 37 * The fiq based profiler. 38 */ 39 40 #include "profiler.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/buf.h> 45 #include <sys/time.h> 46 #include <sys/proc.h> 47 #include <sys/user.h> 48 #include <sys/ioctl.h> 49 #include <sys/conf.h> 50 #include <sys/errno.h> 51 #include <sys/fcntl.h> 52 #include <sys/uio.h> 53 #include <sys/malloc.h> 54 #include <sys/proc.h> 55 56 #include <shark/shark/hat.h> 57 #include <machine/profileio.h> 58 #include <dev/ic/i8253reg.h> 59 60 #define PROFILER_DEBUG 1 61 62 #define countPerTick 500 /* TIMER_FREQ/10000 10 kHz timer */ 63 64 /* Processor Status Defines */ 65 #define STATUS_MODE_MASK 0x1f 66 #define USER_MODE 0x10 67 #define FIQ_MODE 0x11 68 #define IRQ_MODE 0x12 69 #define SVC_MODE 0x13 70 #define ABORT_MODE 0x17 71 #define UNDEF_MODE 0x1b 72 #define SYS_MODE 0x1f 73 74 /* software controller 75 */ 76 struct profiler_sc 77 { 78 int state; 79 #define PROF_OPEN 0x01 80 #define PROF_PROFILING 0x02 81 } prof_sc; 82 83 /* 84 * GLOBAL DATA 85 */ 86 87 /* I need my own stack space for the hat */ 88 #define HATSTACKSIZE 1024 /* size of stack used during a FIQ */ 89 static unsigned char hatStack[HATSTACKSIZE]; /* actual stack used 90 * during a FIQ 91 */ 92 /* Pointer to the list of hash tables. 93 * A backup table is created for every table malloced, this 94 * is used so that we don't miss samples while copying the 95 * data out. Thus the actual number of tables in the array is twice 96 * what nhashTables says. 97 */ 98 struct profHashTable *profTable; 99 struct profHashTable *phashTables[2]; 100 int nhashTables; 101 102 /* 103 * FORWARD DECLARATIONS 104 */ 105 static void profFiq(int x); 106 static void profHatWedge(int nFIQs); 107 void profStop(void); 108 void profStart(struct profStartInfo *); 109 static void profEnter(struct profHashTable * , unsigned int); 110 void displayTable(struct profHashTable * ); 111 112 dev_type_open(profopen); 113 dev_type_close(profclose); 114 dev_type_read(profread); 115 dev_type_ioctl(profioctl); 116 117 const struct cdevsw prof_cdevsw = { 118 profopen, profclose, profread, nowrite, profioctl, 119 nostop, notty, nopoll, nommap, nokqfilter, 120 }; 121 122 void 123 profilerattach(n) 124 int n; 125 { 126 /* reset the profiler state */ 127 prof_sc.state = 0; 128 } 129 130 /* 131 * Open the profiling devicee. 132 * Returns 133 * ENXIO for illegal minor device 134 * ie. if the minor device number is not 0. 135 * EBUSY if file is open by another process. 136 * EROFS if attempt to open in write mode. 137 */ 138 int 139 profopen(dev, flag, mode, p) 140 dev_t dev; 141 int flag; 142 int mode; 143 struct proc *p; 144 { 145 146 /* check that the minor number is correct. */ 147 if (minor(dev) >= NPROFILER) 148 { 149 return ENXIO; 150 } 151 152 /* check that the device is not already open. */ 153 if (prof_sc.state && PROF_OPEN) 154 { 155 return EBUSY; 156 } 157 158 /* check that the flag is set to read only. */ 159 if (!(flag && FWRITE)) 160 { 161 return EROFS; 162 } 163 /* flag the device as open. */ 164 prof_sc.state |= PROF_OPEN; 165 nhashTables = 0; 166 phashTables[0] = phashTables[1] = NULL; 167 return 0; 168 } 169 170 /* 171 * Close the descriptor. 172 * 173 */ 174 int 175 profclose(dev, flag, mode, p) 176 dev_t dev; 177 int flag; 178 int mode; 179 struct proc *p; 180 { 181 /* clear the state, and stop profiling if 182 * it is happening. 183 */ 184 profStop(); 185 prof_sc.state &= ~PROF_OPEN; 186 return 0; 187 } 188 189 int 190 profread(dev, uio, flags) 191 dev_t dev; 192 struct uio *uio; 193 int flags; 194 { 195 int error; 196 int real, backup; 197 198 /* must be profiling to read */ 199 if (!(prof_sc.state & PROF_PROFILING)) 200 { 201 error = EINVAL; 202 } 203 else 204 { 205 if (uio->uio_resid != sizeof(struct profHashHeader) + 206 profTable->hdr.tableSize * sizeof(struct profHashEntry)) 207 { 208 printf("profile read size is incorrect!"); 209 error = EINVAL; 210 } 211 else 212 { 213 /* first work out which table is currently being used. 214 */ 215 if (profTable == phashTables[0]) 216 { 217 real = 0; 218 backup = 1; 219 } 220 else 221 { 222 if (profTable == phashTables[1]) 223 { 224 real = 1; 225 backup = 0; 226 } 227 else 228 { 229 panic("profiler lost buffer"); 230 } 231 } 232 /* now initialise the backup copy before switching over. 233 */ 234 memset(phashTables[backup]->entries, 0, 235 profTable->hdr.tableSize * sizeof(struct profHashEntry)); 236 237 238 /* now initialise the header */ 239 phashTables[backup]->hdr.tableSize = phashTables[real]->hdr.tableSize; 240 phashTables[backup]->hdr.entries = phashTables[backup]->hdr.last 241 = phashTables[real]->hdr.entries; 242 phashTables[backup]->hdr.samples = 0; 243 phashTables[backup]->hdr.missed = 0; 244 phashTables[backup]->hdr.fiqs = 0; 245 phashTables[backup]->hdr.pid = phashTables[real]->hdr.pid; 246 phashTables[backup]->hdr.mode = phashTables[real]->hdr.mode; 247 248 /* ok now swap over. 249 * I don't worry about locking the fiq while I change 250 * this, at this point it won't matter which table the 251 * fiq reads. 252 */ 253 profTable = phashTables[backup]; 254 255 /* don't want to send the pointer, 256 * make assumption that table follows the header. 257 */ 258 if ( (error = uiomove(phashTables[real], 259 sizeof(struct profHashHeader), uio)) 260 != 0) 261 { 262 printf("uiomove failed error is %d\n", error); 263 } 264 else 265 { 266 if ( (error = uiomove(phashTables[real]->entries, 267 phashTables[real]->hdr.tableSize * 268 sizeof(struct profHashEntry), uio)) 269 != 0) 270 { 271 printf("uiomove failed error is %d\n", error); 272 } 273 } 274 } 275 } 276 return error; 277 } 278 279 /* 280 * PROFIOSTART Start Profiling 281 * PROFIOSTOP Stop Profiling 282 */ 283 static int profcount = 0; 284 static int ints = 0; 285 int 286 profioctl(dev, cmd, data, flag, p) 287 dev_t dev; 288 u_long cmd; 289 caddr_t data; 290 int flag; 291 struct proc *p; 292 { 293 int error = 0; 294 struct profStartInfo *info = (struct profStartInfo *) data; 295 296 switch (cmd) 297 { 298 case PROFIOSTART : 299 profStart(info); 300 break; 301 case PROFIOSTOP : 302 profStop(); 303 break; 304 default : 305 error = EINVAL; 306 break; 307 } 308 return error; 309 } 310 311 /* start profiling, returning status information in the 312 * profStartInfo structure. 313 * 314 * presumes pid is running, does no checks here. 315 */ 316 void 317 profStart(struct profStartInfo *info) 318 { 319 unsigned int savedInts; 320 char *buffer; 321 322 /* can't already be sampling */ 323 if ( prof_sc.state & PROF_PROFILING ) 324 { 325 info->status = ALREADY_SAMPLING; 326 return ; 327 } 328 329 /* sanity check that the table sizes are logical */ 330 if (info->entries > info->tableSize) 331 { 332 info->status = BAD_TABLE_SIZE; 333 return ; 334 } 335 336 /* now sanity check that we are sampling either the 337 * kernel or a pid or both. 338 */ 339 if ( !(info->mode & SAMPLE_MODE_MASK) ) 340 { 341 info->status = ILLEGAL_COMMAND; 342 return ; 343 } 344 345 /* alloc two hash tables. */ 346 buffer = malloc(sizeof(struct profHashTable) + 347 info->tableSize * sizeof(struct profHashEntry), 348 M_DEVBUF, M_NOWAIT); 349 if ( (buffer == NULL) ) 350 { 351 info->status = NO_MEMORY; 352 return; 353 } 354 phashTables[0] = (struct profHashTable *) buffer; 355 phashTables[0]->entries = (struct profHashEntry *) 356 ( buffer + sizeof(struct profHashTable)); 357 358 buffer = malloc(sizeof(struct profHashTable) + 359 info->tableSize * sizeof(struct profHashEntry), 360 M_DEVBUF, M_NOWAIT); 361 if ( (buffer == NULL) ) 362 { 363 free(phashTables[0], M_DEVBUF); 364 info->status = NO_MEMORY; 365 return; 366 } 367 phashTables[1] = (struct profHashTable *) buffer; 368 phashTables[1]->entries = (struct profHashEntry *) 369 ( buffer + sizeof(struct profHashTable)); 370 371 memset(phashTables[0]->entries, 0, 372 info->tableSize * sizeof(struct profHashEntry)); 373 memset(phashTables[1]->entries, 0, 374 info->tableSize * sizeof(struct profHashEntry)); 375 376 /* now initialise the header */ 377 profTable = phashTables[0]; 378 profTable->hdr.tableSize = info->tableSize; 379 profTable->hdr.entries = profTable->hdr.last = info->entries; 380 profTable->hdr.samples = 0; 381 profTable->hdr.missed = 0; 382 profTable->hdr.fiqs = 0; 383 profTable->hdr.pid = info->pid; 384 profTable->hdr.mode = info->mode; 385 386 /* now let the pigeons loose. */ 387 savedInts = disable_interrupts(I32_bit | F32_bit); 388 prof_sc.state |= PROF_PROFILING; 389 hatClkOn(countPerTick, 390 profFiq, 391 (int)&prof_sc, 392 hatStack + HATSTACKSIZE - sizeof(unsigned), 393 profHatWedge); 394 restore_interrupts(savedInts); 395 } 396 397 void 398 profStop(void) 399 { 400 unsigned int savedInts; 401 int spl; 402 403 savedInts = disable_interrupts(I32_bit | F32_bit); 404 hatClkOff(); 405 restore_interrupts(savedInts); 406 407 spl = splbio(); 408 /* only free the buffer's if we were profiling, 409 * who cares if we were not, won't alert any one. 410 */ 411 if (prof_sc.state & PROF_PROFILING) 412 { 413 /* now free both buffers. */ 414 free(phashTables[0], M_DEVBUF); 415 free(phashTables[1], M_DEVBUF); 416 } 417 phashTables[0] = phashTables[1] = NULL; 418 prof_sc.state &= ~PROF_PROFILING; 419 splx(spl); 420 421 } 422 423 /* 424 **++ 425 ** FUNCTIONAL DESCRIPTION: 426 ** 427 ** profFiq 428 ** 429 ** This is what the HAT clock calls. This call drives 430 ** the timeout queues, which in turn drive the state machines 431 ** 432 ** Be very carefully when calling a timeout as the function 433 ** that is called may in turn do timeout/untimeout calls 434 ** before returning 435 ** 436 ** FORMAL PARAMETERS: 437 ** 438 ** int x - not used 439 ** 440 ** IMPLICIT INPUTS: 441 ** 442 ** nill 443 ** 444 ** IMPLICIT OUTPUTS: 445 ** 446 ** nill 447 ** 448 ** FUNCTION VALUE: 449 ** 450 ** nill 451 ** 452 ** SIDE EFFECTS: 453 ** 454 ** a timeout may be called if it is due 455 **-- 456 */ 457 static void 458 profFiq(int x) 459 { 460 int i; 461 int *ip; /* the fiq stack pointer */ 462 unsigned int spsr, stacklr; /* the link register, off the stack. */ 463 464 465 /* get the link register and see where we came from. 466 * We do this by getting the stack pointer using, 467 * an inline assembler instruction and then going 9 468 * words up to get the return address from the fiq. 469 * 470 * NOTE: the stack will change if more local variables 471 * are added so beware of modifications to this 472 * function. 473 * the fiq.S handler puts the following on the stack 474 * stmfd sp!, {r0-r3, lr} 475 * then this function does 476 * mov ip, sp 477 * stmfd sp!, {r4, fp, ip, lr, pc} 478 * or some variant of this. 479 * 480 * instead of using sp we can use ip, the saved stack pointer 481 * and be done with the chance of sp changing around on us. 482 * 483 * so by the time we get here we have a stack that looks like. 484 * (see pg 4-23, ARM programming Techniques doco for description 485 * on stm instructions.) 486 * lr-fiq (we want this one). 487 * r3-fiq 488 * r2-fiq 489 * r1-fiq 490 * ip--> r0-fiq 491 * pc-prof 492 * lr-prof 493 * ip-prof 494 * fp-prof 495 * sp--> r4-prof 496 * the sp by the time we get to it will point to r4 at the 497 * bottom of the stack. So we go 9 up to get the lr we want. 498 * or even better we have ip pointing to r0 and we can go 4 up 499 * to get the saved link register. 500 * 501 * We are safer this way because fiq.S is coded assembler, we are 502 * at the mercy of the assembler for our stack. 503 * 504 */ 505 asm("mov %0, ip" : "=r" (ip) : ); 506 stacklr = *(ip+4); 507 508 /* get the spsr register 509 */ 510 asm("mrs %0, spsr" : "=r" (spsr) : ); 511 512 /* now check whether we want this sample. 513 * NB. We place kernel and user level samples in the 514 * same table. 515 */ 516 if ( (profTable->hdr.mode & SAMPLE_PROC) && 517 ((spsr & STATUS_MODE_MASK) == USER_MODE) ) 518 { 519 if ( curproc->p_pid == profTable->hdr.pid ) 520 { 521 profEnter(profTable, stacklr-4); 522 } 523 } 524 525 if ( profTable->hdr.mode & SAMPLE_KERN ) 526 { 527 if ( ((spsr & STATUS_MODE_MASK) == SVC_MODE)/* || 528 ((spsr & STATUS_MODE_MASK) == IRQ_MODE)*/ ) 529 { 530 /* Note: the link register will be two instructions, 531 * ahead of the "blamed" instruction. This is actually 532 * a most likely case and might not actually highlight the 533 * exact cause of problems, some post processing intelligence 534 * will be required to make use of this data. 535 */ 536 profEnter(profTable, stacklr-4); 537 } 538 } 539 /* increment the samples counter */ 540 profTable->hdr.fiqs++; 541 } 542 543 /* 544 **++ 545 ** FUNCTIONAL DESCRIPTION: 546 ** 547 ** profHatWedge 548 ** 549 ** Called if the HAT timer becomes clogged/wedged. Not 550 ** used by this driver, we let upper layers recover 551 ** from this condition 552 ** 553 ** FORMAL PARAMETERS: 554 ** 555 ** int nFIQs - not used 556 ** 557 ** IMPLICIT INPUTS: 558 ** 559 ** nill 560 ** 561 ** IMPLICIT OUTPUTS: 562 ** 563 ** nill 564 ** 565 ** FUNCTION VALUE: 566 ** 567 ** nill 568 ** 569 ** SIDE EFFECTS: 570 ** 571 ** nill 572 **-- 573 */ 574 static void 575 profHatWedge(int nFIQs) 576 { 577 #ifdef PROFILER_DEBUG 578 printf("profHatWedge: nFIQ = %d\n",nFIQs); 579 #endif 580 } 581 582 /* Enter the data in the table. 583 * 584 * To reduce the time taken to find samples with time 585 * an eviction algorithm is implemented. 586 * When a new entry in the overflow area is required 587 * the first entry in the hash table is copied there 588 * and the new entry placed as the hash table entry. The 589 * displaced entry will then be the first entry accessed in 590 * the table. 591 */ 592 static void 593 profEnter(struct profHashTable *table, unsigned int lr) 594 { 595 unsigned int entries, hashShift, index, count; 596 struct profHashEntry *sample; 597 struct profHashEntry *first; 598 struct profHashEntry *prev; 599 struct profHashEntry tmpEntry; 600 int tmpIndex; 601 602 /* work out how many bits 603 * are required to hash the given size. 604 */ 605 entries = table->hdr.entries - 1; 606 hashShift = 0; 607 do 608 { 609 entries = entries << 1; 610 hashShift ++; 611 } while (!(entries & 0x80000000)); 612 613 /* enter the pc in the table. */ 614 /* remove redundant bits. 615 * and save the count offset bits 616 */ 617 lr = lr >> REDUNDANT_BITS; 618 count = lr & COUNT_BIT_MASK; 619 lr = lr >> COUNT_BITS; 620 621 /* this is easier than working out how 622 * many bits to or, based on the hashShift. 623 * maybe it would be better to work out at 624 * the start and save time during the fiq. 625 */ 626 index = (lr << hashShift) >> hashShift; 627 628 first = sample = &table->entries[index]; 629 /* now loop until we either find the entry 630 * or the next free space. 631 */ 632 while ( (sample->pc != lr) && (table->hdr.last < table->hdr.tableSize) ) 633 { 634 if (sample->pc == 0) 635 { 636 /* go ahead and stick it in */ 637 sample->pc = lr; 638 } 639 else 640 { 641 if (sample->next != 0) 642 { 643 /* move along and continue */ 644 prev = sample; 645 sample = &table->entries[sample->next]; 646 } 647 else 648 { 649 /* create a new entry if available */ 650 if (table->hdr.last < table->hdr.tableSize) 651 { 652 sample = &table->entries[table->hdr.last]; 653 /* copy the first sample into the new 654 * field. 655 */ 656 memcpy(sample, first, sizeof(struct profHashEntry)); 657 /* now update the new entry in the first position. 658 */ 659 first->pc = lr; 660 first->next = table->hdr.last; 661 first->counts[0] = 0; 662 first->counts[1] = 0; 663 first->counts[2] = 0; 664 first->counts[3] = 0; 665 table->hdr.last++; 666 /* update the sample pointer so that we 667 * can insert the count. 668 */ 669 sample = first; 670 } 671 } 672 } 673 } 674 675 /* check if we need to do an eviction. */ 676 if (sample != first) 677 { 678 /* copy the sample out of the table. */ 679 memcpy(&tmpEntry, sample, sizeof(struct profHashEntry)); 680 /* remove the sample from the chain. */ 681 tmpIndex = prev->next; 682 prev->next = sample->next; 683 /* now insert it at the beginning. */ 684 memcpy(sample, first, sizeof(struct profHashEntry)); 685 memcpy(first, &tmpEntry, sizeof(struct profHashEntry)); 686 /* now make the new first entry point to the old 687 * first entry. 688 */ 689 first->next = tmpIndex; 690 } 691 692 /* must now check the lr 693 * to see if the table is full. 694 */ 695 if (sample->pc == lr) 696 { 697 /* update the count */ 698 sample->counts[count]++; 699 table->hdr.samples++; 700 } 701 else 702 { 703 table->hdr.missed++; 704 } 705 } 706 707 void 708 displayTable(struct profHashTable *table) 709 { 710 int i; 711 struct profHashEntry *sample; 712 char buff[100] = ".............................................\n"; 713 714 for (i=0; i < table->hdr.tableSize; i++) 715 { 716 sample = &table->entries[i]; 717 if ((i * table->hdr.tableSize) >= table->hdr.entries) 718 { 719 printf("%s", buff); 720 buff[0] = '\0'; 721 } 722 printf("i = %d, pc = 0x%x, next = %d, counts %d %d %d %d\n", 723 i, sample->pc, sample->next, sample->counts[0], 724 sample->counts[1], sample->counts[2], sample->counts[3]); 725 } 726 return; 727 } 728