Sansa AMS: fix system_exception_wait()
[kugel-rb.git] / firmware / profile.c
blob6700eca404afb3ceeb43e32df5eef9816aeb2ad2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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
32 * are met:
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
53 * SUCH DAMAGE.
54 * @(#)gmon.c 5.3 (Berkeley) 5/22/91
57 #include <file.h>
58 #include <sprintf.h>
59 #include <system.h>
60 #include <string.h>
61 #include <timer.h>
62 #include <sys/types.h>
63 #include "profile.h"
65 /* PFD is Profiled Function Data */
67 /* Indices are shorts which means that we use 4k of RAM */
68 #define INDEX_BITS 11 /* What is a reasonable size for this? */
69 #define INDEX_SIZE 2048 /* 2 ^ INDEX_BITS */
70 #define INDEX_MASK 0x7FF /* lower INDEX_BITS 1 */
73 * In the current setup (pfd has 4 longs and 2 shorts) this uses 20k of RAM
74 * for profiling, and allows for profiling sections of code with up-to
75 * 1024 function caller->callee pairs
77 #define NUMPFDS 512
79 struct pfd_struct {
80 void *self_pc;
81 unsigned long count;
82 unsigned long time;
83 unsigned short link;
84 struct pfd_struct *caller;
87 /* Possible states of profiling */
88 #define PROF_ON 0x00
89 #define PROF_BUSY 0x01
90 #define PROF_ERROR 0x02
91 #define PROF_OFF 0x03
92 /* Masks for thread switches */
93 #define PROF_OFF_THREAD 0x10
94 #define PROF_ON_THREAD 0x0F
96 static unsigned short profiling = PROF_OFF;
97 static size_t recursion_level;
98 static unsigned short indices[INDEX_SIZE];
99 static struct pfd_struct pfds[NUMPFDS];
100 /* This holds a pointer to the last pfd effected for time tracking */
101 static struct pfd_struct *last_pfd;
102 /* These are used to track the time when we've lost the CPU so it doesn't count
103 * against any of the profiled functions */
104 static int profiling_thread = -1;
106 /* internal function prototypes */
107 static void profile_timer_tick(void);
108 static void profile_timer_unregister(void);
110 static void write_function_recursive(int fd, struct pfd_struct *pfd, int depth);
112 /* Be careful to use the right one for the size of your variable */
113 #ifdef CPU_COLDFIRE
114 #define ADDQI_L(_var,_value) \
115 asm ("addq.l %[value],%[var];" \
116 : [var] "+g" (_var) \
117 : [value] "I" (_value) )
118 #else
119 #define ADDQI_L(var, value) var += value
120 #endif
122 void profile_thread_stopped(int current_thread) {
123 if (current_thread == profiling_thread) {
124 /* If profiling is busy or idle */
125 if (profiling < PROF_ERROR) {
126 /* Unregister the timer so that other threads aren't interrupted */
127 timer_unregister();
129 /* Make sure we don't waste time profiling when we're running the
130 * wrong thread */
131 profiling |= PROF_OFF_THREAD;
135 void profile_thread_started(int current_thread) {
136 if (current_thread == profiling_thread) {
137 /* Now we are allowed to profile again */
138 profiling &= PROF_ON_THREAD;
139 /* if profiling was busy or idle */
140 if (profiling < PROF_ERROR) {
141 /* After we de-mask, if profiling is active, reactivate the timer */
142 timer_register(0, profile_timer_unregister,
143 TIMER_FREQ/10000, profile_timer_tick IF_COP(, 0 ) );
148 static void profile_timer_tick(void) {
149 if (!profiling) {
150 register struct pfd_struct *my_last_pfd = last_pfd;
151 if (my_last_pfd) {
152 ADDQI_L(my_last_pfd->time,1);
157 static void profile_timer_unregister(void) {
158 profiling = PROF_ERROR;
159 profstop();
162 /* This function clears the links on top level linkers, and clears the needed
163 * parts of memory in the index array */
164 void profstart(int current_thread) {
165 recursion_level = 0;
166 profiling_thread = current_thread;
167 last_pfd = (struct pfd_struct*)0;
168 pfds[0].link = 0;
169 pfds[0].self_pc = 0;
170 memset(indices,0,INDEX_SIZE * sizeof(unsigned short));
171 timer_register(
172 0, profile_timer_unregister, TIMER_FREQ/10000, profile_timer_tick IF_COP(, 0 ) );
173 profiling = PROF_ON;
176 static void write_function_recursive(int fd, struct pfd_struct *pfd, int depth){
177 unsigned short link = pfd->link;
178 fdprintf(fd,"0x%08lX\t%08ld\t%08ld\t%04d\n", (size_t)pfd->self_pc,
179 pfd->count, pfd->time, depth);
180 if (link > 0 && link < NUMPFDS) {
181 write_function_recursive(fd, &pfds[link], ++depth);
185 void profstop() {
186 int profiling_exit = profiling;
187 int fd = 0;
188 int i;
189 unsigned short current_index;
190 timer_unregister();
191 profiling = PROF_OFF;
192 fd = open("/profile.out", O_WRONLY|O_CREAT|O_TRUNC);
193 if (profiling_exit == PROF_ERROR) {
194 fdprintf(fd,"Profiling exited with an error.\n");
195 fdprintf(fd,"Overflow or timer stolen most likely.\n");
197 fdprintf(fd,"PROFILE_THREAD\tPFDS_USED\n");
198 fdprintf(fd,"%08d\t%08d\n", profiling_thread,
199 pfds[0].link);
200 fdprintf(fd,"FUNCTION_PC\tCALL_COUNT\tTICKS\t\tDEPTH\n");
201 for (i = 0; i < INDEX_SIZE; i++) {
202 current_index = indices[i];
203 if (current_index != 0) {
204 write_function_recursive(fd, &pfds[current_index], 0);
207 fdprintf(fd,"DEBUG PROFILE DATA FOLLOWS\n");
208 fdprintf(fd,"INDEX\tLOCATION\tSELF_PC\t\tCOUNT\t\tTIME\t\tLINK\tCALLER_IDX\n");
209 for (i = 0; i < NUMPFDS; i++) {
210 struct pfd_struct *my_last_pfd = &pfds[i];
211 if (my_last_pfd->self_pc != 0) {
212 fdprintf(fd,
213 "%04d\t0x%08lX\t0x%08lX\t0x%08lX\t0x%08lX\t%04d\t0x%08lX\n",
214 i, (size_t)my_last_pfd, (size_t)my_last_pfd->self_pc,
215 my_last_pfd->count, my_last_pfd->time, my_last_pfd->link,
216 (size_t)my_last_pfd->caller );
219 fdprintf(fd,"INDEX_ADDRESS=INDEX\n");
220 for (i=0; i < INDEX_SIZE; i++) {
221 fdprintf(fd,"%08lX=%04d\n",(size_t)&indices[i],indices[i]);
223 close(fd);
226 void __cyg_profile_func_exit(void *self_pc, void *call_site) {
227 (void)call_site;
228 (void)self_pc;
229 /* When we started timing, we set the time to the tick at that time
230 * less the time already used in function */
231 if (profiling) {
232 return;
234 profiling = PROF_BUSY;
236 register unsigned short my_recursion_level = recursion_level;
237 if (my_recursion_level) {
238 my_recursion_level--;
239 recursion_level = my_recursion_level;
240 } else {
241 /* This shouldn't be necessary, maybe exit could be called first */
242 register struct pfd_struct *my_last_pfd = last_pfd;
243 if (my_last_pfd) {
244 last_pfd = my_last_pfd->caller;
248 profiling = PROF_ON;
251 #define ALLOCATE_PFD(temp) \
252 temp = ++pfds[0].link;\
253 if (temp >= NUMPFDS) goto overflow; \
254 pfd = &pfds[temp];\
255 pfd->self_pc = self_pc; pfd->count = 1; pfd->time = 0
257 void __cyg_profile_func_enter(void *self_pc, void *from_pc) {
258 struct pfd_struct *pfd;
259 struct pfd_struct *prev_pfd;
260 unsigned short *pfd_index_pointer;
261 unsigned short pfd_index;
263 /* check that we are profiling and that we aren't recursively invoked
264 * this is equivalent to 'if (profiling != PROF_ON)' but it's faster */
265 if (profiling) {
266 return;
268 profiling = PROF_BUSY;
269 /* A check that the PC is in the code range here wouldn't hurt, but this is
270 * logically guaranteed to be a valid address unless the constants are
271 * breaking the rules. */
272 pfd_index_pointer = &indices[((size_t)from_pc)&INDEX_MASK];
273 pfd_index = *pfd_index_pointer;
274 if (pfd_index == 0) {
275 /* new caller, allocate new storage */
276 ALLOCATE_PFD(pfd_index);
277 pfd->link = 0;
278 *pfd_index_pointer = pfd_index;
279 goto done;
281 pfd = &pfds[pfd_index];
282 if (pfd->self_pc == self_pc) {
283 /* only / most recent function called by this caller, usual case */
284 /* increment count, start timing and exit */
285 goto found;
287 /* collision, bad for performance, look down the list of functions called by
288 * colliding PCs */
289 for (; /* goto done */; ) {
290 pfd_index = pfd->link;
291 if (pfd_index == 0) {
292 /* no more previously called functions, allocate a new one */
293 ALLOCATE_PFD(pfd_index);
294 /* this function becomes the new head, link to the old head */
295 pfd->link = *pfd_index_pointer;
296 /* and set the index to point to this function */
297 *pfd_index_pointer = pfd_index;
298 /* start timing and exit */
299 goto done;
301 /* move along the chain */
302 prev_pfd = pfd;
303 pfd = &pfds[pfd_index];
304 if (pfd->self_pc == self_pc) {
305 /* found ourself */
306 /* Remove me from my old spot */
307 prev_pfd->link = pfd->link;
308 /* Link to the old head */
309 pfd->link = *pfd_index_pointer;
310 /* Make me head */
311 *pfd_index_pointer = pfd_index;
312 /* increment count, start timing and exit */
313 goto found;
317 /* We've found a pfd, increment it */
318 found:
319 ADDQI_L(pfd->count,1);
320 /* We've (found or created) and updated our pfd, save it and start timing */
321 done:
323 register struct pfd_struct *my_last_pfd = last_pfd;
324 if (pfd != my_last_pfd) {
325 /* If we are not recursing */
326 pfd->caller = my_last_pfd;
327 last_pfd = pfd;
328 } else {
329 ADDQI_L(recursion_level,1);
332 /* Start timing this function */
333 profiling = PROF_ON;
334 return; /* normal return restores saved registers */
336 overflow:
337 /* this is the same as 'profiling = PROF_ERROR' */
338 profiling = PROF_ERROR;
339 return;