<!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>