1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Profiling routines counts ticks and calls to each profiled function.
12 * Copyright (C) 2005 by Brandon Low
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************
24 * profile_func_enter() based on mcount found in gmon.c:
26 ***************************************************************************
27 * Copyright (c) 1991, 1998 The Regents of the University of California.
28 * All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. [rescinded 22 July 1999]
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * @(#)gmon.c 5.3 (Berkeley) 5/22/91
62 #include <sys/types.h>
66 /* PFD is Profiled Function Data */
68 /* Indices are shorts which means that we use 4k of RAM */
69 #define INDEX_BITS 11 /* What is a reasonable size for this? */
70 #define INDEX_SIZE 2048 /* 2 ^ INDEX_BITS */
71 #define INDEX_MASK 0x7FF /* lower INDEX_BITS 1 */
74 * In the current setup (pfd has 4 longs and 2 shorts) this uses 20k of RAM
75 * for profiling, and allows for profiling sections of code with up-to
76 * 1024 function caller->callee pairs
85 struct pfd_struct
*caller
;
88 /* Possible states of profiling */
90 #define PROF_BUSY 0x01
91 #define PROF_ERROR 0x02
93 /* Masks for thread switches */
94 #define PROF_OFF_THREAD 0x10
95 #define PROF_ON_THREAD 0x0F
97 static unsigned short profiling
= PROF_OFF
;
98 static size_t recursion_level
;
99 static unsigned short indices
[INDEX_SIZE
];
100 static struct pfd_struct pfds
[NUMPFDS
];
101 /* This holds a pointer to the last pfd effected for time tracking */
102 static struct pfd_struct
*last_pfd
;
103 /* These are used to track the time when we've lost the CPU so it doesn't count
104 * against any of the profiled functions */
105 static int profiling_thread
= -1;
107 /* internal function prototypes */
108 static void profile_timer_tick(void);
109 static void profile_timer_unregister(void);
111 static void write_function_recursive(int fd
, struct pfd_struct
*pfd
, int depth
);
113 /* Be careful to use the right one for the size of your variable */
115 #define ADDQI_L(_var,_value) \
116 asm ("addq.l %[value],%[var];" \
117 : [var] "+g" (_var) \
118 : [value] "I" (_value) )
120 #define ADDQI_L(var, value) var += value
123 void profile_thread_stopped(int current_thread
) {
124 if (current_thread
== profiling_thread
) {
125 /* If profiling is busy or idle */
126 if (profiling
< PROF_ERROR
) {
127 /* Unregister the timer so that other threads aren't interrupted */
130 /* Make sure we don't waste time profiling when we're running the
132 profiling
|= PROF_OFF_THREAD
;
136 void profile_thread_started(int current_thread
) {
137 if (current_thread
== profiling_thread
) {
138 /* Now we are allowed to profile again */
139 profiling
&= PROF_ON_THREAD
;
140 /* if profiling was busy or idle */
141 if (profiling
< PROF_ERROR
) {
142 /* After we de-mask, if profiling is active, reactivate the timer */
143 timer_register(0, profile_timer_unregister
,
144 TIMER_FREQ
/10000, 0, profile_timer_tick
);
149 static void profile_timer_tick(void) {
151 register struct pfd_struct
*my_last_pfd
= last_pfd
;
153 ADDQI_L(my_last_pfd
->time
,1);
158 static void profile_timer_unregister(void) {
159 profiling
= PROF_ERROR
;
163 /* This function clears the links on top level linkers, and clears the needed
164 * parts of memory in the index array */
165 void profstart(int current_thread
) {
167 profiling_thread
= current_thread
;
168 last_pfd
= (struct pfd_struct
*)0;
171 memset(&indices
,0,INDEX_SIZE
* sizeof(unsigned short));
173 0, profile_timer_unregister
, TIMER_FREQ
/10000, 0, profile_timer_tick
);
177 static void write_function_recursive(int fd
, struct pfd_struct
*pfd
, int depth
){
178 unsigned short link
= pfd
->link
;
179 fdprintf(fd
,"0x%08lX\t%08ld\t%08ld\t%04d\n", (size_t)pfd
->self_pc
,
180 pfd
->count
, pfd
->time
, depth
);
181 if (link
> 0 && link
< NUMPFDS
) {
182 write_function_recursive(fd
, &pfds
[link
], depth
++);
187 int profiling_exit
= profiling
;
190 unsigned short current_index
;
192 profiling
= PROF_OFF
;
193 fd
= open("/profile.out", O_WRONLY
|O_CREAT
|O_TRUNC
);
194 if (profiling_exit
== PROF_ERROR
) {
195 fdprintf(fd
,"Profiling exited with an error.\n");
196 fdprintf(fd
,"Overflow or timer stolen most likely.\n");
198 fdprintf(fd
,"PROFILE_THREAD\tPFDS_USED\n");
199 fdprintf(fd
,"%08d\t%08d\n", profiling_thread
,
201 fdprintf(fd
,"FUNCTION_PC\tCALL_COUNT\tTICKS\t\tDEPTH\n");
202 for (i
= 0; i
< INDEX_SIZE
; i
++) {
203 current_index
= indices
[i
];
204 if (current_index
!= 0) {
205 write_function_recursive(fd
, &pfds
[current_index
], 0);
208 fdprintf(fd
,"DEBUG PROFILE DATA FOLLOWS\n");
209 fdprintf(fd
,"INDEX\tLOCATION\tSELF_PC\t\tCOUNT\t\tTIME\t\tLINK\tCALLER\n");
210 for (i
= 0; i
< NUMPFDS
; i
++) {
211 struct pfd_struct
*my_last_pfd
= &pfds
[i
];
212 if (my_last_pfd
->self_pc
!= 0) {
214 "%04d\t0x%08lX\t0x%08lX\t0x%08lX\t0x%08lX\t%04d\t0x%08lX\n",
215 i
, (size_t)my_last_pfd
, (size_t)my_last_pfd
->self_pc
,
216 my_last_pfd
->count
, my_last_pfd
->time
, my_last_pfd
->link
,
217 (size_t)my_last_pfd
->caller
);
220 fdprintf(fd
,"INDEX_ADDRESS=INDEX\n");
221 for (i
=0; i
< INDEX_SIZE
; i
++) {
222 fdprintf(fd
,"%08lX=%04d\n",(size_t)&indices
[i
],indices
[i
]);
227 void profile_func_exit(void *self_pc
, void *call_site
) {
230 /* When we started timing, we set the time to the tick at that time
231 * less the time already used in function */
235 profiling
= PROF_BUSY
;
237 register unsigned short my_recursion_level
= recursion_level
;
238 if (my_recursion_level
) {
239 my_recursion_level
--;
240 recursion_level
= my_recursion_level
;
242 /* This shouldn't be necessary, maybe exit could be called first */
243 register struct pfd_struct
*my_last_pfd
= last_pfd
;
245 last_pfd
= my_last_pfd
->caller
;
252 #define ALLOCATE_PFD(temp) \
253 temp = ++pfds[0].link;\
254 if (temp >= NUMPFDS) goto overflow; \
256 pfd->self_pc = self_pc; pfd->count = 1; pfd->time = 0
258 void profile_func_enter(void *self_pc
, void *from_pc
) {
259 struct pfd_struct
*pfd
;
260 struct pfd_struct
*prev_pfd
;
261 unsigned short *pfd_index_pointer
;
262 unsigned short pfd_index
;
264 /* check that we are profiling and that we aren't recursively invoked
265 * this is equivalent to 'if (profiling != PROF_ON)' but it's faster */
269 profiling
= PROF_BUSY
;
270 /* A check that the PC is in the code range here wouldn't hurt, but this is
271 * logically guaranteed to be a valid address unless the constants are
272 * breaking the rules. */
273 pfd_index_pointer
= &indices
[((size_t)from_pc
)&INDEX_MASK
];
274 pfd_index
= *pfd_index_pointer
;
275 if (pfd_index
== 0) {
276 /* new caller, allocate new storage */
277 ALLOCATE_PFD(pfd_index
);
279 *pfd_index_pointer
= pfd_index
;
282 pfd
= &pfds
[pfd_index
];
283 if (pfd
->self_pc
== self_pc
) {
284 /* only / most recent function called by this caller, usual case */
285 /* increment count, start timing and exit */
288 /* collision, bad for performance, look down the list of functions called by
290 for (; /* goto done */; ) {
291 pfd_index
= pfd
->link
;
292 if (pfd_index
== 0) {
293 /* no more previously called functions, allocate a new one */
294 ALLOCATE_PFD(pfd_index
);
295 /* this function becomes the new head, link to the old head */
296 pfd
->link
= *pfd_index_pointer
;
297 /* and set the index to point to this function */
298 *pfd_index_pointer
= pfd_index
;
299 /* start timing and exit */
302 /* move along the chain */
304 pfd
= &pfds
[pfd_index
];
305 if (pfd
->self_pc
== self_pc
) {
307 /* Remove me from my old spot */
308 prev_pfd
->link
= pfd
->link
;
309 /* Link to the old head */
310 pfd
->link
= *pfd_index_pointer
;
312 *pfd_index_pointer
= pfd_index
;
313 /* increment count, start timing and exit */
318 /* We've found a pfd, increment it */
320 ADDQI_L(pfd
->count
,1);
321 /* We've (found or created) and updated our pfd, save it and start timing */
324 register struct pfd_struct
*my_last_pfd
= last_pfd
;
325 if (pfd
!= my_last_pfd
) {
326 /* If we are not recursing */
327 pfd
->caller
= my_last_pfd
;
330 ADDQI_L(recursion_level
,1);
333 /* Start timing this function */
335 return; /* normal return restores saved registers */
338 /* this is the same as 'profiling = PROF_ERROR' */
339 profiling
= PROF_ERROR
;