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