| OLD | NEW |
| (Empty) |
| 1 <!DOCTYPE html> | |
| 2 <!-- | |
| 3 Copyright 2015 The Chromium Authors. All rights reserved. | |
| 4 Use of this source code is governed by a BSD-style license that can be | |
| 5 found in the LICENSE file. | |
| 6 --> | |
| 7 <link rel="import" href="/perf_insights/mappers/slice_cost.html"> | |
| 8 <link rel="import" href="/perf_insights/mappers/thread_grouping.html"> | |
| 9 <link rel="import" href="/perf_insights/mre/function_handle.html"> | |
| 10 <link rel="import" href="/tracing/base/iteration_helpers.html"> | |
| 11 <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> | |
| 12 <link rel="import" href="/tracing/model/ir_coverage.html"> | |
| 13 <link rel="import" href="/tracing/model/user_model/user_expectation.html"> | |
| 14 <link rel="import" href="/tracing/value/unit.html"> | |
| 15 | |
| 16 <script> | |
| 17 'use strict'; | |
| 18 | |
| 19 | |
| 20 tr.exportTo('pi.m', function() { | |
| 21 function v8ReportMapFunction(result, model) { | |
| 22 var allIRs = []; | |
| 23 model.userModel.expectations.forEach(function(ir) { | |
| 24 if (!(ir instanceof tr.model.um.UserExpectation)) | |
| 25 return; | |
| 26 allIRs.push(ir); | |
| 27 }); | |
| 28 | |
| 29 var railTypeNameByGUID = getStageTitleForEventsByGUID(model, allIRs); | |
| 30 | |
| 31 var threadGrouping = new pi.m.ThreadGrouping(); | |
| 32 threadGrouping.autoInitUsingHelpers(model); | |
| 33 var last_known_framework = ['Unknown/Uncategorized']; | |
| 34 | |
| 35 var sliceCosts = []; | |
| 36 | |
| 37 model.iterateAllEvents(function(event) { | |
| 38 if (!(event instanceof tr.model.ThreadSlice)) | |
| 39 return; | |
| 40 | |
| 41 if (!event.title.startsWith('V8.') && !event.title.startsWith('V8Test.')) | |
| 42 return; | |
| 43 | |
| 44 function _get_parent_data(event) { | |
| 45 var curSlice = event; | |
| 46 | |
| 47 var data = {}; | |
| 48 data['js'] = 'Unknown'; | |
| 49 while (curSlice) { | |
| 50 if (curSlice.title === 'v8.run') { | |
| 51 data['js'] = curSlice.args['fileName']; | |
| 52 } else if (curSlice.title === 'v8.compile') { | |
| 53 data['js'] = curSlice.args['fileName']; | |
| 54 } else if (curSlice.title === 'v8.callModuleMethod') { | |
| 55 data['js'] = 'Unknown'; | |
| 56 } else if (curSlice.title === 'FunctionCall') { | |
| 57 var scriptName = curSlice.args['data']['scriptName']; | |
| 58 if (scriptName.indexOf('http') != -1) { | |
| 59 data['js'] = scriptName; | |
| 60 } | |
| 61 } else if (curSlice.title === 'V8Test.ParseScript') { | |
| 62 data['js'] = curSlice.args['name']; | |
| 63 } else if (curSlice.title === 'V8Test.Compile') { | |
| 64 data['js'] = curSlice.args['name']; | |
| 65 } else if (curSlice.title === 'V8Test.CompileFullCode') { | |
| 66 data['js'] = curSlice.args['name']; | |
| 67 } | |
| 68 curSlice = curSlice.parentSlice; | |
| 69 } | |
| 70 return data; | |
| 71 } | |
| 72 | |
| 73 function _guess_framework_from_js_file(js_file) { | |
| 74 var frameworks = { | |
| 75 // Some commone js libs. | |
| 76 'jQuery': ['jquery'], | |
| 77 'Angular': ['angular'], | |
| 78 'Underscore': ['underscore'], | |
| 79 'Respond.js': ['respond.js'], | |
| 80 'Easeljs': ['easeljs'], | |
| 81 'Modernizr': ['modernizr'], | |
| 82 'Cloudflare': ['cloudflare.min.js'], | |
| 83 'Greensock': ['gsap/'], | |
| 84 'Mootools': ['mootools'], | |
| 85 'Zepto': ['zepto.'], | |
| 86 'Webfont': ['webfont.js'], | |
| 87 'Closure': ['closure/'], | |
| 88 'Ektron': ['ektron'], | |
| 89 'SWFObject': ['swfobject'], | |
| 90 'Html5shiv': ['html5shiv'], | |
| 91 'Requirejs': ['require.js'], | |
| 92 'Tweenjs': ['tweenjs'], | |
| 93 | |
| 94 // Just dividing these out into common sites. | |
| 95 'Google - Search': ['google.com/search?', 'www.google.'], | |
| 96 'Google - Adsense': ['pagead2.googlesyndication.com/pagead/'], | |
| 97 'Google - Analytics': ['google-analytics.com'], | |
| 98 'Google - Misc': ['google.', 'googleapis.'], | |
| 99 'Adobe - Misc': ['adobe.'], | |
| 100 'Facebook': ['facebook.', 'fbcdn.'], | |
| 101 'Outlook': ['outlook.', '.live.'], | |
| 102 'Craigslist': ['craigslist.'], | |
| 103 'Amazon': ['amazon.'], | |
| 104 'Yandex': ['yandex.'], | |
| 105 'Scene7': ['s7sdk/'], | |
| 106 'DoubleClick': [ | |
| 107 '.doubleclick', 'gpt.js', 'gtm.js', '.googletagservices.'], | |
| 108 'Baidu': ['baidu.'], | |
| 109 'Bing': ['bing.'], | |
| 110 'Twitter': ['twitter.'], | |
| 111 'Wish': ['MobileWebsiteCore'], | |
| 112 'Extensions - Misc': ['chrome-extension://', 'chrome://'] | |
| 113 }; | |
| 114 | |
| 115 var js_file_lowercase = js_file.toLowerCase(); | |
| 116 for (var k in frameworks) { | |
| 117 var keywords = frameworks[k]; | |
| 118 for (var i = 0; i < keywords.length; i++) { | |
| 119 if (js_file_lowercase.indexOf(keywords[i]) > -1) { | |
| 120 //last_known_framework[0] = k; | |
| 121 return k; | |
| 122 } | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 // TODO: This is terrible, find a better way to attribute the | |
| 127 // unknown callers to a framework. Ideally we'd actually have | |
| 128 // access to data about the script or method that's running. | |
| 129 return last_known_framework[0]; | |
| 130 } | |
| 131 | |
| 132 function _cleanup_framework_name(name) { | |
| 133 var js_name = name; | |
| 134 if (js_name === '') { | |
| 135 js_name = 'Unknown'; | |
| 136 } | |
| 137 if (js_name.length > 120) { | |
| 138 js_name = js_name.substring(0, 120) + '...'; | |
| 139 } | |
| 140 return js_name; | |
| 141 } | |
| 142 | |
| 143 var ufc = model.getUserFriendlyCategoryFromEvent(event); | |
| 144 var data = _get_parent_data(event); | |
| 145 data.framework = _guess_framework_from_js_file(data.js); | |
| 146 var scriptURLClean = _cleanup_framework_name(data.js); | |
| 147 | |
| 148 var slice = event; | |
| 149 if (slice.title == 'V8.Execute') { | |
| 150 | |
| 151 // V8.Execute events may generate several sliceCostInfo, based on the | |
| 152 // origin of the JS being executed. | |
| 153 var range = new tr.b.Range(); | |
| 154 slice.addBoundsToRange(range); | |
| 155 var filtered = range.filterArray( | |
| 156 slice.parentContainer.samples, | |
| 157 function(sample) {return sample.start;}); | |
| 158 filtered.forEach(function(sample) { | |
| 159 // Let's use the state of the leaf frame. TODO(chiniforooshan): | |
| 160 // understand what it means if frames of a sample stack are in | |
| 161 // different states (BUG #1542). | |
| 162 var sliceData = { | |
| 163 threadGroup: threadGrouping.getGroupNameForEvent(slice), | |
| 164 railTypeName: railTypeNameByGUID[slice.guid], | |
| 165 userFriendlyCategory: ufc || 'other', | |
| 166 title: tr.e.chrome.SliceTitleFixer.fromEvent(slice), | |
| 167 selfTime: sample.weight, | |
| 168 cpuSelfTime: sample.weight, | |
| 169 scriptURL: data.js, | |
| 170 scriptURLClean: scriptURLClean, | |
| 171 framework: data.framework, | |
| 172 traceURL: model.canonicalUrl | |
| 173 }; | |
| 174 | |
| 175 var JSSourceState = tr.model.source_info.JSSourceState; | |
| 176 sliceData.jsTimeByState = {}; | |
| 177 for (var state in JSSourceState) { | |
| 178 sliceData.jsTimeByState[JSSourceState[state]] = 0; | |
| 179 } | |
| 180 | |
| 181 var sourceInfo = sample.leafStackFrame.sourceInfo; | |
| 182 if (sourceInfo === undefined || | |
| 183 !(sourceInfo instanceof tr.model.source_info.JSSourceInfo)) { | |
| 184 sliceData.jsTime = sample.weight; | |
| 185 sliceData.jsTimeByState[JSSourceState.UNKNOWN] = sample.weight; | |
| 186 } else { | |
| 187 sliceData.jsTimeByState[sourceInfo.state] = sample.weight; | |
| 188 } | |
| 189 | |
| 190 var key = sliceData.threadGroup + '/' + | |
| 191 sliceData.railTypeName + '/' + | |
| 192 sliceData.framework + '/' + | |
| 193 //sliceData.scriptURLClean + '/' + | |
| 194 sliceData.title; | |
| 195 sliceCosts.push({key: key, value: sliceData}); | |
| 196 }); | |
| 197 return; | |
| 198 } | |
| 199 | |
| 200 var sliceData = { | |
| 201 threadGroup: threadGrouping.getGroupNameForEvent(event), | |
| 202 railTypeName: railTypeNameByGUID[event.guid], | |
| 203 userFriendlyCategory: ufc || 'other', | |
| 204 title: tr.e.chrome.SliceTitleFixer.fromEvent(event), | |
| 205 selfTime: event.selfTime, | |
| 206 cpuSelfTime: event.cpuSelfTime, | |
| 207 scriptURL: data.js, | |
| 208 scriptURLClean: scriptURLClean, | |
| 209 framework: data.framework, | |
| 210 traceURL: model.canonicalUrl | |
| 211 }; | |
| 212 | |
| 213 var key = sliceData.threadGroup + '/' + | |
| 214 sliceData.railTypeName + '/' + | |
| 215 sliceData.framework + '/' + | |
| 216 //sliceData.scriptURLClean + '/' + | |
| 217 sliceData.title; | |
| 218 | |
| 219 var newElement = { | |
| 220 key: key, | |
| 221 value: sliceData | |
| 222 }; | |
| 223 sliceCosts.push(newElement); | |
| 224 }); | |
| 225 | |
| 226 result.addPair('wr', sliceCosts); | |
| 227 } | |
| 228 | |
| 229 function getStageTitleForEventsByGUID(model, expectations) { | |
| 230 var stageTitleByGUID = {}; | |
| 231 expectations.forEach(function applyAssociatedToRTN(ir) { | |
| 232 ir.associatedEvents.forEach(function applyEventToRTN(event) { | |
| 233 // Unassociated events have already been assigned to a RTN. | |
| 234 if (stageTitleByGUID[event.guid] !== undefined) | |
| 235 return; | |
| 236 stageTitleByGUID[event.guid] = ir.stageTitle; | |
| 237 }, this); | |
| 238 }, this); | |
| 239 | |
| 240 model.iterateAllEvents(function storeEventToUnassociatedSet(event) { | |
| 241 if (stageTitleByGUID[event.guid] !== undefined) | |
| 242 return; | |
| 243 stageTitleByGUID[event.guid] = 'Unknown'; | |
| 244 }); | |
| 245 return stageTitleByGUID; | |
| 246 } | |
| 247 | |
| 248 pi.FunctionRegistry.register(v8ReportMapFunction); | |
| 249 | |
| 250 // Exporting for tests. | |
| 251 return { | |
| 252 v8ReportMapFunction: v8ReportMapFunction | |
| 253 }; | |
| 254 }); | |
| 255 | |
| 256 </script> | |
| OLD | NEW |