<!DOCTYPE html>
<!--
Copyright (c) 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="/experimental/mappers/reduce.html">
<link rel="import" href="/tracing/extras/ads/domain_category.html">
<link rel="import" href="/tracing/extras/chrome/slice_title_fixer.html">
<link rel="import" href="/tracing/model/source_info/js_source_info.html">
<script>
'use strict';
tr.exportTo('pi.m', function() {
var JSSourceState = tr.model.source_info.JSSourceState;
function SliceCostInfo() {
this.threadGroup = undefined;
this.railTypeName = undefined;
this.title = undefined;
this.domainCategory = undefined;
this.domain = undefined;
this.userFriendlyCategory = undefined;
this.selfTime = 0;
this.cpuSelfTime = 0;
this.jsTime = 0;
this.jsTimeByState = {};
for (var state in JSSourceState) {
this.jsTimeByState[JSSourceState[state]] = 0;
}
this.data = {};
}
SliceCostInfo.asReduceTarget = function(key, firstValue) {
var sliceCostInfo = new SliceCostInfo();
sliceCostInfo.threadGroup = firstValue.threadGroup;
sliceCostInfo.railTypeName = firstValue.railTypeName;
sliceCostInfo.title = firstValue.title;
sliceCostInfo.domainCategory = firstValue.domainCategory;
sliceCostInfo.domain = firstValue.domain;
sliceCostInfo.userFriendlyCategory = firstValue.userFriendlyCategory;
sliceCostInfo.data = firstValue.data;
return sliceCostInfo;
};
SliceCostInfo.fromDict = function(d) {
var sliceCostInfo = new SliceCostInfo();
sliceCostInfo.threadGroup = d.threadGroup;
sliceCostInfo.railTypeName = d.railTypeName;
sliceCostInfo.title = d.title;
sliceCostInfo.domainCategory = d.domainCategory;
sliceCostInfo.domain = d.domain;
sliceCostInfo.userFriendlyCategory = d.userFriendlyCategory;
sliceCostInfo.selfTime = d.selfTime;
sliceCostInfo.cpuSelfTime = d.cpuSelfTime;
sliceCostInfo.jsTime = d.jsTime || 0;
for (var state in JSSourceState) {
if (d.jsTimeByState === undefined) {
sliceCostInfo.jsTimeByState[state] = 0;
} else {
sliceCostInfo.jsTimeByState[JSSourceState[state]] =
d.jsTimeByState[JSSourceState[state]] || 0;
}
}
sliceCostInfo.data = d.data;
return sliceCostInfo;
};
SliceCostInfo.prototype = {
push: function(sliceCostKey, threadSlice) {
if (threadSlice.selfTime !== undefined)
this.selfTime += threadSlice.selfTime;
if (threadSlice.cpuSelfTime !== undefined)
this.cpuSelfTime += threadSlice.cpuSelfTime;
if (threadSlice.jsTime !== undefined)
this.jsTime += threadSlice.jsTime;
if (threadSlice.jsTimeByState !== undefined) {
for (var state in JSSourceState) {
this.jsTimeByState[JSSourceState[state]] +=
threadSlice.jsTimeByState[JSSourceState[state]];
}
}
},
finalizeAndGetResult: function() {
return this;
}
};
function getSliceCostReport(model, threadGrouping, railTypeNameByGUID,
filterFunction, dataCB) {
var reduce = new pi.m.StreamingReducer(SliceCostInfo.asReduceTarget);
function generateDomainCosts(slice) {
// 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) {
var sliceCostInfo = new SliceCostInfo();
sliceCostInfo.threadGroup = threadGrouping.getGroupNameForEvent(slice);
sliceCostInfo.railTypeName = railTypeNameByGUID[slice.guid];
var ufc = model.getUserFriendlyCategoryFromEvent(slice);
sliceCostInfo.userFriendlyCategory = ufc || 'other';
sliceCostInfo.title = tr.e.chrome.SliceTitleFixer.fromEvent(slice);
sliceCostInfo.domain = sample.leafStackFrame.domain;
sliceCostInfo.domainCategory =
tr.e.ads.DomainCategory.fromDomain(sliceCostInfo.domain);
sliceCostInfo.selfTime = sample.weight;
sliceCostInfo.cpuSelfTime = sample.weight;
if (dataCB !== undefined)
sliceCostInfo.data = dataCB(slice);
// 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 sourceInfo = sample.leafStackFrame.sourceInfo;
if (sourceInfo === undefined ||
!(sourceInfo instanceof tr.model.source_info.JSSourceInfo)) {
sliceCostInfo.jsTime = sample.weight;
sliceCostInfo.jsTimeByState[JSSourceState.UNKNOWN] = sample.weight;
} else {
sliceCostInfo.jsTimeByState[sourceInfo.state] = sample.weight;
}
var key = sliceCostInfo.threadGroup + '/' +
sliceCostInfo.railTypeName + '/' +
sliceCostInfo.title + '/' +
sliceCostInfo.domain;
reduce.push(key, sliceCostInfo);
});
}
for (var event of model.descendentEvents()) {
if (!(event instanceof tr.model.ThreadSlice))
continue;
if (filterFunction && !filterFunction(event))
continue;
var threadSlice = event;
if (threadSlice.title === 'V8.Execute') {
generateDomainCosts(threadSlice);
continue;
}
var sliceCostInfo = new SliceCostInfo();
sliceCostInfo.threadGroup = threadGrouping.getGroupNameForEvent(
threadSlice);
sliceCostInfo.railTypeName = railTypeNameByGUID[threadSlice.guid];
var ufc = model.getUserFriendlyCategoryFromEvent(threadSlice);
sliceCostInfo.userFriendlyCategory = ufc || 'other';
sliceCostInfo.title = tr.e.chrome.SliceTitleFixer.fromEvent(threadSlice);
// For all other events, just generate one sliceCostInfo.
sliceCostInfo.selfTime = threadSlice.selfTime;
sliceCostInfo.cpuSelfTime = threadSlice.cpuSelfTime;
if (dataCB !== undefined)
sliceCostInfo.data = dataCB(event);
var key = sliceCostInfo.threadGroup + '/' +
sliceCostInfo.railTypeName + '/' +
sliceCostInfo.title;
reduce.push(key, sliceCostInfo);
}
var sliceCostInfos = [];
reduce.finalizeAndIterResults(function(key, sliceCostInfo) {
sliceCostInfos.push(sliceCostInfo);
});
return sliceCostInfos;
}
tr.mre.FunctionRegistry.register(getSliceCostReport);
return {
SliceCostInfo: SliceCostInfo,
getSliceCostReport: getSliceCostReport
};
});
</script>