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