blob: 4e6c29d06691787a2b886fb02ef108080793b397 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/perf_insights/function_handle.html">
<link rel="import" href="/perf_insights/mappers/slice_cost.html">
<link rel="import" href="/perf_insights/mappers/thread_grouping.html">
<link rel="import" href="/tracing/base/iteration_helpers.html">
<link rel="import" href="/tracing/model/helpers/chrome_model_helper.html">
<link rel="import" href="/tracing/model/ir_coverage.html">
<link rel="import" href="/tracing/model/user_model/user_expectation.html">
<link rel="import" href="/tracing/value/unit.html">
<script>
'use strict';
tr.exportTo('pi.m', function() {
function v8ReportMapFunction(result, model) {
var allIRs = [];
model.userModel.expectations.forEach(function(ir) {
if (!(ir instanceof tr.model.um.UserExpectation))
return;
allIRs.push(ir);
});
var railTypeNameByGUID = getStageTitleForEventsByGUID(model, allIRs);
var threadGrouping = new pi.m.ThreadGrouping();
threadGrouping.autoInitUsingHelpers(model);
var last_known_framework = ['Unknown/Uncategorized'];
var sliceCosts = [];
model.iterateAllEvents(function(event) {
if (!(event instanceof tr.model.ThreadSlice))
return;
if (!event.title.startsWith('V8.') && !event.title.startsWith('V8Test.'))
return;
function _get_parent_data(event) {
var curSlice = event;
var data = {};
data['js'] = 'Unknown';
while (curSlice) {
if (curSlice.title === 'v8.run') {
data['js'] = curSlice.args['fileName'];
} else if (curSlice.title === 'v8.compile') {
data['js'] = curSlice.args['fileName'];
} else if (curSlice.title === 'v8.callModuleMethod') {
data['js'] = 'Unknown';
} else if (curSlice.title === 'FunctionCall') {
var scriptName = curSlice.args['data']['scriptName'];
if (scriptName.indexOf('http') != -1) {
data['js'] = scriptName;
}
} else if (curSlice.title === 'V8Test.ParseScript') {
data['js'] = curSlice.args['name'];
} else if (curSlice.title === 'V8Test.Compile') {
data['js'] = curSlice.args['name'];
} else if (curSlice.title === 'V8Test.CompileFullCode') {
data['js'] = curSlice.args['name'];
}
curSlice = curSlice.parentSlice;
}
return data;
}
function _guess_framework_from_js_file(js_file) {
var frameworks = {
// Some commone js libs.
'jQuery': ['jquery'],
'Angular': ['angular'],
'Underscore': ['underscore'],
'Respond.js': ['respond.js'],
'Easeljs': ['easeljs'],
'Modernizr': ['modernizr'],
'Cloudflare': ['cloudflare.min.js'],
'Greensock': ['gsap/'],
'Mootools': ['mootools'],
'Zepto': ['zepto.'],
'Webfont': ['webfont.js'],
'Closure': ['closure/'],
'Ektron': ['ektron'],
'SWFObject': ['swfobject'],
'Html5shiv': ['html5shiv'],
'Requirejs': ['require.js'],
'Tweenjs': ['tweenjs'],
// Just dividing these out into common sites.
'Google - Search': ['google.com/search?', 'www.google.'],
'Google - Adsense': ['pagead2.googlesyndication.com/pagead/'],
'Google - Analytics': ['google-analytics.com'],
'Google - Misc': ['google.', 'googleapis.'],
'Adobe - Misc': ['adobe.'],
'Facebook': ['facebook.', 'fbcdn.'],
'Outlook': ['outlook.', '.live.'],
'Craigslist': ['craigslist.'],
'Amazon': ['amazon.'],
'Yandex': ['yandex.'],
'Scene7': ['s7sdk/'],
'DoubleClick': [
'.doubleclick', 'gpt.js', 'gtm.js', '.googletagservices.'],
'Baidu': ['baidu.'],
'Bing': ['bing.'],
'Twitter': ['twitter.'],
'Wish': ['MobileWebsiteCore'],
'Extensions - Misc': ['chrome-extension://', 'chrome://']
};
var js_file_lowercase = js_file.toLowerCase();
for (var k in frameworks) {
var keywords = frameworks[k];
for (var i = 0; i < keywords.length; i++) {
if (js_file_lowercase.indexOf(keywords[i]) > -1) {
//last_known_framework[0] = k;
return k;
}
}
}
// TODO: This is terrible, find a better way to attribute the
// unknown callers to a framework. Ideally we'd actually have
// access to data about the script or method that's running.
return last_known_framework[0];
}
function _cleanup_framework_name(name) {
var js_name = name;
if (js_name === '') {
js_name = 'Unknown';
}
if (js_name.length > 120) {
js_name = js_name.substring(0, 120) + '...';
}
return js_name;
}
var ufc = model.getUserFriendlyCategoryFromEvent(event);
var data = _get_parent_data(event);
data.framework = _guess_framework_from_js_file(data.js);
var scriptURLClean = _cleanup_framework_name(data.js);
var slice = event;
if (slice.title == 'V8.Execute') {
// V8.Execute events may generate several sliceCostInfo, based on the
// origin of the JS being executed.
var range = new tr.b.Range();
slice.addBoundsToRange(range);
var filtered = range.filterArray(
slice.parentContainer.samples,
function(sample) {return sample.start;});
filtered.forEach(function(sample) {
// Let's use the state of the leaf frame. TODO(chiniforooshan):
// understand what it means if frames of a sample stack are in
// different states (BUG #1542).
var sliceData = {
threadGroup: threadGrouping.getGroupNameForEvent(slice),
railTypeName: railTypeNameByGUID[slice.guid],
userFriendlyCategory: ufc || 'other',
title: tr.e.chrome.SliceTitleFixer.fromEvent(slice),
selfTime: sample.weight,
cpuSelfTime: sample.weight,
scriptURL: data.js,
scriptURLClean: scriptURLClean,
framework: data.framework,
traceURL: model.canonicalUrlThatCreatedThisTrace
};
var JSSourceState = tr.model.source_info.JSSourceState;
sliceData.jsTimeByState = {};
for (var state in JSSourceState) {
sliceData.jsTimeByState[JSSourceState[state]] = 0;
}
var sourceInfo = sample.leafStackFrame.sourceInfo;
if (sourceInfo === undefined ||
!(sourceInfo instanceof tr.model.source_info.JSSourceInfo)) {
sliceData.jsTime = sample.weight;
sliceData.jsTimeByState[JSSourceState.UNKNOWN] = sample.weight;
} else {
sliceData.jsTimeByState[sourceInfo.state] = sample.weight;
}
var key = sliceData.threadGroup + '/' +
sliceData.railTypeName + '/' +
sliceData.framework + '/' +
//sliceData.scriptURLClean + '/' +
sliceData.title;
sliceCosts.push({key: key, value: sliceData});
});
return;
}
var sliceData = {
threadGroup: threadGrouping.getGroupNameForEvent(event),
railTypeName: railTypeNameByGUID[event.guid],
userFriendlyCategory: ufc || 'other',
title: tr.e.chrome.SliceTitleFixer.fromEvent(event),
selfTime: event.selfTime,
cpuSelfTime: event.cpuSelfTime,
scriptURL: data.js,
scriptURLClean: scriptURLClean,
framework: data.framework,
traceURL: model.canonicalUrlThatCreatedThisTrace
};
var key = sliceData.threadGroup + '/' +
sliceData.railTypeName + '/' +
sliceData.framework + '/' +
//sliceData.scriptURLClean + '/' +
sliceData.title;
var newElement = {
key: key,
value: sliceData
};
sliceCosts.push(newElement);
});
result.addPair('wr', sliceCosts);
}
function getStageTitleForEventsByGUID(model, expectations) {
var stageTitleByGUID = {};
expectations.forEach(function applyAssociatedToRTN(ir) {
ir.associatedEvents.forEach(function applyEventToRTN(event) {
// Unassociated events have already been assigned to a RTN.
if (stageTitleByGUID[event.guid] !== undefined)
return;
stageTitleByGUID[event.guid] = ir.stageTitle;
}, this);
}, this);
model.iterateAllEvents(function storeEventToUnassociatedSet(event) {
if (stageTitleByGUID[event.guid] !== undefined)
return;
stageTitleByGUID[event.guid] = 'Unknown';
});
return stageTitleByGUID;
}
pi.FunctionRegistry.register(v8ReportMapFunction);
// Exporting for tests.
return {
v8ReportMapFunction: v8ReportMapFunction
};
});
</script>