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/profile_reader/profile_reader.pl | |
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/profile_reader/profile_reader.pl')
-rwxr-xr-x | tools/profile_reader/profile_reader.pl | 236 |
1 files changed, 236 insertions, 0 deletions
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 | } | ||