xref: /netbsd/sys/arch/shark/shark/profile.c (revision bf9ec67e)
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