Commit FS#9170 by Dave Hooper. Adds support for profiling on ARM, and fixes various issues with compiling profile builds. Also, note that profile_reader.pl syntax has changed.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18142 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
c323d533c2
commit
52bdcbae85
7 changed files with 52 additions and 28 deletions
|
@ -138,8 +138,8 @@ struct codec_api ci = {
|
||||||
#ifdef RB_PROFILE
|
#ifdef RB_PROFILE
|
||||||
profile_thread,
|
profile_thread,
|
||||||
profstop,
|
profstop,
|
||||||
profile_func_enter,
|
__cyg_profile_func_enter,
|
||||||
profile_func_exit,
|
__cyg_profile_func_exit,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
|
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
|
||||||
|
|
|
@ -7,7 +7,9 @@ wav.c
|
||||||
a52.c
|
a52.c
|
||||||
mpc.c
|
mpc.c
|
||||||
wavpack.c
|
wavpack.c
|
||||||
|
#ifndef RB_PROFILE
|
||||||
alac.c
|
alac.c
|
||||||
|
#endif
|
||||||
wma.c
|
wma.c
|
||||||
#if MEMORYSIZE > 1
|
#if MEMORYSIZE > 1
|
||||||
aac.c
|
aac.c
|
||||||
|
|
|
@ -335,8 +335,8 @@ static const struct plugin_api rockbox_api = {
|
||||||
#ifdef RB_PROFILE
|
#ifdef RB_PROFILE
|
||||||
profile_thread,
|
profile_thread,
|
||||||
profstop,
|
profstop,
|
||||||
profile_func_enter,
|
__cyg_profile_func_enter,
|
||||||
profile_func_exit,
|
__cyg_profile_func_exit,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SIMULATOR
|
#ifdef SIMULATOR
|
||||||
|
|
|
@ -33,14 +33,18 @@ pacbox
|
||||||
/* For all the color targets */
|
/* For all the color targets */
|
||||||
#if defined(HAVE_LCD_COLOR) || defined(MROBE_100) || \
|
#if defined(HAVE_LCD_COLOR) || defined(MROBE_100) || \
|
||||||
(LCD_DEPTH == 2) && !defined(ARCHOS_AV300)
|
(LCD_DEPTH == 2) && !defined(ARCHOS_AV300)
|
||||||
|
#ifndef RB_PROFILE
|
||||||
doom
|
doom
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* For all the swcodec targets */
|
/* For all the swcodec targets */
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
midi
|
midi
|
||||||
/* beatbox */
|
/* beatbox */
|
||||||
|
#ifndef RB_PROFILE
|
||||||
mpegplayer
|
mpegplayer
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* IRIVER_IFP7XX_SERIES */
|
#endif /* IRIVER_IFP7XX_SERIES */
|
||||||
|
|
|
@ -39,9 +39,9 @@ void profile_thread_stopped(int current_thread)
|
||||||
void profile_thread_started(int current_thread)
|
void profile_thread_started(int current_thread)
|
||||||
NO_PROF_ATTR;
|
NO_PROF_ATTR;
|
||||||
|
|
||||||
void profile_func_exit(void *this_fn, void *call_site)
|
void __cyg_profile_func_exit(void *this_fn, void *call_site)
|
||||||
NO_PROF_ATTR ICODE_ATTR;
|
NO_PROF_ATTR ICODE_ATTR;
|
||||||
void profile_func_enter(void *this_fn, void *call_site)
|
void __cyg_profile_func_enter(void *this_fn, void *call_site)
|
||||||
NO_PROF_ATTR ICODE_ATTR;
|
NO_PROF_ATTR ICODE_ATTR;
|
||||||
|
|
||||||
#endif /*_SYS_PROFILE_H*/
|
#endif /*_SYS_PROFILE_H*/
|
||||||
|
|
|
@ -62,7 +62,6 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
|
|
||||||
|
|
||||||
/* PFD is Profiled Function Data */
|
/* PFD is Profiled Function Data */
|
||||||
|
|
||||||
/* Indices are shorts which means that we use 4k of RAM */
|
/* Indices are shorts which means that we use 4k of RAM */
|
||||||
|
@ -75,7 +74,7 @@
|
||||||
* for profiling, and allows for profiling sections of code with up-to
|
* for profiling, and allows for profiling sections of code with up-to
|
||||||
* 1024 function caller->callee pairs
|
* 1024 function caller->callee pairs
|
||||||
*/
|
*/
|
||||||
#define NUMPFDS 1024
|
#define NUMPFDS 512
|
||||||
|
|
||||||
struct pfd_struct {
|
struct pfd_struct {
|
||||||
void *self_pc;
|
void *self_pc;
|
||||||
|
@ -141,7 +140,7 @@ void profile_thread_started(int current_thread) {
|
||||||
if (profiling < PROF_ERROR) {
|
if (profiling < PROF_ERROR) {
|
||||||
/* After we de-mask, if profiling is active, reactivate the timer */
|
/* After we de-mask, if profiling is active, reactivate the timer */
|
||||||
timer_register(0, profile_timer_unregister,
|
timer_register(0, profile_timer_unregister,
|
||||||
TIMER_FREQ/10000, 0, profile_timer_tick);
|
TIMER_FREQ/10000, 0, profile_timer_tick IF_COP(, 0 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,9 +167,9 @@ void profstart(int current_thread) {
|
||||||
last_pfd = (struct pfd_struct*)0;
|
last_pfd = (struct pfd_struct*)0;
|
||||||
pfds[0].link = 0;
|
pfds[0].link = 0;
|
||||||
pfds[0].self_pc = 0;
|
pfds[0].self_pc = 0;
|
||||||
memset(&indices,0,INDEX_SIZE * sizeof(unsigned short));
|
memset(indices,0,INDEX_SIZE * sizeof(unsigned short));
|
||||||
timer_register(
|
timer_register(
|
||||||
0, profile_timer_unregister, TIMER_FREQ/10000, 0, profile_timer_tick);
|
0, profile_timer_unregister, TIMER_FREQ/10000, 0, profile_timer_tick IF_COP(, 0 ) );
|
||||||
profiling = PROF_ON;
|
profiling = PROF_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +178,7 @@ static void write_function_recursive(int fd, struct pfd_struct *pfd, int depth){
|
||||||
fdprintf(fd,"0x%08lX\t%08ld\t%08ld\t%04d\n", (size_t)pfd->self_pc,
|
fdprintf(fd,"0x%08lX\t%08ld\t%08ld\t%04d\n", (size_t)pfd->self_pc,
|
||||||
pfd->count, pfd->time, depth);
|
pfd->count, pfd->time, depth);
|
||||||
if (link > 0 && link < NUMPFDS) {
|
if (link > 0 && link < NUMPFDS) {
|
||||||
write_function_recursive(fd, &pfds[link], depth++);
|
write_function_recursive(fd, &pfds[link], ++depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +205,7 @@ void profstop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fdprintf(fd,"DEBUG PROFILE DATA FOLLOWS\n");
|
fdprintf(fd,"DEBUG PROFILE DATA FOLLOWS\n");
|
||||||
fdprintf(fd,"INDEX\tLOCATION\tSELF_PC\t\tCOUNT\t\tTIME\t\tLINK\tCALLER\n");
|
fdprintf(fd,"INDEX\tLOCATION\tSELF_PC\t\tCOUNT\t\tTIME\t\tLINK\tCALLER_IDX\n");
|
||||||
for (i = 0; i < NUMPFDS; i++) {
|
for (i = 0; i < NUMPFDS; i++) {
|
||||||
struct pfd_struct *my_last_pfd = &pfds[i];
|
struct pfd_struct *my_last_pfd = &pfds[i];
|
||||||
if (my_last_pfd->self_pc != 0) {
|
if (my_last_pfd->self_pc != 0) {
|
||||||
|
@ -214,7 +213,7 @@ void profstop() {
|
||||||
"%04d\t0x%08lX\t0x%08lX\t0x%08lX\t0x%08lX\t%04d\t0x%08lX\n",
|
"%04d\t0x%08lX\t0x%08lX\t0x%08lX\t0x%08lX\t%04d\t0x%08lX\n",
|
||||||
i, (size_t)my_last_pfd, (size_t)my_last_pfd->self_pc,
|
i, (size_t)my_last_pfd, (size_t)my_last_pfd->self_pc,
|
||||||
my_last_pfd->count, my_last_pfd->time, my_last_pfd->link,
|
my_last_pfd->count, my_last_pfd->time, my_last_pfd->link,
|
||||||
(size_t)my_last_pfd->caller);
|
(size_t)my_last_pfd->caller );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fdprintf(fd,"INDEX_ADDRESS=INDEX\n");
|
fdprintf(fd,"INDEX_ADDRESS=INDEX\n");
|
||||||
|
@ -224,7 +223,7 @@ void profstop() {
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void profile_func_exit(void *self_pc, void *call_site) {
|
void __cyg_profile_func_exit(void *self_pc, void *call_site) {
|
||||||
(void)call_site;
|
(void)call_site;
|
||||||
(void)self_pc;
|
(void)self_pc;
|
||||||
/* When we started timing, we set the time to the tick at that time
|
/* When we started timing, we set the time to the tick at that time
|
||||||
|
@ -255,7 +254,7 @@ void profile_func_exit(void *self_pc, void *call_site) {
|
||||||
pfd = &pfds[temp];\
|
pfd = &pfds[temp];\
|
||||||
pfd->self_pc = self_pc; pfd->count = 1; pfd->time = 0
|
pfd->self_pc = self_pc; pfd->count = 1; pfd->time = 0
|
||||||
|
|
||||||
void profile_func_enter(void *self_pc, void *from_pc) {
|
void __cyg_profile_func_enter(void *self_pc, void *from_pc) {
|
||||||
struct pfd_struct *pfd;
|
struct pfd_struct *pfd;
|
||||||
struct pfd_struct *prev_pfd;
|
struct pfd_struct *prev_pfd;
|
||||||
unsigned short *pfd_index_pointer;
|
unsigned short *pfd_index_pointer;
|
||||||
|
@ -339,3 +338,7 @@ overflow:
|
||||||
profiling = PROF_ERROR;
|
profiling = PROF_ERROR;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,11 @@ sub read_map {
|
||||||
return %retval;
|
return %retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
# string (filename.[ao]), hash(string:number)
|
# string (filename.(a|o|elf)), hash(number:string), string(objdump_tool)
|
||||||
# return hash(number:string)
|
# return hash(number:string)
|
||||||
sub read_library {
|
sub read_library {
|
||||||
open(OBJECT_FILE,"objdump -t $_[0] |") ||
|
open(OBJECT_FILE,"$_[2] -t $_[0] |") ||
|
||||||
error("Couldn't pipe objdump for $_[0]");
|
error("Couldn't pipe objdump for $_[0]\nCommand was: $_[2] -t $_[0]");
|
||||||
my $library = $_[1];
|
my $library = $_[1];
|
||||||
my %library = %$library;
|
my %library = %$library;
|
||||||
my %retval;
|
my %retval;
|
||||||
|
@ -62,7 +62,7 @@ sub read_library {
|
||||||
if ($parts[3] eq $parts[5]) {
|
if ($parts[3] eq $parts[5]) {
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
if ($parts[3] =~ m/\.(text|data|rodata|bss|icode|idata|irodata|ibss)/) {
|
if ($parts[3] =~ m/\.(text|data|rodata|bss|icode|idata|irodata|ibss|iram)/) {
|
||||||
my $region = $parts[3];
|
my $region = $parts[3];
|
||||||
my $symbolOffset = hex("0x" . $parts[0]);
|
my $symbolOffset = hex("0x" . $parts[0]);
|
||||||
my $sectionOffset = hex($library{$object . $region});
|
my $sectionOffset = hex($library{$object . $region});
|
||||||
|
@ -139,8 +139,16 @@ sub print_sorted {
|
||||||
my $totalCalls = 0;
|
my $totalCalls = 0;
|
||||||
my $totalTicks = 0;
|
my $totalTicks = 0;
|
||||||
$pfds = 0;
|
$pfds = 0;
|
||||||
|
|
||||||
|
# we use a key sort, which means numerical fields need to be
|
||||||
|
# numerically sortable by an alphanumeric sort - we can simply
|
||||||
|
# do this by giving the numeric keys trailing zeros. Note that
|
||||||
|
# simple string concatenation (what this used to do) would not do this
|
||||||
foreach $element(@pfds) {
|
foreach $element(@pfds) {
|
||||||
$elements{@$element[$sort_index] . @$element[2]} = $element;
|
$ne = $element;
|
||||||
|
@$ne[0] = sprintf( "%08d", @$element[0]);
|
||||||
|
@$ne[1] = sprintf( "%08d", @$element[1]);
|
||||||
|
$elements{@$ne[$sort_index] . @$element[2]} = $element;
|
||||||
$pfds++;
|
$pfds++;
|
||||||
$totalCalls += @$element[0];
|
$totalCalls += @$element[0];
|
||||||
$totalTicks += @$element[1];
|
$totalTicks += @$element[1];
|
||||||
|
@ -174,13 +182,17 @@ sub usage {
|
||||||
print STDERR ("Error: @_\n");
|
print STDERR ("Error: @_\n");
|
||||||
}
|
}
|
||||||
print STDERR ("USAGE:\n");
|
print STDERR ("USAGE:\n");
|
||||||
print STDERR ("$0 profile.out map obj[...] [map obj[...]...] sort[...]\n");
|
print STDERR ("$0 profile.out objdump_tool map obj[...] [map obj[...]...] sort[...]\n");
|
||||||
print STDERR
|
print STDERR
|
||||||
("\tprofile.out output from the profiler, extension is .out\n");
|
("\tprofile.out output from the profiler, extension is .out\n");
|
||||||
|
print STDERR
|
||||||
|
("\tobjdump_tool name of objdump executable for this platform\n");
|
||||||
|
print STDERR
|
||||||
|
("\t e.g. arm-elf-objdump\n");
|
||||||
print STDERR
|
print STDERR
|
||||||
("\tmap map file, extension is .map\n");
|
("\tmap map file, extension is .map\n");
|
||||||
print STDERR
|
print STDERR
|
||||||
("\tobj library or object file, extension is .a or .o\n");
|
("\tobj library or object file, extension is .a or .o or .elf\n");
|
||||||
print STDERR
|
print STDERR
|
||||||
("\tformat 0-2[_p] 0: by calls, 1: by ticks, 2: by name\n");
|
("\tformat 0-2[_p] 0: by calls, 1: by ticks, 2: by name\n");
|
||||||
print STDERR
|
print STDERR
|
||||||
|
@ -195,13 +207,13 @@ sub usage {
|
||||||
if ($ARGV[0] =~ m/-(h|help|-help)/) {
|
if ($ARGV[0] =~ m/-(h|help|-help)/) {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
if (@ARGV < 2) {
|
if (@ARGV < 3) {
|
||||||
usage("Requires at least 2 arguments");
|
usage("Requires at least 3 arguments");
|
||||||
}
|
}
|
||||||
if ($ARGV[0] !~ m/\.out$/) {
|
if ($ARGV[0] !~ m/\.out$/) {
|
||||||
usage("Profile file must end in .out");
|
usage("Profile file must end in .out");
|
||||||
}
|
}
|
||||||
my $i = 1;
|
my $i = 2;
|
||||||
my %symbols;
|
my %symbols;
|
||||||
{
|
{
|
||||||
my %map;
|
my %map;
|
||||||
|
@ -209,12 +221,12 @@ my %symbols;
|
||||||
my $file = $ARGV[$i];
|
my $file = $ARGV[$i];
|
||||||
if ($file =~ m/\.map$/) {
|
if ($file =~ m/\.map$/) {
|
||||||
%map = read_map($file);
|
%map = read_map($file);
|
||||||
} elsif ($file =~ m/\.[ao]$/) {
|
} elsif ($file =~ m/\.(a|o|elf)$/) {
|
||||||
if (!%map) {
|
if (!%map) {
|
||||||
usage("No map file found before first object file");
|
usage("No map file found before first object file");
|
||||||
}
|
}
|
||||||
my @parts = split(/\//,$file);
|
my @parts = split(/\//,$file);
|
||||||
my %new_symbols = read_library($file,$map{$parts[$#parts]});
|
my %new_symbols = read_library($file,$map{$parts[$#parts]},$ARGV[1]);
|
||||||
%symbols = merge_hashes(\%symbols,\%new_symbols);
|
%symbols = merge_hashes(\%symbols,\%new_symbols);
|
||||||
} else {
|
} else {
|
||||||
last;
|
last;
|
||||||
|
@ -224,6 +236,9 @@ my %symbols;
|
||||||
if (!%symbols) {
|
if (!%symbols) {
|
||||||
warning("No symbols found");
|
warning("No symbols found");
|
||||||
}
|
}
|
||||||
|
if ($i >= @ARGV) {
|
||||||
|
error("You forgot to specify any sort ordering on output (e.g. 0, 1_p, 2)");
|
||||||
|
}
|
||||||
my @pfds = create_list($ARGV[0],\%symbols);
|
my @pfds = create_list($ARGV[0],\%symbols);
|
||||||
for (; $i < @ARGV; $i++) {
|
for (; $i < @ARGV; $i++) {
|
||||||
print_sorted(\@pfds,split("_",$ARGV[$i]));
|
print_sorted(\@pfds,split("_",$ARGV[$i]));
|
||||||
|
|
Loading…
Reference in a new issue