Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / input_latency_side_panel.js
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.
4
5 'use strict';
6
7 tvcm.require('tracing.analysis.util');
8 tvcm.require('tracing.timeline_view_side_panel');
9 tvcm.require('tvcm.iteration_helpers');
10 tvcm.require('tvcm.statistics');
11 tvcm.require('tvcm.ui.dom_helpers');
12 tvcm.require('tvcm.ui.line_chart');
13
14 tvcm.requireTemplate('tracing.input_latency_side_panel');
15
16 tvcm.exportTo('tracing', function() {
17
18   function createLatencyLineChart(data, title) {
19     var chart = new tvcm.ui.LineChart();
20     var width = 600;
21     if (document.body.clientWidth != undefined)
22       width = document.body.clientWidth * 0.5;
23     chart.setSize({width: width, height: chart.height});
24     chart.chartTitle = title;
25     chart.data = data;
26     return chart;
27   }
28
29   function getSlicesIntersectingRange(rangeOfInterest, slices) {
30     var slicesInFilterRange = [];
31     for (var i = 0; i < slices.length; i++) {
32       var slice = slices[i];
33       if (rangeOfInterest.intersectsExplicitRange(slice.start, slice.end))
34         slicesInFilterRange.push(slice);
35     }
36     return slicesInFilterRange;
37   }
38
39   var BROWSER_PROCESS_NAME = 'CrBrowserMain';
40
41   function findBrowserProcess(model) {
42     var browserProcess;
43     model.getAllProcesses().forEach(function(process) {
44       if (process.findAllThreadsNamed(BROWSER_PROCESS_NAME).length != 0)
45         browserProcess = process;
46     });
47     return browserProcess;
48   }
49
50   var MAIN_RENDERING_STATS =
51       'BenchmarkInstrumentation::MainThreadRenderingStats';
52   var IMPL_RENDERING_STATS =
53       'BenchmarkInstrumentation::ImplThreadRenderingStats';
54
55   var MAIN_FRAMETIME_TYPE = 'main_frametime_type';
56   var IMPL_FRAMETIME_TYPE = 'impl_frametime_type';
57
58   function getFrametimeData(model, frametimeType, rangeOfInterest) {
59     var mainRenderingSlices = [];
60     var implRenderingSlices = [];
61     var browserProcess = findBrowserProcess(model);
62     if (browserProcess != undefined) {
63       browserProcess.iterateAllEvents(function(event) {
64         if (event.title === MAIN_RENDERING_STATS)
65           mainRenderingSlices.push(event);
66         if (event.title === IMPL_RENDERING_STATS)
67           implRenderingSlices.push(event);
68       });
69     }
70
71     var renderingSlices = [];
72     if (frametimeType === MAIN_FRAMETIME_TYPE) {
73       renderingSlices = getSlicesIntersectingRange(rangeOfInterest,
74                                                    mainRenderingSlices);
75     } else if (frametimeType === IMPL_FRAMETIME_TYPE) {
76       renderingSlices = getSlicesIntersectingRange(rangeOfInterest,
77                                                    implRenderingSlices);
78     }
79
80     var frametimeData = [];
81     renderingSlices.sort(function(a, b) {return a.start - b.start});
82     for (var i = 1; i < renderingSlices.length; i++) {
83       var diff = renderingSlices[i].start - renderingSlices[i - 1].start;
84       frametimeData.push({'x': renderingSlices[i].start, 'frametime': diff});
85     }
86     return frametimeData;
87   }
88
89   var UI_COMP_NAME = 'INPUT_EVENT_LATENCY_UI_COMPONENT';
90   var ORIGINAL_COMP_NAME = 'INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';
91   var BEGIN_COMP_NAME = 'INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';
92   var END_COMP_NAME = 'INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';
93
94   function getLatencyData(model, rangeOfInterest) {
95     var latencySlices = [];
96     model.getAllThreads().forEach(function(thread) {
97       thread.iterateAllEvents(function(event) {
98         if (event.title.indexOf('InputLatency') === 0) {
99           latencySlices.push(event);
100         }
101       });
102     });
103
104     latencySlices = getSlicesIntersectingRange(rangeOfInterest,
105                                                latencySlices);
106     var latencyData = [];
107     var latency = 0;
108     var averageLatency = 0;
109
110     // Helper function that computes the input latency for one async slice.
111     function getLatency(event) {
112       if ((!('step' in event.args)) || (!('data' in event.args)))
113         return;
114
115       var data = event.args.data;
116       if (!(END_COMP_NAME in data))
117         return;
118
119       var endTime = data[END_COMP_NAME].time;
120       if (ORIGINAL_COMP_NAME in data) {
121         latency = endTime - data[ORIGINAL_COMP_NAME].time;
122       } else if (UI_COMP_NAME in data) {
123         latency = endTime - data[UI_COMP_NAME].time;
124       } else if (BEGIN_COMP_NAME in data) {
125         latency = endTime - data[BEGIN_COMP_NAME].time;
126       } else {
127         throw new Error('No valid begin latency component');
128       }
129       latencyData.push({'x': event.start, 'latency': latency / 1000.0});
130     };
131
132     latencySlices.forEach(getLatency);
133     latencyData.sort(function(a, b) {return a.x - b.x});
134     return latencyData;
135   }
136
137   /**
138    * @constructor
139    */
140   var InputLatencySidePanel = tvcm.ui.define('x-input-latency-side-panel',
141                                              tracing.TimelineViewSidePanel);
142   InputLatencySidePanel.textLabel = 'Input Latency';
143   InputLatencySidePanel.supportsModel = function(m) {
144     if (m == undefined) {
145       return {
146         supported: false,
147         reason: 'Unknown tracing model'
148       };
149     }
150
151     if (findBrowserProcess(m) === undefined) {
152       return {
153         supported: false,
154         reason: 'No browser process found'
155       };
156     }
157
158     var hasLatencyInfo = false;
159     m.getAllThreads().forEach(function(thread) {
160       thread.iterateAllEvents(function(event) {
161         if (event.title.indexOf('InputLatency') === 0)
162           hasLatencyInfo = true;
163       });
164     });
165
166     if (hasLatencyInfo) {
167       return {
168         supported: true
169       };
170     }
171
172     return {
173       supported: false,
174       reason: 'No InputLatency events trace. Consider enableing "benchmark" and "input" category when recording the trace' // @suppress longLineCheck
175     };
176   };
177
178   InputLatencySidePanel.prototype = {
179     __proto__: tracing.TimelineViewSidePanel.prototype,
180
181     decorate: function() {
182       tracing.TimelineViewSidePanel.prototype.decorate.call(this);
183       this.classList.add('x-input-latency-side-panel');
184       this.appendChild(tvcm.instantiateTemplate(
185           '#x-input-latency-side-panel-template'));
186
187       this.rangeOfInterest_ = new tvcm.Range();
188       this.frametimeType_ = MAIN_FRAMETIME_TYPE;
189       this.latencyChart_ = undefined;
190       this.frametimeChart_ = undefined;
191
192       var toolbarEl = this.querySelector('toolbar');
193       toolbarEl.appendChild(tvcm.ui.createSelector(
194           this, 'frametimeType',
195           'inputLatencySidePanel.frametimeType', this.frametimeType_,
196           [{label: 'Main Thread Frame Times', value: MAIN_FRAMETIME_TYPE},
197            {label: 'Impl Thread Frame Times', value: IMPL_FRAMETIME_TYPE}
198           ]));
199     },
200
201     get model() {
202       return this.model_;
203     },
204
205     set model(model) {
206       this.model_ = model;
207       this.updateContents_();
208     },
209
210     get frametimeType() {
211       return this.frametimeType_;
212     },
213
214     set frametimeType(type) {
215       if (this.frametimeType_ === type)
216         return;
217       this.frametimeType_ = type;
218       this.updateContents_();
219     },
220
221     updateContents_: function() {
222       var resultArea = this.querySelector('result-area');
223       this.latencyChart_ = undefined;
224       this.frametimeChart_ = undefined;
225       resultArea.textContent = '';
226
227       if (this.model_ === undefined)
228         return;
229
230       var rangeOfInterest;
231       if (this.rangeOfInterest_.isEmpty)
232         rangeOfInterest = this.model_.bounds;
233       else
234         rangeOfInterest = this.rangeOfInterest_;
235
236
237       var frametimeData = getFrametimeData(this.model_, this.frametimeType,
238                                            rangeOfInterest);
239       var averageFrametime = tvcm.Statistics.mean(frametimeData, function(d) {
240         return d.frametime});
241
242       var latencyData = getLatencyData(this.model_, rangeOfInterest);
243       var averageLatency = tvcm.Statistics.mean(latencyData, function(d) {
244         return d.latency});
245
246       // Create summary.
247       var latencySummaryText = document.createElement('div');
248       latencySummaryText.appendChild(tvcm.ui.createSpan({
249         textContent: 'Average Latency ' + averageLatency + 'ms',
250         bold: true}));
251       resultArea.appendChild(latencySummaryText);
252
253       var frametimeSummaryText = document.createElement('div');
254       frametimeSummaryText.appendChild(tvcm.ui.createSpan({
255         textContent: 'Average Frame Time ' + averageFrametime + 'ms',
256         bold: true}));
257       resultArea.appendChild(frametimeSummaryText);
258
259       if (latencyData.length != 0) {
260         this.latencyChart_ = createLatencyLineChart(latencyData,
261                                                     'Latency Over Time');
262         resultArea.appendChild(this.latencyChart_);
263       }
264
265       if (frametimeData.length != 0) {
266         this.frametimeChart_ = createLatencyLineChart(frametimeData,
267                                                       'Frame Times');
268         this.frametimeChart_.style.display = 'block';
269         resultArea.appendChild(this.frametimeChart_);
270       }
271     },
272
273     get rangeOfInterest() {
274       return this.rangeOfInterest_;
275     },
276
277     set rangeOfInterest(rangeOfInterest) {
278       this.rangeOfInterest_ = rangeOfInterest;
279       this.updateContents_();
280     }
281   };
282
283   tracing.TimelineViewSidePanel.registerPanelSubtype(InputLatencySidePanel);
284
285   return {
286     createLatencyLineChart: createLatencyLineChart,
287     getLatencyData: getLatencyData,
288     getFrametimeData: getFrametimeData,
289     InputLatencySidePanel: InputLatencySidePanel
290   };
291 });