Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(857)

Side by Side Diff: Source/devtools/front_end/CPUProfileModel.js

Issue 236893002: DevTools: Move CPUProfileDataModel and CPUFlameChartDataProvider into a separate file (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/devtools/devtools.gypi ('k') | Source/devtools/front_end/CPUProfileView.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5
6 /**
7 * @constructor
8 * @param {!ProfilerAgent.CPUProfile} profile
9 */
10 WebInspector.CPUProfileDataModel = function(profile)
11 {
12 this.profileHead = profile.head;
13 this.samples = profile.samples;
14 this._calculateTimes(profile);
15 this._assignParentsInProfile();
16 if (this.samples)
17 this._buildIdToNodeMap();
18 }
19
20 WebInspector.CPUProfileDataModel.prototype = {
21 /**
22 * @param {!ProfilerAgent.CPUProfile} profile
23 */
24 _calculateTimes: function(profile)
25 {
26 function totalHitCount(node) {
27 var result = node.hitCount;
28 for (var i = 0; i < node.children.length; i++)
29 result += totalHitCount(node.children[i]);
30 return result;
31 }
32 profile.totalHitCount = totalHitCount(profile.head);
33
34 var durationMs = 1000 * (profile.endTime - profile.startTime);
35 var samplingInterval = durationMs / profile.totalHitCount;
36 this.samplingIntervalMs = samplingInterval;
37
38 function calculateTimesForNode(node) {
39 node.selfTime = node.hitCount * samplingInterval;
40 var totalHitCount = node.hitCount;
41 for (var i = 0; i < node.children.length; i++)
42 totalHitCount += calculateTimesForNode(node.children[i]);
43 node.totalTime = totalHitCount * samplingInterval;
44 return totalHitCount;
45 }
46 calculateTimesForNode(profile.head);
47 },
48
49 _assignParentsInProfile: function()
50 {
51 var head = this.profileHead;
52 head.parent = null;
53 head.head = null;
54 var nodesToTraverse = [ head ];
55 while (nodesToTraverse.length) {
56 var parent = nodesToTraverse.pop();
57 var children = parent.children;
58 var length = children.length;
59 for (var i = 0; i < length; ++i) {
60 var child = children[i];
61 child.head = head;
62 child.parent = parent;
63 if (child.children.length)
64 nodesToTraverse.push(child);
65 }
66 }
67 },
68
69 _buildIdToNodeMap: function()
70 {
71 /** @type {!Object.<number, !ProfilerAgent.CPUProfileNode>} */
72 this._idToNode = {};
73 var idToNode = this._idToNode;
74 var stack = [this.profileHead];
75 while (stack.length) {
76 var node = stack.pop();
77 idToNode[node.id] = node;
78 for (var i = 0; i < node.children.length; i++)
79 stack.push(node.children[i]);
80 }
81
82 var topLevelNodes = this.profileHead.children;
83 for (var i = 0; i < topLevelNodes.length; i++) {
84 var node = topLevelNodes[i];
85 if (node.functionName === "(garbage collector)") {
86 this._gcNode = node;
87 break;
88 }
89 }
90 }
91 }
92
93
94 /**
95 * @constructor
96 * @implements {WebInspector.FlameChartDataProvider}
97 * @param {!WebInspector.CPUProfileDataModel} cpuProfile
98 * @param {!WebInspector.Target} target
99 */
100 WebInspector.CPUFlameChartDataProvider = function(cpuProfile, target)
101 {
102 WebInspector.FlameChartDataProvider.call(this);
103 this._cpuProfile = cpuProfile;
104 this._target = target;
105 this._colorGenerator = WebInspector.CPUProfileView.colorGenerator();
106 }
107
108 WebInspector.CPUFlameChartDataProvider.prototype = {
109 /**
110 * @return {number}
111 */
112 barHeight: function()
113 {
114 return 15;
115 },
116
117 /**
118 * @return {number}
119 */
120 textBaseline: function()
121 {
122 return 4;
123 },
124
125 /**
126 * @return {number}
127 */
128 textPadding: function()
129 {
130 return 2;
131 },
132
133 /**
134 * @param {number} startTime
135 * @param {number} endTime
136 * @return {?Array.<number>}
137 */
138 dividerOffsets: function(startTime, endTime)
139 {
140 return null;
141 },
142
143 /**
144 * @return {number}
145 */
146 zeroTime: function()
147 {
148 return 0;
149 },
150
151 /**
152 * @return {number}
153 */
154 totalTime: function()
155 {
156 return this._cpuProfile.profileHead.totalTime;
157 },
158
159 /**
160 * @return {number}
161 */
162 maxStackDepth: function()
163 {
164 return this._maxStackDepth;
165 },
166
167 /**
168 * @return {?WebInspector.FlameChart.TimelineData}
169 */
170 timelineData: function()
171 {
172 return this._timelineData || this._calculateTimelineData();
173 },
174
175 /**
176 * @return {?WebInspector.FlameChart.TimelineData}
177 */
178 _calculateTimelineData: function()
179 {
180 if (!this._cpuProfile.profileHead)
181 return null;
182
183 var samples = this._cpuProfile.samples;
184 var idToNode = this._cpuProfile._idToNode;
185 var gcNode = this._cpuProfile._gcNode;
186 var samplesCount = samples.length;
187 var samplingInterval = this._cpuProfile.samplingIntervalMs;
188
189 var index = 0;
190
191 var openIntervals = [];
192 var stackTrace = [];
193 var maxDepth = 5; // minimum stack depth for the case when we see no act ivity.
194 var depth = 0;
195
196 /**
197 * @constructor
198 * @param {number} depth
199 * @param {number} duration
200 * @param {number} startTime
201 * @param {!Object} node
202 */
203 function ChartEntry(depth, duration, startTime, node)
204 {
205 this.depth = depth;
206 this.duration = duration;
207 this.startTime = startTime;
208 this.node = node;
209 this.selfTime = 0;
210 }
211 var entries = /** @type {!Array.<!ChartEntry>} */ ([]);
212
213 for (var sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++) {
214 var node = idToNode[samples[sampleIndex]];
215 stackTrace.length = 0;
216 while (node) {
217 stackTrace.push(node);
218 node = node.parent;
219 }
220 stackTrace.pop(); // Remove (root) node
221
222 maxDepth = Math.max(maxDepth, depth);
223 depth = 0;
224 node = stackTrace.pop();
225 var intervalIndex;
226
227 // GC samples have no stack, so we just put GC node on top of the la st recoreded sample.
228 if (node === gcNode) {
229 while (depth < openIntervals.length) {
230 intervalIndex = openIntervals[depth].index;
231 entries[intervalIndex].duration += samplingInterval;
232 ++depth;
233 }
234 // If previous stack is also GC then just continue.
235 if (openIntervals.length > 0 && openIntervals.peekLast().node == = node) {
236 entries[intervalIndex].selfTime += samplingInterval;
237 continue;
238 }
239 }
240
241 while (node && depth < openIntervals.length && node === openInterval s[depth].node) {
242 intervalIndex = openIntervals[depth].index;
243 entries[intervalIndex].duration += samplingInterval;
244 node = stackTrace.pop();
245 ++depth;
246 }
247 if (depth < openIntervals.length)
248 openIntervals.length = depth;
249 if (!node) {
250 entries[intervalIndex].selfTime += samplingInterval;
251 continue;
252 }
253
254 var colorGenerator = this._colorGenerator;
255 var color = "";
256 while (node) {
257 entries.push(new ChartEntry(depth, samplingInterval, sampleIndex * samplingInterval, node));
258 openIntervals.push({node: node, index: index});
259 ++index;
260
261 node = stackTrace.pop();
262 ++depth;
263 }
264 entries[entries.length - 1].selfTime += samplingInterval;
265 }
266
267 /** @type {!Array.<!ProfilerAgent.CPUProfileNode>} */
268 var entryNodes = new Array(entries.length);
269 var entryLevels = new Uint8Array(entries.length);
270 var entryTotalTimes = new Float32Array(entries.length);
271 var entrySelfTimes = new Float32Array(entries.length);
272 var entryOffsets = new Float32Array(entries.length);
273
274 for (var i = 0; i < entries.length; ++i) {
275 var entry = entries[i];
276 entryNodes[i] = entry.node;
277 entryLevels[i] = entry.depth;
278 entryTotalTimes[i] = entry.duration;
279 entryOffsets[i] = entry.startTime;
280 entrySelfTimes[i] = entry.selfTime;
281 }
282
283 this._maxStackDepth = Math.max(maxDepth, depth);
284
285 this._timelineData = {
286 entryLevels: entryLevels,
287 entryTotalTimes: entryTotalTimes,
288 entryOffsets: entryOffsets,
289 };
290
291 /** @type {!Array.<!ProfilerAgent.CPUProfileNode>} */
292 this._entryNodes = entryNodes;
293 this._entrySelfTimes = entrySelfTimes;
294
295 return /** @type {!WebInspector.FlameChart.TimelineData} */ (this._timel ineData);
296 },
297
298 /**
299 * @param {number} ms
300 * @return {string}
301 */
302 _millisecondsToString: function(ms)
303 {
304 if (ms === 0)
305 return "0";
306 if (ms < 1000)
307 return WebInspector.UIString("%.1f\u2009ms", ms);
308 return Number.secondsToString(ms / 1000, true);
309 },
310
311 /**
312 * @param {number} entryIndex
313 * @return {?Array.<!{title: string, text: string}>}
314 */
315 prepareHighlightedEntryInfo: function(entryIndex)
316 {
317 var timelineData = this._timelineData;
318 var node = this._entryNodes[entryIndex];
319 if (!node)
320 return null;
321
322 var entryInfo = [];
323 function pushEntryInfoRow(title, text)
324 {
325 var row = {};
326 row.title = title;
327 row.text = text;
328 entryInfo.push(row);
329 }
330
331 pushEntryInfoRow(WebInspector.UIString("Name"), node.functionName);
332 var selfTime = this._millisecondsToString(this._entrySelfTimes[entryInde x]);
333 var totalTime = this._millisecondsToString(timelineData.entryTotalTimes[ entryIndex]);
334 pushEntryInfoRow(WebInspector.UIString("Self time"), selfTime);
335 pushEntryInfoRow(WebInspector.UIString("Total time"), totalTime);
336 var target = this._target;
337 var text = WebInspector.Linkifier.liveLocationText(target, node.scriptId , node.lineNumber, node.columnNumber);
338 pushEntryInfoRow(WebInspector.UIString("URL"), text);
339 pushEntryInfoRow(WebInspector.UIString("Aggregated self time"), Number.s econdsToString(node.selfTime / 1000, true));
340 pushEntryInfoRow(WebInspector.UIString("Aggregated total time"), Number. secondsToString(node.totalTime / 1000, true));
341 if (node.deoptReason && node.deoptReason !== "no reason")
342 pushEntryInfoRow(WebInspector.UIString("Not optimized"), node.deoptR eason);
343
344 return entryInfo;
345 },
346
347 /**
348 * @param {number} entryIndex
349 * @return {boolean}
350 */
351 canJumpToEntry: function(entryIndex)
352 {
353 return this._entryNodes[entryIndex].scriptId !== "0";
354 },
355
356 /**
357 * @param {number} entryIndex
358 * @return {?string}
359 */
360 entryTitle: function(entryIndex)
361 {
362 var node = this._entryNodes[entryIndex];
363 return node.functionName;
364 },
365
366 /**
367 * @param {number} entryIndex
368 * @return {?string}
369 */
370 entryFont: function(entryIndex)
371 {
372 if (!this._font) {
373 this._font = (this.barHeight() - 4) + "px " + WebInspector.fontFamil y();
374 this._boldFont = "bold " + this._font;
375 }
376 var node = this._entryNodes[entryIndex];
377 var reason = node.deoptReason;
378 return (reason && reason !== "no reason") ? this._boldFont : this._font;
379 },
380
381 /**
382 * @param {number} entryIndex
383 * @return {!string}
384 */
385 entryColor: function(entryIndex)
386 {
387 var node = this._entryNodes[entryIndex];
388 return this._colorGenerator.colorForID(node.functionName + ":" + node.ur l + ":" + node.lineNumber);
389 },
390
391 /**
392 * @param {number} entryIndex
393 * @param {!CanvasRenderingContext2D} context
394 * @param {?string} text
395 * @param {number} barX
396 * @param {number} barY
397 * @param {number} barWidth
398 * @param {number} barHeight
399 * @param {function(number):number} offsetToPosition
400 * @return {boolean}
401 */
402 decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, bar Height, offsetToPosition)
403 {
404 return false;
405 },
406
407 /**
408 * @param {number} entryIndex
409 * @return {boolean}
410 */
411 forceDecoration: function(entryIndex)
412 {
413 return false;
414 },
415
416 /**
417 * @param {number} entryIndex
418 * @return {!{startTimeOffset: number, endTimeOffset: number}}
419 */
420 highlightTimeRange: function(entryIndex)
421 {
422 var startTimeOffset = this._timelineData.entryOffsets[entryIndex];
423 return {
424 startTimeOffset: startTimeOffset,
425 endTimeOffset: startTimeOffset + this._timelineData.entryTotalTimes[ entryIndex]
426 };
427 },
428
429 /**
430 * @return {number}
431 */
432 paddingLeft: function()
433 {
434 return 15;
435 },
436
437 /**
438 * @param {number} entryIndex
439 * @return {!string}
440 */
441 textColor: function(entryIndex)
442 {
443 return "#333";
444 }
445 }
OLDNEW
« no previous file with comments | « Source/devtools/devtools.gypi ('k') | Source/devtools/front_end/CPUProfileView.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698