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