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

Side by Side Diff: Source/devtools/front_end/CPUProfileView.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
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * 12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 26
27 /** 27 /**
28 * @constructor 28 * @constructor
29 * @param {!ProfilerAgent.CPUProfile} profile
30 */
31 WebInspector.CPUProfileDataModel = function(profile)
32 {
33 this.profileHead = profile.head;
34 this.samples = profile.samples;
35 this._calculateTimes(profile);
36 this._assignParentsInProfile();
37 if (this.samples)
38 this._buildIdToNodeMap();
39 }
40
41 WebInspector.CPUProfileDataModel.prototype = {
42 /**
43 * @param {!ProfilerAgent.CPUProfile} profile
44 */
45 _calculateTimes: function(profile)
46 {
47 function totalHitCount(node) {
48 var result = node.hitCount;
49 for (var i = 0; i < node.children.length; i++)
50 result += totalHitCount(node.children[i]);
51 return result;
52 }
53 profile.totalHitCount = totalHitCount(profile.head);
54
55 var durationMs = 1000 * (profile.endTime - profile.startTime);
56 var samplingInterval = durationMs / profile.totalHitCount;
57 this.samplingIntervalMs = samplingInterval;
58
59 function calculateTimesForNode(node) {
60 node.selfTime = node.hitCount * samplingInterval;
61 var totalHitCount = node.hitCount;
62 for (var i = 0; i < node.children.length; i++)
63 totalHitCount += calculateTimesForNode(node.children[i]);
64 node.totalTime = totalHitCount * samplingInterval;
65 return totalHitCount;
66 }
67 calculateTimesForNode(profile.head);
68 },
69
70 _assignParentsInProfile: function()
71 {
72 var head = this.profileHead;
73 head.parent = null;
74 head.head = null;
75 var nodesToTraverse = [ head ];
76 while (nodesToTraverse.length) {
77 var parent = nodesToTraverse.pop();
78 var children = parent.children;
79 var length = children.length;
80 for (var i = 0; i < length; ++i) {
81 var child = children[i];
82 child.head = head;
83 child.parent = parent;
84 if (child.children.length)
85 nodesToTraverse.push(child);
86 }
87 }
88 },
89
90 _buildIdToNodeMap: function()
91 {
92 /** @type {!Object.<number, !ProfilerAgent.CPUProfileNode>} */
93 this._idToNode = {};
94 var idToNode = this._idToNode;
95 var stack = [this.profileHead];
96 while (stack.length) {
97 var node = stack.pop();
98 idToNode[node.id] = node;
99 for (var i = 0; i < node.children.length; i++)
100 stack.push(node.children[i]);
101 }
102
103 var topLevelNodes = this.profileHead.children;
104 for (var i = 0; i < topLevelNodes.length; i++) {
105 var node = topLevelNodes[i];
106 if (node.functionName === "(garbage collector)") {
107 this._gcNode = node;
108 break;
109 }
110 }
111 }
112 }
113
114
115 /**
116 * @constructor
117 * @extends {WebInspector.VBox} 29 * @extends {WebInspector.VBox}
118 * @param {!WebInspector.CPUProfileHeader} profileHeader 30 * @param {!WebInspector.CPUProfileHeader} profileHeader
119 */ 31 */
120 WebInspector.CPUProfileView = function(profileHeader) 32 WebInspector.CPUProfileView = function(profileHeader)
121 { 33 {
122 WebInspector.VBox.call(this); 34 WebInspector.VBox.call(this);
123 this.element.classList.add("cpu-profile-view"); 35 this.element.classList.add("cpu-profile-view");
124 36
125 this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebI nspector.CPUProfileView._TypeHeavy); 37 this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebI nspector.CPUProfileView._TypeHeavy);
126 38
(...skipping 862 matching lines...) Expand 10 before | Expand all | Expand 10 after
989 { 901 {
990 if (!WebInspector.CPUProfileView._colorGenerator) { 902 if (!WebInspector.CPUProfileView._colorGenerator) {
991 var colorGenerator = new WebInspector.CPUProfileFlameChart.ColorGenerato r(); 903 var colorGenerator = new WebInspector.CPUProfileFlameChart.ColorGenerato r();
992 colorGenerator.colorForID("(idle)::0", 50); 904 colorGenerator.colorForID("(idle)::0", 50);
993 colorGenerator.colorForID("(program)::0", 50); 905 colorGenerator.colorForID("(program)::0", 50);
994 colorGenerator.colorForID("(garbage collector)::0", 50); 906 colorGenerator.colorForID("(garbage collector)::0", 50);
995 WebInspector.CPUProfileView._colorGenerator = colorGenerator; 907 WebInspector.CPUProfileView._colorGenerator = colorGenerator;
996 } 908 }
997 return WebInspector.CPUProfileView._colorGenerator; 909 return WebInspector.CPUProfileView._colorGenerator;
998 } 910 }
999
1000 /**
1001 * @constructor
1002 * @implements {WebInspector.FlameChartDataProvider}
1003 * @param {!WebInspector.CPUProfileDataModel} cpuProfile
1004 * @param {!WebInspector.Target} target
1005 */
1006 WebInspector.CPUFlameChartDataProvider = function(cpuProfile, target)
1007 {
1008 WebInspector.FlameChartDataProvider.call(this);
1009 this._cpuProfile = cpuProfile;
1010 this._target = target;
1011 this._colorGenerator = WebInspector.CPUProfileView.colorGenerator();
1012 }
1013
1014 WebInspector.CPUFlameChartDataProvider.prototype = {
1015 /**
1016 * @return {number}
1017 */
1018 barHeight: function()
1019 {
1020 return 15;
1021 },
1022
1023 /**
1024 * @return {number}
1025 */
1026 textBaseline: function()
1027 {
1028 return 4;
1029 },
1030
1031 /**
1032 * @return {number}
1033 */
1034 textPadding: function()
1035 {
1036 return 2;
1037 },
1038
1039 /**
1040 * @param {number} startTime
1041 * @param {number} endTime
1042 * @return {?Array.<number>}
1043 */
1044 dividerOffsets: function(startTime, endTime)
1045 {
1046 return null;
1047 },
1048
1049 /**
1050 * @return {number}
1051 */
1052 zeroTime: function()
1053 {
1054 return 0;
1055 },
1056
1057 /**
1058 * @return {number}
1059 */
1060 totalTime: function()
1061 {
1062 return this._cpuProfile.profileHead.totalTime;
1063 },
1064
1065 /**
1066 * @return {number}
1067 */
1068 maxStackDepth: function()
1069 {
1070 return this._maxStackDepth;
1071 },
1072
1073 /**
1074 * @return {?WebInspector.FlameChart.TimelineData}
1075 */
1076 timelineData: function()
1077 {
1078 return this._timelineData || this._calculateTimelineData();
1079 },
1080
1081 /**
1082 * @return {?WebInspector.FlameChart.TimelineData}
1083 */
1084 _calculateTimelineData: function()
1085 {
1086 if (!this._cpuProfile.profileHead)
1087 return null;
1088
1089 var samples = this._cpuProfile.samples;
1090 var idToNode = this._cpuProfile._idToNode;
1091 var gcNode = this._cpuProfile._gcNode;
1092 var samplesCount = samples.length;
1093 var samplingInterval = this._cpuProfile.samplingIntervalMs;
1094
1095 var index = 0;
1096
1097 var openIntervals = [];
1098 var stackTrace = [];
1099 var maxDepth = 5; // minimum stack depth for the case when we see no act ivity.
1100 var depth = 0;
1101
1102 /**
1103 * @constructor
1104 * @param {number} depth
1105 * @param {number} duration
1106 * @param {number} startTime
1107 * @param {!Object} node
1108 */
1109 function ChartEntry(depth, duration, startTime, node)
1110 {
1111 this.depth = depth;
1112 this.duration = duration;
1113 this.startTime = startTime;
1114 this.node = node;
1115 this.selfTime = 0;
1116 }
1117 var entries = /** @type {!Array.<!ChartEntry>} */ ([]);
1118
1119 for (var sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++) {
1120 var node = idToNode[samples[sampleIndex]];
1121 stackTrace.length = 0;
1122 while (node) {
1123 stackTrace.push(node);
1124 node = node.parent;
1125 }
1126 stackTrace.pop(); // Remove (root) node
1127
1128 maxDepth = Math.max(maxDepth, depth);
1129 depth = 0;
1130 node = stackTrace.pop();
1131 var intervalIndex;
1132
1133 // GC samples have no stack, so we just put GC node on top of the la st recoreded sample.
1134 if (node === gcNode) {
1135 while (depth < openIntervals.length) {
1136 intervalIndex = openIntervals[depth].index;
1137 entries[intervalIndex].duration += samplingInterval;
1138 ++depth;
1139 }
1140 // If previous stack is also GC then just continue.
1141 if (openIntervals.length > 0 && openIntervals.peekLast().node == = node) {
1142 entries[intervalIndex].selfTime += samplingInterval;
1143 continue;
1144 }
1145 }
1146
1147 while (node && depth < openIntervals.length && node === openInterval s[depth].node) {
1148 intervalIndex = openIntervals[depth].index;
1149 entries[intervalIndex].duration += samplingInterval;
1150 node = stackTrace.pop();
1151 ++depth;
1152 }
1153 if (depth < openIntervals.length)
1154 openIntervals.length = depth;
1155 if (!node) {
1156 entries[intervalIndex].selfTime += samplingInterval;
1157 continue;
1158 }
1159
1160 var colorGenerator = this._colorGenerator;
1161 var color = "";
1162 while (node) {
1163 entries.push(new ChartEntry(depth, samplingInterval, sampleIndex * samplingInterval, node));
1164 openIntervals.push({node: node, index: index});
1165 ++index;
1166
1167 node = stackTrace.pop();
1168 ++depth;
1169 }
1170 entries[entries.length - 1].selfTime += samplingInterval;
1171 }
1172
1173 /** @type {!Array.<!ProfilerAgent.CPUProfileNode>} */
1174 var entryNodes = new Array(entries.length);
1175 var entryLevels = new Uint8Array(entries.length);
1176 var entryTotalTimes = new Float32Array(entries.length);
1177 var entrySelfTimes = new Float32Array(entries.length);
1178 var entryOffsets = new Float32Array(entries.length);
1179
1180 for (var i = 0; i < entries.length; ++i) {
1181 var entry = entries[i];
1182 entryNodes[i] = entry.node;
1183 entryLevels[i] = entry.depth;
1184 entryTotalTimes[i] = entry.duration;
1185 entryOffsets[i] = entry.startTime;
1186 entrySelfTimes[i] = entry.selfTime;
1187 }
1188
1189 this._maxStackDepth = Math.max(maxDepth, depth);
1190
1191 this._timelineData = {
1192 entryLevels: entryLevels,
1193 entryTotalTimes: entryTotalTimes,
1194 entryOffsets: entryOffsets,
1195 };
1196
1197 /** @type {!Array.<!ProfilerAgent.CPUProfileNode>} */
1198 this._entryNodes = entryNodes;
1199 this._entrySelfTimes = entrySelfTimes;
1200
1201 return /** @type {!WebInspector.FlameChart.TimelineData} */ (this._timel ineData);
1202 },
1203
1204 /**
1205 * @param {number} ms
1206 * @return {string}
1207 */
1208 _millisecondsToString: function(ms)
1209 {
1210 if (ms === 0)
1211 return "0";
1212 if (ms < 1000)
1213 return WebInspector.UIString("%.1f\u2009ms", ms);
1214 return Number.secondsToString(ms / 1000, true);
1215 },
1216
1217 /**
1218 * @param {number} entryIndex
1219 * @return {?Array.<!{title: string, text: string}>}
1220 */
1221 prepareHighlightedEntryInfo: function(entryIndex)
1222 {
1223 var timelineData = this._timelineData;
1224 var node = this._entryNodes[entryIndex];
1225 if (!node)
1226 return null;
1227
1228 var entryInfo = [];
1229 function pushEntryInfoRow(title, text)
1230 {
1231 var row = {};
1232 row.title = title;
1233 row.text = text;
1234 entryInfo.push(row);
1235 }
1236
1237 pushEntryInfoRow(WebInspector.UIString("Name"), node.functionName);
1238 var selfTime = this._millisecondsToString(this._entrySelfTimes[entryInde x]);
1239 var totalTime = this._millisecondsToString(timelineData.entryTotalTimes[ entryIndex]);
1240 pushEntryInfoRow(WebInspector.UIString("Self time"), selfTime);
1241 pushEntryInfoRow(WebInspector.UIString("Total time"), totalTime);
1242 var target = this._target;
1243 var text = WebInspector.Linkifier.liveLocationText(target, node.scriptId , node.lineNumber, node.columnNumber);
1244 pushEntryInfoRow(WebInspector.UIString("URL"), text);
1245 pushEntryInfoRow(WebInspector.UIString("Aggregated self time"), Number.s econdsToString(node.selfTime / 1000, true));
1246 pushEntryInfoRow(WebInspector.UIString("Aggregated total time"), Number. secondsToString(node.totalTime / 1000, true));
1247 if (node.deoptReason && node.deoptReason !== "no reason")
1248 pushEntryInfoRow(WebInspector.UIString("Not optimized"), node.deoptR eason);
1249
1250 return entryInfo;
1251 },
1252
1253 /**
1254 * @param {number} entryIndex
1255 * @return {boolean}
1256 */
1257 canJumpToEntry: function(entryIndex)
1258 {
1259 return this._entryNodes[entryIndex].scriptId !== "0";
1260 },
1261
1262 /**
1263 * @param {number} entryIndex
1264 * @return {?string}
1265 */
1266 entryTitle: function(entryIndex)
1267 {
1268 var node = this._entryNodes[entryIndex];
1269 return node.functionName;
1270 },
1271
1272 /**
1273 * @param {number} entryIndex
1274 * @return {?string}
1275 */
1276 entryFont: function(entryIndex)
1277 {
1278 if (!this._font) {
1279 this._font = (this.barHeight() - 4) + "px " + WebInspector.fontFamil y();
1280 this._boldFont = "bold " + this._font;
1281 }
1282 var node = this._entryNodes[entryIndex];
1283 var reason = node.deoptReason;
1284 return (reason && reason !== "no reason") ? this._boldFont : this._font;
1285 },
1286
1287 /**
1288 * @param {number} entryIndex
1289 * @return {!string}
1290 */
1291 entryColor: function(entryIndex)
1292 {
1293 var node = this._entryNodes[entryIndex];
1294 return this._colorGenerator.colorForID(node.functionName + ":" + node.ur l + ":" + node.lineNumber);
1295 },
1296
1297 /**
1298 * @param {number} entryIndex
1299 * @param {!CanvasRenderingContext2D} context
1300 * @param {?string} text
1301 * @param {number} barX
1302 * @param {number} barY
1303 * @param {number} barWidth
1304 * @param {number} barHeight
1305 * @param {function(number):number} offsetToPosition
1306 * @return {boolean}
1307 */
1308 decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, bar Height, offsetToPosition)
1309 {
1310 return false;
1311 },
1312
1313 /**
1314 * @param {number} entryIndex
1315 * @return {boolean}
1316 */
1317 forceDecoration: function(entryIndex)
1318 {
1319 return false;
1320 },
1321
1322 /**
1323 * @param {number} entryIndex
1324 * @return {!{startTimeOffset: number, endTimeOffset: number}}
1325 */
1326 highlightTimeRange: function(entryIndex)
1327 {
1328 var startTimeOffset = this._timelineData.entryOffsets[entryIndex];
1329 return {
1330 startTimeOffset: startTimeOffset,
1331 endTimeOffset: startTimeOffset + this._timelineData.entryTotalTimes[ entryIndex]
1332 };
1333 },
1334
1335 /**
1336 * @return {number}
1337 */
1338 paddingLeft: function()
1339 {
1340 return 15;
1341 },
1342
1343 /**
1344 * @param {number} entryIndex
1345 * @return {!string}
1346 */
1347 textColor: function(entryIndex)
1348 {
1349 return "#333";
1350 }
1351 }
OLDNEW
« no previous file with comments | « Source/devtools/front_end/CPUProfileModel.js ('k') | Source/devtools/front_end/ProfilesPanel.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698