| 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 WebInspector.TimelineJSProfileProcessor = {}; |
| 5 | |
| 6 WebInspector.TimelineJSProfileProcessor = { }; | |
| 7 | 5 |
| 8 /** | 6 /** |
| 9 * @param {!WebInspector.CPUProfileDataModel} jsProfileModel | 7 * @param {!WebInspector.CPUProfileDataModel} jsProfileModel |
| 10 * @param {!WebInspector.TracingModel.Thread} thread | 8 * @param {!WebInspector.TracingModel.Thread} thread |
| 11 * @return {!Array<!WebInspector.TracingModel.Event>} | 9 * @return {!Array<!WebInspector.TracingModel.Event>} |
| 12 */ | 10 */ |
| 13 WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile = fu
nction(jsProfileModel, thread) | 11 WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile = fu
nction(jsProfileModel, thread) { |
| 14 { | 12 var idleNode = jsProfileModel.idleNode; |
| 15 var idleNode = jsProfileModel.idleNode; | 13 var programNode = jsProfileModel.programNode; |
| 16 var programNode = jsProfileModel.programNode; | 14 var gcNode = jsProfileModel.gcNode; |
| 17 var gcNode = jsProfileModel.gcNode; | 15 var samples = jsProfileModel.samples; |
| 18 var samples = jsProfileModel.samples; | 16 var timestamps = jsProfileModel.timestamps; |
| 19 var timestamps = jsProfileModel.timestamps; | 17 var jsEvents = []; |
| 20 var jsEvents = []; | 18 /** @type {!Map<!Object, !Array<!RuntimeAgent.CallFrame>>} */ |
| 21 /** @type {!Map<!Object, !Array<!RuntimeAgent.CallFrame>>} */ | 19 var nodeToStackMap = new Map(); |
| 22 var nodeToStackMap = new Map(); | 20 nodeToStackMap.set(programNode, []); |
| 23 nodeToStackMap.set(programNode, []); | 21 for (var i = 0; i < samples.length; ++i) { |
| 24 for (var i = 0; i < samples.length; ++i) { | 22 var node = jsProfileModel.nodeByIndex(i); |
| 25 var node = jsProfileModel.nodeByIndex(i); | 23 if (!node) { |
| 26 if (!node) { | 24 console.error(`Node with unknown id ${samples[i]} at index ${i}`); |
| 27 console.error(`Node with unknown id ${samples[i]} at index ${i}`); | 25 continue; |
| 28 continue; | 26 } |
| 29 } | 27 if (node === gcNode || node === idleNode) |
| 30 if (node === gcNode || node === idleNode) | 28 continue; |
| 31 continue; | 29 var callFrames = nodeToStackMap.get(node); |
| 32 var callFrames = nodeToStackMap.get(node); | 30 if (!callFrames) { |
| 33 if (!callFrames) { | 31 callFrames = /** @type {!Array<!RuntimeAgent.CallFrame>} */ (new Array(nod
e.depth + 1)); |
| 34 callFrames = /** @type {!Array<!RuntimeAgent.CallFrame>} */ (new Arr
ay(node.depth + 1)); | 32 nodeToStackMap.set(node, callFrames); |
| 35 nodeToStackMap.set(node, callFrames); | 33 for (var j = 0; node.parent; node = node.parent) |
| 36 for (var j = 0; node.parent; node = node.parent) | 34 callFrames[j++] = /** @type {!RuntimeAgent.CallFrame} */ (node); |
| 37 callFrames[j++] = /** @type {!RuntimeAgent.CallFrame} */ (node); | 35 } |
| 38 } | 36 var jsSampleEvent = new WebInspector.TracingModel.Event( |
| 39 var jsSampleEvent = new WebInspector.TracingModel.Event(WebInspector.Tra
cingModel.DevToolsTimelineEventCategory, | 37 WebInspector.TracingModel.DevToolsTimelineEventCategory, WebInspector.Ti
melineModel.RecordType.JSSample, |
| 40 WebInspector.TimelineModel.RecordType.JSSample, | 38 WebInspector.TracingModel.Phase.Instant, timestamps[i], thread); |
| 41 WebInspector.TracingModel.Phase.Instant, timestamps[i], thread); | 39 jsSampleEvent.args['data'] = {stackTrace: callFrames}; |
| 42 jsSampleEvent.args["data"] = { stackTrace: callFrames }; | 40 jsEvents.push(jsSampleEvent); |
| 43 jsEvents.push(jsSampleEvent); | 41 } |
| 44 } | 42 return jsEvents; |
| 45 return jsEvents; | |
| 46 }; | 43 }; |
| 47 | 44 |
| 48 /** | 45 /** |
| 49 * @param {!Array<!WebInspector.TracingModel.Event>} events | 46 * @param {!Array<!WebInspector.TracingModel.Event>} events |
| 50 * @return {!Array<!WebInspector.TracingModel.Event>} | 47 * @return {!Array<!WebInspector.TracingModel.Event>} |
| 51 */ | 48 */ |
| 52 WebInspector.TimelineJSProfileProcessor.generateJSFrameEvents = function(events) | 49 WebInspector.TimelineJSProfileProcessor.generateJSFrameEvents = function(events)
{ |
| 53 { | 50 /** |
| 54 /** | 51 * @param {!RuntimeAgent.CallFrame} frame1 |
| 55 * @param {!RuntimeAgent.CallFrame} frame1 | 52 * @param {!RuntimeAgent.CallFrame} frame2 |
| 56 * @param {!RuntimeAgent.CallFrame} frame2 | 53 * @return {boolean} |
| 57 * @return {boolean} | 54 */ |
| 58 */ | 55 function equalFrames(frame1, frame2) { |
| 59 function equalFrames(frame1, frame2) | 56 return frame1.scriptId === frame2.scriptId && frame1.functionName === frame2
.functionName; |
| 60 { | 57 } |
| 61 return frame1.scriptId === frame2.scriptId && frame1.functionName === fr
ame2.functionName; | 58 |
| 62 } | 59 /** |
| 63 | 60 * @param {!WebInspector.TracingModel.Event} e |
| 64 /** | 61 * @return {number} |
| 65 * @param {!WebInspector.TracingModel.Event} e | 62 */ |
| 66 * @return {number} | 63 function eventEndTime(e) { |
| 67 */ | 64 return e.endTime || e.startTime; |
| 68 function eventEndTime(e) | 65 } |
| 69 { | 66 |
| 70 return e.endTime || e.startTime; | 67 /** |
| 71 } | 68 * @param {!WebInspector.TracingModel.Event} e |
| 72 | 69 * @return {boolean} |
| 73 /** | 70 */ |
| 74 * @param {!WebInspector.TracingModel.Event} e | 71 function isJSInvocationEvent(e) { |
| 75 * @return {boolean} | 72 switch (e.name) { |
| 76 */ | 73 case WebInspector.TimelineModel.RecordType.RunMicrotasks: |
| 77 function isJSInvocationEvent(e) | 74 case WebInspector.TimelineModel.RecordType.FunctionCall: |
| 78 { | 75 case WebInspector.TimelineModel.RecordType.EvaluateScript: |
| 79 switch (e.name) { | 76 return true; |
| 80 case WebInspector.TimelineModel.RecordType.RunMicrotasks: | 77 } |
| 81 case WebInspector.TimelineModel.RecordType.FunctionCall: | 78 return false; |
| 82 case WebInspector.TimelineModel.RecordType.EvaluateScript: | 79 } |
| 83 return true; | 80 |
| 84 } | 81 var jsFrameEvents = []; |
| 85 return false; | 82 var jsFramesStack = []; |
| 86 } | 83 var lockedJsStackDepth = []; |
| 87 | 84 var ordinal = 0; |
| 88 var jsFrameEvents = []; | 85 var filterNativeFunctions = !WebInspector.moduleSetting('showNativeFunctionsIn
JSProfile').get(); |
| 89 var jsFramesStack = []; | 86 |
| 90 var lockedJsStackDepth = []; | 87 /** |
| 91 var ordinal = 0; | 88 * @param {!WebInspector.TracingModel.Event} e |
| 92 var filterNativeFunctions = !WebInspector.moduleSetting("showNativeFunctions
InJSProfile").get(); | 89 */ |
| 93 | 90 function onStartEvent(e) { |
| 94 /** | 91 e.ordinal = ++ordinal; |
| 95 * @param {!WebInspector.TracingModel.Event} e | 92 extractStackTrace(e); |
| 96 */ | 93 // For the duration of the event we cannot go beyond the stack associated wi
th it. |
| 97 function onStartEvent(e) | 94 lockedJsStackDepth.push(jsFramesStack.length); |
| 98 { | 95 } |
| 99 e.ordinal = ++ordinal; | 96 |
| 100 extractStackTrace(e); | 97 /** |
| 101 // For the duration of the event we cannot go beyond the stack associate
d with it. | 98 * @param {!WebInspector.TracingModel.Event} e |
| 102 lockedJsStackDepth.push(jsFramesStack.length); | 99 * @param {?WebInspector.TracingModel.Event} parent |
| 103 } | 100 */ |
| 104 | 101 function onInstantEvent(e, parent) { |
| 105 /** | 102 e.ordinal = ++ordinal; |
| 106 * @param {!WebInspector.TracingModel.Event} e | 103 if (parent && isJSInvocationEvent(parent)) |
| 107 * @param {?WebInspector.TracingModel.Event} parent | 104 extractStackTrace(e); |
| 108 */ | 105 } |
| 109 function onInstantEvent(e, parent) | 106 |
| 110 { | 107 /** |
| 111 e.ordinal = ++ordinal; | 108 * @param {!WebInspector.TracingModel.Event} e |
| 112 if (parent && isJSInvocationEvent(parent)) | 109 */ |
| 113 extractStackTrace(e); | 110 function onEndEvent(e) { |
| 114 } | 111 truncateJSStack(lockedJsStackDepth.pop(), e.endTime); |
| 115 | 112 } |
| 116 /** | 113 |
| 117 * @param {!WebInspector.TracingModel.Event} e | 114 /** |
| 118 */ | 115 * @param {number} depth |
| 119 function onEndEvent(e) | 116 * @param {number} time |
| 120 { | 117 */ |
| 121 truncateJSStack(lockedJsStackDepth.pop(), e.endTime); | 118 function truncateJSStack(depth, time) { |
| 122 } | 119 if (lockedJsStackDepth.length) { |
| 123 | 120 var lockedDepth = lockedJsStackDepth.peekLast(); |
| 124 /** | 121 if (depth < lockedDepth) { |
| 125 * @param {number} depth | 122 console.error( |
| 126 * @param {number} time | 123 'Child stack is shallower (' + depth + ') than the parent stack (' +
lockedDepth + ') at ' + time); |
| 127 */ | 124 depth = lockedDepth; |
| 128 function truncateJSStack(depth, time) | 125 } |
| 129 { | 126 } |
| 130 if (lockedJsStackDepth.length) { | 127 if (jsFramesStack.length < depth) { |
| 131 var lockedDepth = lockedJsStackDepth.peekLast(); | 128 console.error('Trying to truncate higher than the current stack size at '
+ time); |
| 132 if (depth < lockedDepth) { | 129 depth = jsFramesStack.length; |
| 133 console.error("Child stack is shallower (" + depth + ") than the
parent stack (" + lockedDepth + ") at " + time); | 130 } |
| 134 depth = lockedDepth; | 131 for (var k = 0; k < jsFramesStack.length; ++k) |
| 135 } | 132 jsFramesStack[k].setEndTime(time); |
| 136 } | 133 jsFramesStack.length = depth; |
| 137 if (jsFramesStack.length < depth) { | 134 } |
| 138 console.error("Trying to truncate higher than the current stack size
at " + time); | 135 |
| 139 depth = jsFramesStack.length; | 136 /** |
| 140 } | 137 * @param {!Array<!RuntimeAgent.CallFrame>} stack |
| 141 for (var k = 0; k < jsFramesStack.length; ++k) | 138 */ |
| 142 jsFramesStack[k].setEndTime(time); | 139 function filterStackFrames(stack) { |
| 143 jsFramesStack.length = depth; | 140 for (var i = 0, j = 0; i < stack.length; ++i) { |
| 144 } | 141 var url = stack[i].url; |
| 145 | 142 if (url && url.startsWith('native ')) |
| 146 /** | 143 continue; |
| 147 * @param {!Array<!RuntimeAgent.CallFrame>} stack | 144 stack[j++] = stack[i]; |
| 148 */ | 145 } |
| 149 function filterStackFrames(stack) | 146 stack.length = j; |
| 150 { | 147 } |
| 151 for (var i = 0, j = 0; i < stack.length; ++i) { | 148 |
| 152 var url = stack[i].url; | 149 /** |
| 153 if (url && url.startsWith("native ")) | 150 * @param {!WebInspector.TracingModel.Event} e |
| 154 continue; | 151 */ |
| 155 stack[j++] = stack[i]; | 152 function extractStackTrace(e) { |
| 156 } | 153 var recordTypes = WebInspector.TimelineModel.RecordType; |
| 157 stack.length = j; | 154 var callFrames; |
| 158 } | 155 if (e.name === recordTypes.JSSample) { |
| 159 | 156 var eventData = e.args['data'] || e.args['beginData']; |
| 160 /** | 157 callFrames = /** @type {!Array<!RuntimeAgent.CallFrame>} */ (eventData &&
eventData['stackTrace']); |
| 161 * @param {!WebInspector.TracingModel.Event} e | 158 } else { |
| 162 */ | 159 callFrames = /** @type {!Array<!RuntimeAgent.CallFrame>} */ ( |
| 163 function extractStackTrace(e) | 160 jsFramesStack.map(frameEvent => frameEvent.args['data']).reverse()); |
| 164 { | 161 } |
| 165 var recordTypes = WebInspector.TimelineModel.RecordType; | 162 if (filterNativeFunctions) |
| 166 var callFrames; | 163 filterStackFrames(callFrames); |
| 167 if (e.name === recordTypes.JSSample) { | 164 var endTime = eventEndTime(e); |
| 168 var eventData = e.args["data"] || e.args["beginData"]; | 165 var numFrames = callFrames.length; |
| 169 callFrames = /** @type {!Array<!RuntimeAgent.CallFrame>} */ (eventDa
ta && eventData["stackTrace"]); | 166 var minFrames = Math.min(numFrames, jsFramesStack.length); |
| 170 } else { | 167 var i; |
| 171 callFrames = /** @type {!Array<!RuntimeAgent.CallFrame>} */ (jsFrame
sStack.map(frameEvent => frameEvent.args["data"]).reverse()); | 168 for (i = lockedJsStackDepth.peekLast() || 0; i < minFrames; ++i) { |
| 172 } | 169 var newFrame = callFrames[numFrames - 1 - i]; |
| 173 if (filterNativeFunctions) | 170 var oldFrame = jsFramesStack[i].args['data']; |
| 174 filterStackFrames(callFrames); | 171 if (!equalFrames(newFrame, oldFrame)) |
| 175 var endTime = eventEndTime(e); | 172 break; |
| 176 var numFrames = callFrames.length; | 173 jsFramesStack[i].setEndTime(Math.max(jsFramesStack[i].endTime, endTime)); |
| 177 var minFrames = Math.min(numFrames, jsFramesStack.length); | 174 } |
| 178 var i; | 175 truncateJSStack(i, e.startTime); |
| 179 for (i = lockedJsStackDepth.peekLast() || 0; i < minFrames; ++i) { | 176 for (; i < numFrames; ++i) { |
| 180 var newFrame = callFrames[numFrames - 1 - i]; | 177 var frame = callFrames[numFrames - 1 - i]; |
| 181 var oldFrame = jsFramesStack[i].args["data"]; | 178 var jsFrameEvent = new WebInspector.TracingModel.Event( |
| 182 if (!equalFrames(newFrame, oldFrame)) | 179 WebInspector.TracingModel.DevToolsTimelineEventCategory, recordTypes.J
SFrame, |
| 183 break; | 180 WebInspector.TracingModel.Phase.Complete, e.startTime, e.thread); |
| 184 jsFramesStack[i].setEndTime(Math.max(jsFramesStack[i].endTime, endTi
me)); | 181 jsFrameEvent.ordinal = e.ordinal; |
| 185 } | 182 jsFrameEvent.addArgs({data: frame}); |
| 186 truncateJSStack(i, e.startTime); | 183 jsFrameEvent.setEndTime(endTime); |
| 187 for (; i < numFrames; ++i) { | 184 jsFramesStack.push(jsFrameEvent); |
| 188 var frame = callFrames[numFrames - 1 - i]; | 185 jsFrameEvents.push(jsFrameEvent); |
| 189 var jsFrameEvent = new WebInspector.TracingModel.Event(WebInspector.
TracingModel.DevToolsTimelineEventCategory, recordTypes.JSFrame, | 186 } |
| 190 WebInspector.TracingModel.Phase.Complete, e.startTime, e.thread)
; | 187 } |
| 191 jsFrameEvent.ordinal = e.ordinal; | 188 |
| 192 jsFrameEvent.addArgs({ data: frame }); | 189 /** |
| 193 jsFrameEvent.setEndTime(endTime); | 190 * @param {!Array<!WebInspector.TracingModel.Event>} events |
| 194 jsFramesStack.push(jsFrameEvent); | 191 * @return {?WebInspector.TracingModel.Event} |
| 195 jsFrameEvents.push(jsFrameEvent); | 192 */ |
| 196 } | 193 function findFirstTopLevelEvent(events) { |
| 197 } | 194 for (var i = 0; i < events.length; ++i) { |
| 198 | 195 if (WebInspector.TracingModel.isTopLevelEvent(events[i])) |
| 199 /** | 196 return events[i]; |
| 200 * @param {!Array<!WebInspector.TracingModel.Event>} events | 197 } |
| 201 * @return {?WebInspector.TracingModel.Event} | 198 return null; |
| 202 */ | 199 } |
| 203 function findFirstTopLevelEvent(events) | 200 |
| 204 { | 201 var firstTopLevelEvent = findFirstTopLevelEvent(events); |
| 205 for (var i = 0; i < events.length; ++i) { | 202 if (firstTopLevelEvent) |
| 206 if (WebInspector.TracingModel.isTopLevelEvent(events[i])) | 203 WebInspector.TimelineModel.forEachEvent( |
| 207 return events[i]; | 204 events, onStartEvent, onEndEvent, onInstantEvent, firstTopLevelEvent.sta
rtTime); |
| 208 } | 205 return jsFrameEvents; |
| 209 return null; | |
| 210 } | |
| 211 | |
| 212 var firstTopLevelEvent = findFirstTopLevelEvent(events); | |
| 213 if (firstTopLevelEvent) | |
| 214 WebInspector.TimelineModel.forEachEvent(events, onStartEvent, onEndEvent
, onInstantEvent, firstTopLevelEvent.startTime); | |
| 215 return jsFrameEvents; | |
| 216 }; | 206 }; |
| OLD | NEW |