diff options
author | Brandon Low <lostlogic@rockbox.org> | 2006-01-18 20:54:13 +0000 |
---|---|---|
committer | Brandon Low <lostlogic@rockbox.org> | 2006-01-18 20:54:13 +0000 |
commit | 05dccc355144dc717b3cb9ef0074a9ab38a520f4 (patch) | |
tree | 0a36425cf1321817480a82ed05564a2790e2fca9 | |
parent | 1060e447f83128a78dfaa8d59ba0baa642d15a4d (diff) | |
download | rockbox-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
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 | ||
17 | TREMOROPTS = -O2 | 17 | TREMOROPTS = -O2 |
18 | CFLAGS = $(GCCOPTS) $(TREMOROPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 18 | CFLAGS = $(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 |
22 | include $(TOOLSDIR)/makesrc.inc | 22 | include $(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 | |||
178 | OFLAGS := -O2 -ffast-math -fomit-frame-pointer | 178 | OFLAGS := -O2 -ffast-math -fomit-frame-pointer |
179 | DBGFLAGS := -DDEBUGMODE=1 -g3 | 179 | DBGFLAGS := -DDEBUGMODE=1 -g3 |
180 | 180 | ||
181 | CFLAGS_RELEASE := -Iinclude $(WFLAGS) $(OFLAGS) | 181 | CFLAGS_RELEASE := -Iinclude $(WFLAGS) $(OFLAGS) $(PROFILE_OPTS) |
182 | CFLAGS_DEBUG := -Iinclude $(WFLAGS) $(DBGFLAGS) | 182 | CFLAGS_DEBUG := -Iinclude $(WFLAGS) $(DBGFLAGS) |
183 | 183 | ||
184 | LDFLAGS := -s | 184 | LDFLAGS := -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 | ||
154 | void __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 | |||
159 | void __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 | |||
58 | int codec_init(struct codec_api* rb); | 58 | int codec_init(struct codec_api* rb); |
59 | void codec_set_replaygain(struct mp3entry* id3); | 59 | void codec_set_replaygain(struct mp3entry* id3); |
60 | 60 | ||
61 | #ifdef RB_PROFILE | ||
62 | void __cyg_profile_func_enter(void *this_fn, void *call_site) | ||
63 | NO_PROF_ATTR ICODE_ATTR; | ||
64 | void __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 |
28 | endif | 28 | endif |
29 | 29 | ||
30 | CFLAGS = $(GCCOPTS) $(FLACOPTS)\ | 30 | CFLAGS = $(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 | ||
17 | A52OPTS = -O2 | 17 | A52OPTS = -O2 |
18 | CFLAGS = $(GCCOPTS) $(A52OPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 18 | CFLAGS = $(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 |
22 | include $(TOOLSDIR)/makesrc.inc | 22 | include $(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 | ||
17 | FLACOPTS = -O2 | 17 | FLACOPTS = -O2 |
18 | CFLAGS = $(GCCOPTS) $(FLACOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 18 | CFLAGS = $(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 |
22 | include $(TOOLSDIR)/makesrc.inc | 22 | include $(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 | ||
17 | M4AOPTS = -O3 | 17 | M4AOPTS = -O3 |
18 | CFLAGS = $(GCCOPTS) $(M4AOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 18 | CFLAGS = $(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 |
22 | include $(TOOLSDIR)/makesrc.inc | 22 | include $(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 |
18 | MADOPTS = -DNDEBUG -O2 | 18 | MADOPTS = -DNDEBUG -O2 |
19 | CFLAGS = $(GCCOPTS) $(MADOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 19 | CFLAGS = $(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 |
23 | include $(TOOLSDIR)/makesrc.inc | 23 | include $(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 | ||
17 | MUSEPACKOPTS = -O2 | 17 | MUSEPACKOPTS = -O2 |
18 | CFLAGS = $(GCCOPTS) $(MUSEPACKOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 18 | CFLAGS = $(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 |
22 | include $(TOOLSDIR)/makesrc.inc | 22 | include $(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 | ||
17 | WAVPACKOPTS = -O2 | 17 | WAVPACKOPTS = -O2 |
18 | CFLAGS = $(GCCOPTS) $(WAVPACKOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | 18 | CFLAGS = $(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 |
22 | include $(TOOLSDIR)/makesrc.inc | 22 | include $(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 |
13 | playergfx.c | 13 | playergfx.c |
14 | #endif | 14 | #endif |
15 | #ifdef RB_PROFILE | ||
16 | profile_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 | |||
24 | static struct plugin_api *local_rb = NULL; /* global api struct pointer */ | ||
25 | |||
26 | void profile_init(struct plugin_api* pa) | ||
27 | { | ||
28 | local_rb = pa; | ||
29 | } | ||
30 | |||
31 | void __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 | |||
36 | void __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 | |||
27 | void profile_init(struct plugin_api* pa); | ||
28 | |||
29 | void __cyg_profile_func_enter(void *this_fn, void *call_site) | ||
30 | NO_PROF_ATTR ICODE_ATTR; | ||
31 | void __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 | ||
290 | enum plugin_status plugin_start(struct plugin_api* api, void *parameter) | 293 | enum 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 | |||
8 | FAQ | 8 | FAQ |
9 | FILES | 9 | FILES |
10 | HISTORY | 10 | HISTORY |
11 | LICENSES | ||
11 | NODO | 12 | NODO |
12 | README | 13 | README |
13 | TECH | 14 | TECH |
diff --git a/docs/LICENSES b/docs/LICENSES new file mode 100644 index 0000000000..652a276eed --- /dev/null +++ b/docs/LICENSES | |||
@@ -0,0 +1,36 @@ | |||
1 | This file contains license for software imported into Rockbox but governed by | ||
2 | a previous license. Each section begins by identifying the code in Rockbox | ||
3 | and the source from of those code, and is followed by the license text. | ||
4 | |||
5 | ************************************************************************* | ||
6 | In: profile.c, profile_func_enter | ||
7 | From: gcc - gmon.c, mcount | ||
8 | ************************************************************************* | ||
9 | Copyright (c) 1991, 1998 The Regents of the University of California. | ||
10 | All rights reserved. | ||
11 | |||
12 | Redistribution and use in source and binary forms, with or without | ||
13 | modification, are permitted provided that the following conditions | ||
14 | are met: | ||
15 | 1. Redistributions of source code must retain the above copyright | ||
16 | notice, this list of conditions and the following disclaimer. | ||
17 | 2. 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. | ||
20 | 3. [rescinded 22 July 1999] | ||
21 | 4. 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 | |||
25 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
26 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
27 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
28 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
31 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
32 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
33 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
34 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
35 | SUCH DAMAGE. | ||
36 | @(#)gmon.c 5.3 (Berkeley) 5/22/91 | ||
@@ -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 | |||
176 | Profiling | ||
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 |
124 | id3.c | 124 | id3.c |
125 | #ifndef SIMULATOR | 125 | #ifndef SIMULATOR |
126 | #ifdef RB_PROFILE | ||
127 | profile.c | ||
128 | #endif /* RB_PROFILE */ | ||
126 | hwcompat.c | 129 | hwcompat.c |
127 | kernel.c | 130 | kernel.c |
128 | rolo.c | 131 | rolo.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 | |||
40 | struct 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 | |||
57 | extern int current_thread; | ||
58 | |||
59 | /* Initialize and start profiling */ | ||
60 | void profstart(int current_thread) | ||
61 | NO_PROF_ATTR; | ||
62 | |||
63 | /* Clean up and write profile data */ | ||
64 | void 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 */ | ||
69 | void 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 */ | ||
72 | void profile_thread_started(int current_thread) | ||
73 | NO_PROF_ATTR; | ||
74 | |||
75 | void profile_func_exit(void *this_fn, void *call_site) | ||
76 | NO_PROF_ATTR ICODE_ATTR; | ||
77 | void 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); | |||
37 | void wake_up_thread(void); | 37 | void wake_up_thread(void); |
38 | void init_threads(void); | 38 | void init_threads(void); |
39 | int thread_stack_usage(int threadnum); | 39 | int thread_stack_usage(int threadnum); |
40 | #ifdef RB_PROFILE | ||
41 | void 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 | |||
62 | static unsigned short profiling = PROF_OFF; | ||
63 | static size_t recursion_level; | ||
64 | static unsigned short indices[INDEX_SIZE]; | ||
65 | static struct pfd_struct pfds[NUMPFDS]; | ||
66 | /* This holds a pointer to the last pfd effected for time tracking */ | ||
67 | static 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 */ | ||
70 | static int profiling_thread = -1; | ||
71 | |||
72 | /* internal function prototypes */ | ||
73 | static void profile_timer_tick(void); | ||
74 | static void profile_timer_unregister(void); | ||
75 | |||
76 | static 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 | |||
84 | void 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 | |||
97 | void 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 | |||
110 | static 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 | |||
119 | static 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 */ | ||
126 | void 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 | |||
138 | static 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 | |||
147 | void 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 | |||
188 | void 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 | |||
219 | void 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 */ | ||
281 | found: | ||
282 | ADDQI_L(pfd->count,1); | ||
283 | /* We've (found or created) and updated our pfd, save it and start timing */ | ||
284 | done: | ||
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 | |||
299 | overflow: | ||
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; | |||
78 | static inline void store_context(void* addr) __attribute__ ((always_inline)); | 78 | static inline void store_context(void* addr) __attribute__ ((always_inline)); |
79 | static inline void load_context(const void* addr) __attribute__ ((always_inline)); | 79 | static inline void load_context(const void* addr) __attribute__ ((always_inline)); |
80 | 80 | ||
81 | #ifdef RB_PROFILE | ||
82 | #include <profile.h> | ||
83 | void 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 | */ |
246 | void switch_thread(void) | 253 | void 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 | ||
289 | void sleep_thread(void) | 302 | void 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] | |||
23 | ucl/src/*.[ch] | 23 | ucl/src/*.[ch] |
24 | ucl/src/Makefile | 24 | ucl/src/Makefile |
25 | ucl/include/ucl/*.h | 25 | ucl/include/ucl/*.h |
26 | profile_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 | ||
279 | whichsim () { | 301 | whichsim () { |
@@ -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@ | |||
952 | export DLLTOOL=@DLLTOOL@ | 975 | export DLLTOOL=@DLLTOOL@ |
953 | export DLLWRAP=@DLLWRAP@ | 976 | export DLLWRAP=@DLLWRAP@ |
954 | export RANLIB=@RANLIB@ | 977 | export RANLIB=@RANLIB@ |
978 | export PROFILE_OPTS=@PROFILE_OPTS@ | ||
955 | export GCCOPTS=@GCCOPTS@ | 979 | export GCCOPTS=@GCCOPTS@ |
956 | export LOADADDRESS=@LOADADDRESS@ | 980 | export LOADADDRESS=@LOADADDRESS@ |
957 | export SIMVER=@SIMVER@ | 981 | export 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 | ||
2 | sub error { | ||
3 | print("Error: @_\n"); | ||
4 | exit(1); | ||
5 | } | ||
6 | sub 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 | } | ||
18 | if ($ARGV[0] =~ m/-(h|help|-help)/) { | ||
19 | usage(); | ||
20 | } | ||
21 | if (@ARGV < 2) { | ||
22 | usage("Requires at least 2 arguments"); | ||
23 | } | ||
24 | open(FILE1,shift) || error("Couldn't open file1"); | ||
25 | my @file1 = <FILE1>; | ||
26 | close(FILE1); | ||
27 | open(FILE2,shift) || error("Couldn't open file2"); | ||
28 | my @file2 = <FILE2>; | ||
29 | close(FILE2); | ||
30 | my $showcalldiff = shift; | ||
31 | my %calls1; | ||
32 | my %calls2; | ||
33 | my @calls = (\%calls1,\%calls2); | ||
34 | my $start = 0; | ||
35 | my @files = (\@file1,\@file2); | ||
36 | my @allcalls = (0,0); | ||
37 | my @allticks = (0,0); | ||
38 | for ( $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 | } | ||
58 | printf("File one calls: %08ld, ticks: %08ld\n",$allcalls[0],$allticks[0]); | ||
59 | printf("File two calls: %08ld, ticks: %08ld\n",$allcalls[1],$allticks[1]); | ||
60 | printf("Percent change: %+7.2f%%, ticks: %+7.2f%%\n", | ||
61 | ($allcalls[1]-$allcalls[0])/$allcalls[0]*100, | ||
62 | ($allticks[1]-$allticks[0])/$allticks[0]*100); | ||
63 | my @allkeys = keys(%calls1); | ||
64 | push(@allkeys,keys(%calls2)); | ||
65 | my %u = (); | ||
66 | my @keys = grep {defined} map { | ||
67 | if (exists $u{$_}) { undef; } else { $u{$_}=undef;$_; } | ||
68 | } @allkeys; | ||
69 | undef %u; | ||
70 | my %byticks; | ||
71 | my %bycalls; | ||
72 | foreach $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 | } | ||
95 | my @calls = sort(keys(%bycalls)); | ||
96 | print("By calls\n"); | ||
97 | foreach $call(@calls) { | ||
98 | print($bycalls{$call}); | ||
99 | } | ||
100 | my @ticks = sort(keys(%byticks)); | ||
101 | print("By ticks\n"); | ||
102 | foreach $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 | |||
3 | sub error { | ||
4 | print STDERR ("Error: @_\n"); | ||
5 | exit(1); | ||
6 | } | ||
7 | |||
8 | sub warning { | ||
9 | print STDERR ("Warning: @_\n"); | ||
10 | } | ||
11 | |||
12 | # string (filename.map) | ||
13 | # return hash(string:hash(string:number)) | ||
14 | sub 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) | ||
41 | sub 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 | ||
79 | sub 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)) | ||
95 | sub 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) | ||
139 | sub 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 | ||
172 | sub merge_hashes { | ||
173 | my $hash1 = $_[0]; | ||
174 | my $hash2 = $_[1]; | ||
175 | return (%$hash1,%$hash2); | ||
176 | } | ||
177 | |||
178 | sub 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 | |||
201 | if ($ARGV[0] =~ m/-(h|help|-help)/) { | ||
202 | usage(); | ||
203 | } | ||
204 | if (@ARGV < 2) { | ||
205 | usage("Requires at least 2 arguments"); | ||
206 | } | ||
207 | if ($ARGV[0] !~ m/\.out$/) { | ||
208 | usage("Profile file must end in .out"); | ||
209 | } | ||
210 | my $i = 1; | ||
211 | my %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 | } | ||
230 | if (!%symbols) { | ||
231 | warning("No symbols found"); | ||
232 | } | ||
233 | my @pfds = create_list($ARGV[0],\%symbols); | ||
234 | for (; $i < @ARGV; $i++) { | ||
235 | print_sorted(\@pfds,split("_",$ARGV[$i])); | ||
236 | } | ||