1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
\r
2 // Use of this source code is governed by a BSD-style license that can be
\r
3 // found in the LICENSE file.
\r
6 using System.Collections;
\r
7 using System.Collections.Generic;
\r
8 using System.ComponentModel;
\r
10 using System.Diagnostics;
\r
11 using System.Drawing;
\r
13 using System.Windows.Forms;
\r
16 namespace StatsViewer {
\r
17 public partial class StatsViewer : Form {
\r
19 /// Create a StatsViewer.
\r
21 public StatsViewer() {
\r
22 InitializeComponent();
\r
25 #region Protected Methods
\r
27 /// Callback when the form loads.
\r
29 /// <param name="e"></param>
\r
30 protected override void OnLoad(EventArgs e) {
\r
33 timer_ = new Timer();
\r
34 timer_.Interval = kPollInterval;
\r
35 timer_.Tick += new EventHandler(PollTimerTicked);
\r
40 #region Private Methods
\r
42 /// Attempt to open the stats file.
\r
43 /// Return true on success, false otherwise.
\r
45 private bool OpenStatsFile() {
\r
46 StatsTable table = new StatsTable();
\r
47 if (table.Open(kStatsTableName)) {
\r
48 stats_table_ = table;
\r
55 /// Close the open stats file.
\r
57 private void CloseStatsFile() {
\r
58 if (this.stats_table_ != null)
\r
60 this.stats_table_.Close();
\r
61 this.stats_table_ = null;
\r
62 this.listViewCounters.Items.Clear();
\r
67 /// Updates the process list in the UI.
\r
69 private void UpdateProcessList() {
\r
70 int current_pids = comboBoxFilter.Items.Count;
\r
71 int table_pids = stats_table_.Processes.Count;
\r
72 if (current_pids != table_pids + 1) // Add one because of the "all" entry.
\r
74 int selected_index = this.comboBoxFilter.SelectedIndex;
\r
75 this.comboBoxFilter.Items.Clear();
\r
76 this.comboBoxFilter.Items.Add(kStringAllProcesses);
\r
77 foreach (int pid in stats_table_.Processes)
\r
78 this.comboBoxFilter.Items.Add(kStringProcess + pid.ToString());
\r
79 this.comboBoxFilter.SelectedIndex = selected_index;
\r
84 /// Updates the UI for a counter.
\r
86 /// <param name="counter"></param>
\r
87 private void UpdateCounter(IStatsCounter counter) {
\r
90 // Figure out which list this counter goes into.
\r
91 if (counter is StatsCounterRate)
\r
92 view = listViewRates;
\r
93 else if (counter is StatsCounter || counter is StatsTimer)
\r
94 view = listViewCounters;
\r
96 return; // Counter type not supported yet.
\r
98 // See if the counter is already in the list.
\r
99 ListViewItem item = view.Items[counter.name];
\r
102 // Update an existing counter.
\r
103 Debug.Assert(item is StatsCounterListViewItem);
\r
104 StatsCounterListViewItem counter_item = item as StatsCounterListViewItem;
\r
105 counter_item.Update(counter, filter_pid_);
\r
109 // Create a new counter
\r
110 StatsCounterListViewItem new_item = null;
\r
111 if (counter is StatsCounterRate)
\r
112 new_item = new RateListViewItem(counter, filter_pid_);
\r
113 else if (counter is StatsCounter || counter is StatsTimer)
\r
114 new_item = new CounterListViewItem(counter, filter_pid_);
\r
115 Debug.Assert(new_item != null);
\r
116 view.Items.Add(new_item);
\r
121 /// Sample the data and update the UI
\r
123 private void SampleData() {
\r
124 // If the table isn't open, try to open it again.
\r
125 if (stats_table_ == null)
\r
126 if (!OpenStatsFile())
\r
129 if (stats_counters_ == null)
\r
130 stats_counters_ = stats_table_.Counters();
\r
132 if (pause_updates_)
\r
135 stats_counters_.Update();
\r
137 UpdateProcessList();
\r
139 foreach (IStatsCounter counter in stats_counters_)
\r
140 UpdateCounter(counter);
\r
144 /// Set the background color based on the value
\r
146 /// <param name="item"></param>
\r
147 /// <param name="value"></param>
\r
148 private void ColorItem(ListViewItem item, int value)
\r
151 item.ForeColor = Color.Red;
\r
152 else if (value > 0)
\r
153 item.ForeColor = Color.DarkGreen;
\r
155 item.ForeColor = Color.Black;
\r
159 /// Called when the timer fires.
\r
161 /// <param name="sender"></param>
\r
162 /// <param name="e"></param>
\r
163 void PollTimerTicked(object sender, EventArgs e) {
\r
168 /// Called when the interval is changed by the user.
\r
170 /// <param name="sender"></param>
\r
171 /// <param name="e"></param>
\r
172 private void interval_changed(object sender, EventArgs e) {
\r
174 if (int.TryParse(comboBoxInterval.Text, out interval)) {
\r
175 if (timer_ != null) {
\r
177 timer_.Interval = interval * 1000;
\r
181 comboBoxInterval.Text = timer_.Interval.ToString();
\r
186 /// Called when the user changes the filter
\r
188 /// <param name="sender"></param>
\r
189 /// <param name="e"></param>
\r
190 private void filter_changed(object sender, EventArgs e) {
\r
191 // While in this event handler, don't allow recursive events!
\r
192 this.comboBoxFilter.SelectedIndexChanged -= new System.EventHandler(this.filter_changed);
\r
193 if (this.comboBoxFilter.Text == kStringAllProcesses)
\r
196 int.TryParse(comboBoxFilter.Text.Substring(kStringProcess.Length), out filter_pid_);
\r
198 this.comboBoxFilter.SelectedIndexChanged += new System.EventHandler(this.filter_changed);
\r
202 /// Callback when the mouse enters a control
\r
204 /// <param name="sender"></param>
\r
205 /// <param name="e"></param>
\r
206 private void mouse_Enter(object sender, EventArgs e) {
\r
207 // When the dropdown is expanded, we pause
\r
208 // updates, as it messes with the UI.
\r
209 pause_updates_ = true;
\r
213 /// Callback when the mouse leaves a control
\r
215 /// <param name="sender"></param>
\r
216 /// <param name="e"></param>
\r
217 private void mouse_Leave(object sender, EventArgs e) {
\r
218 pause_updates_ = false;
\r
222 /// Called when the user clicks the zero-stats button.
\r
224 /// <param name="sender"></param>
\r
225 /// <param name="e"></param>
\r
226 private void buttonZero_Click(object sender, EventArgs e) {
\r
227 this.stats_table_.Zero();
\r
232 /// Called when the user clicks a column heading.
\r
234 /// <param name="sender"></param>
\r
235 /// <param name="e"></param>
\r
236 private void column_Click(object sender, ColumnClickEventArgs e) {
\r
237 if (e.Column != sort_column_) {
\r
238 sort_column_ = e.Column;
\r
239 this.listViewCounters.Sorting = SortOrder.Ascending;
\r
241 if (this.listViewCounters.Sorting == SortOrder.Ascending)
\r
242 this.listViewCounters.Sorting = SortOrder.Descending;
\r
244 this.listViewCounters.Sorting = SortOrder.Ascending;
\r
247 this.listViewCounters.ListViewItemSorter =
\r
248 new ListViewItemComparer(e.Column, this.listViewCounters.Sorting);
\r
249 this.listViewCounters.Sort();
\r
253 /// Called when the user clicks the button "Export".
\r
255 /// <param name="sender"></param>
\r
256 /// <param name="e"></param>
\r
257 private void buttonExport_Click(object sender, EventArgs e) {
\r
258 //Have to pick a textfile to export to.
\r
259 //Saves what is shown in listViewStats in the format: function value
\r
260 //(with a tab in between), so that it is easy to copy paste into a spreadsheet.
\r
261 //(Does not save the delta values.)
\r
262 TextWriter tw = null;
\r
264 saveFileDialogExport.CheckFileExists = false;
\r
265 saveFileDialogExport.ShowDialog();
\r
266 tw = new StreamWriter(saveFileDialogExport.FileName);
\r
268 for (int i = 0; i < listViewCounters.Items.Count; i++) {
\r
269 tw.Write(listViewCounters.Items[i].SubItems[0].Text + "\t");
\r
270 tw.WriteLine(listViewCounters.Items[i].SubItems[1].Text);
\r
273 catch (IOException ex) {
\r
274 MessageBox.Show(string.Format("There was an error while saving your results file. The results might not have been saved correctly.: {0}", ex.Message));
\r
277 if (tw != null) tw.Close();
\r
283 class ListViewItemComparer : IComparer {
\r
284 public ListViewItemComparer() {
\r
286 this.order_ = SortOrder.Ascending;
\r
289 public ListViewItemComparer(int column, SortOrder order) {
\r
290 this.col_ = column;
\r
291 this.order_ = order;
\r
294 public int Compare(object x, object y) {
\r
295 int return_value = -1;
\r
297 object x_tag = ((ListViewItem)x).SubItems[col_].Tag;
\r
298 object y_tag = ((ListViewItem)y).SubItems[col_].Tag;
\r
300 if (Comparable(x_tag, y_tag))
\r
301 return_value = ((IComparable)x_tag).CompareTo(y_tag);
\r
303 return_value = String.Compare(((ListViewItem)x).SubItems[col_].Text,
\r
304 ((ListViewItem)y).SubItems[col_].Text);
\r
306 if (order_ == SortOrder.Descending)
\r
307 return_value *= -1;
\r
309 return return_value;
\r
312 #region Private Methods
\r
313 private bool Comparable(object x, object y) {
\r
314 if (x == null || y == null)
\r
317 return x is IComparable && y is IComparable;
\r
321 #region Private Members
\r
323 private SortOrder order_;
\r
327 #region Private Members
\r
328 private const string kStringAllProcesses = "All Processes";
\r
329 private const string kStringProcess = "Process ";
\r
330 private const int kPollInterval = 1000; // 1 second
\r
331 private const string kStatsTableName = "ChromeStats";
\r
332 private StatsTable stats_table_;
\r
333 private StatsTableCounters stats_counters_;
\r
334 private Timer timer_;
\r
335 private int filter_pid_;
\r
336 private bool pause_updates_;
\r
337 private int sort_column_ = -1;
\r
340 #region Private Event Callbacks
\r
341 private void openToolStripMenuItem_Click(object sender, EventArgs e)
\r
343 OpenDialog dialog = new OpenDialog();
\r
344 dialog.ShowDialog();
\r
348 StatsTable table = new StatsTable();
\r
349 bool rv = table.Open(dialog.FileName);
\r
352 MessageBox.Show("Could not open statsfile: " + dialog.FileName);
\r
356 stats_table_ = table;
\r
360 private void closeToolStripMenuItem_Click(object sender, EventArgs e)
\r
365 private void quitToolStripMenuItem_Click(object sender, EventArgs e)
\r
367 Application.Exit();
\r
373 /// Base class for counter list view items.
\r
375 internal class StatsCounterListViewItem : ListViewItem
\r
378 /// Create the ListViewItem
\r
380 /// <param name="text"></param>
\r
381 public StatsCounterListViewItem(string text) : base(text) { }
\r
384 /// Update the ListViewItem given a new counter value.
\r
386 /// <param name="counter"></param>
\r
387 /// <param name="filter_pid"></param>
\r
388 public virtual void Update(IStatsCounter counter, int filter_pid) { }
\r
391 /// Set the background color based on the value
\r
393 /// <param name="value"></param>
\r
394 protected void ColorItem(int value)
\r
397 ForeColor = Color.Red;
\r
398 else if (value > 0)
\r
399 ForeColor = Color.DarkGreen;
\r
401 ForeColor = Color.Black;
\r
405 /// Create a new subitem with a zeroed Tag.
\r
407 /// <returns></returns>
\r
408 protected ListViewSubItem NewSubItem()
\r
410 ListViewSubItem item = new ListViewSubItem();
\r
411 item.Tag = -1; // Arbitrarily initialize to -1.
\r
416 /// Set the value for a subitem.
\r
418 /// <param name="item"></param>
\r
419 /// <param name="val"></param>
\r
420 /// <returns>True if the value changed, false otherwise</returns>
\r
421 protected bool SetSubItem(ListViewSubItem item, int val)
\r
423 // The reason for doing this extra compare is because
\r
424 // we introduce flicker if we unnecessarily update the
\r
425 // subitems. The UI is much less likely to cause you
\r
426 // a seizure when we do this.
\r
427 if (val != (int)item.Tag)
\r
429 item.Text = val.ToString();
\r
438 /// A listview item which contains a rate.
\r
440 internal class RateListViewItem : StatsCounterListViewItem
\r
442 public RateListViewItem(IStatsCounter ctr, int filter_pid) :
\r
445 StatsCounterRate rate = ctr as StatsCounterRate;
\r
447 SubItems.Add(NewSubItem());
\r
448 SubItems.Add(NewSubItem());
\r
449 SubItems.Add(NewSubItem());
\r
450 Update(ctr, filter_pid);
\r
453 public override void Update(IStatsCounter counter, int filter_pid)
\r
455 Debug.Assert(counter is StatsCounterRate);
\r
457 StatsCounterRate new_rate = counter as StatsCounterRate;
\r
458 int new_count = new_rate.GetCount(filter_pid);
\r
459 int new_time = new_rate.GetTime(filter_pid);
\r
460 int old_avg = Tag != null ? (int)Tag : 0;
\r
461 int new_avg = new_count > 0 ? (new_time / new_count) : 0;
\r
462 int delta = new_avg - old_avg;
\r
464 SetSubItem(SubItems[column_count_index], new_count);
\r
465 SetSubItem(SubItems[column_time_index], new_time);
\r
466 if (SetSubItem(SubItems[column_avg_index], new_avg))
\r
471 private const int column_count_index = 1;
\r
472 private const int column_time_index = 2;
\r
473 private const int column_avg_index = 3;
\r
477 /// A listview item which contains a counter.
\r
479 internal class CounterListViewItem : StatsCounterListViewItem
\r
481 public CounterListViewItem(IStatsCounter ctr, int filter_pid) :
\r
485 SubItems.Add(NewSubItem());
\r
486 SubItems.Add(NewSubItem());
\r
487 Update(ctr, filter_pid);
\r
490 public override void Update(IStatsCounter counter, int filter_pid) {
\r
491 Debug.Assert(counter is StatsCounter || counter is StatsTimer);
\r
494 if (counter is StatsCounter)
\r
495 new_value = ((StatsCounter)counter).GetValue(filter_pid);
\r
496 else if (counter is StatsTimer)
\r
497 new_value = ((StatsTimer)counter).GetValue(filter_pid);
\r
499 int old_value = Tag != null ? (int)Tag : 0;
\r
500 int delta = new_value - old_value;
\r
501 SetSubItem(SubItems[column_value_index], new_value);
\r
502 if (SetSubItem(SubItems[column_delta_index], delta))
\r
507 private const int column_value_index = 1;
\r
508 private const int column_delta_index = 2;
\r