OLD | NEW |
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 /** | |
27 * @constructor | |
28 * @implements {WebInspector.Searchable} | 26 * @implements {WebInspector.Searchable} |
29 * @extends {WebInspector.ProfileView} | 27 * @unrestricted |
30 * @param {!WebInspector.CPUProfileHeader} profileHeader | 28 */ |
31 */ | 29 WebInspector.CPUProfileView = class extends WebInspector.ProfileView { |
32 WebInspector.CPUProfileView = function(profileHeader) | 30 /** |
33 { | 31 * @param {!WebInspector.CPUProfileHeader} profileHeader |
34 WebInspector.ProfileView.call(this); | 32 */ |
| 33 constructor(profileHeader) { |
| 34 super(); |
35 this._profileHeader = profileHeader; | 35 this._profileHeader = profileHeader; |
36 this.profile = new WebInspector.CPUProfileDataModel(profileHeader._profile |
| profileHeader.protocolProfile()); | 36 this.profile = new WebInspector.CPUProfileDataModel(profileHeader._profile |
| profileHeader.protocolProfile()); |
37 this.adjustedTotal = this.profile.profileHead.total; | 37 this.adjustedTotal = this.profile.profileHead.total; |
38 this.adjustedTotal -= this.profile.idleNode ? this.profile.idleNode.total :
0; | 38 this.adjustedTotal -= this.profile.idleNode ? this.profile.idleNode.total :
0; |
39 this.initialize(new WebInspector.CPUProfileView.NodeFormatter(this)); | 39 this.initialize(new WebInspector.CPUProfileView.NodeFormatter(this)); |
40 }; | 40 } |
41 | 41 |
42 WebInspector.CPUProfileView.prototype = { | 42 /** |
| 43 * @override |
| 44 */ |
| 45 wasShown() { |
| 46 super.wasShown(); |
| 47 var lineLevelProfile = WebInspector.LineLevelProfile.instance(); |
| 48 lineLevelProfile.reset(); |
| 49 lineLevelProfile.appendCPUProfile(this.profile); |
| 50 } |
| 51 |
| 52 /** |
| 53 * @override |
| 54 * @param {string} columnId |
| 55 * @return {string} |
| 56 */ |
| 57 columnHeader(columnId) { |
| 58 switch (columnId) { |
| 59 case 'self': |
| 60 return WebInspector.UIString('Self Time'); |
| 61 case 'total': |
| 62 return WebInspector.UIString('Total Time'); |
| 63 } |
| 64 return ''; |
| 65 } |
| 66 |
| 67 /** |
| 68 * @override |
| 69 * @return {!WebInspector.FlameChartDataProvider} |
| 70 */ |
| 71 createFlameChartDataProvider() { |
| 72 return new WebInspector.CPUFlameChartDataProvider(this.profile, this._profil
eHeader.target()); |
| 73 } |
| 74 }; |
| 75 |
| 76 /** |
| 77 * @unrestricted |
| 78 */ |
| 79 WebInspector.CPUProfileType = class extends WebInspector.ProfileType { |
| 80 constructor() { |
| 81 super(WebInspector.CPUProfileType.TypeId, WebInspector.UIString('Record Java
Script CPU Profile')); |
| 82 this._recording = false; |
| 83 |
| 84 this._nextAnonymousConsoleProfileNumber = 1; |
| 85 this._anonymousConsoleProfileIdToTitle = {}; |
| 86 |
| 87 WebInspector.CPUProfileType.instance = this; |
| 88 WebInspector.targetManager.addModelListener( |
| 89 WebInspector.CPUProfilerModel, WebInspector.CPUProfilerModel.Events.Cons
oleProfileStarted, |
| 90 this._consoleProfileStarted, this); |
| 91 WebInspector.targetManager.addModelListener( |
| 92 WebInspector.CPUProfilerModel, WebInspector.CPUProfilerModel.Events.Cons
oleProfileFinished, |
| 93 this._consoleProfileFinished, this); |
| 94 } |
| 95 |
| 96 /** |
| 97 * @override |
| 98 * @return {string} |
| 99 */ |
| 100 typeName() { |
| 101 return 'CPU'; |
| 102 } |
| 103 |
| 104 /** |
| 105 * @override |
| 106 * @return {string} |
| 107 */ |
| 108 fileExtension() { |
| 109 return '.cpuprofile'; |
| 110 } |
| 111 |
| 112 get buttonTooltip() { |
| 113 return this._recording ? WebInspector.UIString('Stop CPU profiling') : WebIn
spector.UIString('Start CPU profiling'); |
| 114 } |
| 115 |
| 116 /** |
| 117 * @override |
| 118 * @return {boolean} |
| 119 */ |
| 120 buttonClicked() { |
| 121 if (this._recording) { |
| 122 this.stopRecordingProfile(); |
| 123 return false; |
| 124 } else { |
| 125 this.startRecordingProfile(); |
| 126 return true; |
| 127 } |
| 128 } |
| 129 |
| 130 get treeItemTitle() { |
| 131 return WebInspector.UIString('CPU PROFILES'); |
| 132 } |
| 133 |
| 134 get description() { |
| 135 return WebInspector.UIString( |
| 136 'CPU profiles show where the execution time is spent in your page\'s Jav
aScript functions.'); |
| 137 } |
| 138 |
| 139 /** |
| 140 * @param {!WebInspector.Event} event |
| 141 */ |
| 142 _consoleProfileStarted(event) { |
| 143 var data = /** @type {!WebInspector.CPUProfilerModel.EventData} */ (event.da
ta); |
| 144 var resolvedTitle = data.title; |
| 145 if (!resolvedTitle) { |
| 146 resolvedTitle = WebInspector.UIString('Profile %s', this._nextAnonymousCon
soleProfileNumber++); |
| 147 this._anonymousConsoleProfileIdToTitle[data.id] = resolvedTitle; |
| 148 } |
| 149 this._addMessageToConsole( |
| 150 WebInspector.ConsoleMessage.MessageType.Profile, data.scriptLocation, |
| 151 WebInspector.UIString('Profile \'%s\' started.', resolvedTitle)); |
| 152 } |
| 153 |
| 154 /** |
| 155 * @param {!WebInspector.Event} event |
| 156 */ |
| 157 _consoleProfileFinished(event) { |
| 158 var data = /** @type {!WebInspector.CPUProfilerModel.EventData} */ (event.da
ta); |
| 159 var cpuProfile = /** @type {!ProfilerAgent.Profile} */ (data.cpuProfile); |
| 160 var resolvedTitle = data.title; |
| 161 if (typeof resolvedTitle === 'undefined') { |
| 162 resolvedTitle = this._anonymousConsoleProfileIdToTitle[data.id]; |
| 163 delete this._anonymousConsoleProfileIdToTitle[data.id]; |
| 164 } |
| 165 var profile = new WebInspector.CPUProfileHeader(data.scriptLocation.target()
, this, resolvedTitle); |
| 166 profile.setProtocolProfile(cpuProfile); |
| 167 this.addProfile(profile); |
| 168 this._addMessageToConsole( |
| 169 WebInspector.ConsoleMessage.MessageType.ProfileEnd, data.scriptLocation, |
| 170 WebInspector.UIString('Profile \'%s\' finished.', resolvedTitle)); |
| 171 } |
| 172 |
| 173 /** |
| 174 * @param {string} type |
| 175 * @param {!WebInspector.DebuggerModel.Location} scriptLocation |
| 176 * @param {string} messageText |
| 177 */ |
| 178 _addMessageToConsole(type, scriptLocation, messageText) { |
| 179 var script = scriptLocation.script(); |
| 180 var target = scriptLocation.target(); |
| 181 var message = new WebInspector.ConsoleMessage( |
| 182 target, WebInspector.ConsoleMessage.MessageSource.ConsoleAPI, WebInspect
or.ConsoleMessage.MessageLevel.Debug, |
| 183 messageText, type, undefined, undefined, undefined, undefined, [{ |
| 184 functionName: '', |
| 185 scriptId: scriptLocation.scriptId, |
| 186 url: script ? script.contentURL() : '', |
| 187 lineNumber: scriptLocation.lineNumber, |
| 188 columnNumber: scriptLocation.columnNumber || 0 |
| 189 }]); |
| 190 |
| 191 target.consoleModel.addMessage(message); |
| 192 } |
| 193 |
| 194 startRecordingProfile() { |
| 195 var target = WebInspector.context.flavor(WebInspector.Target); |
| 196 if (this._profileBeingRecorded || !target) |
| 197 return; |
| 198 var profile = new WebInspector.CPUProfileHeader(target, this); |
| 199 this.setProfileBeingRecorded(profile); |
| 200 WebInspector.targetManager.suspendAllTargets(); |
| 201 this.addProfile(profile); |
| 202 profile.updateStatus(WebInspector.UIString('Recording\u2026')); |
| 203 this._recording = true; |
| 204 target.cpuProfilerModel.startRecording(); |
| 205 } |
| 206 |
| 207 stopRecordingProfile() { |
| 208 this._recording = false; |
| 209 if (!this._profileBeingRecorded || !this._profileBeingRecorded.target()) |
| 210 return; |
| 211 |
| 212 var recordedProfile; |
| 213 |
43 /** | 214 /** |
44 * @override | 215 * @param {?ProfilerAgent.Profile} profile |
| 216 * @this {WebInspector.CPUProfileType} |
45 */ | 217 */ |
46 wasShown: function() | 218 function didStopProfiling(profile) { |
47 { | 219 if (!this._profileBeingRecorded) |
48 WebInspector.ProfileView.prototype.wasShown.call(this); | 220 return; |
49 var lineLevelProfile = WebInspector.LineLevelProfile.instance(); | 221 console.assert(profile); |
50 lineLevelProfile.reset(); | 222 this._profileBeingRecorded.setProtocolProfile(profile); |
51 lineLevelProfile.appendCPUProfile(this.profile); | 223 this._profileBeingRecorded.updateStatus(''); |
52 }, | 224 recordedProfile = this._profileBeingRecorded; |
| 225 this.setProfileBeingRecorded(null); |
| 226 } |
53 | 227 |
54 /** | 228 /** |
55 * @override | 229 * @this {WebInspector.CPUProfileType} |
56 * @param {string} columnId | 230 */ |
| 231 function fireEvent() { |
| 232 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileCompl
ete, recordedProfile); |
| 233 } |
| 234 |
| 235 this._profileBeingRecorded.target() |
| 236 .cpuProfilerModel.stopRecording() |
| 237 .then(didStopProfiling.bind(this)) |
| 238 .then(WebInspector.targetManager.resumeAllTargets.bind(WebInspector.targ
etManager)) |
| 239 .then(fireEvent.bind(this)); |
| 240 } |
| 241 |
| 242 /** |
| 243 * @override |
| 244 * @param {string} title |
| 245 * @return {!WebInspector.ProfileHeader} |
| 246 */ |
| 247 createProfileLoadedFromFile(title) { |
| 248 return new WebInspector.CPUProfileHeader(null, this, title); |
| 249 } |
| 250 |
| 251 /** |
| 252 * @override |
| 253 */ |
| 254 profileBeingRecordedRemoved() { |
| 255 this.stopRecordingProfile(); |
| 256 } |
| 257 }; |
| 258 |
| 259 WebInspector.CPUProfileType.TypeId = 'CPU'; |
| 260 |
| 261 /** |
| 262 * @unrestricted |
| 263 */ |
| 264 WebInspector.CPUProfileHeader = class extends WebInspector.WritableProfileHeader
{ |
| 265 /** |
| 266 * @param {?WebInspector.Target} target |
| 267 * @param {!WebInspector.CPUProfileType} type |
| 268 * @param {string=} title |
| 269 */ |
| 270 constructor(target, type, title) { |
| 271 super(target, type, title); |
| 272 } |
| 273 |
| 274 /** |
| 275 * @override |
| 276 * @return {!WebInspector.ProfileView} |
| 277 */ |
| 278 createView() { |
| 279 return new WebInspector.CPUProfileView(this); |
| 280 } |
| 281 |
| 282 /** |
| 283 * @return {!ProfilerAgent.Profile} |
| 284 */ |
| 285 protocolProfile() { |
| 286 return this._protocolProfile; |
| 287 } |
| 288 }; |
| 289 |
| 290 /** |
| 291 * @implements {WebInspector.ProfileDataGridNode.Formatter} |
| 292 * @unrestricted |
| 293 */ |
| 294 WebInspector.CPUProfileView.NodeFormatter = class { |
| 295 constructor(profileView) { |
| 296 this._profileView = profileView; |
| 297 } |
| 298 |
| 299 /** |
| 300 * @override |
| 301 * @param {number} value |
| 302 * @return {string} |
| 303 */ |
| 304 formatValue(value) { |
| 305 return WebInspector.UIString('%.1f\u2009ms', value); |
| 306 } |
| 307 |
| 308 /** |
| 309 * @override |
| 310 * @param {number} value |
| 311 * @param {!WebInspector.ProfileDataGridNode} node |
| 312 * @return {string} |
| 313 */ |
| 314 formatPercent(value, node) { |
| 315 return node.profileNode === this._profileView.profile.idleNode ? '' : WebIns
pector.UIString('%.2f\u2009%%', value); |
| 316 } |
| 317 |
| 318 /** |
| 319 * @override |
| 320 * @param {!WebInspector.ProfileDataGridNode} node |
| 321 * @return {?Element} |
| 322 */ |
| 323 linkifyNode(node) { |
| 324 return this._profileView.linkifier().maybeLinkifyConsoleCallFrame( |
| 325 this._profileView.target(), node.profileNode.callFrame, 'profile-node-fi
le'); |
| 326 } |
| 327 }; |
| 328 |
| 329 /** |
| 330 * @unrestricted |
| 331 */ |
| 332 WebInspector.CPUFlameChartDataProvider = class extends WebInspector.ProfileFlame
ChartDataProvider { |
| 333 /** |
| 334 * @param {!WebInspector.CPUProfileDataModel} cpuProfile |
| 335 * @param {?WebInspector.Target} target |
| 336 */ |
| 337 constructor(cpuProfile, target) { |
| 338 super(target); |
| 339 this._cpuProfile = cpuProfile; |
| 340 } |
| 341 |
| 342 /** |
| 343 * @override |
| 344 * @return {!WebInspector.FlameChart.TimelineData} |
| 345 */ |
| 346 _calculateTimelineData() { |
| 347 /** @type {!Array.<?WebInspector.CPUFlameChartDataProvider.ChartEntry>} */ |
| 348 var entries = []; |
| 349 /** @type {!Array.<number>} */ |
| 350 var stack = []; |
| 351 var maxDepth = 5; |
| 352 |
| 353 function onOpenFrame() { |
| 354 stack.push(entries.length); |
| 355 // Reserve space for the entry, as they have to be ordered by startTime. |
| 356 // The entry itself will be put there in onCloseFrame. |
| 357 entries.push(null); |
| 358 } |
| 359 /** |
| 360 * @param {number} depth |
| 361 * @param {!WebInspector.CPUProfileNode} node |
| 362 * @param {number} startTime |
| 363 * @param {number} totalTime |
| 364 * @param {number} selfTime |
| 365 */ |
| 366 function onCloseFrame(depth, node, startTime, totalTime, selfTime) { |
| 367 var index = stack.pop(); |
| 368 entries[index] = |
| 369 new WebInspector.CPUFlameChartDataProvider.ChartEntry(depth, totalTime
, startTime, selfTime, node); |
| 370 maxDepth = Math.max(maxDepth, depth); |
| 371 } |
| 372 this._cpuProfile.forEachFrame(onOpenFrame, onCloseFrame); |
| 373 |
| 374 /** @type {!Array<!WebInspector.CPUProfileNode>} */ |
| 375 var entryNodes = new Array(entries.length); |
| 376 var entryLevels = new Uint16Array(entries.length); |
| 377 var entryTotalTimes = new Float32Array(entries.length); |
| 378 var entrySelfTimes = new Float32Array(entries.length); |
| 379 var entryStartTimes = new Float64Array(entries.length); |
| 380 var minimumBoundary = this.minimumBoundary(); |
| 381 |
| 382 for (var i = 0; i < entries.length; ++i) { |
| 383 var entry = entries[i]; |
| 384 entryNodes[i] = entry.node; |
| 385 entryLevels[i] = entry.depth; |
| 386 entryTotalTimes[i] = entry.duration; |
| 387 entryStartTimes[i] = entry.startTime; |
| 388 entrySelfTimes[i] = entry.selfTime; |
| 389 } |
| 390 |
| 391 this._maxStackDepth = maxDepth; |
| 392 |
| 393 this._timelineData = new WebInspector.FlameChart.TimelineData(entryLevels, e
ntryTotalTimes, entryStartTimes, null); |
| 394 |
| 395 /** @type {!Array<!WebInspector.CPUProfileNode>} */ |
| 396 this._entryNodes = entryNodes; |
| 397 this._entrySelfTimes = entrySelfTimes; |
| 398 |
| 399 return this._timelineData; |
| 400 } |
| 401 |
| 402 /** |
| 403 * @override |
| 404 * @param {number} entryIndex |
| 405 * @return {?Element} |
| 406 */ |
| 407 prepareHighlightedEntryInfo(entryIndex) { |
| 408 var timelineData = this._timelineData; |
| 409 var node = this._entryNodes[entryIndex]; |
| 410 if (!node) |
| 411 return null; |
| 412 |
| 413 var entryInfo = []; |
| 414 /** |
| 415 * @param {string} title |
| 416 * @param {string} value |
| 417 */ |
| 418 function pushEntryInfoRow(title, value) { |
| 419 entryInfo.push({title: title, value: value}); |
| 420 } |
| 421 /** |
| 422 * @param {number} ms |
57 * @return {string} | 423 * @return {string} |
58 */ | 424 */ |
59 columnHeader: function(columnId) | 425 function millisecondsToString(ms) { |
60 { | 426 if (ms === 0) |
61 switch (columnId) { | 427 return '0'; |
62 case "self": return WebInspector.UIString("Self Time"); | 428 if (ms < 1000) |
63 case "total": return WebInspector.UIString("Total Time"); | 429 return WebInspector.UIString('%.1f\u2009ms', ms); |
64 } | 430 return Number.secondsToString(ms / 1000, true); |
65 return ""; | 431 } |
66 }, | 432 var name = WebInspector.beautifyFunctionName(node.functionName); |
67 | 433 pushEntryInfoRow(WebInspector.UIString('Name'), name); |
68 /** | 434 var selfTime = millisecondsToString(this._entrySelfTimes[entryIndex]); |
69 * @override | 435 var totalTime = millisecondsToString(timelineData.entryTotalTimes[entryIndex
]); |
70 * @return {!WebInspector.FlameChartDataProvider} | 436 pushEntryInfoRow(WebInspector.UIString('Self time'), selfTime); |
71 */ | 437 pushEntryInfoRow(WebInspector.UIString('Total time'), totalTime); |
72 createFlameChartDataProvider: function() | 438 var linkifier = new WebInspector.Linkifier(); |
73 { | 439 var link = linkifier.maybeLinkifyConsoleCallFrame(this._target, node.callFra
me); |
74 return new WebInspector.CPUFlameChartDataProvider(this.profile, this._pr
ofileHeader.target()); | 440 if (link) |
75 }, | 441 pushEntryInfoRow(WebInspector.UIString('URL'), link.textContent); |
76 | 442 linkifier.dispose(); |
77 __proto__: WebInspector.ProfileView.prototype | 443 pushEntryInfoRow(WebInspector.UIString('Aggregated self time'), Number.secon
dsToString(node.self / 1000, true)); |
78 }; | 444 pushEntryInfoRow(WebInspector.UIString('Aggregated total time'), Number.seco
ndsToString(node.total / 1000, true)); |
79 | 445 if (node.deoptReason) |
80 /** | 446 pushEntryInfoRow(WebInspector.UIString('Not optimized'), node.deoptReason)
; |
81 * @constructor | 447 |
82 * @extends {WebInspector.ProfileType} | 448 return WebInspector.ProfileView.buildPopoverTable(entryInfo); |
83 */ | 449 } |
84 WebInspector.CPUProfileType = function() | 450 }; |
85 { | 451 |
86 WebInspector.ProfileType.call(this, WebInspector.CPUProfileType.TypeId, WebI
nspector.UIString("Record JavaScript CPU Profile")); | 452 /** |
87 this._recording = false; | 453 * @unrestricted |
88 | 454 */ |
89 this._nextAnonymousConsoleProfileNumber = 1; | 455 WebInspector.CPUFlameChartDataProvider.ChartEntry = class { |
90 this._anonymousConsoleProfileIdToTitle = {}; | 456 /** |
91 | 457 * @param {number} depth |
92 WebInspector.CPUProfileType.instance = this; | 458 * @param {number} duration |
93 WebInspector.targetManager.addModelListener(WebInspector.CPUProfilerModel, W
ebInspector.CPUProfilerModel.Events.ConsoleProfileStarted, this._consoleProfileS
tarted, this); | 459 * @param {number} startTime |
94 WebInspector.targetManager.addModelListener(WebInspector.CPUProfilerModel, W
ebInspector.CPUProfilerModel.Events.ConsoleProfileFinished, this._consoleProfile
Finished, this); | 460 * @param {number} selfTime |
95 }; | 461 * @param {!WebInspector.CPUProfileNode} node |
96 | 462 */ |
97 WebInspector.CPUProfileType.TypeId = "CPU"; | 463 constructor(depth, duration, startTime, selfTime, node) { |
98 | 464 this.depth = depth; |
99 WebInspector.CPUProfileType.prototype = { | 465 this.duration = duration; |
100 /** | 466 this.startTime = startTime; |
101 * @override | 467 this.selfTime = selfTime; |
102 * @return {string} | 468 this.node = node; |
103 */ | 469 } |
104 typeName: function() | 470 }; |
105 { | |
106 return "CPU"; | |
107 }, | |
108 | |
109 /** | |
110 * @override | |
111 * @return {string} | |
112 */ | |
113 fileExtension: function() | |
114 { | |
115 return ".cpuprofile"; | |
116 }, | |
117 | |
118 get buttonTooltip() | |
119 { | |
120 return this._recording ? WebInspector.UIString("Stop CPU profiling") : W
ebInspector.UIString("Start CPU profiling"); | |
121 }, | |
122 | |
123 /** | |
124 * @override | |
125 * @return {boolean} | |
126 */ | |
127 buttonClicked: function() | |
128 { | |
129 if (this._recording) { | |
130 this.stopRecordingProfile(); | |
131 return false; | |
132 } else { | |
133 this.startRecordingProfile(); | |
134 return true; | |
135 } | |
136 }, | |
137 | |
138 get treeItemTitle() | |
139 { | |
140 return WebInspector.UIString("CPU PROFILES"); | |
141 }, | |
142 | |
143 get description() | |
144 { | |
145 return WebInspector.UIString("CPU profiles show where the execution time
is spent in your page's JavaScript functions."); | |
146 }, | |
147 | |
148 /** | |
149 * @param {!WebInspector.Event} event | |
150 */ | |
151 _consoleProfileStarted: function(event) | |
152 { | |
153 var data = /** @type {!WebInspector.CPUProfilerModel.EventData} */ (even
t.data); | |
154 var resolvedTitle = data.title; | |
155 if (!resolvedTitle) { | |
156 resolvedTitle = WebInspector.UIString("Profile %s", this._nextAnonym
ousConsoleProfileNumber++); | |
157 this._anonymousConsoleProfileIdToTitle[data.id] = resolvedTitle; | |
158 } | |
159 this._addMessageToConsole(WebInspector.ConsoleMessage.MessageType.Profil
e, data.scriptLocation, WebInspector.UIString("Profile '%s' started.", resolvedT
itle)); | |
160 }, | |
161 | |
162 /** | |
163 * @param {!WebInspector.Event} event | |
164 */ | |
165 _consoleProfileFinished: function(event) | |
166 { | |
167 var data = /** @type {!WebInspector.CPUProfilerModel.EventData} */ (even
t.data); | |
168 var cpuProfile = /** @type {!ProfilerAgent.Profile} */ (data.cpuProfile)
; | |
169 var resolvedTitle = data.title; | |
170 if (typeof resolvedTitle === "undefined") { | |
171 resolvedTitle = this._anonymousConsoleProfileIdToTitle[data.id]; | |
172 delete this._anonymousConsoleProfileIdToTitle[data.id]; | |
173 } | |
174 var profile = new WebInspector.CPUProfileHeader(data.scriptLocation.targ
et(), this, resolvedTitle); | |
175 profile.setProtocolProfile(cpuProfile); | |
176 this.addProfile(profile); | |
177 this._addMessageToConsole(WebInspector.ConsoleMessage.MessageType.Profil
eEnd, data.scriptLocation, WebInspector.UIString("Profile '%s' finished.", resol
vedTitle)); | |
178 }, | |
179 | |
180 /** | |
181 * @param {string} type | |
182 * @param {!WebInspector.DebuggerModel.Location} scriptLocation | |
183 * @param {string} messageText | |
184 */ | |
185 _addMessageToConsole: function(type, scriptLocation, messageText) | |
186 { | |
187 var script = scriptLocation.script(); | |
188 var target = scriptLocation.target(); | |
189 var message = new WebInspector.ConsoleMessage( | |
190 target, | |
191 WebInspector.ConsoleMessage.MessageSource.ConsoleAPI, | |
192 WebInspector.ConsoleMessage.MessageLevel.Debug, | |
193 messageText, | |
194 type, | |
195 undefined, | |
196 undefined, | |
197 undefined, | |
198 undefined, | |
199 [{ | |
200 functionName: "", | |
201 scriptId: scriptLocation.scriptId, | |
202 url: script ? script.contentURL() : "", | |
203 lineNumber: scriptLocation.lineNumber, | |
204 columnNumber: scriptLocation.columnNumber || 0 | |
205 }]); | |
206 | |
207 target.consoleModel.addMessage(message); | |
208 }, | |
209 | |
210 startRecordingProfile: function() | |
211 { | |
212 var target = WebInspector.context.flavor(WebInspector.Target); | |
213 if (this._profileBeingRecorded || !target) | |
214 return; | |
215 var profile = new WebInspector.CPUProfileHeader(target, this); | |
216 this.setProfileBeingRecorded(profile); | |
217 WebInspector.targetManager.suspendAllTargets(); | |
218 this.addProfile(profile); | |
219 profile.updateStatus(WebInspector.UIString("Recording\u2026")); | |
220 this._recording = true; | |
221 target.cpuProfilerModel.startRecording(); | |
222 }, | |
223 | |
224 stopRecordingProfile: function() | |
225 { | |
226 this._recording = false; | |
227 if (!this._profileBeingRecorded || !this._profileBeingRecorded.target()) | |
228 return; | |
229 | |
230 var recordedProfile; | |
231 | |
232 /** | |
233 * @param {?ProfilerAgent.Profile} profile | |
234 * @this {WebInspector.CPUProfileType} | |
235 */ | |
236 function didStopProfiling(profile) | |
237 { | |
238 if (!this._profileBeingRecorded) | |
239 return; | |
240 console.assert(profile); | |
241 this._profileBeingRecorded.setProtocolProfile(profile); | |
242 this._profileBeingRecorded.updateStatus(""); | |
243 recordedProfile = this._profileBeingRecorded; | |
244 this.setProfileBeingRecorded(null); | |
245 } | |
246 | |
247 /** | |
248 * @this {WebInspector.CPUProfileType} | |
249 */ | |
250 function fireEvent() | |
251 { | |
252 this.dispatchEventToListeners(WebInspector.ProfileType.Events.Profil
eComplete, recordedProfile); | |
253 } | |
254 | |
255 this._profileBeingRecorded.target().cpuProfilerModel.stopRecording() | |
256 .then(didStopProfiling.bind(this)) | |
257 .then(WebInspector.targetManager.resumeAllTargets.bind(WebInspector.
targetManager)) | |
258 .then(fireEvent.bind(this)); | |
259 }, | |
260 | |
261 /** | |
262 * @override | |
263 * @param {string} title | |
264 * @return {!WebInspector.ProfileHeader} | |
265 */ | |
266 createProfileLoadedFromFile: function(title) | |
267 { | |
268 return new WebInspector.CPUProfileHeader(null, this, title); | |
269 }, | |
270 | |
271 /** | |
272 * @override | |
273 */ | |
274 profileBeingRecordedRemoved: function() | |
275 { | |
276 this.stopRecordingProfile(); | |
277 }, | |
278 | |
279 __proto__: WebInspector.ProfileType.prototype | |
280 }; | |
281 | |
282 /** | |
283 * @constructor | |
284 * @extends {WebInspector.WritableProfileHeader} | |
285 * @param {?WebInspector.Target} target | |
286 * @param {!WebInspector.CPUProfileType} type | |
287 * @param {string=} title | |
288 */ | |
289 WebInspector.CPUProfileHeader = function(target, type, title) | |
290 { | |
291 WebInspector.WritableProfileHeader.call(this, target, type, title); | |
292 }; | |
293 | |
294 WebInspector.CPUProfileHeader.prototype = { | |
295 /** | |
296 * @override | |
297 * @return {!WebInspector.ProfileView} | |
298 */ | |
299 createView: function() | |
300 { | |
301 return new WebInspector.CPUProfileView(this); | |
302 }, | |
303 | |
304 /** | |
305 * @return {!ProfilerAgent.Profile} | |
306 */ | |
307 protocolProfile: function() | |
308 { | |
309 return this._protocolProfile; | |
310 }, | |
311 | |
312 __proto__: WebInspector.WritableProfileHeader.prototype | |
313 }; | |
314 | |
315 /** | |
316 * @implements {WebInspector.ProfileDataGridNode.Formatter} | |
317 * @constructor | |
318 */ | |
319 WebInspector.CPUProfileView.NodeFormatter = function(profileView) | |
320 { | |
321 this._profileView = profileView; | |
322 }; | |
323 | |
324 WebInspector.CPUProfileView.NodeFormatter.prototype = { | |
325 /** | |
326 * @override | |
327 * @param {number} value | |
328 * @return {string} | |
329 */ | |
330 formatValue: function(value) | |
331 { | |
332 return WebInspector.UIString("%.1f\u2009ms", value); | |
333 }, | |
334 | |
335 /** | |
336 * @override | |
337 * @param {number} value | |
338 * @param {!WebInspector.ProfileDataGridNode} node | |
339 * @return {string} | |
340 */ | |
341 formatPercent: function(value, node) | |
342 { | |
343 return node.profileNode === this._profileView.profile.idleNode ? "" : We
bInspector.UIString("%.2f\u2009%%", value); | |
344 }, | |
345 | |
346 /** | |
347 * @override | |
348 * @param {!WebInspector.ProfileDataGridNode} node | |
349 * @return {?Element} | |
350 */ | |
351 linkifyNode: function(node) | |
352 { | |
353 return this._profileView.linkifier().maybeLinkifyConsoleCallFrame(this._
profileView.target(), node.profileNode.callFrame, "profile-node-file"); | |
354 } | |
355 }; | |
356 | |
357 /** | |
358 * @constructor | |
359 * @extends {WebInspector.ProfileFlameChartDataProvider} | |
360 * @param {!WebInspector.CPUProfileDataModel} cpuProfile | |
361 * @param {?WebInspector.Target} target | |
362 */ | |
363 WebInspector.CPUFlameChartDataProvider = function(cpuProfile, target) | |
364 { | |
365 WebInspector.ProfileFlameChartDataProvider.call(this, target); | |
366 this._cpuProfile = cpuProfile; | |
367 }; | |
368 | |
369 WebInspector.CPUFlameChartDataProvider.prototype = { | |
370 /** | |
371 * @override | |
372 * @return {!WebInspector.FlameChart.TimelineData} | |
373 */ | |
374 _calculateTimelineData: function() | |
375 { | |
376 /** | |
377 * @constructor | |
378 * @param {number} depth | |
379 * @param {number} duration | |
380 * @param {number} startTime | |
381 * @param {number} selfTime | |
382 * @param {!WebInspector.CPUProfileNode} node | |
383 */ | |
384 function ChartEntry(depth, duration, startTime, selfTime, node) | |
385 { | |
386 this.depth = depth; | |
387 this.duration = duration; | |
388 this.startTime = startTime; | |
389 this.selfTime = selfTime; | |
390 this.node = node; | |
391 } | |
392 | |
393 /** @type {!Array.<?ChartEntry>} */ | |
394 var entries = []; | |
395 /** @type {!Array.<number>} */ | |
396 var stack = []; | |
397 var maxDepth = 5; | |
398 | |
399 function onOpenFrame() | |
400 { | |
401 stack.push(entries.length); | |
402 // Reserve space for the entry, as they have to be ordered by startT
ime. | |
403 // The entry itself will be put there in onCloseFrame. | |
404 entries.push(null); | |
405 } | |
406 /** | |
407 * @param {number} depth | |
408 * @param {!WebInspector.CPUProfileNode} node | |
409 * @param {number} startTime | |
410 * @param {number} totalTime | |
411 * @param {number} selfTime | |
412 */ | |
413 function onCloseFrame(depth, node, startTime, totalTime, selfTime) | |
414 { | |
415 var index = stack.pop(); | |
416 entries[index] = new ChartEntry(depth, totalTime, startTime, selfTim
e, node); | |
417 maxDepth = Math.max(maxDepth, depth); | |
418 } | |
419 this._cpuProfile.forEachFrame(onOpenFrame, onCloseFrame); | |
420 | |
421 /** @type {!Array<!WebInspector.CPUProfileNode>} */ | |
422 var entryNodes = new Array(entries.length); | |
423 var entryLevels = new Uint16Array(entries.length); | |
424 var entryTotalTimes = new Float32Array(entries.length); | |
425 var entrySelfTimes = new Float32Array(entries.length); | |
426 var entryStartTimes = new Float64Array(entries.length); | |
427 var minimumBoundary = this.minimumBoundary(); | |
428 | |
429 for (var i = 0; i < entries.length; ++i) { | |
430 var entry = entries[i]; | |
431 entryNodes[i] = entry.node; | |
432 entryLevels[i] = entry.depth; | |
433 entryTotalTimes[i] = entry.duration; | |
434 entryStartTimes[i] = entry.startTime; | |
435 entrySelfTimes[i] = entry.selfTime; | |
436 } | |
437 | |
438 this._maxStackDepth = maxDepth; | |
439 | |
440 this._timelineData = new WebInspector.FlameChart.TimelineData(entryLevel
s, entryTotalTimes, entryStartTimes, null); | |
441 | |
442 /** @type {!Array<!WebInspector.CPUProfileNode>} */ | |
443 this._entryNodes = entryNodes; | |
444 this._entrySelfTimes = entrySelfTimes; | |
445 | |
446 return this._timelineData; | |
447 }, | |
448 | |
449 /** | |
450 * @override | |
451 * @param {number} entryIndex | |
452 * @return {?Element} | |
453 */ | |
454 prepareHighlightedEntryInfo: function(entryIndex) | |
455 { | |
456 var timelineData = this._timelineData; | |
457 var node = this._entryNodes[entryIndex]; | |
458 if (!node) | |
459 return null; | |
460 | |
461 var entryInfo = []; | |
462 /** | |
463 * @param {string} title | |
464 * @param {string} value | |
465 */ | |
466 function pushEntryInfoRow(title, value) | |
467 { | |
468 entryInfo.push({ title: title, value: value }); | |
469 } | |
470 /** | |
471 * @param {number} ms | |
472 * @return {string} | |
473 */ | |
474 function millisecondsToString(ms) | |
475 { | |
476 if (ms === 0) | |
477 return "0"; | |
478 if (ms < 1000) | |
479 return WebInspector.UIString("%.1f\u2009ms", ms); | |
480 return Number.secondsToString(ms / 1000, true); | |
481 } | |
482 var name = WebInspector.beautifyFunctionName(node.functionName); | |
483 pushEntryInfoRow(WebInspector.UIString("Name"), name); | |
484 var selfTime = millisecondsToString(this._entrySelfTimes[entryIndex]); | |
485 var totalTime = millisecondsToString(timelineData.entryTotalTimes[entryI
ndex]); | |
486 pushEntryInfoRow(WebInspector.UIString("Self time"), selfTime); | |
487 pushEntryInfoRow(WebInspector.UIString("Total time"), totalTime); | |
488 var linkifier = new WebInspector.Linkifier(); | |
489 var link = linkifier.maybeLinkifyConsoleCallFrame(this._target, node.cal
lFrame); | |
490 if (link) | |
491 pushEntryInfoRow(WebInspector.UIString("URL"), link.textContent); | |
492 linkifier.dispose(); | |
493 pushEntryInfoRow(WebInspector.UIString("Aggregated self time"), Number.s
econdsToString(node.self / 1000, true)); | |
494 pushEntryInfoRow(WebInspector.UIString("Aggregated total time"), Number.
secondsToString(node.total / 1000, true)); | |
495 if (node.deoptReason) | |
496 pushEntryInfoRow(WebInspector.UIString("Not optimized"), node.deoptR
eason); | |
497 | |
498 return WebInspector.ProfileView.buildPopoverTable(entryInfo); | |
499 }, | |
500 | |
501 __proto__: WebInspector.ProfileFlameChartDataProvider.prototype | |
502 }; | |
OLD | NEW |