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