Home | History | Annotate | Download | only in mappers
      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