Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / side_panel / sampling_summary.html
1 <!DOCTYPE html>
2 <!--
3 Copyright (c) 2014 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
6 -->
7
8 <link rel="import" href="/tracing/analysis/util.html">
9 <link rel="import" href="/tracing/selection.html">
10 <link rel="import" href="/tracing/side_panel/timeline_view.html">
11 <link rel="import" href="/tvcm/iteration_helpers.html">
12 <link rel="import" href="/tvcm/statistics.html">
13 <link rel="import" href="/tvcm/ui/dom_helpers.html">
14 <link rel="import" href="/tvcm/ui/pie_chart.html">
15 <link rel="import" href="/tvcm/ui/sortable_table.html">
16 <link rel="import" href="/tvcm/ui/sunburst_chart.html">
17
18 <template id="x-sample-summary-panel-template">
19   <style>
20     .x-sample-summary-panel {
21       display: block;
22       overflow: auto;
23       padding: 5px;
24     }
25     .x-sample-summary-panel > x-toolbar {
26       display: block;
27       border-bottom: 1px solid black;
28     }
29
30     .x-sample-summary-panel > x-left-panel {
31       position: relative;
32       width: 610px;
33       left: 0;
34       top: 0;
35       height: 610px;
36       padding: 5px;
37     }
38
39     .x-sample-summary-panel > x-left-panel > result-area {
40       display: block;
41       width: 610px;
42       position: absolute;
43       left: 0;
44       top: 0;
45       height: 610px;
46     }
47
48     .x-sample-summary-panel > x-left-panel > x-explanation {
49       display: block;
50       position: absolute;
51       top: 300px;
52       left: 250px;
53       width: 100px;
54       height: 100px;
55       text-align: center;
56       vertical-align:middle;
57       color: #666;
58       font-size: 12px;
59     }
60
61     .x-sample-summary-panel > x-right-panel {
62       display: block;
63       min-height: 610px;
64       margin-left: 610px;
65       padding: 5px;
66     }
67
68     .x-sample-summary-panel > x-right-panel td {
69       color: #fff;
70       padding: 1px 5px;
71       font-size: 1.0em;
72       white-space: nowrap;
73     }
74
75     .x-sample-summary-panel > x-right-panel > x-sequence {
76       display: block;
77       overflow: auto;
78       width: 600px;
79       height: 400px;
80       font-size: 14px;
81       margin: 10px;
82     }
83
84     .x-sample-summary-panel > x-right-panel > x-sequence table {
85       min-width: 600px;
86     }
87
88     .x-sample-summary-panel > x-right-panel > x-callees {
89       display: block;
90       position: relative;
91       width: 600px;
92       height: auto;
93       overflow: auto;
94       font-size: 14px;
95       margin: 10px;
96     }
97
98     .x-sample-summary-panel > x-right-panel > x-callees .x-col-numeric {
99       width: 30px;
100     }
101
102     .x-sample-summary-panel > x-right-panel > x-callees .x-td-numeric {
103       text-align: right;
104     }
105
106     .x-sample-summary-panel > x-right-panel > x-callees table {
107       min-width: 600px;
108       max-width: 600px;
109     }
110
111     .x-sample-summary-panel > x-right-panel > x-callees {
112       display: block;
113       overflow: auto;
114       height: 300px;
115     }
116
117   </style>
118
119   <x-toolbar></x-toolbar>
120   <x-left-panel>
121     <result-area></result-area>
122     <x-explanation></x-explanation>
123   </x-left-panel>
124   <x-right-panel>
125     <x-sequence></x-sequence>
126     <x-callees></x-callees>
127   </x-right-panel>
128 </template>
129
130 <script>
131 'use strict';
132
133 tvcm.exportTo('tracing', function() {
134   var THIS_DOC = document.currentScript.ownerDocument;
135
136   var RequestSelectionChangeEvent = tracing.RequestSelectionChangeEvent;
137   var getColorOfKey = tvcm.ui.getColorOfKey;
138
139   /**
140     * @constructor
141     */
142   var CallTreeNode = function(name, category) {
143     this.parent = undefined;
144     this.name = name;
145     this.category = category;
146     this.selfTime = 0.0;
147     this.children = [];
148
149     // Defined only for leaf nodes, leaf_node_id corresponds to
150     // the id of the leaf stack frame.
151     this.leaf_node_id = undefined;
152   };
153
154   CallTreeNode.prototype = {
155     addChild: function(node) {
156       this.children.push(node);
157       node.parent = this;
158     }
159   };
160
161   /**
162    * @constructor
163    */
164   var Thread = function(thread) {
165     this.thread = thread;
166     this.rootNode = new CallTreeNode('root', 'root');
167     this.rootCategories = {};
168     this.sfToNode = {};
169     this.sfToNode[0] = self.rootNode;
170   };
171
172   Thread.prototype = {
173     getCallTreeNode: function(stackFrame) {
174       if (stackFrame.id in this.sfToNode)
175         return this.sfToNode[stackFrame.id];
176
177       // Create & save the node.
178       var newNode = new CallTreeNode(stackFrame.title, stackFrame.category);
179       this.sfToNode[stackFrame.id] = newNode;
180
181       // Add node to parent tree node.
182       if (stackFrame.parentFrame) {
183         var parentNode = this.getCallTreeNode(stackFrame.parentFrame);
184         parentNode.addChild(newNode);
185       } else {
186         // Creating a root category node for each category helps group samples
187         // that may be missing call stacks.
188         var rootCategory = this.rootCategories[stackFrame.category];
189         if (!rootCategory) {
190           rootCategory =
191               new CallTreeNode(stackFrame.category, stackFrame.category);
192           this.rootNode.addChild(rootCategory);
193           this.rootCategories[stackFrame.category] = rootCategory;
194         }
195         rootCategory.addChild(newNode);
196       }
197       return newNode;
198     },
199
200     addSample: function(sample) {
201       var leaf_node = this.getCallTreeNode(sample.leafStackFrame);
202       leaf_node.leaf_node_id = sample.leafStackFrame.id;
203       leaf_node.selfTime += sample.weight;
204     }
205   };
206
207   function genCallTree(node, isRoot) {
208     var ret = {
209       category: node.category,
210       name: node.name,
211       leaf_node_id: node.leaf_node_id
212     };
213
214     if (isRoot || node.children.length > 0) {
215       ret.children = [];
216       for (var c = 0; c < node.children.length; c++)
217         ret.children.push(genCallTree(node.children[c], false));
218       if (node.selfTime > 0.0) {
219         // say, caller (r) calls callee (e) which calls callee2 (2)
220         // and following are the samples
221         // r r r r r r r
222         //   e e e
223         //   2 2 2
224         // In this case, r has a non-zero self time (4 samples to be precise)
225         // The <self> node makes the representation resemble the following
226         // where s denotes the selftime.
227         // r r r r r r r
228         // s e e e s s s
229         //   2 2 2
230         //
231         // Among the obvious visualization benefit, this also creates the
232         // invariance that a node can not simultaneously have samples and children.
233         ret.children.push({
234           name: '<self>',
235           category: ret.category,
236           size: node.selfTime,
237           leaf_node_id: node.leaf_node_id
238         });
239         delete ret.leaf_node_id;  // ret is not a leaf node anymore.
240       }
241     }
242     else {
243       ret.size = node.selfTime;
244     }
245     if (isRoot)
246       return ret.children;
247     return ret;
248   }
249
250   function getSampleTypes(selection) {
251     var sampleDict = {};
252     var samples = selection.getEventsOrganizedByType().samples;
253     for (var i = 0; i < samples.length; i++) {
254       sampleDict[samples[i].title] = null;
255     }
256     return Object.keys(sampleDict);
257   }
258
259   // Create sunburst data from the selection.
260   function createSunburstData(selection, sampleType) {
261     var threads = {};
262     function getOrCreateThread(thread) {
263       var ret = undefined;
264       if (thread.tid in threads) {
265         ret = threads[thread.tid];
266       } else {
267         ret = new Thread(thread);
268         threads[thread.tid] = ret;
269       }
270       return ret;
271     }
272
273     // Process samples.
274     var samples = selection.getEventsOrganizedByType().samples;
275     for (var i = 0; i < samples.length; i++) {
276       var sample = samples[i];
277       if (sample.title == sampleType)
278         getOrCreateThread(sample.thread).addSample(sample);
279     }
280
281     // Generate sunburst data.
282     var sunburstData = {
283       name: '<All Threads>',
284       category: 'root',
285       children: []
286     };
287     for (var t in threads) {
288       if (!threads.hasOwnProperty(t)) continue;
289       var thread = threads[t];
290       var threadData = {
291         name: 'Thread ' + thread.thread.tid + ': ' + thread.thread.name,
292         category: 'Thread',
293         children: genCallTree(thread.rootNode, true)
294       };
295       sunburstData.children.push(threadData);
296     }
297     return sunburstData;
298   }
299
300   /**
301    * @constructor
302    */
303   var SamplingSummaryPanel =
304       tvcm.ui.define('x-sample-summary-panel',
305                      tracing.TimelineViewSidePanel);
306   SamplingSummaryPanel.textLabel = 'Sampling Summary';
307   SamplingSummaryPanel.supportsModel = function(m) {
308     if (m == undefined) {
309       return {
310         supported: false,
311         reason: 'Unknown tracing model'
312       };
313     }
314
315     if (m.samples.length == 0) {
316       return {
317         supported: false,
318         reason: 'No sampling data in trace'
319       };
320     }
321
322     return {
323       supported: true
324     };
325   };
326
327   // Return a dict with keys as stack-frame ids
328   // and values as selection objects which contain all the
329   // samples whose leaf stack-frame id matches the key.
330   function divideSamplesBasedOnLeafStackFrame(selection) {
331     var stackFrameIdToSamples = {};
332     for (var i = 0; i < selection.length; ++i) {
333       var sample = selection[i];
334       var id = sample.leafStackFrame.id;
335       if (!stackFrameIdToSamples[id])
336         stackFrameIdToSamples[id] = new tracing.Selection();
337       stackFrameIdToSamples[id].push(sample);
338     }
339     return stackFrameIdToSamples;
340   }
341
342   SamplingSummaryPanel.prototype = {
343     __proto__: tracing.TimelineViewSidePanel.prototype,
344
345     decorate: function() {
346       tracing.TimelineViewSidePanel.prototype.decorate.call(this);
347       this.classList.add('x-sample-summary-panel');
348       this.appendChild(tvcm.instantiateTemplate(
349           '#x-sample-summary-panel-template', THIS_DOC));
350
351       this.sampleType_ = undefined;
352       this.sampleTypeSelector_ = undefined;
353       this.chart_ = undefined;
354       this.selection_ = undefined;
355     },
356
357     get selection() {
358       return this.selection_;
359     },
360
361     set selection(selection) {
362       this.selection_ = selection;
363       this.stackFrameIdToSamples_ = divideSamplesBasedOnLeafStackFrame(selection);
364       this.updateContents_();
365     },
366
367     get sampleType() {
368       return this.sampleType_;
369     },
370
371     set sampleType(type) {
372       this.sampleType_ = type;
373       if (this.sampleTypeSelector_)
374         this.sampleTypeSelector_.selectedValue = type;
375       this.updateResultArea_();
376     },
377
378     updateCallees_: function(d) {
379       // Update callee table.
380       var that = this;
381       var table = document.createElement('table');
382
383       // Add column styles.
384       var col0 = document.createElement('col');
385       var col1 = document.createElement('col');
386       col0.className += 'x-col-numeric';
387       col1.className += 'x-col-numeric';
388       table.appendChild(col0);
389       table.appendChild(col1);
390
391       // Add headers.
392       var thead = table.createTHead();
393       var headerRow = thead.insertRow(0);
394       headerRow.style.backgroundColor = '#888';
395       headerRow.insertCell(0).appendChild(document.createTextNode('Samples'));
396       headerRow.insertCell(1).appendChild(document.createTextNode('Percent'));
397       headerRow.insertCell(2).appendChild(document.createTextNode('Symbol'));
398
399       // Add body.
400       var tbody = table.createTBody();
401       if (d.children) {
402         for (var i = 0; i < d.children.length; i++) {
403           var c = d.children[i];
404           var row = tbody.insertRow(i);
405           var bgColor = getColorOfKey(c.category);
406           if (bgColor == undefined)
407             bgColor = '#444444';
408           row.style.backgroundColor = bgColor;
409           var cell0 = row.insertCell(0);
410           var cell1 = row.insertCell(1);
411           var cell2 = row.insertCell(2);
412           cell0.className += 'x-td-numeric';
413           cell1.className += 'x-td-numeric';
414           cell0.appendChild(document.createTextNode(c.value.toString()));
415           cell1.appendChild(document.createTextNode(
416               (100 * c.value / d.value).toFixed(2) + '%'));
417           cell2.appendChild(document.createTextNode(c.name));
418         }
419       }
420
421       // Make it sortable.
422       tvcm.ui.SortableTable.decorate(table);
423
424       var calleeArea = that.querySelector('x-callees');
425       calleeArea.textContent = '';
426       calleeArea.appendChild(table);
427     },
428
429     updateHighlight_: function(d) {
430       var that = this;
431
432       // Update explanation.
433       var percent = 100.0;
434       if (that.chart_.selectedNode != null)
435         percent = 100.0 * d.value / that.chart_.selectedNode.value;
436       that.querySelector('x-explanation').innerHTML =
437           d.value + '<br>' + percent.toFixed(2) + '%';
438
439       // Update call stack table.
440       var table = document.createElement('table');
441       var thead = table.createTHead();
442       var tbody = table.createTBody();
443       var headerRow = thead.insertRow(0);
444       headerRow.style.backgroundColor = '#888';
445       headerRow.insertCell(0).appendChild(
446           document.createTextNode('Call Stack'));
447
448       var callStack = [];
449       var frame = d;
450       while (frame && frame.id) {
451         callStack.push(frame);
452         frame = frame.parent;
453       }
454
455       for (var i = 0; i < callStack.length; i++) {
456         var row = tbody.insertRow(i);
457         var bgColor = getColorOfKey(callStack[i].category);
458         if (bgColor == undefined)
459           bgColor = '#444444';
460         row.style.backgroundColor = bgColor;
461         if (i == 0)
462           row.style.fontWeight = 'bold';
463         row.insertCell(0).appendChild(
464             document.createTextNode(callStack[i].name));
465       }
466
467       var sequenceArea = that.querySelector('x-sequence');
468       sequenceArea.textContent = '';
469       sequenceArea.appendChild(table);
470     },
471
472     getSamplesFromNode_: function(node) {
473       // A node has samples associated with it, if it's a leaf node.
474       var selection = new tracing.Selection();
475       if (node.leaf_node_id !== undefined) {
476         selection.addSelection(this.stackFrameIdToSamples_[node.leaf_node_id]);
477       }
478       else if (node.children === undefined ||
479                node.children.length === 0) {
480         throw new Error('A node should either have samples, or children');
481       }
482       else {
483         for (var i = 0; i < node.children.length; ++i)
484           selection.addSelection(this.getSamplesFromNode_(node.children[i]));
485       }
486       return selection;
487     },
488
489     updateResultArea_: function() {
490       if (this.selection_ === undefined)
491         return;
492
493       var resultArea = this.querySelector('result-area');
494       this.chart_ = undefined;
495       resultArea.textContent = '';
496
497       var sunburstData =
498           createSunburstData(this.selection_, this.sampleType_);
499       this.chart_ = new tvcm.ui.SunburstChart();
500       this.chart_.width = 600;
501       this.chart_.height = 600;
502       this.chart_.chartTitle = 'Sampling Summary';
503
504       this.chart_.addEventListener('node-selected', (function(e) {
505         this.updateCallees_(e.node);
506       }).bind(this));
507
508       this.chart_.addEventListener('node-clicked', (function(e) {
509         var event = new RequestSelectionChangeEvent();
510         var depth = e.node.depth;
511         if (e.node.name === '<self>')
512           depth--;
513         event.selection = this.getSamplesFromNode_(e.node);
514         event.selection.sunburst_zoom_level = depth;
515         this.dispatchEvent(event);
516       }).bind(this));
517
518       this.chart_.addEventListener('node-highlighted', (function(e) {
519         this.updateHighlight_(e.node);
520       }).bind(this));
521
522       this.chart_.data = {
523         nodes: sunburstData
524       };
525
526       resultArea.appendChild(this.chart_);
527       this.chart_.setSize(this.chart_.getMinSize());
528
529       if (this.selection_.sunburst_zoom_level !== undefined) {
530         this.chart_.zoomToDepth(this.selection_.sunburst_zoom_level);
531       }
532     },
533
534     updateContents_: function() {
535       if (this.selection_ === undefined || this.selection_.length == 0)
536         return;
537
538       // Get available sample types in range.
539       var sampleTypes = getSampleTypes(this.selection_);
540       if (sampleTypes.indexOf(this.sampleType_) == -1)
541         this.sampleType_ = sampleTypes[0];
542
543       // Create sample type dropdown.
544       var sampleTypeOptions = [];
545       for (var i = 0; i < sampleTypes.length; i++)
546         sampleTypeOptions.push({label: sampleTypes[i], value: sampleTypes[i]});
547
548       var toolbarEl = this.querySelector('x-toolbar');
549       this.sampleTypeSelector_ = tvcm.ui.createSelector(
550           this,
551           'sampleType',
552           'samplingSummaryPanel.sampleType',
553           this.sampleType_,
554           sampleTypeOptions);
555       toolbarEl.textContent = 'Sample Type: ';
556       toolbarEl.appendChild(this.sampleTypeSelector_);
557     }
558   };
559
560   return {
561     SamplingSummaryPanel: SamplingSummaryPanel,
562     createSunburstData: createSunburstData
563   };
564 });
565 </script>