| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 WebInspector.TimelineJSProfileProcessor = class { | 5 TimelineModel.TimelineJSProfileProcessor = class { |
| 6 /** | 6 /** |
| 7 * @param {!WebInspector.CPUProfileDataModel} jsProfileModel | 7 * @param {!SDK.CPUProfileDataModel} jsProfileModel |
| 8 * @param {!WebInspector.TracingModel.Thread} thread | 8 * @param {!SDK.TracingModel.Thread} thread |
| 9 * @return {!Array<!WebInspector.TracingModel.Event>} | 9 * @return {!Array<!SDK.TracingModel.Event>} |
| 10 */ | 10 */ |
| 11 static generateTracingEventsFromCpuProfile(jsProfileModel, thread) { | 11 static generateTracingEventsFromCpuProfile(jsProfileModel, thread) { |
| 12 var idleNode = jsProfileModel.idleNode; | 12 var idleNode = jsProfileModel.idleNode; |
| 13 var programNode = jsProfileModel.programNode; | 13 var programNode = jsProfileModel.programNode; |
| 14 var gcNode = jsProfileModel.gcNode; | 14 var gcNode = jsProfileModel.gcNode; |
| 15 var samples = jsProfileModel.samples; | 15 var samples = jsProfileModel.samples; |
| 16 var timestamps = jsProfileModel.timestamps; | 16 var timestamps = jsProfileModel.timestamps; |
| 17 var jsEvents = []; | 17 var jsEvents = []; |
| 18 /** @type {!Map<!Object, !Array<!Protocol.Runtime.CallFrame>>} */ | 18 /** @type {!Map<!Object, !Array<!Protocol.Runtime.CallFrame>>} */ |
| 19 var nodeToStackMap = new Map(); | 19 var nodeToStackMap = new Map(); |
| 20 nodeToStackMap.set(programNode, []); | 20 nodeToStackMap.set(programNode, []); |
| 21 for (var i = 0; i < samples.length; ++i) { | 21 for (var i = 0; i < samples.length; ++i) { |
| 22 var node = jsProfileModel.nodeByIndex(i); | 22 var node = jsProfileModel.nodeByIndex(i); |
| 23 if (!node) { | 23 if (!node) { |
| 24 console.error(`Node with unknown id ${samples[i]} at index ${i}`); | 24 console.error(`Node with unknown id ${samples[i]} at index ${i}`); |
| 25 continue; | 25 continue; |
| 26 } | 26 } |
| 27 if (node === gcNode || node === idleNode) | 27 if (node === gcNode || node === idleNode) |
| 28 continue; | 28 continue; |
| 29 var callFrames = nodeToStackMap.get(node); | 29 var callFrames = nodeToStackMap.get(node); |
| 30 if (!callFrames) { | 30 if (!callFrames) { |
| 31 callFrames = /** @type {!Array<!Protocol.Runtime.CallFrame>} */ (new Arr
ay(node.depth + 1)); | 31 callFrames = /** @type {!Array<!Protocol.Runtime.CallFrame>} */ (new Arr
ay(node.depth + 1)); |
| 32 nodeToStackMap.set(node, callFrames); | 32 nodeToStackMap.set(node, callFrames); |
| 33 for (var j = 0; node.parent; node = node.parent) | 33 for (var j = 0; node.parent; node = node.parent) |
| 34 callFrames[j++] = /** @type {!Protocol.Runtime.CallFrame} */ (node); | 34 callFrames[j++] = /** @type {!Protocol.Runtime.CallFrame} */ (node); |
| 35 } | 35 } |
| 36 var jsSampleEvent = new WebInspector.TracingModel.Event( | 36 var jsSampleEvent = new SDK.TracingModel.Event( |
| 37 WebInspector.TracingModel.DevToolsTimelineEventCategory, WebInspector.
TimelineModel.RecordType.JSSample, | 37 SDK.TracingModel.DevToolsTimelineEventCategory, TimelineModel.Timeline
Model.RecordType.JSSample, |
| 38 WebInspector.TracingModel.Phase.Instant, timestamps[i], thread); | 38 SDK.TracingModel.Phase.Instant, timestamps[i], thread); |
| 39 jsSampleEvent.args['data'] = {stackTrace: callFrames}; | 39 jsSampleEvent.args['data'] = {stackTrace: callFrames}; |
| 40 jsEvents.push(jsSampleEvent); | 40 jsEvents.push(jsSampleEvent); |
| 41 } | 41 } |
| 42 return jsEvents; | 42 return jsEvents; |
| 43 } | 43 } |
| 44 | 44 |
| 45 /** | 45 /** |
| 46 * @param {!Array<!WebInspector.TracingModel.Event>} events | 46 * @param {!Array<!SDK.TracingModel.Event>} events |
| 47 * @return {!Array<!WebInspector.TracingModel.Event>} | 47 * @return {!Array<!SDK.TracingModel.Event>} |
| 48 */ | 48 */ |
| 49 static generateJSFrameEvents(events) { | 49 static generateJSFrameEvents(events) { |
| 50 /** | 50 /** |
| 51 * @param {!Protocol.Runtime.CallFrame} frame1 | 51 * @param {!Protocol.Runtime.CallFrame} frame1 |
| 52 * @param {!Protocol.Runtime.CallFrame} frame2 | 52 * @param {!Protocol.Runtime.CallFrame} frame2 |
| 53 * @return {boolean} | 53 * @return {boolean} |
| 54 */ | 54 */ |
| 55 function equalFrames(frame1, frame2) { | 55 function equalFrames(frame1, frame2) { |
| 56 return frame1.scriptId === frame2.scriptId && frame1.functionName === fram
e2.functionName; | 56 return frame1.scriptId === frame2.scriptId && frame1.functionName === fram
e2.functionName; |
| 57 } | 57 } |
| 58 | 58 |
| 59 /** | 59 /** |
| 60 * @param {!WebInspector.TracingModel.Event} e | 60 * @param {!SDK.TracingModel.Event} e |
| 61 * @return {boolean} | 61 * @return {boolean} |
| 62 */ | 62 */ |
| 63 function isJSInvocationEvent(e) { | 63 function isJSInvocationEvent(e) { |
| 64 switch (e.name) { | 64 switch (e.name) { |
| 65 case WebInspector.TimelineModel.RecordType.RunMicrotasks: | 65 case TimelineModel.TimelineModel.RecordType.RunMicrotasks: |
| 66 case WebInspector.TimelineModel.RecordType.FunctionCall: | 66 case TimelineModel.TimelineModel.RecordType.FunctionCall: |
| 67 case WebInspector.TimelineModel.RecordType.EvaluateScript: | 67 case TimelineModel.TimelineModel.RecordType.EvaluateScript: |
| 68 return true; | 68 return true; |
| 69 } | 69 } |
| 70 return false; | 70 return false; |
| 71 } | 71 } |
| 72 | 72 |
| 73 var jsFrameEvents = []; | 73 var jsFrameEvents = []; |
| 74 var jsFramesStack = []; | 74 var jsFramesStack = []; |
| 75 var lockedJsStackDepth = []; | 75 var lockedJsStackDepth = []; |
| 76 var ordinal = 0; | 76 var ordinal = 0; |
| 77 const showAllEvents = Runtime.experiments.isEnabled('timelineShowAllEvents')
; | 77 const showAllEvents = Runtime.experiments.isEnabled('timelineShowAllEvents')
; |
| 78 const showRuntimeCallStats = Runtime.experiments.isEnabled('timelineV8Runtim
eCallStats'); | 78 const showRuntimeCallStats = Runtime.experiments.isEnabled('timelineV8Runtim
eCallStats'); |
| 79 const showNativeFunctions = WebInspector.moduleSetting('showNativeFunctionsI
nJSProfile').get(); | 79 const showNativeFunctions = Common.moduleSetting('showNativeFunctionsInJSPro
file').get(); |
| 80 | 80 |
| 81 /** | 81 /** |
| 82 * @param {!WebInspector.TracingModel.Event} e | 82 * @param {!SDK.TracingModel.Event} e |
| 83 */ | 83 */ |
| 84 function onStartEvent(e) { | 84 function onStartEvent(e) { |
| 85 e.ordinal = ++ordinal; | 85 e.ordinal = ++ordinal; |
| 86 extractStackTrace(e); | 86 extractStackTrace(e); |
| 87 // For the duration of the event we cannot go beyond the stack associated
with it. | 87 // For the duration of the event we cannot go beyond the stack associated
with it. |
| 88 lockedJsStackDepth.push(jsFramesStack.length); | 88 lockedJsStackDepth.push(jsFramesStack.length); |
| 89 } | 89 } |
| 90 | 90 |
| 91 /** | 91 /** |
| 92 * @param {!WebInspector.TracingModel.Event} e | 92 * @param {!SDK.TracingModel.Event} e |
| 93 * @param {?WebInspector.TracingModel.Event} parent | 93 * @param {?SDK.TracingModel.Event} parent |
| 94 */ | 94 */ |
| 95 function onInstantEvent(e, parent) { | 95 function onInstantEvent(e, parent) { |
| 96 e.ordinal = ++ordinal; | 96 e.ordinal = ++ordinal; |
| 97 if (parent && isJSInvocationEvent(parent)) | 97 if (parent && isJSInvocationEvent(parent)) |
| 98 extractStackTrace(e); | 98 extractStackTrace(e); |
| 99 } | 99 } |
| 100 | 100 |
| 101 /** | 101 /** |
| 102 * @param {!WebInspector.TracingModel.Event} e | 102 * @param {!SDK.TracingModel.Event} e |
| 103 */ | 103 */ |
| 104 function onEndEvent(e) { | 104 function onEndEvent(e) { |
| 105 truncateJSStack(lockedJsStackDepth.pop(), e.endTime); | 105 truncateJSStack(lockedJsStackDepth.pop(), e.endTime); |
| 106 } | 106 } |
| 107 | 107 |
| 108 /** | 108 /** |
| 109 * @param {number} depth | 109 * @param {number} depth |
| 110 * @param {number} time | 110 * @param {number} time |
| 111 */ | 111 */ |
| 112 function truncateJSStack(depth, time) { | 112 function truncateJSStack(depth, time) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 124 for (var k = 0; k < jsFramesStack.length; ++k) | 124 for (var k = 0; k < jsFramesStack.length; ++k) |
| 125 jsFramesStack[k].setEndTime(time); | 125 jsFramesStack[k].setEndTime(time); |
| 126 jsFramesStack.length = depth; | 126 jsFramesStack.length = depth; |
| 127 } | 127 } |
| 128 | 128 |
| 129 /** | 129 /** |
| 130 * @param {string} name | 130 * @param {string} name |
| 131 * @return {boolean} | 131 * @return {boolean} |
| 132 */ | 132 */ |
| 133 function showNativeName(name) { | 133 function showNativeName(name) { |
| 134 return showRuntimeCallStats && !!WebInspector.TimelineJSProfileProcessor.n
ativeGroup(name); | 134 return showRuntimeCallStats && !!TimelineModel.TimelineJSProfileProcessor.
nativeGroup(name); |
| 135 } | 135 } |
| 136 | 136 |
| 137 /** | 137 /** |
| 138 * @param {!Array<!Protocol.Runtime.CallFrame>} stack | 138 * @param {!Array<!Protocol.Runtime.CallFrame>} stack |
| 139 */ | 139 */ |
| 140 function filterStackFrames(stack) { | 140 function filterStackFrames(stack) { |
| 141 if (showAllEvents) | 141 if (showAllEvents) |
| 142 return; | 142 return; |
| 143 var isPreviousFrameNative = false; | 143 var isPreviousFrameNative = false; |
| 144 for (var i = 0, j = 0; i < stack.length; ++i) { | 144 for (var i = 0, j = 0; i < stack.length; ++i) { |
| 145 const frame = stack[i]; | 145 const frame = stack[i]; |
| 146 const url = frame.url; | 146 const url = frame.url; |
| 147 const isNativeFrame = url && url.startsWith('native '); | 147 const isNativeFrame = url && url.startsWith('native '); |
| 148 if (!showNativeFunctions && isNativeFrame) | 148 if (!showNativeFunctions && isNativeFrame) |
| 149 continue; | 149 continue; |
| 150 if (WebInspector.TimelineJSProfileProcessor.isNativeRuntimeFrame(frame)
&& !showNativeName(frame.functionName)) | 150 if (TimelineModel.TimelineJSProfileProcessor.isNativeRuntimeFrame(frame)
&& !showNativeName(frame.functionName)) |
| 151 continue; | 151 continue; |
| 152 if (isPreviousFrameNative && isNativeFrame) | 152 if (isPreviousFrameNative && isNativeFrame) |
| 153 continue; | 153 continue; |
| 154 isPreviousFrameNative = isNativeFrame; | 154 isPreviousFrameNative = isNativeFrame; |
| 155 stack[j++] = frame; | 155 stack[j++] = frame; |
| 156 } | 156 } |
| 157 stack.length = j; | 157 stack.length = j; |
| 158 } | 158 } |
| 159 | 159 |
| 160 /** | 160 /** |
| 161 * @param {!WebInspector.TracingModel.Event} e | 161 * @param {!SDK.TracingModel.Event} e |
| 162 */ | 162 */ |
| 163 function extractStackTrace(e) { | 163 function extractStackTrace(e) { |
| 164 const recordTypes = WebInspector.TimelineModel.RecordType; | 164 const recordTypes = TimelineModel.TimelineModel.RecordType; |
| 165 /** @type {!Array<!Protocol.Runtime.CallFrame>} */ | 165 /** @type {!Array<!Protocol.Runtime.CallFrame>} */ |
| 166 const callFrames = e.name === recordTypes.JSSample | 166 const callFrames = e.name === recordTypes.JSSample |
| 167 ? e.args['data']['stackTrace'].slice().reverse() | 167 ? e.args['data']['stackTrace'].slice().reverse() |
| 168 : jsFramesStack.map(frameEvent => frameEvent.args['data']); | 168 : jsFramesStack.map(frameEvent => frameEvent.args['data']); |
| 169 filterStackFrames(callFrames); | 169 filterStackFrames(callFrames); |
| 170 const endTime = e.endTime || e.startTime; | 170 const endTime = e.endTime || e.startTime; |
| 171 const minFrames = Math.min(callFrames.length, jsFramesStack.length); | 171 const minFrames = Math.min(callFrames.length, jsFramesStack.length); |
| 172 var i; | 172 var i; |
| 173 for (i = lockedJsStackDepth.peekLast() || 0; i < minFrames; ++i) { | 173 for (i = lockedJsStackDepth.peekLast() || 0; i < minFrames; ++i) { |
| 174 const newFrame = callFrames[i]; | 174 const newFrame = callFrames[i]; |
| 175 const oldFrame = jsFramesStack[i].args['data']; | 175 const oldFrame = jsFramesStack[i].args['data']; |
| 176 if (!equalFrames(newFrame, oldFrame)) | 176 if (!equalFrames(newFrame, oldFrame)) |
| 177 break; | 177 break; |
| 178 jsFramesStack[i].setEndTime(Math.max(jsFramesStack[i].endTime, endTime))
; | 178 jsFramesStack[i].setEndTime(Math.max(jsFramesStack[i].endTime, endTime))
; |
| 179 } | 179 } |
| 180 truncateJSStack(i, e.startTime); | 180 truncateJSStack(i, e.startTime); |
| 181 for (; i < callFrames.length; ++i) { | 181 for (; i < callFrames.length; ++i) { |
| 182 const frame = callFrames[i]; | 182 const frame = callFrames[i]; |
| 183 const jsFrameEvent = new WebInspector.TracingModel.Event( | 183 const jsFrameEvent = new SDK.TracingModel.Event( |
| 184 WebInspector.TracingModel.DevToolsTimelineEventCategory, recordTypes
.JSFrame, | 184 SDK.TracingModel.DevToolsTimelineEventCategory, recordTypes.JSFrame, |
| 185 WebInspector.TracingModel.Phase.Complete, e.startTime, e.thread); | 185 SDK.TracingModel.Phase.Complete, e.startTime, e.thread); |
| 186 jsFrameEvent.ordinal = e.ordinal; | 186 jsFrameEvent.ordinal = e.ordinal; |
| 187 jsFrameEvent.addArgs({data: frame}); | 187 jsFrameEvent.addArgs({data: frame}); |
| 188 jsFrameEvent.setEndTime(endTime); | 188 jsFrameEvent.setEndTime(endTime); |
| 189 jsFramesStack.push(jsFrameEvent); | 189 jsFramesStack.push(jsFrameEvent); |
| 190 jsFrameEvents.push(jsFrameEvent); | 190 jsFrameEvents.push(jsFrameEvent); |
| 191 } | 191 } |
| 192 } | 192 } |
| 193 | 193 |
| 194 const firstTopLevelEvent = events.find(WebInspector.TracingModel.isTopLevelE
vent); | 194 const firstTopLevelEvent = events.find(SDK.TracingModel.isTopLevelEvent); |
| 195 if (firstTopLevelEvent) | 195 if (firstTopLevelEvent) |
| 196 WebInspector.TimelineModel.forEachEvent( | 196 TimelineModel.TimelineModel.forEachEvent( |
| 197 events, onStartEvent, onEndEvent, onInstantEvent, firstTopLevelEvent.s
tartTime); | 197 events, onStartEvent, onEndEvent, onInstantEvent, firstTopLevelEvent.s
tartTime); |
| 198 return jsFrameEvents; | 198 return jsFrameEvents; |
| 199 } | 199 } |
| 200 | 200 |
| 201 /** | 201 /** |
| 202 * @param {!Protocol.Runtime.CallFrame} frame | 202 * @param {!Protocol.Runtime.CallFrame} frame |
| 203 * @return {boolean} | 203 * @return {boolean} |
| 204 */ | 204 */ |
| 205 static isNativeRuntimeFrame(frame) { | 205 static isNativeRuntimeFrame(frame) { |
| 206 return frame.url === 'native V8Runtime'; | 206 return frame.url === 'native V8Runtime'; |
| 207 } | 207 } |
| 208 | 208 |
| 209 /** | 209 /** |
| 210 * @param {string} nativeName | 210 * @param {string} nativeName |
| 211 * @return {?WebInspector.TimelineJSProfileProcessor.NativeGroups} | 211 * @return {?TimelineModel.TimelineJSProfileProcessor.NativeGroups} |
| 212 */ | 212 */ |
| 213 static nativeGroup(nativeName) { | 213 static nativeGroup(nativeName) { |
| 214 var map = WebInspector.TimelineJSProfileProcessor.nativeGroup._map; | 214 var map = TimelineModel.TimelineJSProfileProcessor.nativeGroup._map; |
| 215 if (!map) { | 215 if (!map) { |
| 216 const nativeGroups = WebInspector.TimelineJSProfileProcessor.NativeGroups; | 216 const nativeGroups = TimelineModel.TimelineJSProfileProcessor.NativeGroups
; |
| 217 map = new Map([ | 217 map = new Map([ |
| 218 ['Compile', nativeGroups.Compile], | 218 ['Compile', nativeGroups.Compile], |
| 219 ['CompileCode', nativeGroups.Compile], | 219 ['CompileCode', nativeGroups.Compile], |
| 220 ['CompileCodeLazy', nativeGroups.Compile], | 220 ['CompileCodeLazy', nativeGroups.Compile], |
| 221 ['CompileDeserialize', nativeGroups.Compile], | 221 ['CompileDeserialize', nativeGroups.Compile], |
| 222 ['CompileEval', nativeGroups.Compile], | 222 ['CompileEval', nativeGroups.Compile], |
| 223 ['CompileFullCode', nativeGroups.Compile], | 223 ['CompileFullCode', nativeGroups.Compile], |
| 224 ['CompileIgnition', nativeGroups.Compile], | 224 ['CompileIgnition', nativeGroups.Compile], |
| 225 ['CompilerDispatcher', nativeGroups.Compile], | 225 ['CompilerDispatcher', nativeGroups.Compile], |
| 226 ['CompileSerialize', nativeGroups.Compile], | 226 ['CompileSerialize', nativeGroups.Compile], |
| 227 ['ParseProgram', nativeGroups.Parse], | 227 ['ParseProgram', nativeGroups.Parse], |
| 228 ['ParseFunction', nativeGroups.Parse], | 228 ['ParseFunction', nativeGroups.Parse], |
| 229 ['RecompileConcurrent', nativeGroups.Compile], | 229 ['RecompileConcurrent', nativeGroups.Compile], |
| 230 ['RecompileSynchronous', nativeGroups.Compile], | 230 ['RecompileSynchronous', nativeGroups.Compile], |
| 231 ['ParseLazy', nativeGroups.Parse] | 231 ['ParseLazy', nativeGroups.Parse] |
| 232 ]); | 232 ]); |
| 233 /** @type {!Map<string, !WebInspector.TimelineJSProfileProcessor.NativeGro
ups>} */ | 233 /** @type {!Map<string, !TimelineModel.TimelineJSProfileProcessor.NativeGr
oups>} */ |
| 234 WebInspector.TimelineJSProfileProcessor.nativeGroup._map = map; | 234 TimelineModel.TimelineJSProfileProcessor.nativeGroup._map = map; |
| 235 } | 235 } |
| 236 return map.get(nativeName) || null; | 236 return map.get(nativeName) || null; |
| 237 } | 237 } |
| 238 }; | 238 }; |
| 239 | 239 |
| 240 /** @enum {string} */ | 240 /** @enum {string} */ |
| 241 WebInspector.TimelineJSProfileProcessor.NativeGroups = { | 241 TimelineModel.TimelineJSProfileProcessor.NativeGroups = { |
| 242 'Compile': 'Compile', | 242 'Compile': 'Compile', |
| 243 'Parse': 'Parse' | 243 'Parse': 'Parse' |
| 244 }; | 244 }; |
| OLD | NEW |