summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Low <lostlogic@rockbox.org>2006-01-18 20:54:13 +0000
committerBrandon Low <lostlogic@rockbox.org>2006-01-18 20:54:13 +0000
commit05dccc355144dc717b3cb9ef0074a9ab38a520f4 (patch)
tree0a36425cf1321817480a82ed05564a2790e2fca9
parent1060e447f83128a78dfaa8d59ba0baa642d15a4d (diff)
downloadrockbox-05dccc355144dc717b3cb9ef0074a9ab38a520f4.tar.gz
rockbox-05dccc355144dc717b3cb9ef0074a9ab38a520f4.zip
Profiling support, tools and documentation.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8375 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/codecs.c6
-rw-r--r--apps/codecs.h11
-rw-r--r--apps/codecs/Tremor/Makefile2
-rw-r--r--apps/codecs/dumb/Makefile2
-rw-r--r--apps/codecs/lib/codeclib.c11
-rw-r--r--apps/codecs/lib/codeclib.h6
-rw-r--r--apps/codecs/libFLAC/Makefile2
-rw-r--r--apps/codecs/liba52/Makefile2
-rw-r--r--apps/codecs/libffmpegFLAC/Makefile2
-rw-r--r--apps/codecs/libm4a/Makefile2
-rw-r--r--apps/codecs/libmad/Makefile2
-rw-r--r--apps/codecs/libmusepack/Makefile2
-rw-r--r--apps/codecs/libwavpack/Makefile2
-rw-r--r--apps/plugin.c6
-rw-r--r--apps/plugin.h11
-rw-r--r--apps/plugins/lib/SOURCES3
-rw-r--r--apps/plugins/lib/profile_plugin.c38
-rw-r--r--apps/plugins/lib/profile_plugin.h35
-rw-r--r--apps/plugins/wav2wv.c12
-rw-r--r--docs/FILES1
-rw-r--r--docs/LICENSES36
-rw-r--r--docs/TECH26
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/export/config.h8
-rw-r--r--firmware/export/profile.h80
-rw-r--r--firmware/export/thread.h3
-rw-r--r--firmware/profile.c303
-rw-r--r--firmware/thread.c13
-rw-r--r--tools/FILES1
-rwxr-xr-xtools/buildzip.pl1
-rwxr-xr-xtools/configure38
-rwxr-xr-xtools/profile_reader/profile_comparator.pl104
-rwxr-xr-xtools/profile_reader/profile_reader.pl236
33 files changed, 992 insertions, 18 deletions
diff --git a/apps/codecs.c b/apps/codecs.c
index d8ad7146df..88b2ea4e07 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -218,6 +218,12 @@ struct codec_api ci = {
218 218
219 /* new stuff at the end, sort into place next time 219 /* new stuff at the end, sort into place next time
220 the API gets incompatible */ 220 the API gets incompatible */
221#ifdef RB_PROFILE
222 profile_thread,
223 profstop,
224 profile_func_enter,
225 profile_func_exit,
226#endif
221 227
222}; 228};
223 229
diff --git a/apps/codecs.h b/apps/codecs.h
index 320431f3bf..70799f790e 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -43,6 +43,9 @@
43#include "mpeg.h" 43#include "mpeg.h"
44#include "audio.h" 44#include "audio.h"
45#include "mp3_playback.h" 45#include "mp3_playback.h"
46#ifdef RB_PROFILE
47#include "profile.h"
48#endif
46#if (CONFIG_CODEC == SWCODEC) 49#if (CONFIG_CODEC == SWCODEC)
47#include "dsp.h" 50#include "dsp.h"
48#include "pcm_playback.h" 51#include "pcm_playback.h"
@@ -83,7 +86,7 @@
83#define CODEC_MAGIC 0x52434F44 /* RCOD */ 86#define CODEC_MAGIC 0x52434F44 /* RCOD */
84 87
85/* increase this every time the api struct changes */ 88/* increase this every time the api struct changes */
86#define CODEC_API_VERSION 1 89#define CODEC_API_VERSION 2
87 90
88/* update this to latest version if a change to the api struct breaks 91/* update this to latest version if a change to the api struct breaks
89 backwards compatibility (and please take the opportunity to sort in any 92 backwards compatibility (and please take the opportunity to sort in any
@@ -289,6 +292,12 @@ struct codec_api {
289 292
290 /* new stuff at the end, sort into place next time 293 /* new stuff at the end, sort into place next time
291 the API gets incompatible */ 294 the API gets incompatible */
295#ifdef RB_PROFILE
296 void (*profile_thread)(void);
297 void (*profstop)(void);
298 void (*profile_func_enter)(void *this_fn, void *call_site);
299 void (*profile_func_exit)(void *this_fn, void *call_site);
300#endif
292 301
293}; 302};
294 303
diff --git a/apps/codecs/Tremor/Makefile b/apps/codecs/Tremor/Makefile
index 7fd5de852b..cec9797f63 100644
--- a/apps/codecs/Tremor/Makefile
+++ b/apps/codecs/Tremor/Makefile
@@ -16,7 +16,7 @@ endif
16 16
17TREMOROPTS = -O2 17TREMOROPTS = -O2
18CFLAGS = $(GCCOPTS) $(TREMOROPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 18CFLAGS = $(GCCOPTS) $(TREMOROPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
19 -DMEM=${MEMORYSIZE} 19 -DMEM=${MEMORYSIZE} ${PROFILE_OPTS}
20 20
21# This sets up 'SRC' based on the files mentioned in SOURCES 21# This sets up 'SRC' based on the files mentioned in SOURCES
22include $(TOOLSDIR)/makesrc.inc 22include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/codecs/dumb/Makefile b/apps/codecs/dumb/Makefile
index fa647f3924..d2a045285e 100644
--- a/apps/codecs/dumb/Makefile
+++ b/apps/codecs/dumb/Makefile
@@ -178,7 +178,7 @@ WFLAGS_ALLEGRO := -Wno-missing-declarations
178OFLAGS := -O2 -ffast-math -fomit-frame-pointer 178OFLAGS := -O2 -ffast-math -fomit-frame-pointer
179DBGFLAGS := -DDEBUGMODE=1 -g3 179DBGFLAGS := -DDEBUGMODE=1 -g3
180 180
181CFLAGS_RELEASE := -Iinclude $(WFLAGS) $(OFLAGS) 181CFLAGS_RELEASE := -Iinclude $(WFLAGS) $(OFLAGS) $(PROFILE_OPTS)
182CFLAGS_DEBUG := -Iinclude $(WFLAGS) $(DBGFLAGS) 182CFLAGS_DEBUG := -Iinclude $(WFLAGS) $(DBGFLAGS)
183 183
184LDFLAGS := -s 184LDFLAGS := -s
diff --git a/apps/codecs/lib/codeclib.c b/apps/codecs/lib/codeclib.c
index cad8f53deb..1f070e8eac 100644
--- a/apps/codecs/lib/codeclib.c
+++ b/apps/codecs/lib/codeclib.c
@@ -149,3 +149,14 @@ void qsort(void *base, size_t nmemb, size_t size,
149{ 149{
150 local_rb->qsort(base,nmemb,size,compar); 150 local_rb->qsort(base,nmemb,size,compar);
151} 151}
152
153#ifdef RB_PROFILE
154void __cyg_profile_func_enter(void *this_fn, void *call_site) {
155 (void)call_site;
156 local_rb->profile_func_enter(this_fn, __builtin_return_address(1));
157}
158
159void __cyg_profile_func_exit(void *this_fn, void *call_site) {
160 local_rb->profile_func_exit(this_fn,call_site);
161}
162#endif
diff --git a/apps/codecs/lib/codeclib.h b/apps/codecs/lib/codeclib.h
index e112112756..c2e7869aa4 100644
--- a/apps/codecs/lib/codeclib.h
+++ b/apps/codecs/lib/codeclib.h
@@ -58,3 +58,9 @@ void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, con
58int codec_init(struct codec_api* rb); 58int codec_init(struct codec_api* rb);
59void codec_set_replaygain(struct mp3entry* id3); 59void codec_set_replaygain(struct mp3entry* id3);
60 60
61#ifdef RB_PROFILE
62void __cyg_profile_func_enter(void *this_fn, void *call_site)
63 NO_PROF_ATTR ICODE_ATTR;
64void __cyg_profile_func_exit(void *this_fn, void *call_site)
65 NO_PROF_ATTR ICODE_ATTR;
66#endif
diff --git a/apps/codecs/libFLAC/Makefile b/apps/codecs/libFLAC/Makefile
index 4008479649..d1e78bd229 100644
--- a/apps/codecs/libFLAC/Makefile
+++ b/apps/codecs/libFLAC/Makefile
@@ -27,7 +27,7 @@ ifeq ($(NEWGCC), 1)
27 FLACOPTS += --param large-function-insns=10000 27 FLACOPTS += --param large-function-insns=10000
28endif 28endif
29 29
30CFLAGS = $(GCCOPTS) $(FLACOPTS)\ 30CFLAGS = $(GCCOPTS) $(PROFILE_OPTS) $(FLACOPTS)\
31$(INCLUDES) $(TARGET) $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} 31$(INCLUDES) $(TARGET) $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
32 32
33# This sets up 'SRC' based on the files mentioned in SOURCES 33# This sets up 'SRC' based on the files mentioned in SOURCES
diff --git a/apps/codecs/liba52/Makefile b/apps/codecs/liba52/Makefile
index 8e0501798d..ea3c01c931 100644
--- a/apps/codecs/liba52/Makefile
+++ b/apps/codecs/liba52/Makefile
@@ -16,7 +16,7 @@ endif
16 16
17A52OPTS = -O2 17A52OPTS = -O2
18CFLAGS = $(GCCOPTS) $(A52OPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 18CFLAGS = $(GCCOPTS) $(A52OPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
19 -DMEM=${MEMORYSIZE} 19 -DMEM=${MEMORYSIZE} $(PROFILE_OPTS)
20 20
21# This sets up 'SRC' based on the files mentioned in SOURCES 21# This sets up 'SRC' based on the files mentioned in SOURCES
22include $(TOOLSDIR)/makesrc.inc 22include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/codecs/libffmpegFLAC/Makefile b/apps/codecs/libffmpegFLAC/Makefile
index 60da5ae7ff..7d9030e648 100644
--- a/apps/codecs/libffmpegFLAC/Makefile
+++ b/apps/codecs/libffmpegFLAC/Makefile
@@ -16,7 +16,7 @@ endif
16 16
17FLACOPTS = -O2 17FLACOPTS = -O2
18CFLAGS = $(GCCOPTS) $(FLACOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 18CFLAGS = $(GCCOPTS) $(FLACOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
19 -DMEM=${MEMORYSIZE} 19 -DMEM=${MEMORYSIZE} $(PROFILE_OPTS)
20 20
21# This sets up 'SRC' based on the files mentioned in SOURCES 21# This sets up 'SRC' based on the files mentioned in SOURCES
22include $(TOOLSDIR)/makesrc.inc 22include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/codecs/libm4a/Makefile b/apps/codecs/libm4a/Makefile
index 7f870c9407..fcbc10045e 100644
--- a/apps/codecs/libm4a/Makefile
+++ b/apps/codecs/libm4a/Makefile
@@ -16,7 +16,7 @@ endif
16 16
17M4AOPTS = -O3 17M4AOPTS = -O3
18CFLAGS = $(GCCOPTS) $(M4AOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 18CFLAGS = $(GCCOPTS) $(M4AOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
19 -DMEM=${MEMORYSIZE} 19 -DMEM=${MEMORYSIZE} $(PROFILE_OPTS)
20 20
21# This sets up 'SRC' based on the files mentioned in SOURCES 21# This sets up 'SRC' based on the files mentioned in SOURCES
22include $(TOOLSDIR)/makesrc.inc 22include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/codecs/libmad/Makefile b/apps/codecs/libmad/Makefile
index e2f2643b27..5eaf9f1517 100644
--- a/apps/codecs/libmad/Makefile
+++ b/apps/codecs/libmad/Makefile
@@ -17,7 +17,7 @@ endif
17# NOTE: FPM_ define has been moved to global.h 17# NOTE: FPM_ define has been moved to global.h
18MADOPTS = -DNDEBUG -O2 18MADOPTS = -DNDEBUG -O2
19CFLAGS = $(GCCOPTS) $(MADOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 19CFLAGS = $(GCCOPTS) $(MADOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
20 -DMEM=${MEMORYSIZE} 20 -DMEM=${MEMORYSIZE} $(PROFILE_OPTS)
21 21
22# This sets up 'SRC' based on the files mentioned in SOURCES 22# This sets up 'SRC' based on the files mentioned in SOURCES
23include $(TOOLSDIR)/makesrc.inc 23include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/codecs/libmusepack/Makefile b/apps/codecs/libmusepack/Makefile
index 8e65915feb..6bfa2f9462 100644
--- a/apps/codecs/libmusepack/Makefile
+++ b/apps/codecs/libmusepack/Makefile
@@ -16,7 +16,7 @@ endif
16 16
17MUSEPACKOPTS = -O2 17MUSEPACKOPTS = -O2
18CFLAGS = $(GCCOPTS) $(MUSEPACKOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 18CFLAGS = $(GCCOPTS) $(MUSEPACKOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
19 -DMEM=${MEMORYSIZE} 19 -DMEM=${MEMORYSIZE} $(PROFILE_OPTS)
20 20
21# This sets up 'SRC' based on the files mentioned in SOURCES 21# This sets up 'SRC' based on the files mentioned in SOURCES
22include $(TOOLSDIR)/makesrc.inc 22include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/codecs/libwavpack/Makefile b/apps/codecs/libwavpack/Makefile
index 39478799bf..eba67fa7fc 100644
--- a/apps/codecs/libwavpack/Makefile
+++ b/apps/codecs/libwavpack/Makefile
@@ -16,7 +16,7 @@ endif
16 16
17WAVPACKOPTS = -O2 17WAVPACKOPTS = -O2
18CFLAGS = $(GCCOPTS) $(WAVPACKOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ 18CFLAGS = $(GCCOPTS) $(WAVPACKOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \
19 -DMEM=${MEMORYSIZE} 19 -DMEM=${MEMORYSIZE} $(PROFILE_OPTS)
20 20
21# This sets up 'SRC' based on the files mentioned in SOURCES 21# This sets up 'SRC' based on the files mentioned in SOURCES
22include $(TOOLSDIR)/makesrc.inc 22include $(TOOLSDIR)/makesrc.inc
diff --git a/apps/plugin.c b/apps/plugin.c
index 44eb0dc04c..b907604528 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -363,6 +363,12 @@ static const struct plugin_api rockbox_api = {
363 363
364 /* new stuff at the end, sort into place next time 364 /* new stuff at the end, sort into place next time
365 the API gets incompatible */ 365 the API gets incompatible */
366#ifdef RB_PROFILE
367 profile_thread,
368 profstop,
369 profile_func_enter,
370 profile_func_exit,
371#endif
366 372
367}; 373};
368 374
diff --git a/apps/plugin.h b/apps/plugin.h
index 69a2a79ba8..157831bf19 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -44,6 +44,9 @@
44#include "mpeg.h" 44#include "mpeg.h"
45#include "audio.h" 45#include "audio.h"
46#include "mp3_playback.h" 46#include "mp3_playback.h"
47#ifdef RB_PROFILE
48#include "profile.h"
49#endif
47#include "misc.h" 50#include "misc.h"
48#if (HWCODEC == SWCODEC) 51#if (HWCODEC == SWCODEC)
49#include "pcm_playback.h" 52#include "pcm_playback.h"
@@ -93,7 +96,7 @@
93#define PLUGIN_MAGIC 0x526F634B /* RocK */ 96#define PLUGIN_MAGIC 0x526F634B /* RocK */
94 97
95/* increase this every time the api struct changes */ 98/* increase this every time the api struct changes */
96#define PLUGIN_API_VERSION 1 99#define PLUGIN_API_VERSION 2
97 100
98/* update this to latest version if a change to the api struct breaks 101/* update this to latest version if a change to the api struct breaks
99 backwards compatibility (and please take the opportunity to sort in any 102 backwards compatibility (and please take the opportunity to sort in any
@@ -424,6 +427,12 @@ struct plugin_api {
424 427
425 /* new stuff at the end, sort into place next time 428 /* new stuff at the end, sort into place next time
426 the API gets incompatible */ 429 the API gets incompatible */
430#ifdef RB_PROFILE
431 void (*profile_thread)(void);
432 void (*profstop)(void);
433 void (*profile_func_enter)(void *this_fn, void *call_site);
434 void (*profile_func_exit)(void *this_fn, void *call_site);
435#endif
427 436
428}; 437};
429 438
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index 4d4247e3bb..0f6e13ebb4 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -12,3 +12,6 @@ xlcd.c
12#ifdef HAVE_LCD_CHARCELLS 12#ifdef HAVE_LCD_CHARCELLS
13playergfx.c 13playergfx.c
14#endif 14#endif
15#ifdef RB_PROFILE
16profile_plugin.c
17#endif
diff --git a/apps/plugins/lib/profile_plugin.c b/apps/plugins/lib/profile_plugin.c
new file mode 100644
index 0000000000..3318476a89
--- /dev/null
+++ b/apps/plugins/lib/profile_plugin.c
@@ -0,0 +1,38 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Passthrough routines for plugin profiling
11*
12* Copyright (C) 2006 Brandon Low
13*
14* All files in this archive are subject to the GNU General Public License.
15* See the file COPYING in the source tree root for full license agreement.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#include "plugin.h"
23
24static struct plugin_api *local_rb = NULL; /* global api struct pointer */
25
26void profile_init(struct plugin_api* pa)
27{
28 local_rb = pa;
29}
30
31void __cyg_profile_func_enter(void *this_fn, void *call_site) {
32 (void)call_site;
33 local_rb->profile_func_enter(this_fn, __builtin_return_address(1));
34}
35
36void __cyg_profile_func_exit(void *this_fn, void *call_site) {
37 local_rb->profile_func_exit(this_fn,call_site);
38}
diff --git a/apps/plugins/lib/profile_plugin.h b/apps/plugins/lib/profile_plugin.h
new file mode 100644
index 0000000000..71cff37033
--- /dev/null
+++ b/apps/plugins/lib/profile_plugin.h
@@ -0,0 +1,35 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Passthrough routines for plugin profiling.
11*
12* Copyright (C) 2005 Brandon Low
13*
14* All files in this archive are subject to the GNU General Public License.
15* See the file COPYING in the source tree root for full license agreement.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#ifndef __PROFILE_PLUGIN_H__
23#define __PROFILE_PLUGIN_H__
24
25#include "plugin.h"
26
27void profile_init(struct plugin_api* pa);
28
29void __cyg_profile_func_enter(void *this_fn, void *call_site)
30 NO_PROF_ATTR ICODE_ATTR;
31void __cyg_profile_func_exit(void *this_fn, void *call_site)
32 NO_PROF_ATTR ICODE_ATTR;
33
34#endif /* __PROFILE_PLUGIN_H__ */
35
diff --git a/apps/plugins/wav2wv.c b/apps/plugins/wav2wv.c
index 20a458960f..812ed176c4 100644
--- a/apps/plugins/wav2wv.c
+++ b/apps/plugins/wav2wv.c
@@ -17,6 +17,9 @@
17 * 17 *
18 ****************************************************************************/ 18 ****************************************************************************/
19#include "plugin.h" 19#include "plugin.h"
20#ifdef RB_PROFILE
21#include "lib/profile_plugin.h"
22#endif
20 23
21#include <codecs/libwavpack/wavpack.h> 24#include <codecs/libwavpack/wavpack.h>
22 25
@@ -289,6 +292,15 @@ static int wav2wv (char *filename)
289 292
290enum plugin_status plugin_start(struct plugin_api* api, void *parameter) 293enum plugin_status plugin_start(struct plugin_api* api, void *parameter)
291{ 294{
295#ifdef RB_PROFILE
296 /* This doesn't start profiling or anything, it just gives the
297 * profiling functions that are compiled in someplace to call,
298 * this is needed here to let this compile with profiling support
299 * since it calls code from a codec that is compiled with profiling
300 * support */
301 profile_init(api);
302#endif
303
292 rb = api; 304 rb = api;
293 305
294 if (!parameter) 306 if (!parameter)
diff --git a/docs/FILES b/docs/FILES
index f1054eadd9..21f2faf0a8 100644
--- a/docs/FILES
+++ b/docs/FILES
@@ -8,6 +8,7 @@ CUSTOM_WPS_FORMAT
8FAQ 8FAQ
9FILES 9FILES
10HISTORY 10HISTORY
11LICENSES
11NODO 12NODO
12README 13README
13TECH 14TECH
diff --git a/docs/LICENSES b/docs/LICENSES
new file mode 100644
index 0000000000..652a276eed
--- /dev/null
+++ b/docs/LICENSES
@@ -0,0 +1,36 @@
1This file contains license for software imported into Rockbox but governed by
2a previous license. Each section begins by identifying the code in Rockbox
3and the source from of those code, and is followed by the license text.
4
5*************************************************************************
6In: profile.c, profile_func_enter
7From: gcc - gmon.c, mcount
8*************************************************************************
9Copyright (c) 1991, 1998 The Regents of the University of California.
10All rights reserved.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions
14are met:
151. Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
172. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
203. [rescinded 22 July 1999]
214. Neither the name of the University nor the names of its contributors
22 may be used to endorse or promote products derived from this software
23 without specific prior written permission.
24
25THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35SUCH DAMAGE.
36@(#)gmon.c 5.3 (Berkeley) 5/22/91
diff --git a/docs/TECH b/docs/TECH
index 4532af3c42..b22a8c4c97 100644
--- a/docs/TECH
+++ b/docs/TECH
@@ -172,3 +172,29 @@ Charging
172 controlled charging that Rockbox can't affect.) 172 controlled charging that Rockbox can't affect.)
173 173
174 ... 174 ...
175
176Profiling
177
178 Rockbox contains a profiling system which can be used to monitor call count
179 and time in function for a specific set of functions on a single thread.
180
181 To use this functionality:
182 1) Configure a developer build with profiling support.
183 2) Make sure that the functions of interest will be compiled with the
184 PROFILE_OPTS added to their CFLAGS
185 3) On the same thread as these functions will be run, surround the relevent
186 running time with calls to profile_thread and profstop. (For codecs,
187 this can be done in the codec.c file for example)
188 4) Compile and run the code on the target, after the section to be profiled
189 exits (when profstop is called) a profile.out file will be written to
190 the player's root.
191 5) Use the tools/profile_reader/profile_reader.pl script to convert the
192 profile.out into a human readable format. This script requires the
193 relevent map files and object (or library) files created in the build.
194 (ex: ./profile_reader.pl profile.out vorbis.map libTremor.a 0)
195
196 There is also a profile_comparator.pl script which can compare two profile
197 runs as output by the above script to show percent change from optimization
198
199 profile_reader.pl requires a recent binutils that can automatically handle
200 target object files, or objdump in path to be the target-objdump.
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 4fe5fc91f5..3a5f551585 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -123,6 +123,9 @@ font.c
123#endif 123#endif
124id3.c 124id3.c
125#ifndef SIMULATOR 125#ifndef SIMULATOR
126#ifdef RB_PROFILE
127profile.c
128#endif /* RB_PROFILE */
126hwcompat.c 129hwcompat.c
127kernel.c 130kernel.c
128rolo.c 131rolo.c
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 5e3bbeb645..8df596665f 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -174,6 +174,14 @@
174#define CODEC_SIZE 0 174#define CODEC_SIZE 0
175#endif 175#endif
176 176
177/* This attribute can be used to ensure that certain symbols are never profiled
178 * which can be important as profiling a function de-inlines it */
179#ifdef RB_PROFILE
180#define NO_PROF_ATTR __attribute__ ((no_instrument_function))
181#else
182#define NO_PROF_ATTR
183#endif
184
177/* IRAM usage */ 185/* IRAM usage */
178#if !defined(SIMULATOR) && /* Not for simulators */ \ 186#if !defined(SIMULATOR) && /* Not for simulators */ \
179 (((CONFIG_CPU == SH7034) && !defined(PLUGIN)) || /* SH1 archos: core only */ \ 187 (((CONFIG_CPU == SH7034) && !defined(PLUGIN)) || /* SH1 archos: core only */ \
diff --git a/firmware/export/profile.h b/firmware/export/profile.h
new file mode 100644
index 0000000000..cb751328ae
--- /dev/null
+++ b/firmware/export/profile.h
@@ -0,0 +1,80 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Profiling routines counts ticks and calls to each profiled function.
11 *
12 * Copyright (C) 2005 by Brandon Low
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 ****************************************************************************/
20
21#ifndef _SYS_PROFILE_H
22#define _SYS_PROFILE_H 1
23
24#include <sys/types.h>
25
26/* PFD is Profiled Function Data */
27
28/* Indices are shorts which means that we use 4k of RAM */
29#define INDEX_BITS 11 /* What is a reasonable size for this? */
30#define INDEX_SIZE 2048 /* 2 ^ INDEX_BITS */
31#define INDEX_MASK 0x7FF /* lower INDEX_BITS 1 */
32
33/*
34 * In the current setup (pfd has 4 longs and 2 shorts) this uses 20k of RAM
35 * for profiling, and allows for profiling sections of code with up-to
36 * 1024 function caller->callee pairs
37 */
38#define NUMPFDS 1024
39
40struct pfd_struct {
41 void *self_pc;
42 unsigned long count;
43 unsigned long time;
44 unsigned short link;
45 struct pfd_struct *caller;
46};
47
48/* Possible states of profiling */
49#define PROF_ON 0x00
50#define PROF_BUSY 0x01
51#define PROF_ERROR 0x02
52#define PROF_OFF 0x03
53/* Masks for thread switches */
54#define PROF_OFF_THREAD 0x10
55#define PROF_ON_THREAD 0x0F
56
57extern int current_thread;
58
59/* Initialize and start profiling */
60void profstart(int current_thread)
61 NO_PROF_ATTR;
62
63/* Clean up and write profile data */
64void profstop (void)
65 NO_PROF_ATTR;
66
67/* Called every time a thread stops, we check if it's our thread and store
68 * temporary timing data if it is */
69void profile_thread_stopped(int current_thread)
70 NO_PROF_ATTR;
71/* Called when a thread starts, we check if it's our thread and resume timing */
72void profile_thread_started(int current_thread)
73 NO_PROF_ATTR;
74
75void profile_func_exit(void *this_fn, void *call_site)
76 NO_PROF_ATTR ICODE_ATTR;
77void profile_func_enter(void *this_fn, void *call_site)
78 NO_PROF_ATTR ICODE_ATTR;
79
80#endif /*_SYS_PROFILE_H*/
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index fe1612d2f4..da61d1a632 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -37,5 +37,8 @@ void sleep_thread(void);
37void wake_up_thread(void); 37void wake_up_thread(void);
38void init_threads(void); 38void init_threads(void);
39int thread_stack_usage(int threadnum); 39int thread_stack_usage(int threadnum);
40#ifdef RB_PROFILE
41void profile_thread(void);
42#endif
40 43
41#endif 44#endif
diff --git a/firmware/profile.c b/firmware/profile.c
new file mode 100644
index 0000000000..8ad46515f8
--- /dev/null
+++ b/firmware/profile.c
@@ -0,0 +1,303 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Profiling routines counts ticks and calls to each profiled function.
11 *
12 * Copyright (C) 2005 by Brandon Low
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************
21 *
22 * profile_func_enter() based on mcount found in gmon.c:
23 *
24 ***************************************************************************
25 * Copyright (c) 1991, 1998 The Regents of the University of California.
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. [rescinded 22 July 1999]
37 * 4. Neither the name of the University nor the names of its contributors
38 * may be used to endorse or promote products derived from this software
39 * without specific prior written permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 * @(#)gmon.c 5.3 (Berkeley) 5/22/91
53 */
54
55#include <file.h>
56#include <sprintf.h>
57#include <system.h>
58#include <string.h>
59#include <timer.h>
60#include "profile.h"
61
62static unsigned short profiling = PROF_OFF;
63static size_t recursion_level;
64static unsigned short indices[INDEX_SIZE];
65static struct pfd_struct pfds[NUMPFDS];
66/* This holds a pointer to the last pfd effected for time tracking */
67static struct pfd_struct *last_pfd;
68/* These are used to track the time when we've lost the CPU so it doesn't count
69 * against any of the profiled functions */
70static int profiling_thread = -1;
71
72/* internal function prototypes */
73static void profile_timer_tick(void);
74static void profile_timer_unregister(void);
75
76static void write_function_recursive(int fd, struct pfd_struct *pfd, int depth);
77
78/* Be careful to use the right one for the size of your variable */
79#define ADDQI_L(_var,_value) \
80 asm ("addq.l %[value],%[var];" \
81 : [var] "+g" (_var) \
82 : [value] "I" (_value) )
83
84void profile_thread_stopped(int current_thread) {
85 if (current_thread == profiling_thread) {
86 /* If profiling is busy or idle */
87 if (profiling < PROF_ERROR) {
88 /* Unregister the timer so that other threads aren't interrupted */
89 timer_unregister();
90 }
91 /* Make sure we don't waste time profiling when we're running the
92 * wrong thread */
93 profiling |= PROF_OFF_THREAD;
94 }
95}
96
97void profile_thread_started(int current_thread) {
98 if (current_thread == profiling_thread) {
99 /* Now we are allowed to profile again */
100 profiling &= PROF_ON_THREAD;
101 /* if profiling was busy or idle */
102 if (profiling < PROF_ERROR) {
103 /* After we de-mask, if profiling is active, reactivate the timer */
104 timer_register(0, profile_timer_unregister,
105 CPU_FREQ/10000, 0, profile_timer_tick);
106 }
107 }
108}
109
110static void profile_timer_tick(void) {
111 if (!profiling) {
112 register struct pfd_struct *my_last_pfd = last_pfd;
113 if (my_last_pfd) {
114 ADDQI_L(my_last_pfd->time,1);
115 }
116 }
117}
118
119static void profile_timer_unregister(void) {
120 profiling = PROF_ERROR;
121 profstop();
122}
123
124/* This function clears the links on top level linkers, and clears the needed
125 * parts of memory in the index array */
126void profstart(int current_thread) {
127 recursion_level = 0;
128 profiling_thread = current_thread;
129 last_pfd = (struct pfd_struct*)0;
130 pfds[0].link = 0;
131 pfds[0].self_pc = 0;
132 memset(&indices,0,INDEX_SIZE * sizeof(unsigned short));
133 timer_register(
134 0, profile_timer_unregister, CPU_FREQ/10000, 0, profile_timer_tick);
135 profiling = PROF_ON;
136}
137
138static void write_function_recursive(int fd, struct pfd_struct *pfd, int depth){
139 unsigned short link = pfd->link;
140 fdprintf(fd,"0x%08lX\t%08ld\t%08ld\t%04d\n", (size_t)pfd->self_pc,
141 pfd->count, pfd->time, depth);
142 if (link > 0 && link < NUMPFDS) {
143 write_function_recursive(fd, &pfds[link], depth++);
144 }
145}
146
147void profstop() {
148 int profiling_exit = profiling;
149 int fd = 0;
150 int i;
151 unsigned short current_index;
152 timer_unregister();
153 profiling = PROF_OFF;
154 fd = open("/profile.out", O_WRONLY|O_CREAT|O_TRUNC);
155 if (profiling_exit == PROF_ERROR) {
156 fdprintf(fd,"Profiling exited with an error.\n");
157 fdprintf(fd,"Overflow or timer stolen most likely.\n");
158 }
159 fdprintf(fd,"PROFILE_THREAD\tPFDS_USED\n");
160 fdprintf(fd,"%08d\t%08d\n", profiling_thread,
161 pfds[0].link);
162 fdprintf(fd,"FUNCTION_PC\tCALL_COUNT\tTICKS\t\tDEPTH\n");
163 for (i = 0; i < INDEX_SIZE; i++) {
164 current_index = indices[i];
165 if (current_index != 0) {
166 write_function_recursive(fd, &pfds[current_index], 0);
167 }
168 }
169 fdprintf(fd,"DEBUG PROFILE DATA FOLLOWS\n");
170 fdprintf(fd,"INDEX\tLOCATION\tSELF_PC\t\tCOUNT\t\tTIME\t\tLINK\tCALLER\n");
171 for (i = 0; i < NUMPFDS; i++) {
172 struct pfd_struct *my_last_pfd = &pfds[i];
173 if (my_last_pfd->self_pc != 0) {
174 fdprintf(fd,
175 "%04d\t0x%08lX\t0x%08lX\t0x%08lX\t0x%08lX\t%04d\t0x%08lX\n",
176 i, (size_t)my_last_pfd, (size_t)my_last_pfd->self_pc,
177 my_last_pfd->count, my_last_pfd->time, my_last_pfd->link,
178 (size_t)my_last_pfd->caller);
179 }
180 }
181 fdprintf(fd,"INDEX_ADDRESS=INDEX\n");
182 for (i=0; i < INDEX_SIZE; i++) {
183 fdprintf(fd,"%08lX=%04d\n",(size_t)&indices[i],indices[i]);
184 }
185 close(fd);
186}
187
188void profile_func_exit(void *self_pc, void *call_site) {
189 (void)call_site;
190 (void)self_pc;
191 /* When we started timing, we set the time to the tick at that time
192 * less the time already used in function */
193 if (profiling) {
194 return;
195 }
196 profiling = PROF_BUSY;
197 {
198 register unsigned short my_recursion_level = recursion_level;
199 if (my_recursion_level) {
200 my_recursion_level--;
201 recursion_level = my_recursion_level;
202 } else {
203 /* This shouldn't be necessary, maybe exit could be called first */
204 register struct pfd_struct *my_last_pfd = last_pfd;
205 if (my_last_pfd) {
206 last_pfd = my_last_pfd->caller;
207 }
208 }
209 }
210 profiling = PROF_ON;
211}
212
213#define ALLOCATE_PFD(temp) \
214 temp = ++pfds[0].link;\
215 if (temp >= NUMPFDS) goto overflow; \
216 pfd = &pfds[temp];\
217 pfd->self_pc = self_pc; pfd->count = 1; pfd->time = 0
218
219void profile_func_enter(void *self_pc, void *from_pc) {
220 struct pfd_struct *pfd;
221 struct pfd_struct *prev_pfd;
222 unsigned short *pfd_index_pointer;
223 unsigned short pfd_index;
224
225 /* check that we are profiling and that we aren't recursively invoked
226 * this is equivalent to 'if (profiling != PROF_ON)' but it's faster */
227 if (profiling) {
228 return;
229 }
230 /* this is equivalent to 'profiling = PROF_BUSY;' but it's faster */
231 profiling = PROF_BUSY;
232 /* A check that the PC is in the code range here wouldn't hurt, but this is
233 * logically guaranteed to be a valid address unless the constants are
234 * breaking the rules. */
235 pfd_index_pointer = &indices[((size_t)from_pc)&INDEX_MASK];
236 pfd_index = *pfd_index_pointer;
237 if (pfd_index == 0) {
238 /* new caller, allocate new storage */
239 ALLOCATE_PFD(pfd_index);
240 pfd->link = 0;
241 *pfd_index_pointer = pfd_index;
242 goto done;
243 }
244 pfd = &pfds[pfd_index];
245 if (pfd->self_pc == self_pc) {
246 /* only / most recent function called by this caller, usual case */
247 /* increment count, start timing and exit */
248 goto found;
249 }
250 /* collision, bad for performance, look down the list of functions called by
251 * colliding PCs */
252 for (; /* goto done */; ) {
253 pfd_index = pfd->link;
254 if (pfd_index == 0) {
255 /* no more previously called functions, allocate a new one */
256 ALLOCATE_PFD(pfd_index);
257 /* this function becomes the new head, link to the old head */
258 pfd->link = *pfd_index_pointer;
259 /* and set the index to point to this function */
260 *pfd_index_pointer = pfd_index;
261 /* start timing and exit */
262 goto done;
263 }
264 /* move along the chain */
265 prev_pfd = pfd;
266 pfd = &pfds[pfd_index];
267 if (pfd->self_pc == self_pc) {
268 /* found ourself */
269 /* Remove me from my old spot */
270 prev_pfd->link = pfd->link;
271 /* Link to the old head */
272 pfd->link = *pfd_index_pointer;
273 /* Make me head */
274 *pfd_index_pointer = pfd_index;
275 /* increment count, start timing and exit */
276 goto found;
277 }
278
279 }
280/* We've found a pfd, increment it */
281found:
282 ADDQI_L(pfd->count,1);
283/* We've (found or created) and updated our pfd, save it and start timing */
284done:
285 {
286 register struct pfd_struct *my_last_pfd = last_pfd;
287 if (pfd != my_last_pfd) {
288 /* If we are not recursing */
289 pfd->caller = my_last_pfd;
290 last_pfd = pfd;
291 } else {
292 ADDQI_L(recursion_level,1);
293 }
294 }
295 /* Start timing this function */
296 profiling = PROF_ON;
297 return; /* normal return restores saved registers */
298
299overflow:
300 /* this is the same as 'profiling = PROF_ERROR' */
301 profiling = PROF_ERROR;
302 return;
303}
diff --git a/firmware/thread.c b/firmware/thread.c
index 281801418f..d8282111b5 100644
--- a/firmware/thread.c
+++ b/firmware/thread.c
@@ -78,6 +78,13 @@ void switch_thread(void) ICODE_ATTR;
78static inline void store_context(void* addr) __attribute__ ((always_inline)); 78static inline void store_context(void* addr) __attribute__ ((always_inline));
79static inline void load_context(const void* addr) __attribute__ ((always_inline)); 79static inline void load_context(const void* addr) __attribute__ ((always_inline));
80 80
81#ifdef RB_PROFILE
82#include <profile.h>
83void profile_thread(void) {
84 profstart(current_thread);
85}
86#endif
87
81#if defined(CPU_ARM) 88#if defined(CPU_ARM)
82/*--------------------------------------------------------------------------- 89/*---------------------------------------------------------------------------
83 * Store non-volatile context. 90 * Store non-volatile context.
@@ -245,6 +252,9 @@ static inline void load_context(const void* addr)
245 */ 252 */
246void switch_thread(void) 253void switch_thread(void)
247{ 254{
255#ifdef RB_PROFILE
256 profile_thread_stopped(current_thread);
257#endif
248 int current; 258 int current;
249 unsigned int *stackptr; 259 unsigned int *stackptr;
250 260
@@ -284,6 +294,9 @@ void switch_thread(void)
284 294
285 current_thread = current; 295 current_thread = current;
286 load_context(&thread_contexts[current]); 296 load_context(&thread_contexts[current]);
297#ifdef RB_PROFILE
298 profile_thread_started(current_thread);
299#endif
287} 300}
288 301
289void sleep_thread(void) 302void sleep_thread(void)
diff --git a/tools/FILES b/tools/FILES
index 19d3a5576f..f4b1c6dca7 100644
--- a/tools/FILES
+++ b/tools/FILES
@@ -23,3 +23,4 @@ ucl/*.[ch]
23ucl/src/*.[ch] 23ucl/src/*.[ch]
24ucl/src/Makefile 24ucl/src/Makefile
25ucl/include/ucl/*.h 25ucl/include/ucl/*.h
26profile_reader/*.pl
diff --git a/tools/buildzip.pl b/tools/buildzip.pl
index eabf739d0d..934b0035e4 100755
--- a/tools/buildzip.pl
+++ b/tools/buildzip.pl
@@ -199,6 +199,7 @@ sub buildzip {
199 "CUSTOM_CFG_FORMAT", 199 "CUSTOM_CFG_FORMAT",
200 "CUSTOM_WPS_FORMAT", 200 "CUSTOM_WPS_FORMAT",
201 "FAQ", 201 "FAQ",
202 "LICENSES",
202 "NODO", 203 "NODO",
203 "TECH")) { 204 "TECH")) {
204 `cp $ROOT/docs/$_ .rockbox/docs/$_.txt`; 205 `cp $ROOT/docs/$_ .rockbox/docs/$_.txt`;
diff --git a/tools/configure b/tools/configure
index 2cd855e0c5..2bea201324 100755
--- a/tools/configure
+++ b/tools/configure
@@ -240,7 +240,7 @@ whichdevel () {
240 # 240 #
241 echo "" 241 echo ""
242 echo "Enter your developer options (press enter when done)" 242 echo "Enter your developer options (press enter when done)"
243 echo "(D)EBUG, (L)ogf, (S)imulator" 243 echo "(D)EBUG, (L)ogf, (S)imulator, (P)rofiling"
244 cont=1 244 cont=1
245 245
246 while [ $cont = "1" ]; do 246 while [ $cont = "1" ]; do
@@ -249,19 +249,29 @@ whichdevel () {
249 249
250 case $option in 250 case $option in
251 [Dd]) 251 [Dd])
252 echo "define DEBUG" 252 if [ "yes" = "$profile" ]; then
253 debug="-DDEBUG" 253 echo "Debug is incompatible with profiling"
254 GCCOPTS="$GCCOPTS -g -DDEBUG" 254 else
255 echo "define DEBUG"
256 use_debug="yes"
257 fi
255 ;; 258 ;;
256 [Ll]) 259 [Ll])
257 logf="yes"
258 echo "logf() support enabled" 260 echo "logf() support enabled"
259 use_logf="#define ROCKBOX_HAS_LOGF 1" 261 logf="yes"
260 ;; 262 ;;
261 [Ss]) 263 [Ss])
262 echo "Simulator build enabled" 264 echo "Simulator build enabled"
263 simulator="yes" 265 simulator="yes"
264 ;; 266 ;;
267 [Pp])
268 if [ "yes" = "$use_debug" ]; then
269 echo "Profiling is incompatible with debug"
270 else
271 echo "Profiling support is enabled"
272 profile="yes"
273 fi
274 ;;
265 *) 275 *)
266 echo "done" 276 echo "done"
267 cont=0 277 cont=0
@@ -269,11 +279,23 @@ whichdevel () {
269 esac 279 esac
270 done 280 done
271 281
282 if [ "yes" = "$use_debug" ]; then
283 debug="-DDEBUG"
284 GCCOPTS="$GCCOPTS -g -DDEBUG"
285 fi
286 if [ "yes" = "$logf" ]; then
287 use_logf="#define ROCKBOX_HAS_LOGF 1"
288 fi
272 if [ "yes" = "$simulator" ]; then 289 if [ "yes" = "$simulator" ]; then
273 debug="-DDEBUG" 290 debug="-DDEBUG"
274 extradefines="-DSIMULATOR" 291 extradefines="$extradefines -DSIMULATOR"
275 whichsim 292 whichsim
276 fi 293 fi
294 if [ "yes" = "$profile" ]; then
295 extradefines="$extradefines -DRB_PROFILE"
296 PROFILE_OPTS="-finstrument-functions"
297 GCCOPTS="$GCCOPTS $GCCOPTIMIZE"
298 fi
277} 299}
278 300
279whichsim () { 301whichsim () {
@@ -902,6 +924,7 @@ sed > Makefile \
902 -e "s,@FLASHFILE@,${flash},g" \ 924 -e "s,@FLASHFILE@,${flash},g" \
903 -e "s,@PLUGINS@,${plugins},g" \ 925 -e "s,@PLUGINS@,${plugins},g" \
904 -e "s,@CODECS@,${codecs},g" \ 926 -e "s,@CODECS@,${codecs},g" \
927 -e "s,@PROFILE_OPTS@,${PROFILE_OPTS},g" \
905 -e "s,@GCCOPTS@,${GCCOPTS},g" \ 928 -e "s,@GCCOPTS@,${GCCOPTS},g" \
906 -e "s!@LDOPTS@!${LDOPTS}!g" \ 929 -e "s!@LDOPTS@!${LDOPTS}!g" \
907 -e "s,@LOADADDRESS@,${loadaddress},g" \ 930 -e "s,@LOADADDRESS@,${loadaddress},g" \
@@ -952,6 +975,7 @@ export WINDRES=@WINDRES@
952export DLLTOOL=@DLLTOOL@ 975export DLLTOOL=@DLLTOOL@
953export DLLWRAP=@DLLWRAP@ 976export DLLWRAP=@DLLWRAP@
954export RANLIB=@RANLIB@ 977export RANLIB=@RANLIB@
978export PROFILE_OPTS=@PROFILE_OPTS@
955export GCCOPTS=@GCCOPTS@ 979export GCCOPTS=@GCCOPTS@
956export LOADADDRESS=@LOADADDRESS@ 980export LOADADDRESS=@LOADADDRESS@
957export SIMVER=@SIMVER@ 981export SIMVER=@SIMVER@
diff --git a/tools/profile_reader/profile_comparator.pl b/tools/profile_reader/profile_comparator.pl
new file mode 100755
index 0000000000..da5e3004c9
--- /dev/null
+++ b/tools/profile_reader/profile_comparator.pl
@@ -0,0 +1,104 @@
1#!/usr/bin/perl
2sub error {
3 print("Error: @_\n");
4 exit(1);
5}
6sub usage {
7 if (@_) {
8 print STDERR ("Error: @_\n");
9 }
10 print STDERR ("USAGE:\n");
11 print STDERR ("$0 file1 file2 [showcalldiff]\n");
12 print STDERR
13 ("\tfile[12] output from profile_reader.pl to compare\n");
14 print STDERR
15 ("\tshowcalldiff show the percent change in calls instead of ticks\n");
16 exit(1);
17}
18if ($ARGV[0] =~ m/-(h|help|-help)/) {
19 usage();
20}
21if (@ARGV < 2) {
22 usage("Requires at least 2 arguments");
23}
24open(FILE1,shift) || error("Couldn't open file1");
25my @file1 = <FILE1>;
26close(FILE1);
27open(FILE2,shift) || error("Couldn't open file2");
28my @file2 = <FILE2>;
29close(FILE2);
30my $showcalldiff = shift;
31my %calls1;
32my %calls2;
33my @calls = (\%calls1,\%calls2);
34my $start = 0;
35my @files = (\@file1,\@file2);
36my @allcalls = (0,0);
37my @allticks = (0,0);
38for ( $i=0; $i <= $#files; $i++ ) {
39 my $file = $files[$i];
40 foreach $line(@$file) {
41 chomp($line);
42 if ( $line =~ m/By calls/ ) {
43 $start = 1;
44 next;
45 }
46 if ( $line =~ m/By ticks/ ) {
47 $start = 0;
48 last;
49 }
50 if ( $start == 1) {
51 my @line = split(/[[:space:]]+/,$line);
52 $allcalls[$i] += $line[1];
53 $allticks[$i] += $line[3];
54 $calls[$i]{$line[5]} = [($line[1],$line[3])];
55 }
56 }
57}
58printf("File one calls: %08ld, ticks: %08ld\n",$allcalls[0],$allticks[0]);
59printf("File two calls: %08ld, ticks: %08ld\n",$allcalls[1],$allticks[1]);
60printf("Percent change: %+7.2f%%, ticks: %+7.2f%%\n",
61 ($allcalls[1]-$allcalls[0])/$allcalls[0]*100,
62 ($allticks[1]-$allticks[0])/$allticks[0]*100);
63my @allkeys = keys(%calls1);
64push(@allkeys,keys(%calls2));
65my %u = ();
66my @keys = grep {defined} map {
67 if (exists $u{$_}) { undef; } else { $u{$_}=undef;$_; }
68} @allkeys;
69undef %u;
70my %byticks;
71my %bycalls;
72foreach $key(@keys) {
73 my $values1 = $calls1{$key};
74 my $values2 = $calls2{$key};
75 my $calldiff = @$values2[0]-@$values1[0];
76 my $totalcalls = @$values2[0]+@$values1[0];
77 my $tickdiff = @$values2[1]-@$values1[1];
78 my $totalticks = @$values2[1]+@$values1[1];
79 my $pdiff;
80 my $result;
81 if ($showcalldiff) {
82 $pdiff = $calldiff/(@$values1[0]>0?@$values1[0]:1)*100;
83 $result = sprintf("%+7.2f%% Calls: %+09d Symbol: %s$key\n",
84 $pdiff, $calldiff,
85 (exists $calls1{$key} && exists $calls2{$key})?"":"LONE ");
86 } else {
87 $pdiff = $tickdiff/(@$values1[1]>0?@$values1[1]:1)*100;
88 $result = sprintf("%+7.2f%% Ticks: %+09d Symbol: %s$key\n",
89 $pdiff, $tickdiff,
90 (exists $calls1{$key} && exists $calls2{$key})?"":"LONE ");
91 }
92 $bycalls{sprintf("%08X$key",$totalcalls)} = $result;
93 $byticks{sprintf("%08X$key",$totalticks)} = $result;
94}
95my @calls = sort(keys(%bycalls));
96print("By calls\n");
97foreach $call(@calls) {
98 print($bycalls{$call});
99}
100my @ticks = sort(keys(%byticks));
101print("By ticks\n");
102foreach $tick(@ticks) {
103 print($byticks{$tick});
104}
diff --git a/tools/profile_reader/profile_reader.pl b/tools/profile_reader/profile_reader.pl
new file mode 100755
index 0000000000..088ba7186e
--- /dev/null
+++ b/tools/profile_reader/profile_reader.pl
@@ -0,0 +1,236 @@
1#!/usr/bin/perl
2
3sub error {
4 print STDERR ("Error: @_\n");
5 exit(1);
6}
7
8sub warning {
9 print STDERR ("Warning: @_\n");
10}
11
12# string (filename.map)
13# return hash(string:hash(string:number))
14sub read_map {
15 open(MAP_FILE,$_[0]) || error("Couldn't open a map $_[0]");
16 my %retval;
17 while (<MAP_FILE>) {
18 chomp;
19 my @parts = split(/[[:space:]]+/);
20 if (@parts != 5) {
21 next;
22 }
23 if ($parts[1] =~ m/\.(text|data|rodata|bss|icode|idata|irodata|ibss)/) {
24 my $region = $parts[1];
25 my $number = $parts[2];
26 @parts = split(/\//,$parts[4]);
27 @parts = split(/[\(\)]/,$parts[$#parts]);
28 my $library = $retval{$parts[0]};
29 my %library = %$library;
30 my $object = $parts[$#parts];
31 $library{$object . $region} = $number;
32 $retval{$parts[0]} = \%library;
33 }
34 }
35 close(MAP_FILE);
36 return %retval;
37}
38
39# string (filename.[ao]), hash(string:number)
40# return hash(number:string)
41sub read_library {
42 open(OBJECT_FILE,"objdump -t $_[0] |") ||
43 error("Couldn't pipe objdump for $_[0]");
44 my $library = $_[1];
45 my %library = %$library;
46 my %retval;
47 my $object;
48 while (<OBJECT_FILE>) {
49 chomp;
50 my @parts = split(/[[:space:]]+/);
51 if ($parts[0] =~ m/:$/) {
52 $object = $parts[0];
53 $object =~ s/:$//;
54 next;
55 }
56 if (@parts != 6) {
57 next;
58 }
59 if ($parts[0] eq "") {
60 next;
61 }
62 if ($parts[3] eq $parts[5]) {
63 next;
64 }
65 if ($parts[3] =~ m/\.(text|data|rodata|bss|icode|idata|irodata|ibss)/) {
66 my $region = $parts[3];
67 my $symbolOffset = hex("0x" . $parts[0]);
68 my $sectionOffset = hex($library{$object . $region});
69 my $location = $symbolOffset + $sectionOffset;
70 $retval{$location} = $parts[5] . "(" . $object . ")";
71 }
72 }
73 close(OBJECT_FILE);
74 return %retval;
75}
76
77# string (0xFFFFFFFF), hash(number:string)
78# return string
79sub get_name {
80 my $location = hex($_[0]);
81 my $offsets = $_[1];
82 my %offsets = %$offsets;
83 if (exists $offsets{$location}) {
84 return $offsets{$location};
85 } else {
86 my $retval = $_[0];
87 $retval =~ y/[A-Z]/a-z/;
88 warning("No symbol found for $retval");
89 return $retval;
90 }
91}
92
93# string (filename), hash(number:string)
94# return array(array(number,number,string))
95sub create_list {
96 open(PROFILE_FILE,$_[0]) ||
97 error("Could not open profile file: $profile_file");
98 my $offsets = $_[1];
99 my $started = 0;
100 my %pfds;
101# my $totalCalls = 0;
102# my $totalTicks = 0;
103# my $pfds = 0;
104 while (<PROFILE_FILE>) {
105 if ($started == 0) {
106 if (m/^0x/) {
107 $started = 1;
108 } else {
109 next;
110 }
111 }
112 my @parts = split(/[[:space:]]+/);
113 if ($parts[0] =~ m/^0x/) {
114 my $callName = get_name($parts[0],$offsets);
115 my $calls = $parts[1];
116 my $ticks = $parts[2];
117 my @pfd = ($calls,$ticks,$callName);
118 if (exists $pfds{$callName}) {
119 my $old_pfd = $pfds{$callName};
120 $pfd[0]+=@$old_pfd[0];
121 $pfd[1]+=@$old_pfd[1];
122 }
123 $pfds{$callName} = \@pfd;
124# $pfds++;
125# $totalCalls+=$calls;
126# $totalTicks+=$ticks;
127 } else {
128 last;
129 }
130
131 }
132 close(PROFILE_FILE);
133# print("FUNCTIONS\tTOTAL_CALLS\tTOTAL_TICKS\n");
134# printf(" %4d\t %8d\t %8d\n",$pfds,$totalCalls,$totalTicks);
135 return values(%pfds);
136}
137
138# array(array(number,number,string)), number (sort element)
139sub print_sorted {
140 my $pfds = $_[0];
141 my @pfds = @$pfds;
142 my $sort_index = $_[1];
143 my $percent = $_[2];
144 my %elements;
145 my $totalCalls = 0;
146 my $totalTicks = 0;
147 $pfds = 0;
148 foreach $element(@pfds) {
149 $elements{@$element[$sort_index] . @$element[2]} = $element;
150 $pfds++;
151 $totalCalls += @$element[0];
152 $totalTicks += @$element[1];
153 }
154 my @keys = sort(keys(%elements));
155 print("FUNCTIONS\tTOTAL_CALLS\tTOTAL_TICKS\n");
156 printf(" %4d\t %8d\t %8d\n",$pfds,$totalCalls,$totalTicks);
157 foreach $key(@keys) {
158 my $element = $elements{$key};
159 if ($percent) {
160 printf("Calls: %7.2f%% Ticks: %7.2f%% Symbol: %s\n",
161 @$element[0]/$totalCalls*100,
162 @$element[1]/$totalTicks*100,
163 @$element[2]);
164 } else {
165 printf("Calls: %08d Ticks: %08d Symbol: %s\n",
166 @$element);
167 }
168 }
169}
170
171# merges two hashes
172sub merge_hashes {
173 my $hash1 = $_[0];
174 my $hash2 = $_[1];
175 return (%$hash1,%$hash2);
176}
177
178sub usage {
179 if (@_) {
180 print STDERR ("Error: @_\n");
181 }
182 print STDERR ("USAGE:\n");
183 print STDERR ("$0 profile.out map obj[...] [map obj[...]...] sort[...]\n");
184 print STDERR
185 ("\tprofile.out output from the profiler, extension is .out\n");
186 print STDERR
187 ("\tmap map file, extension is .map\n");
188 print STDERR
189 ("\tobj library or object file, extension is .a or .o\n");
190 print STDERR
191 ("\tformat 0-2[_p] 0: by calls, 1: by ticks, 2: by name\n");
192 print STDERR
193 ("\t _p shows percents instead of counts\n");
194 print STDERR ("NOTES:\n");
195 print STDERR
196 ("\tmaps and objects come in sets, one map then many objects\n");
197 exit(1);
198}
199
200
201if ($ARGV[0] =~ m/-(h|help|-help)/) {
202 usage();
203}
204if (@ARGV < 2) {
205 usage("Requires at least 2 arguments");
206}
207if ($ARGV[0] !~ m/\.out$/) {
208 usage("Profile file must end in .out");
209}
210my $i = 1;
211my %symbols;
212{
213 my %map;
214 for (; $i < @ARGV; $i++) {
215 my $file = $ARGV[$i];
216 if ($file =~ m/\.map$/) {
217 %map = read_map($file);
218 } elsif ($file =~ m/\.[ao]$/) {
219 if (!%map) {
220 usage("No map file found before first object file");
221 }
222 my @parts = split(/\//,$file);
223 my %new_symbols = read_library($file,$map{$parts[$#parts]});
224 %symbols = merge_hashes(\%symbols,\%new_symbols);
225 } else {
226 last;
227 }
228 }
229}
230if (!%symbols) {
231 warning("No symbols found");
232}
233my @pfds = create_list($ARGV[0],\%symbols);
234for (; $i < @ARGV; $i++) {
235 print_sorted(\@pfds,split("_",$ARGV[$i]));
236}