1/* 2 * Copyright (C) 2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.ProfileNode = function(id, type, functionName, sourceCodeLocation, calls, childNodes) 27{ 28 WebInspector.Object.call(this); 29 30 childNodes = childNodes || []; 31 32 console.assert(id); 33 console.assert(calls instanceof Array); 34 console.assert(calls.length >= 1); 35 console.assert(calls.reduce(function(previousValue, call) { return previousValue && call instanceof WebInspector.ProfileNodeCall; }, true)); 36 console.assert(childNodes instanceof Array); 37 console.assert(childNodes.reduce(function(previousValue, node) { return previousValue && node instanceof WebInspector.ProfileNode; }, true)); 38 39 this._id = id; 40 this._type = type || WebInspector.ProfileNode.Type.Function; 41 this._functionName = functionName || null; 42 this._sourceCodeLocation = sourceCodeLocation || null; 43 this._calls = calls; 44 this._childNodes = childNodes; 45 this._parentNode = null; 46 this._previousSibling = null; 47 this._nextSibling = null; 48 this._computedTotalTimes = false; 49 50 for (var i = 0; i < this._childNodes.length; ++i) 51 this._childNodes[i].establishRelationships(this, this._childNodes[i - 1], this._childNodes[i + 1]); 52 53 for (var i = 0; i < this._calls.length; ++i) 54 this._calls[i].establishRelationships(this, this._calls[i - 1], this._calls[i + 1]); 55}; 56 57WebInspector.ProfileNode.Type = { 58 Function: "profile-node-type-function", 59 Program: "profile-node-type-program" 60}; 61 62WebInspector.ProfileNode.TypeIdentifier = "profile-node"; 63WebInspector.ProfileNode.TypeCookieKey = "profile-node-type"; 64WebInspector.ProfileNode.FunctionNameCookieKey = "profile-node-function-name"; 65WebInspector.ProfileNode.SourceCodeURLCookieKey = "profile-node-source-code-url"; 66WebInspector.ProfileNode.SourceCodeLocationLineCookieKey = "profile-node-source-code-location-line"; 67WebInspector.ProfileNode.SourceCodeLocationColumnCookieKey = "profile-node-source-code-location-column"; 68 69WebInspector.ProfileNode.prototype = { 70 constructor: WebInspector.ProfileNode, 71 __proto__: WebInspector.Object.prototype, 72 73 // Public 74 75 get id() 76 { 77 return this._id; 78 }, 79 80 get type() 81 { 82 return this._type; 83 }, 84 85 get functionName() 86 { 87 return this._functionName; 88 }, 89 90 get sourceCodeLocation() 91 { 92 return this._sourceCodeLocation; 93 }, 94 95 get startTime() 96 { 97 if (this._startTime === undefined) 98 this._startTime = Math.max(0, this._calls[0].startTime); 99 return this._startTime; 100 }, 101 102 get endTime() 103 { 104 if (this._endTime === undefined) 105 this._endTime = Math.min(this._calls.lastValue.endTime, Infinity); 106 return this._endTime; 107 }, 108 109 get selfTime() 110 { 111 this._computeTotalTimesIfNeeded(); 112 return this._selfTime; 113 }, 114 115 get totalTime() 116 { 117 this._computeTotalTimesIfNeeded(); 118 return this._totalTime; 119 }, 120 121 get calls() 122 { 123 return this._calls; 124 }, 125 126 get previousSibling() 127 { 128 return this._previousSibling; 129 }, 130 131 get nextSibling() 132 { 133 return this._nextSibling; 134 }, 135 136 get parentNode() 137 { 138 return this._parentNode; 139 }, 140 141 get childNodes() 142 { 143 return this._childNodes; 144 }, 145 146 computeCallInfoForTimeRange: function(rangeStartTime, rangeEndTime) 147 { 148 console.assert(typeof rangeStartTime === "number"); 149 console.assert(typeof rangeEndTime === "number"); 150 151 var recordCallCount = true; 152 var callCount = 0; 153 154 function totalTimeInRange(previousValue, call) 155 { 156 if (rangeStartTime > call.endTime || rangeEndTime < call.startTime) 157 return previousValue; 158 159 if (recordCallCount) 160 ++callCount; 161 162 return previousValue + Math.min(call.endTime, rangeEndTime) - Math.max(rangeStartTime, call.startTime); 163 } 164 165 var startTime = Math.max(rangeStartTime, this._calls[0].startTime); 166 var endTime = Math.min(this._calls.lastValue.endTime, rangeEndTime); 167 var totalTime = this._calls.reduce(totalTimeInRange, 0); 168 169 recordCallCount = false; 170 171 var childNodesTotalTime = 0; 172 for (var childNode of this._childNodes) 173 childNodesTotalTime += childNode.calls.reduce(totalTimeInRange, 0); 174 175 var selfTime = totalTime - childNodesTotalTime; 176 var averageTime = selfTime / callCount; 177 178 return {startTime: startTime, endTime: endTime, totalTime: totalTime, selfTime: selfTime, callCount: callCount, averageTime: averageTime}; 179 }, 180 181 traverseNextProfileNode: function(stayWithin) 182 { 183 var profileNode = this._childNodes[0]; 184 if (profileNode) 185 return profileNode; 186 187 if (this === stayWithin) 188 return null; 189 190 profileNode = this._nextSibling; 191 if (profileNode) 192 return profileNode; 193 194 profileNode = this; 195 while (profileNode && !profileNode.nextSibling && profileNode.parentNode !== stayWithin) 196 profileNode = profileNode.parentNode; 197 198 if (!profileNode) 199 return null; 200 201 return profileNode.nextSibling; 202 }, 203 204 saveIdentityToCookie: function(cookie) 205 { 206 cookie[WebInspector.ProfileNode.TypeCookieKey] = this._type || null; 207 cookie[WebInspector.ProfileNode.FunctionNameCookieKey] = this._functionName || null; 208 cookie[WebInspector.ProfileNode.SourceCodeURLCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.sourceCode.url ? this._sourceCodeLocation.sourceCode.url.hash : null : null; 209 cookie[WebInspector.ProfileNode.SourceCodeLocationLineCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.lineNumber : null; 210 cookie[WebInspector.ProfileNode.SourceCodeLocationColumnCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.columnNumber : null; 211 }, 212 213 // Protected 214 215 establishRelationships: function(parentNode, previousSibling, nextSibling) 216 { 217 this._parentNode = parentNode || null; 218 this._previousSibling = previousSibling || null; 219 this._nextSibling = nextSibling || null; 220 }, 221 222 // Private 223 224 _computeTotalTimes: function() 225 { 226 if (this._computedTotalTimes) 227 return; 228 229 this._computedTotalTimes = true; 230 231 var info = this.computeCallInfoForTimeRange(0, Infinity); 232 this._startTime = info.startTime; 233 this._endTime = info.endTime; 234 this._selfTime = info.selfTime; 235 this._totalTime = info.totalTime; 236 } 237}; 238