processes.js revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1// Copyright 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
5processes = new (function() {
6
7this.PS_INTERVAL_SEC_ = 2;
8this.DEV_STATS_INTERVAL_SEC_ = 2;
9this.PROC_STATS_INTERVAL_SEC_ = 1;
10this.TRACER_POLL_INTERVAL_SEC_ = 2;
11
12this.selProcUri_ = null;
13this.selProcName_ = null;
14this.psTable_ = null;
15this.psTableData_ = null;
16this.memChart_ = null;
17this.memChartData_ = null;
18this.cpuChart_ = null;
19this.cpuChartData_ = null;
20this.procCpuChart_ = null;
21this.procCpuChartData_ = null;
22this.procMemChart_ = null;
23this.procMemChartData_ = null;
24this.tracerTaskId_ = null;
25
26this.onDomReady_ = function() {
27  $('#device_tabs').tabs();
28  $('#device_tabs').on('tabsactivate', this.redrawPsStats_.bind(this));
29  $('#device_tabs').on('tabsactivate', this.redrawDevStats_.bind(this));
30
31  // Initialize the toolbar.
32  $('#ps-quick_snapshot').button({icons:{primary: 'ui-icon-image'}})
33      .click(this.snapshotSelectedProcess_.bind(this));
34  $('#ps-dump_mmaps').button({icons:{primary: 'ui-icon-calculator'}})
35      .click(this.dumpSelectedProcessMmaps_.bind(this));
36  $('#ps-full_profile').button({icons:{primary: 'ui-icon-clock'}})
37      .click(this.showTracingDialog_.bind(this));
38
39  // Set-up the tracer dialog.
40  $('#ps-tracer-dialog').dialog({autoOpen: false, modal: true, width: 400,
41      buttons: {'Start': this.startTracingSelectedProcess_.bind(this)}});
42  $('#ps-tracer-period').spinner({min: 0, step: 20});
43  $('#ps-tracer-snapshots').spinner({min: 1, max: 100});
44
45  // Create the process table.
46  this.psTable_ = new google.visualization.Table($('#ps-table')[0]);
47  google.visualization.events.addListener(
48      this.psTable_, 'select', this.onPsTableRowSelect_.bind(this));
49  $('#ps-table').on('dblclick', this.snapshotSelectedProcess_.bind(this));
50
51  // Create the device stats charts.
52  this.memChart_ = new google.visualization.PieChart($('#os-mem_chart')[0]);
53  this.cpuChart_ = new google.visualization.BarChart($('#os-cpu_chart')[0]);
54
55  // Create the selected process stats charts.
56  this.procCpuChart_ =
57      new google.visualization.ComboChart($('#proc-cpu_chart')[0]);
58  this.procMemChart_ =
59      new google.visualization.ComboChart($('#proc-mem_chart')[0]);
60};
61
62this.getSelectedProcessURI = function() {
63  return this.selProcUri_;
64};
65
66this.snapshotSelectedProcess_ = function() {
67  if (!this.selProcUri_)
68    return alert('Must select a process!');
69  mmap.dumpMmaps(this.selProcUri_, true);
70  rootUi.showTab('prof');
71};
72
73this.dumpSelectedProcessMmaps_ = function() {
74  if (!this.selProcUri_)
75    return alert('Must select a process!');
76  mmap.dumpMmaps(this.selProcUri_, false);
77  rootUi.showTab('mm');
78};
79
80this.showTracingDialog_ = function() {
81  if (!this.selProcUri_)
82    return alert('Must select a process!');
83  $('#ps-tracer-process').val(this.selProcName_);
84  $('#ps-tracer-dialog').dialog('open');
85};
86
87this.startTracingSelectedProcess_ = function() {
88  if (!this.selProcUri_)
89    return alert('The process ' + this.selProcUri_ + ' died.');
90  var traceNativeHeap = $('#ps-tracer-bt').prop('checked');
91
92  $('#ps-tracer-dialog').dialog('close');
93
94  if (traceNativeHeap && !devices.getSelectedDevice().isNativeTracingEnabled) {
95    var shouldProvision = confirm('Native heap tracing is not enabled.\n' +
96        'Do you want to enable it (will cause a reboot on Android)?');
97    if (shouldProvision) {
98      devices.initializeSelectedDevice(true);
99      alert('Wait device to complete reboot and then retry.');
100      return;
101    }
102  }
103
104  var postArgs = {interval: $('#ps-tracer-period').val(),
105                  count: $('#ps-tracer-snapshots').val(),
106                  traceNativeHeap: traceNativeHeap};
107
108  webservice.ajaxRequest('/tracer/start/' + this.selProcUri_,
109                         this.onStartTracerAjaxResponse_.bind(this),
110                         null,  // Use default error handler
111                         postArgs);
112};
113
114this.onStartTracerAjaxResponse_ = function(data) {
115  this.tracerTaskId_ = data;
116  timers.start('tracer',
117               this.pollTracerStatus_.bind(this),
118               this.TRACER_POLL_INTERVAL_SEC_);
119};
120
121this.pollTracerStatus_ = function() {
122  if (!this.tracerTaskId_) {
123    timers.stop('tracer');
124    return;
125  }
126  webservice.ajaxRequest('/tracer/status/' + this.tracerTaskId_,
127                         this.onTracerStatusAjaxResponse_.bind(this));
128};
129
130this.onTracerStatusAjaxResponse_ = function(data) {
131  var logMessages = '';
132  var completionRate = 0;
133  data.forEach(function(progress) {
134    completionRate = progress[0];
135    logMessages += '\n' + progress[1];
136  }, this);
137  rootUi.setProgress(completionRate);
138  rootUi.setStatusMessage(logMessages);
139
140  if (completionRate >= 100) {
141    tracerTaskId_ = null;
142    timers.stop('tracer');
143  }
144};
145
146this.refreshPsTable = function() {
147  var targetDevUri = devices.getSelectedURI();
148  if (!targetDevUri)
149    return this.stopPsTable();
150
151  var showAllParam = $('#ps-show_all').prop('checked') ? '?all=1' : '';
152  webservice.ajaxRequest('/ps/' + targetDevUri + showAllParam,
153                         this.onPsAjaxResponse_.bind(this),
154                         this.stopPsTable.bind(this));
155};
156
157this.startPsTable = function() {
158  timers.start('ps_table',
159               this.refreshPsTable.bind(this),
160               this.PS_INTERVAL_SEC_);
161};
162
163this.stopPsTable = function() {
164  this.selProcUri_ = null;
165  this.selProcName_ = null;
166  timers.stop('ps_table');
167};
168
169this.onPsTableRowSelect_ = function() {
170  var targetDevUri = devices.getSelectedURI();
171  if (!targetDevUri)
172    return;
173
174  var sel = this.psTable_.getSelection();
175  if (!sel.length || !this.psTableData_)
176    return;
177  var pid = this.psTableData_.getValue(sel[0].row, 0);
178  this.selProcUri_ = targetDevUri + '/' + pid;
179  this.selProcName_ = this.psTableData_.getValue(sel[0].row, 1);
180  this.startSelectedProcessStats();
181};
182
183this.onPsAjaxResponse_ = function(data) {
184  this.psTableData_ = new google.visualization.DataTable(data);
185  this.redrawPsTable_();
186};
187
188this.redrawPsTable_ = function(data) {
189  if (!this.psTableData_)
190    return;
191
192  // Redraw table preserving sorting info.
193  var sort = this.psTable_.getSortInfo() || {column: -1, ascending: false};
194  this.psTable_.draw(this.psTableData_, {sortColumn: sort.column,
195                                         sortAscending: sort.ascending});
196};
197
198this.refreshDeviceStats = function() {
199  var targetDevUri = devices.getSelectedURI();
200  if (!targetDevUri)
201    return this.stopDeviceStats();
202
203  webservice.ajaxRequest('/stats/' + targetDevUri,
204                         this.onDevStatsAjaxResponse_.bind(this),
205                         this.stopDeviceStats.bind(this));
206};
207
208this.startDeviceStats = function() {
209  timers.start('device_stats',
210               this.refreshDeviceStats.bind(this),
211               this.DEV_STATS_INTERVAL_SEC_);
212};
213
214this.stopDeviceStats = function() {
215  timers.stop('device_stats');
216};
217
218this.onDevStatsAjaxResponse_ = function(data) {
219  this.memChartData_ = new google.visualization.DataTable(data.mem);
220  this.cpuChartData_ = new google.visualization.DataTable(data.cpu);
221  this.redrawDevStats_();
222};
223
224this.redrawDevStats_ = function(data) {
225  if (!this.memChartData_ || !this.cpuChartData_)
226    return;
227
228  this.memChart_.draw(this.memChartData_,
229                       {title: 'System Memory Usage (MB)', is3D: true});
230  this.cpuChart_.draw(this.cpuChartData_,
231                       {title: 'CPU Usage',
232                        isStacked: true,
233                        hAxis: {maxValue: 100, viewWindow: {max: 100}}});
234};
235
236this.refreshSelectedProcessStats = function() {
237  if (!this.selProcUri_)
238    return this.stopSelectedProcessStats();
239
240  webservice.ajaxRequest('/stats/' + this.selProcUri_,
241                         this.onPsStatsAjaxResponse_.bind(this),
242                         this.stopSelectedProcessStats.bind(this));
243};
244
245this.startSelectedProcessStats = function() {
246  timers.start('proc_stats',
247               this.refreshSelectedProcessStats.bind(this),
248               this.PROC_STATS_INTERVAL_SEC_);
249  $('#device_tabs').tabs('option', 'active', 1);
250};
251
252this.stopSelectedProcessStats = function() {
253  timers.stop('proc_stats');
254};
255
256this.onPsStatsAjaxResponse_ = function(data) {
257  this.procCpuChartData_ = new google.visualization.DataTable(data.cpu);
258  this.procMemChartData_ = new google.visualization.DataTable(data.mem);
259  this.redrawPsStats_();
260};
261
262this.redrawPsStats_ = function() {
263  if (!this.procCpuChartData_ || !this.procMemChartData_)
264    return;
265
266  this.procCpuChart_.draw(this.procCpuChartData_, {
267      title: 'CPU stats for ' + this.selProcUri_,
268      seriesType: 'line',
269      vAxes: {0: {title: 'CPU %', maxValue: 100}, 1: {title: '# Threads'}},
270      series: {1: {type: 'bars', targetAxisIndex: 1}},
271      hAxis: {title: 'Run Time'},
272      legend: {alignment: 'end'},
273  });
274  this.procMemChart_.draw(this.procMemChartData_, {
275      title: 'Memory stats for ' + this.selProcUri_,
276      seriesType: 'line',
277      vAxes: {0: {title: 'VM Rss KB'}, 1: {title: '# Page Faults'}},
278      series: {1: {type: 'bars', targetAxisIndex: 1}},
279      hAxis: {title: 'Run Time'},
280      legend: {alignment: 'end'},
281  });
282};
283
284this.redraw = function() {
285  this.redrawPsTable_();
286  if ($('#device_tabs').tabs('option', 'active') == 0)
287    this.redrawDevStats_();
288  else
289    this.redrawPsStats_();
290};
291
292$(document).ready(this.onDomReady_.bind(this));
293
294})();