• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.ComponentModel;
9 using System.Data;
10 using System.Diagnostics;
11 using System.Drawing;
12 using System.Text;
13 using System.Windows.Forms;
14 using System.IO;
15 
16 namespace StatsViewer {
17   public partial class StatsViewer : Form  {
18     /// <summary>
19     /// Create a StatsViewer.
20     /// </summary>
StatsViewer()21     public StatsViewer() {
22       InitializeComponent();
23     }
24 
25     #region Protected Methods
26     /// <summary>
27     /// Callback when the form loads.
28     /// </summary>
29     /// <param name="e"></param>
OnLoad(EventArgs e)30     protected override void OnLoad(EventArgs e) {
31       base.OnLoad(e);
32 
33       timer_ = new Timer();
34       timer_.Interval = kPollInterval;
35       timer_.Tick += new EventHandler(PollTimerTicked);
36       timer_.Start();
37     }
38     #endregion
39 
40     #region Private Methods
41     /// <summary>
42     /// Attempt to open the stats file.
43     /// Return true on success, false otherwise.
44     /// </summary>
OpenStatsFile()45     private bool OpenStatsFile() {
46       StatsTable table = new StatsTable();
47       if (table.Open(kStatsTableName)) {
48         stats_table_ = table;
49         return true;
50       }
51       return false;
52     }
53 
54     /// <summary>
55     /// Close the open stats file.
56     /// </summary>
CloseStatsFile()57     private void CloseStatsFile() {
58       if (this.stats_table_ != null)
59       {
60         this.stats_table_.Close();
61         this.stats_table_ = null;
62         this.listViewCounters.Items.Clear();
63       }
64     }
65 
66     /// <summary>
67     /// Updates the process list in the UI.
68     /// </summary>
UpdateProcessList()69     private void UpdateProcessList() {
70       int current_pids = comboBoxFilter.Items.Count;
71       int table_pids = stats_table_.Processes.Count;
72       if (current_pids != table_pids + 1)  // Add one because of the "all" entry.
73       {
74         int selected_index = this.comboBoxFilter.SelectedIndex;
75         this.comboBoxFilter.Items.Clear();
76         this.comboBoxFilter.Items.Add(kStringAllProcesses);
77         foreach (int pid in stats_table_.Processes)
78           this.comboBoxFilter.Items.Add(kStringProcess + pid.ToString());
79         this.comboBoxFilter.SelectedIndex = selected_index;
80       }
81     }
82 
83     /// <summary>
84     /// Updates the UI for a counter.
85     /// </summary>
86     /// <param name="counter"></param>
UpdateCounter(IStatsCounter counter)87     private void UpdateCounter(IStatsCounter counter) {
88       ListView view;
89 
90       // Figure out which list this counter goes into.
91       if (counter is StatsCounterRate)
92         view = listViewRates;
93       else if (counter is StatsCounter || counter is StatsTimer)
94         view = listViewCounters;
95       else
96         return; // Counter type not supported yet.
97 
98       // See if the counter is already in the list.
99       ListViewItem item = view.Items[counter.name];
100       if (item != null)
101       {
102         // Update an existing counter.
103         Debug.Assert(item is StatsCounterListViewItem);
104         StatsCounterListViewItem counter_item = item as StatsCounterListViewItem;
105         counter_item.Update(counter, filter_pid_);
106       }
107       else
108       {
109         // Create a new counter
110         StatsCounterListViewItem new_item = null;
111         if (counter is StatsCounterRate)
112           new_item = new RateListViewItem(counter, filter_pid_);
113         else if (counter is StatsCounter || counter is StatsTimer)
114           new_item = new CounterListViewItem(counter, filter_pid_);
115         Debug.Assert(new_item != null);
116         view.Items.Add(new_item);
117       }
118     }
119 
120     /// <summary>
121     /// Sample the data and update the UI
122     /// </summary>
SampleData()123     private void SampleData() {
124       // If the table isn't open, try to open it again.
125       if (stats_table_ == null)
126         if (!OpenStatsFile())
127           return;
128 
129       if (stats_counters_ == null)
130         stats_counters_ = stats_table_.Counters();
131 
132       if (pause_updates_)
133         return;
134 
135       stats_counters_.Update();
136 
137       UpdateProcessList();
138 
139       foreach (IStatsCounter counter in stats_counters_)
140         UpdateCounter(counter);
141     }
142 
143     /// <summary>
144     /// Set the background color based on the value
145     /// </summary>
146     /// <param name="item"></param>
147     /// <param name="value"></param>
ColorItem(ListViewItem item, int value)148     private void ColorItem(ListViewItem item, int value)
149     {
150       if (value < 0)
151         item.ForeColor = Color.Red;
152       else if (value > 0)
153         item.ForeColor = Color.DarkGreen;
154       else
155         item.ForeColor = Color.Black;
156     }
157 
158     /// <summary>
159     /// Called when the timer fires.
160     /// </summary>
161     /// <param name="sender"></param>
162     /// <param name="e"></param>
PollTimerTicked(object sender, EventArgs e)163     void PollTimerTicked(object sender, EventArgs e) {
164       SampleData();
165     }
166 
167     /// <summary>
168     /// Called when the interval is changed by the user.
169     /// </summary>
170     /// <param name="sender"></param>
171     /// <param name="e"></param>
interval_changed(object sender, EventArgs e)172     private void interval_changed(object sender, EventArgs e) {
173       int interval = 1;
174       if (int.TryParse(comboBoxInterval.Text, out interval)) {
175         if (timer_ != null) {
176           timer_.Stop();
177           timer_.Interval = interval * 1000;
178           timer_.Start();
179         }
180       } else {
181         comboBoxInterval.Text = timer_.Interval.ToString();
182       }
183     }
184 
185     /// <summary>
186     /// Called when the user changes the filter
187     /// </summary>
188     /// <param name="sender"></param>
189     /// <param name="e"></param>
filter_changed(object sender, EventArgs e)190     private void filter_changed(object sender, EventArgs e) {
191       // While in this event handler, don't allow recursive events!
192       this.comboBoxFilter.SelectedIndexChanged -= new System.EventHandler(this.filter_changed);
193       if (this.comboBoxFilter.Text == kStringAllProcesses)
194         filter_pid_ = 0;
195       else
196         int.TryParse(comboBoxFilter.Text.Substring(kStringProcess.Length), out filter_pid_);
197       SampleData();
198       this.comboBoxFilter.SelectedIndexChanged += new System.EventHandler(this.filter_changed);
199     }
200 
201     /// <summary>
202     /// Callback when the mouse enters a control
203     /// </summary>
204     /// <param name="sender"></param>
205     /// <param name="e"></param>
mouse_Enter(object sender, EventArgs e)206     private void mouse_Enter(object sender, EventArgs e) {
207       // When the dropdown is expanded, we pause
208       // updates, as it messes with the UI.
209       pause_updates_ = true;
210     }
211 
212     /// <summary>
213     /// Callback when the mouse leaves a control
214     /// </summary>
215     /// <param name="sender"></param>
216     /// <param name="e"></param>
mouse_Leave(object sender, EventArgs e)217     private void mouse_Leave(object sender, EventArgs e) {
218       pause_updates_ = false;
219     }
220 
221     /// <summary>
222     /// Called when the user clicks the zero-stats button.
223     /// </summary>
224     /// <param name="sender"></param>
225     /// <param name="e"></param>
buttonZero_Click(object sender, EventArgs e)226     private void buttonZero_Click(object sender, EventArgs e) {
227       this.stats_table_.Zero();
228       SampleData();
229     }
230 
231     /// <summary>
232     /// Called when the user clicks a column heading.
233     /// </summary>
234     /// <param name="sender"></param>
235     /// <param name="e"></param>
column_Click(object sender, ColumnClickEventArgs e)236     private void column_Click(object sender, ColumnClickEventArgs e) {
237       if (e.Column != sort_column_) {
238         sort_column_ = e.Column;
239         this.listViewCounters.Sorting = SortOrder.Ascending;
240       } else {
241         if (this.listViewCounters.Sorting == SortOrder.Ascending)
242           this.listViewCounters.Sorting = SortOrder.Descending;
243         else
244           this.listViewCounters.Sorting = SortOrder.Ascending;
245       }
246 
247       this.listViewCounters.ListViewItemSorter =
248           new ListViewItemComparer(e.Column, this.listViewCounters.Sorting);
249       this.listViewCounters.Sort();
250     }
251 
252     /// <summary>
253     /// Called when the user clicks the button "Export".
254     /// </summary>
255     /// <param name="sender"></param>
256     /// <param name="e"></param>
buttonExport_Click(object sender, EventArgs e)257     private void buttonExport_Click(object sender, EventArgs e) {
258       //Have to pick a textfile to export to.
259       //Saves what is shown in listViewStats in the format: function   value
260       //(with a tab in between), so that it is easy to copy paste into a spreadsheet.
261       //(Does not save the delta values.)
262       TextWriter tw = null;
263       try {
264         saveFileDialogExport.CheckFileExists = false;
265         saveFileDialogExport.ShowDialog();
266         tw = new StreamWriter(saveFileDialogExport.FileName);
267 
268         for (int i = 0; i < listViewCounters.Items.Count; i++) {
269           tw.Write(listViewCounters.Items[i].SubItems[0].Text + "\t");
270           tw.WriteLine(listViewCounters.Items[i].SubItems[1].Text);
271         }
272       }
273       catch (IOException ex) {
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));
275       }
276       finally{
277         if (tw != null) tw.Close();
278       }
279     }
280 
281     #endregion
282 
283     class ListViewItemComparer : IComparer {
ListViewItemComparer()284       public ListViewItemComparer() {
285         this.col_ = 0;
286         this.order_ = SortOrder.Ascending;
287       }
288 
ListViewItemComparer(int column, SortOrder order)289       public ListViewItemComparer(int column, SortOrder order) {
290         this.col_ = column;
291         this.order_ = order;
292       }
293 
Compare(object x, object y)294       public int Compare(object x, object y) {
295         int return_value = -1;
296 
297         object x_tag = ((ListViewItem)x).SubItems[col_].Tag;
298         object y_tag = ((ListViewItem)y).SubItems[col_].Tag;
299 
300         if (Comparable(x_tag, y_tag))
301           return_value = ((IComparable)x_tag).CompareTo(y_tag);
302         else
303           return_value = String.Compare(((ListViewItem)x).SubItems[col_].Text,
304               ((ListViewItem)y).SubItems[col_].Text);
305 
306         if (order_ == SortOrder.Descending)
307           return_value *= -1;
308 
309         return return_value;
310       }
311 
312       #region Private Methods
Comparable(object x, object y)313       private bool Comparable(object x, object y) {
314         if (x == null || y == null)
315           return false;
316 
317         return x is IComparable && y is IComparable;
318       }
319       #endregion
320 
321       #region Private Members
322       private int col_;
323       private SortOrder order_;
324       #endregion
325     }
326 
327     #region Private Members
328     private const string kStringAllProcesses = "All Processes";
329     private const string kStringProcess = "Process ";
330     private const int kPollInterval = 1000;  // 1 second
331     private const string kStatsTableName = "ChromeStats";
332     private StatsTable stats_table_;
333     private StatsTableCounters stats_counters_;
334     private Timer timer_;
335     private int filter_pid_;
336     private bool pause_updates_;
337     private int sort_column_ = -1;
338     #endregion
339 
340     #region Private Event Callbacks
openToolStripMenuItem_Click(object sender, EventArgs e)341     private void openToolStripMenuItem_Click(object sender, EventArgs e)
342     {
343       OpenDialog dialog = new OpenDialog();
344       dialog.ShowDialog();
345 
346       CloseStatsFile();
347 
348       StatsTable table = new StatsTable();
349       bool rv = table.Open(dialog.FileName);
350       if (!rv)
351       {
352         MessageBox.Show("Could not open statsfile: " + dialog.FileName);
353       }
354       else
355       {
356         stats_table_ = table;
357       }
358     }
359 
closeToolStripMenuItem_Click(object sender, EventArgs e)360     private void closeToolStripMenuItem_Click(object sender, EventArgs e)
361     {
362       CloseStatsFile();
363     }
364 
quitToolStripMenuItem_Click(object sender, EventArgs e)365     private void quitToolStripMenuItem_Click(object sender, EventArgs e)
366     {
367       Application.Exit();
368     }
369     #endregion
370   }
371 
372   /// <summary>
373   /// Base class for counter list view items.
374   /// </summary>
375   internal class StatsCounterListViewItem : ListViewItem
376   {
377     /// <summary>
378     /// Create the ListViewItem
379     /// </summary>
380     /// <param name="text"></param>
StatsCounterListViewItem(string text)381     public StatsCounterListViewItem(string text) : base(text) { }
382 
383     /// <summary>
384     /// Update the ListViewItem given a new counter value.
385     /// </summary>
386     /// <param name="counter"></param>
387     /// <param name="filter_pid"></param>
Update(IStatsCounter counter, int filter_pid)388     public virtual void Update(IStatsCounter counter, int filter_pid) { }
389 
390     /// <summary>
391     /// Set the background color based on the value
392     /// </summary>
393     /// <param name="value"></param>
ColorItem(int value)394     protected void ColorItem(int value)
395     {
396       if (value < 0)
397         ForeColor = Color.Red;
398       else if (value > 0)
399         ForeColor = Color.DarkGreen;
400       else
401         ForeColor = Color.Black;
402     }
403 
404     /// <summary>
405     /// Create a new subitem with a zeroed Tag.
406     /// </summary>
407     /// <returns></returns>
NewSubItem()408     protected ListViewSubItem NewSubItem()
409     {
410       ListViewSubItem item = new ListViewSubItem();
411       item.Tag = -1;  // Arbitrarily initialize to -1.
412       return item;
413     }
414 
415     /// <summary>
416     /// Set the value for a subitem.
417     /// </summary>
418     /// <param name="item"></param>
419     /// <param name="val"></param>
420     /// <returns>True if the value changed, false otherwise</returns>
SetSubItem(ListViewSubItem item, int val)421     protected bool SetSubItem(ListViewSubItem item, int val)
422     {
423       // The reason for doing this extra compare is because
424       // we introduce flicker if we unnecessarily update the
425       // subitems.  The UI is much less likely to cause you
426       // a seizure when we do this.
427       if (val != (int)item.Tag)
428       {
429         item.Text = val.ToString();
430         item.Tag = val;
431         return true;
432       }
433       return false;
434     }
435   }
436 
437   /// <summary>
438   /// A listview item which contains a rate.
439   /// </summary>
440   internal class RateListViewItem : StatsCounterListViewItem
441   {
RateListViewItem(IStatsCounter ctr, int filter_pid)442     public RateListViewItem(IStatsCounter ctr, int filter_pid) :
443       base(ctr.name)
444     {
445       StatsCounterRate rate = ctr as StatsCounterRate;
446       Name = rate.name;
447       SubItems.Add(NewSubItem());
448       SubItems.Add(NewSubItem());
449       SubItems.Add(NewSubItem());
450       Update(ctr, filter_pid);
451     }
452 
Update(IStatsCounter counter, int filter_pid)453     public override void Update(IStatsCounter counter, int filter_pid)
454     {
455       Debug.Assert(counter is StatsCounterRate);
456 
457       StatsCounterRate new_rate = counter as StatsCounterRate;
458       int new_count = new_rate.GetCount(filter_pid);
459       int new_time = new_rate.GetTime(filter_pid);
460       int old_avg = Tag != null ? (int)Tag : 0;
461       int new_avg = new_count > 0 ? (new_time / new_count) : 0;
462       int delta = new_avg - old_avg;
463 
464       SetSubItem(SubItems[column_count_index], new_count);
465       SetSubItem(SubItems[column_time_index], new_time);
466       if (SetSubItem(SubItems[column_avg_index], new_avg))
467         ColorItem(delta);
468       Tag = new_avg;
469     }
470 
471     private const int column_count_index = 1;
472     private const int column_time_index = 2;
473     private const int column_avg_index = 3;
474   }
475 
476   /// <summary>
477   /// A listview item which contains a counter.
478   /// </summary>
479   internal class CounterListViewItem : StatsCounterListViewItem
480   {
CounterListViewItem(IStatsCounter ctr, int filter_pid)481     public CounterListViewItem(IStatsCounter ctr, int filter_pid) :
482       base(ctr.name)
483     {
484       Name = ctr.name;
485       SubItems.Add(NewSubItem());
486       SubItems.Add(NewSubItem());
487       Update(ctr, filter_pid);
488     }
489 
Update(IStatsCounter counter, int filter_pid)490     public override void Update(IStatsCounter counter, int filter_pid) {
491       Debug.Assert(counter is StatsCounter || counter is StatsTimer);
492 
493       int new_value = 0;
494       if (counter is StatsCounter)
495         new_value = ((StatsCounter)counter).GetValue(filter_pid);
496       else if (counter is StatsTimer)
497         new_value = ((StatsTimer)counter).GetValue(filter_pid);
498 
499       int old_value = Tag != null ? (int)Tag : 0;
500       int delta = new_value - old_value;
501       SetSubItem(SubItems[column_value_index], new_value);
502       if (SetSubItem(SubItems[column_delta_index], delta))
503         ColorItem(delta);
504       Tag = new_value;
505     }
506 
507     private const int column_value_index = 1;
508     private const int column_delta_index = 2;
509   }
510 }
511