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