OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of cpu_profiler; | |
6 | |
7 class CodeTrieNode { | |
8 final ProfileCode profileCode; | |
9 final int count; | |
10 final children = new List<CodeTrieNode>(); | |
11 CodeTrieNode(this.profileCode, this.count); | |
12 } | |
13 | |
14 class FunctionTrieNodeCode { | |
15 final ProfileCode code; | |
16 final int ticks; | |
17 FunctionTrieNodeCode(this.code, this.ticks); | |
18 } | |
19 | |
20 class FunctionTrieNode { | |
21 final ProfileFunction profileFunction; | |
22 final int count; | |
23 final children = new List<FunctionTrieNode>(); | |
24 final codes = new List<FunctionTrieNodeCode>(); | |
25 int _totalCodeTicks = 0; | |
26 int get totalCodesTicks => _totalCodeTicks; | |
27 FunctionTrieNode(this.profileFunction, this.count); | |
28 } | |
29 | |
30 class CodeTick { | |
31 final int address; | |
32 final int exclusiveTicks; | |
33 final int inclusiveTicks; | |
34 CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks); | |
35 } | |
36 | |
37 class InlineIntervalTick { | |
38 final int startAddress; | |
39 int _inclusiveTicks = 0; | |
40 int get inclusiveTicks => _inclusiveTicks; | |
41 int _exclusiveTicks = 0; | |
42 int get exclusiveTicks => _exclusiveTicks; | |
43 InlineIntervalTick(this.startAddress); | |
44 } | |
45 | |
46 class ProfileCode { | |
47 final CpuProfile profile; | |
48 final Code code; | |
49 int exclusiveTicks; | |
50 int inclusiveTicks; | |
51 final addressTicks = new Map<int, CodeTick>(); | |
52 final intervalTicks = new Map<int, InlineIntervalTick>(); | |
53 String formattedInclusiveTicks = ''; | |
54 String formattedExclusiveTicks = ''; | |
55 | |
56 void _processTicks(List<String> profileTicks) { | |
57 assert(profileTicks != null); | |
58 assert((profileTicks.length % 3) == 0); | |
59 for (var i = 0; i < profileTicks.length; i += 3) { | |
60 var address = int.parse(profileTicks[i], radix:16); | |
61 var exclusive = int.parse(profileTicks[i + 1]); | |
62 var inclusive = int.parse(profileTicks[i + 2]); | |
63 var tick = new CodeTick(address, exclusive, inclusive); | |
64 addressTicks[address] = tick; | |
siva
2015/02/25 22:58:38
Does the key (address) also have to be in the Code
Cutch
2015/02/26 18:44:39
Done.
| |
65 | |
66 var interval = code.findInterval(address); | |
67 if (interval != null) { | |
68 var intervalTick = intervalTicks[interval.start]; | |
69 if (intervalTick == null) { | |
70 // Insert into map. | |
71 intervalTick = new InlineIntervalTick(interval.start); | |
72 intervalTicks[interval.start] = intervalTick; | |
73 } | |
74 intervalTick._inclusiveTicks += inclusive; | |
75 intervalTick._exclusiveTicks += exclusive; | |
76 } | |
77 } | |
78 } | |
79 | |
80 ProfileCode.fromMap(this.profile, this.code, Map data) { | |
81 assert(profile != null); | |
82 assert(code != null); | |
83 | |
84 code.profile = this; | |
85 | |
86 inclusiveTicks = int.parse(data['inclusiveTicks']); | |
87 exclusiveTicks = int.parse(data['exclusiveTicks']); | |
88 var ticks = data['ticks']; | |
89 if (ticks != null) { | |
90 _processTicks(ticks); | |
91 } | |
92 | |
93 formattedInclusiveTicks = | |
94 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | |
95 '($inclusiveTicks)'; | |
96 | |
97 formattedExclusiveTicks = | |
98 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | |
99 '($exclusiveTicks)'; | |
100 } | |
101 } | |
102 | |
103 class ProfileFunction { | |
104 final CpuProfile profile; | |
105 final ServiceFunction function; | |
106 // List of compiled code objects containing this function. | |
107 final List<ProfileCode> profileCodes = new List<ProfileCode>(); | |
108 | |
109 // Absolute ticks: | |
110 int exclusiveTicks = 0; | |
111 int inclusiveTicks = 0; | |
112 | |
113 // Global percentages: | |
114 double globalExclusiveTicks = 0.0; | |
115 double globalInclusiveTicks = 0.0; | |
116 | |
117 int _sortCodes(ProfileCode a, ProfileCode b) { | |
118 if (a.code.isOptimized == b.code.isOptimized) { | |
119 return b.code.profile.exclusiveTicks - a.code.profile.exclusiveTicks; | |
120 } | |
121 if (a.code.isOptimized) { | |
122 return -1; | |
123 } | |
124 return 1; | |
125 } | |
126 | |
127 ProfileFunction.fromMap(this.profile, this.function, Map data) { | |
128 for (var codeIndex in data['codes']) { | |
129 var profileCode = profile.codes[codeIndex]; | |
130 profileCodes.add(profileCode); | |
131 } | |
132 profileCodes.sort(_sortCodes); | |
133 | |
134 exclusiveTicks = int.parse(data['exclusiveTicks']); | |
135 inclusiveTicks = int.parse(data['inclusiveTicks']); | |
136 | |
137 globalExclusiveTicks = exclusiveTicks / profile.sampleCount; | |
138 globalInclusiveTicks = inclusiveTicks / profile.sampleCount; | |
139 } | |
140 } | |
141 | |
142 | |
143 class CpuProfile { | |
144 final double MICROSECONDS_PER_SECOND = 1000000.0; | |
145 final double displayThreshold = 0.0002; // 0.02%. | |
146 | |
147 Isolate isolate; | |
148 | |
149 int sampleCount = 0; | |
150 int samplePeriod = 0; | |
151 double sampleRate = 0.0; | |
152 | |
153 int stackDepth = 0; | |
154 | |
155 double timeSpan = 0.0; | |
156 | |
157 CodeTrieNode codeTrieRoot; | |
158 FunctionTrieNode functionTrieRoot; | |
159 | |
160 final List<ProfileCode> codes = new List<ProfileCode>(); | |
161 final List<ProfileFunction> functions = new List<ProfileFunction>(); | |
162 | |
163 void clear() { | |
164 sampleCount = 0; | |
165 samplePeriod = 0; | |
166 sampleRate = 0.0; | |
167 stackDepth = 0; | |
168 timeSpan = 0.0; | |
169 codeTrieRoot = null; | |
170 functionTrieRoot = null; | |
171 codes.clear(); | |
172 functions.clear(); | |
173 } | |
174 | |
175 void load(Isolate isolate, ServiceMap profile) { | |
176 if ((isolate == null) || (profile == null)) { | |
177 return; | |
178 } | |
179 | |
180 this.isolate = isolate; | |
181 isolate.resetCachedProfileData(); | |
182 | |
183 clear(); | |
184 | |
185 sampleCount = profile['sampleCount']; | |
186 samplePeriod = profile['samplePeriod']; | |
187 sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod); | |
188 stackDepth = profile['stackDepth']; | |
189 timeSpan = profile['timeSpan']; | |
190 | |
191 // Process code table. | |
192 for (var codeRegion in profile['codes']) { | |
193 Code code = codeRegion['code']; | |
194 assert(code != null); | |
195 codes.add(new ProfileCode.fromMap(this, code, codeRegion)); | |
196 } | |
197 | |
198 // Process function table. | |
199 for (var profileFunction in profile['functions']) { | |
200 ServiceFunction function = profileFunction['function']; | |
201 assert(function != null); | |
202 functions.add( | |
203 new ProfileFunction.fromMap(this, function, profileFunction)); | |
204 } | |
205 | |
206 // Process code trie. | |
207 var exclusiveCodeTrie = profile['exclusiveCodeTrie']; | |
208 assert(exclusiveCodeTrie != null); | |
209 codeTrieRoot = _processCodeTrie(exclusiveCodeTrie); | |
210 | |
211 // Process function trie. | |
212 var exclusiveFunctionTrie = profile['exclusiveFunctionTrie']; | |
213 assert(exclusiveFunctionTrie != null); | |
214 functionTrieRoot = _processFunctionTrie(exclusiveFunctionTrie); | |
215 } | |
216 | |
217 // Data shared across calls to _read*TrieNode. | |
218 int _trieDataCursor; | |
219 List<int> _trieData; | |
220 | |
221 // The code trie is serialized as a list of integers. Each node | |
222 // is recreated by consuming some portion of the list. The format is as | |
223 // follows: | |
224 // [0] index into codeTable of code object. | |
225 // [1] tick count (number of times this stack frame occured). | |
226 // [2] child node count | |
227 // Reading the trie is done by recursively reading the tree depth-first | |
228 // pre-order. | |
229 CodeTrieNode _processCodeTrie(List<int> data) { | |
230 // Setup state shared across calls to _readTrieNode. | |
231 _trieDataCursor = 0; | |
232 _trieData = data; | |
233 if (_trieData == null) { | |
234 return null; | |
235 } | |
236 if (_trieData.length < 3) { | |
237 // Not enough integers for 1 node. | |
238 return null; | |
239 } | |
240 // Read the tree, returns the root node. | |
241 return _readCodeTrieNode(); | |
242 } | |
243 | |
244 CodeTrieNode _readCodeTrieNode() { | |
245 // Read index into code table. | |
246 var index = _trieData[_trieDataCursor++]; | |
247 // Lookup code object. | |
248 var code = codes[index]; | |
249 // Frame counter. | |
250 var count = _trieData[_trieDataCursor++]; | |
251 // Create node. | |
252 var node = new CodeTrieNode(code, count); | |
253 // Number of children. | |
254 var children = _trieData[_trieDataCursor++]; | |
255 // Recursively read child nodes. | |
256 for (var i = 0; i < children; i++) { | |
257 var child = _readCodeTrieNode(); | |
258 node.children.add(child); | |
259 } | |
260 return node; | |
261 } | |
262 | |
263 FunctionTrieNode _processFunctionTrie(List<int> data) { | |
264 // Setup state shared across calls to _readTrieNode. | |
265 _trieDataCursor = 0; | |
266 _trieData = data; | |
267 if (_trieData == null) { | |
268 return null; | |
269 } | |
270 if (_trieData.length < 3) { | |
271 // Not enough integers for 1 node. | |
272 return null; | |
273 } | |
274 // Read the tree, returns the root node. | |
275 return _readFunctionTrieNode(); | |
276 } | |
277 | |
278 FunctionTrieNode _readFunctionTrieNode() { | |
279 // Read index into function table. | |
280 var index = _trieData[_trieDataCursor++]; | |
281 // Lookup function object. | |
282 var function = functions[index]; | |
283 // Frame counter. | |
284 var count = _trieData[_trieDataCursor++]; | |
285 // Create node. | |
286 var node = new FunctionTrieNode(function, count); | |
287 // Number of code index / count pairs. | |
288 var codeCount = _trieData[_trieDataCursor++]; | |
289 var totalCodeTicks = 0; | |
290 for (var i = 0; i < codeCount; i++) { | |
291 var codeIndex = _trieData[_trieDataCursor++]; | |
292 var code = codes[codeIndex]; | |
293 var codeTicks = _trieData[_trieDataCursor++]; | |
294 totalCodeTicks += codeTicks; | |
295 var nodeCode = new FunctionTrieNodeCode(code, codeTicks); | |
296 node.codes.add(nodeCode); | |
297 } | |
298 node._totalCodeTicks = totalCodeTicks; | |
299 // Number of children. | |
300 var children = _trieData[_trieDataCursor++]; | |
301 // Recursively read child nodes. | |
302 for (var i = 0; i < children; i++) { | |
303 var child = _readFunctionTrieNode(); | |
304 node.children.add(child); | |
305 } | |
306 return node; | |
307 } | |
308 | |
309 double approximateSecondsForCount(count) { | |
310 var MICROSECONDS_PER_SECOND = 1000000.0; | |
311 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | |
312 } | |
313 } | |
OLD | NEW |