1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 tvcm.require('tracing.analysis.util');
8 tvcm.require('tracing.selection');
9 tvcm.require('tracing.timeline_view_side_panel');
10 tvcm.require('tvcm.iteration_helpers');
11 tvcm.require('tvcm.statistics');
12 tvcm.require('tvcm.ui.dom_helpers');
13 tvcm.require('tvcm.ui.pie_chart');
15 tvcm.requireTemplate('tracing.time_summary_side_panel');
17 tvcm.exportTo('tracing', function() {
18 var ThreadSlice = tracing.trace_model.ThreadSlice;
20 var OVERHEAD_TRACE_CATEGORY = 'trace_event_overhead';
21 var OVERHEAD_TRACE_NAME = 'overhead';
23 var tsRound = tracing.analysis.tsRound;
25 var RequestSelectionChangeEvent = tracing.RequestSelectionChangeEvent;
27 function getWallTimeOverheadForEvent(event) {
28 if (event.category == OVERHEAD_TRACE_CATEGORY &&
29 event.name == OVERHEAD_TRACE_NAME) {
30 return event.duration;
35 function getCpuTimeOverheadForEvent(event) {
36 if (event.category == OVERHEAD_TRACE_CATEGORY &&
38 return event.cpuDuration;
43 function getSlicesIntersectingRange(rangeOfInterest, slices) {
44 var slicesInFilterRange = [];
45 for (var i = 0; i < slices.length; i++) {
46 var slice = slices[i];
47 if (rangeOfInterest.intersectsExplicitRange(slice.start, slice.end))
48 slicesInFilterRange.push(slice);
50 return slicesInFilterRange;
54 * This function takes an array of groups and merges smaller groups into the
55 * provided 'Other' group item such that the remaining items are ready for
56 * pie-chart consumption. Otherwise, the pie chart gets overwhelmed with tons
59 function trimPieChartData(groups, otherGroup, getValue, opt_extraValue) {
60 // Copy the array so it can be mutated.
61 groups = groups.filter(function(d) {
62 return getValue(d) != 0;
65 // Figure out total array range.
66 var sum = tvcm.Statistics.sum(groups, getValue);
67 if (opt_extraValue !== undefined)
68 sum += opt_extraValue;
71 function compareByValue(a, b) {
72 return getValue(a) - getValue(b);
74 groups.sort(compareByValue);
76 // Now start fusing elements until none are less than threshold in size.
77 var thresshold = 0.1 * sum;
78 while (groups.length > 1) {
79 var group = groups[0];
80 if (getValue(group) >= thresshold)
83 var v = getValue(group);
84 if (v + getValue(otherGroup) > thresshold)
87 // Remove the group from the list and add it to the 'Other' group.
89 otherGroup.appendGroupContents(group);
93 if (getValue(otherGroup) > 0)
94 groups.push(otherGroup);
96 groups.sort(compareByValue);
101 function createPieChartFromResultGroups(
102 groups, title, getValue, opt_extraData) {
103 var chart = new tvcm.ui.PieChart();
105 function pushDataForGroup(data, resultsForGroup, value) {
107 label: resultsForGroup.name,
109 valueText: tsRound(value) + 'ms',
110 onClick: function() {
111 var event = new tracing.RequestSelectionChangeEvent();
112 event.selection = new tracing.Selection(resultsForGroup.allSlices);
113 event.selection.timeSummaryGroupName = resultsForGroup.name;
114 chart.dispatchEvent(event);
121 groups.forEach(function(resultsForGroup) {
122 var value = getValue(resultsForGroup);
125 pushDataForGroup(data, resultsForGroup, value);
128 data.push.apply(data, opt_extraData);
130 chart.chartTitle = title;
139 function ResultsForGroup(model, name) {
142 this.topLevelSlices = [];
146 ResultsForGroup.prototype = {
148 var wallSum = tvcm.Statistics.sum(
149 this.topLevelSlices, function(x) { return x.duration; });
155 for (var i = 0; i < this.topLevelSlices.length; i++) {
156 var x = this.topLevelSlices[i];
157 // Only report thread-duration if we have it for all events.
159 // A thread_duration of 0 is valid, so this only returns 0 if it is
161 if (x.cpuDuration === undefined) {
162 if (x.duration === undefined)
166 cpuDuration += x.cpuDuration;
173 appendGroupContents: function(group) {
174 if (group.model != this.model)
175 throw new Error('Models must be the same');
177 group.allSlices.forEach(function(slice) {
178 this.allSlices.push(slice);
180 group.topLevelSlices.forEach(function(slice) {
181 this.topLevelSlices.push(slice);
185 appendThreadSlices: function(rangeOfInterest, thread) {
186 var tmp = getSlicesIntersectingRange(
187 rangeOfInterest, thread.sliceGroup.slices);
188 tmp.forEach(function(slice) {
189 this.allSlices.push(slice);
191 tmp = getSlicesIntersectingRange(
192 rangeOfInterest, thread.sliceGroup.topLevelSlices);
193 tmp.forEach(function(slice) {
194 this.topLevelSlices.push(slice);
199 var GROUP_BY_PROCESS_NAME = 'process';
200 var GROUP_BY_THREAD_NAME = 'thread';
202 var WALL_TIME_GROUPING_UNIT = 'Wall time';
203 var CPU_TIME_GROUPING_UNIT = 'CPU time';
208 var TimeSummarySidePanel = tvcm.ui.define('x-time-summary-side-panel',
209 tracing.TimelineViewSidePanel);
210 TimeSummarySidePanel.textLabel = 'Thread Times';
211 TimeSummarySidePanel.supportsModel = function(m) {
217 TimeSummarySidePanel.prototype = {
218 __proto__: tracing.TimelineViewSidePanel.prototype,
220 decorate: function() {
221 tracing.TimelineViewSidePanel.prototype.decorate.call(this);
222 this.classList.add('x-time-summary-side-panel');
223 this.appendChild(tvcm.instantiateTemplate(
224 '#x-time-summary-side-panel-template'));
226 this.rangeOfInterest_ = new tvcm.Range();
227 this.selection_ = undefined;
228 this.groupBy_ = GROUP_BY_PROCESS_NAME;
229 this.groupingUnit_ = CPU_TIME_GROUPING_UNIT;
230 this.showCpuIdleTime_ = true;
231 this.chart_ = undefined;
233 var toolbarEl = this.querySelector('toolbar');
234 this.groupBySelector_ = tvcm.ui.createSelector(
236 'timeSummarySidePanel.groupBy', this.groupBy_,
237 [{label: 'Group by process', value: GROUP_BY_PROCESS_NAME},
238 {label: 'Group by thread', value: GROUP_BY_THREAD_NAME}
240 toolbarEl.appendChild(this.groupBySelector_);
242 this.groupingUnitSelector_ = tvcm.ui.createSelector(
243 this, 'groupingUnit',
244 'timeSummarySidePanel.groupingUnit', this.groupingUnit_,
245 [{label: 'Wall time', value: WALL_TIME_GROUPING_UNIT},
246 {label: 'CPU time', value: CPU_TIME_GROUPING_UNIT}
248 toolbarEl.appendChild(this.groupingUnitSelector_);
250 this.showCpuIdleTimeCheckbox_ = tvcm.ui.createCheckBox(
251 this, 'showCpuIdleTime',
252 'timeSummarySidePanel.showCpuIdleTime', this.showCpuIdleTime_,
253 'Show CPU idle time');
254 toolbarEl.appendChild(this.showCpuIdleTimeCheckbox_);
255 this.updateShowCpuIdleTimeCheckboxVisibility_();
264 this.updateContents_();
271 set groupBy(groupBy) {
272 this.groupBy_ = groupBy;
273 if (this.groupBySelector_)
274 this.groupBySelector_.selectedValue = groupBy;
275 this.updateContents_();
279 return groupingUnit_;
282 set groupingUnit(groupingUnit) {
283 this.groupingUnit_ = groupingUnit;
284 if (this.groupingUnitSelector_)
285 this.groupingUnitSelector_.selectedValue = groupingUnit;
286 this.updateShowCpuIdleTimeCheckboxVisibility_();
287 this.updateContents_();
290 get showCpuIdleTime() {
291 return this.showCpuIdleTime_;
294 set showCpuIdleTime(showCpuIdleTime) {
295 this.showCpuIdleTime_ = showCpuIdleTime;
296 if (this.showCpuIdleTimeCheckbox_)
297 this.showCpuIdleTimeCheckbox_.checked = showCpuIdleTime;
298 this.updateContents_();
301 updateShowCpuIdleTimeCheckboxVisibility_: function() {
302 if (!this.showCpuIdleTimeCheckbox_)
304 var visible = this.groupingUnit_ == CPU_TIME_GROUPING_UNIT;
306 this.showCpuIdleTimeCheckbox_.style.display = '';
308 this.showCpuIdleTimeCheckbox_.style.display = 'none';
311 getGroupNameForThread_: function(thread) {
312 if (this.groupBy_ == GROUP_BY_THREAD_NAME)
313 return thread.name ? thread.name : thread.userFriendlyName;
315 if (this.groupBy_ == GROUP_BY_PROCESS_NAME)
316 return thread.parent.userFriendlyName;
319 updateContents_: function() {
320 var resultArea = this.querySelector('result-area');
321 this.chart_ = undefined;
322 resultArea.textContent = '';
324 if (this.model_ === undefined)
328 if (this.rangeOfInterest_.isEmpty)
329 rangeOfInterest = this.model_.bounds;
331 rangeOfInterest = this.rangeOfInterest_;
333 var allGroup = new ResultsForGroup(this.model_, 'all');
334 var resultsByGroupName = {};
335 this.model_.getAllThreads().forEach(function(thread) {
336 var groupName = this.getGroupNameForThread_(thread);
337 if (resultsByGroupName[groupName] === undefined) {
338 resultsByGroupName[groupName] = new ResultsForGroup(
339 this.model_, groupName);
341 resultsByGroupName[groupName].appendThreadSlices(
342 rangeOfInterest, thread);
344 allGroup.appendThreadSlices(rangeOfInterest, thread);
347 // Helper function for working with the produced group.
348 var getValueFromGroup = function(group) {
349 if (this.groupingUnit_ == WALL_TIME_GROUPING_UNIT)
350 return group.wallTime;
351 return group.cpuTime;
355 var summaryText = document.createElement('div');
356 summaryText.appendChild(tvcm.ui.createSpan({
357 textContent: 'Total ' + this.groupingUnit_ + ': ',
359 summaryText.appendChild(tvcm.ui.createSpan({
360 textContent: tsRound(getValueFromGroup(allGroup)) + 'ms'
362 resultArea.appendChild(summaryText);
364 // If needed, add in the idle time.
367 if (this.showCpuIdleTime_ &&
368 this.groupingUnit_ === CPU_TIME_GROUPING_UNIT &&
369 this.model.kernel.bestGuessAtCpuCount !== undefined) {
370 var maxCpuTime = rangeOfInterest.range *
371 this.model.kernel.bestGuessAtCpuCount;
372 var idleTime = Math.max(0, maxCpuTime - allGroup.cpuTime);
376 valueText: tsRound(idleTime) + 'ms'
378 extraValue += idleTime;
381 // Create the actual chart.
382 var otherGroup = new ResultsForGroup(this.model_, 'Other');
383 var groups = trimPieChartData(
384 tvcm.dictionaryValues(resultsByGroupName),
389 if (groups.length == 0) {
390 resultArea.appendChild(tvcm.ui.createSpan({textContent: 'No data'}));
394 this.chart_ = createPieChartFromResultGroups(
396 this.groupingUnit_ + ' breakdown by ' + this.groupBy_,
397 getValueFromGroup, extraData);
398 resultArea.appendChild(this.chart_);
399 this.chart_.addEventListener('click', function() {
400 var event = new tracing.RequestSelectionChangeEvent();
401 event.selection = new tracing.Selection([]);
402 this.dispatchEvent(event);
404 this.chart_.setSize(this.chart_.getMinSize());
411 set selection(selection) {
412 this.selection_ = selection;
414 if (this.chart_ === undefined)
417 if (selection.timeSummaryGroupName) {
418 this.chart_.highlightedLegendKey =
419 selection.timeSummaryGroupName;
421 this.chart_.highlightedLegendKey = undefined;
425 get rangeOfInterest() {
426 return this.rangeOfInterest_;
429 set rangeOfInterest(rangeOfInterest) {
430 this.rangeOfInterest_ = rangeOfInterest;
431 this.updateContents_();
435 tracing.TimelineViewSidePanel.registerPanelSubtype(TimeSummarySidePanel);
438 trimPieChartData: trimPieChartData,
439 createPieChartFromResultGroups: createPieChartFromResultGroups,
440 ResultsForGroup: ResultsForGroup,
441 TimeSummarySidePanel: TimeSummarySidePanel,
443 GROUP_BY_PROCESS_NAME: GROUP_BY_PROCESS_NAME,
444 GROUP_BY_THREAD_NAME: GROUP_BY_THREAD_NAME,
445 WALL_TIME_GROUPING_UNIT: WALL_TIME_GROUPING_UNIT,
446 CPU_TIME_GROUPING_UNIT: CPU_TIME_GROUPING_UNIT