|
1 # Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 # All rights reserved. |
|
3 # This component and the accompanying materials are made available |
|
4 # under the terms of "Eclipse Public License v1.0" |
|
5 # which accompanies this distribution, and is available |
|
6 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 # |
|
8 # Initial Contributors: |
|
9 # Nokia Corporation - initial contribution. |
|
10 # |
|
11 # Contributors: |
|
12 # |
|
13 # Description: |
|
14 # Postprocess EPOC memory usage trace file |
|
15 # |
|
16 # |
|
17 |
|
18 no strict 'vars'; |
|
19 use English; |
|
20 # Standard Symbian boilerplate to find and load E32env library |
|
21 use FindBin; # for FindBin::Bin |
|
22 use Getopt::Long; |
|
23 |
|
24 my $PerlLibPath; # fully qualified pathname of the directory containing our Perl modules |
|
25 |
|
26 BEGIN { |
|
27 # check user has a version of perl that will cope |
|
28 require 5.005_03; |
|
29 # establish the path to the Perl libraries: currently the same directory as this script |
|
30 $PerlLibPath = $FindBin::Bin; # X:/epoc32/tools |
|
31 $PerlLibPath =~ s/\//\\/g; # X:\epoc32\tools |
|
32 $PerlLibPath .= "\\"; |
|
33 } |
|
34 |
|
35 use lib $PerlLibPath; |
|
36 use Modload; |
|
37 Load_SetModulePath($PerlLibPath); |
|
38 |
|
39 my $tickperiod = 0; |
|
40 my $emulproc = quotemeta("epoc[00000000]0001::"); |
|
41 my $apprun = quotemeta("apprun[10003a4b]"); |
|
42 my $ramdrive = quotemeta("TheRamDriveChunk"); |
|
43 |
|
44 my @chunknames; # Array of chunk names, indexed by chunkid |
|
45 my @traces; # Array of chunk traces, each trace being an array |
|
46 # of (tick,chunkid,size) triples |
|
47 my @chunkgroup; # Group that a chunk belongs to, indexed by chunkid |
|
48 my @groupnames; # Array of chunk group names (normally a group==a process) |
|
49 my @groups; # Array of chunk groups, each group being an array of chunkids |
|
50 |
|
51 my %opts; |
|
52 my $result = GetOptions (\%opts, "detailed", "help"); |
|
53 if($result && ($opts{'help'} || $#ARGV<0)) |
|
54 { |
|
55 &help; |
|
56 exit 0; |
|
57 } |
|
58 if(!$result || $#ARGV>0) |
|
59 { |
|
60 &usage; |
|
61 exit 1; |
|
62 } |
|
63 |
|
64 my $file = $ARGV[0]; |
|
65 open TRACE,$file or die "Error: Can't open trace file \"$file\".\n"; |
|
66 |
|
67 # |
|
68 # Parse trace file |
|
69 # |
|
70 my %current; # chunkids hashed by DChunk address |
|
71 my $nextchunkid; |
|
72 while(<TRACE>) |
|
73 { |
|
74 if (/MT:P\s+(\d+)\s*$/) |
|
75 { |
|
76 $tickperiod = $1; |
|
77 } |
|
78 elsif (/MT:C\s+(\d+)\s+([0-9a-fA-F]+)\s+(.+)\s*$/) |
|
79 { |
|
80 $current{$2} = $nextchunkid++; |
|
81 push @chunknames, $3; |
|
82 } |
|
83 elsif (my ($tick, $addr, $size, $name) = |
|
84 (/MT:A\s+(\d+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+(.+)\s*$/)) |
|
85 { |
|
86 my $chunkid = $current{$addr}; |
|
87 die "Error: Parsing failure - is trace file complete ?" |
|
88 unless (defined $chunkid); |
|
89 push @traces, [0+$tick, $chunkid, hex($size)]; |
|
90 |
|
91 # Check whether chunk has been renamed to something more useful |
|
92 $chunknames[$chunkid] = $name |
|
93 if (($chunknames[$chunkid] =~ /^$emulproc/io) || |
|
94 ($chunknames[$chunkid] =~ /^$apprun/io)); |
|
95 } |
|
96 elsif (/MT:D\s+(\d+)\s+([0-9a-fA-F]+)\s+(.+)\s*$/) |
|
97 { |
|
98 die "Error: Parsing failure" unless (defined $current{$2}); |
|
99 push @traces, [0+$1, $current{$2}, 0]; |
|
100 delete $current{$2}; |
|
101 } |
|
102 elsif (/(MT:.\s+.*)/) |
|
103 { |
|
104 printf "Warning: Unrecognised trace line \"$1\".\n"; |
|
105 } |
|
106 } |
|
107 close TRACE; |
|
108 die "Error: File \"$file\" does not contain any memory traces.\n" |
|
109 unless ($#chunknames>0); |
|
110 |
|
111 |
|
112 # |
|
113 # Group chunks together by name |
|
114 # |
|
115 for (0..$#chunknames) |
|
116 { |
|
117 my $chunkid = $_; |
|
118 my $name = $chunknames[$chunkid]; |
|
119 $name = $1 if ($name =~ /($ramdrive)$/i); # Special handling for ramdrive |
|
120 $name = $1 if ($name =~ /^$emulproc(.*)/i); # Use thread names on Emulator |
|
121 ($name) = ($name =~ /([^:]+)/); # otherwise strip thread name |
|
122 |
|
123 # yuck! linear search |
|
124 my $found = 0; |
|
125 for (0..$#groupnames) |
|
126 { |
|
127 my $groupid = $_; |
|
128 if ($groupnames[$groupid] eq $name) |
|
129 { |
|
130 $found = 1; |
|
131 push @{$groups[$groupid]}, $chunkid; |
|
132 $chunkgroup[$chunkid] = $groupid; |
|
133 last; |
|
134 } |
|
135 } |
|
136 |
|
137 if (!$found) |
|
138 { |
|
139 push @groupnames, $name; |
|
140 push @groups, [$chunkid]; |
|
141 $chunkgroup[$chunkid] = $#groups; |
|
142 } |
|
143 } |
|
144 |
|
145 # Strip instance number (if any) from group name for presentation |
|
146 for (0..$#groupnames) |
|
147 { |
|
148 $groupnames[$_] = $1 if ($groupnames[$_] =~ /^([^]]+]?)/); |
|
149 } |
|
150 |
|
151 # |
|
152 # Output |
|
153 # |
|
154 my @chunksizes; # Array of chunk sizes, indexed by chunkid |
|
155 for (0..$#chunknames) { $chunksizes[$_] = 0 }; |
|
156 |
|
157 if ($opts{'detailed'}) |
|
158 { |
|
159 # Detailed output |
|
160 |
|
161 foreach my $name (@groupnames) { print ",\"$name\"" }; |
|
162 print "\n"; |
|
163 |
|
164 # if the tick size in microseconds hasn't been reported in the log with MT:P, take a guess |
|
165 my $tickdiv = 0; |
|
166 if ($tickperiod == 0) |
|
167 { |
|
168 # Uses hacky method to determine whether on Emulator or Target |
|
169 $tickdiv = ($chunknames[0] =~ /^$emulproc/io) ? 10 : 64; |
|
170 } |
|
171 else |
|
172 { |
|
173 # tickperiod is number of microseconds |
|
174 $tickdiv = 1000000 / $tickperiod; |
|
175 } |
|
176 |
|
177 my ($oldtick, $minitick) = (0,0); |
|
178 my @groupsizes; |
|
179 for my $trace (@traces) |
|
180 { |
|
181 my ($tick, $chunkid, $size) = @$trace; |
|
182 if ($oldtick != $tick) |
|
183 { |
|
184 $oldtick = $tick; |
|
185 $minitick=0; |
|
186 } |
|
187 |
|
188 $chunksizes[$chunkid] = $size; |
|
189 my $groupid=$chunkgroup[$chunkid]; |
|
190 my $groupsize = 0; |
|
191 foreach $chunkid (@{$groups[$groupid]}) |
|
192 { |
|
193 $groupsize += $chunksizes[$chunkid]; |
|
194 } |
|
195 $groupsizes[$groupid] = $groupsize; |
|
196 |
|
197 print $tick/$tickdiv + ($minitick++)/1000000; |
|
198 foreach $groupsize (@groupsizes) |
|
199 { |
|
200 if ($groupsize) |
|
201 { |
|
202 printf ",%d", $groupsize/1024; |
|
203 } |
|
204 else |
|
205 { |
|
206 print ','; # make output prettier by omitting 0s |
|
207 } |
|
208 } |
|
209 print "\n"; |
|
210 } |
|
211 } |
|
212 else |
|
213 { |
|
214 # Summary output |
|
215 |
|
216 my @grouphighs; |
|
217 for my $trace (@traces) |
|
218 { |
|
219 my ($tick, $chunkid, $size) = @$trace; |
|
220 $chunksizes[$chunkid] = $size; |
|
221 my $groupid=$chunkgroup[$chunkid]; |
|
222 my $groupsize = 0; |
|
223 foreach $chunkid (@{$groups[$groupid]}) |
|
224 { |
|
225 $groupsize += $chunksizes[$chunkid]; |
|
226 } |
|
227 $grouphighs[$groupid] = $groupsize |
|
228 if (!defined($grouphighs[$groupid]) || |
|
229 ($grouphighs[$groupid] < $groupsize)); |
|
230 } |
|
231 |
|
232 printf "\"Process\", Size [K]\n"; |
|
233 for (0..$#groupnames) |
|
234 { |
|
235 printf "\"$groupnames[$_]\", %d\n", $grouphighs[$_]/1024; |
|
236 } |
|
237 } |
|
238 exit 0; |
|
239 |
|
240 |
|
241 sub help () |
|
242 { |
|
243 my $build; |
|
244 |
|
245 &Load_ModuleL('E32TPVER'); |
|
246 print "\nmemtrace - " . |
|
247 "Postprocess EPOC memory usage trace (Build ", &E32tpver, ")\n"; |
|
248 &usage; |
|
249 } |
|
250 |
|
251 sub usage () |
|
252 { |
|
253 print <<EOF |
|
254 |
|
255 Usage: |
|
256 memtrace [-d] <logfile> |
|
257 |
|
258 Where: |
|
259 <logfile> Memory usage trace file. |
|
260 |
|
261 Options: |
|
262 -d Produce a detailed listing. |
|
263 EOF |
|
264 ; |
|
265 } |