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 /tools | |
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
Diffstat (limited to 'tools')
-rw-r--r-- | tools/FILES | 1 | ||||
-rwxr-xr-x | tools/buildzip.pl | 1 | ||||
-rwxr-xr-x | tools/configure | 38 | ||||
-rwxr-xr-x | tools/profile_reader/profile_comparator.pl | 104 | ||||
-rwxr-xr-x | tools/profile_reader/profile_reader.pl | 236 |
5 files changed, 373 insertions, 7 deletions
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 | } | ||