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.
6 using System
.Collections
;
7 using System
.Collections
.Generic
;
8 using System
.ComponentModel
;
10 using System
.Diagnostics
;
13 using System
.Windows
.Forms
;
16 namespace StatsViewer
{
17 public partial class StatsViewer
: Form
{
19 /// Create a StatsViewer.
21 public StatsViewer() {
22 InitializeComponent();
25 #region Protected Methods
27 /// Callback when the form loads.
29 /// <param name="e"></param>
30 protected override void OnLoad(EventArgs e
) {
34 timer_
.Interval
= kPollInterval
;
35 timer_
.Tick
+= new EventHandler(PollTimerTicked
);
40 #region Private Methods
42 /// Attempt to open the stats file.
43 /// Return true on success, false otherwise.
45 private bool OpenStatsFile() {
46 StatsTable table
= new StatsTable();
47 if (table
.Open(kStatsTableName
)) {
55 /// Close the open stats file.
57 private void CloseStatsFile() {
58 if (this.stats_table_
!= null)
60 this.stats_table_
.Close();
61 this.stats_table_
= null;
62 this.listViewCounters
.Items
.Clear();
67 /// Updates the process list in the UI.
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.
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
;
84 /// Updates the UI for a counter.
86 /// <param name="counter"></param>
87 private void UpdateCounter(IStatsCounter counter
) {
90 // Figure out which list this counter goes into.
91 if (counter
is StatsCounterRate
)
93 else if (counter
is StatsCounter
|| counter
is StatsTimer
)
94 view
= listViewCounters
;
96 return; // Counter type not supported yet.
98 // See if the counter is already in the list.
99 ListViewItem item
= view
.Items
[counter
.name
];
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_
);
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
);
121 /// Sample the data and update the UI
123 private void SampleData() {
124 // If the table isn't open, try to open it again.
125 if (stats_table_
== null)
126 if (!OpenStatsFile())
129 if (stats_counters_
== null)
130 stats_counters_
= stats_table_
.Counters();
135 stats_counters_
.Update();
139 foreach (IStatsCounter counter
in stats_counters_
)
140 UpdateCounter(counter
);
144 /// Set the background color based on the value
146 /// <param name="item"></param>
147 /// <param name="value"></param>
148 private void ColorItem(ListViewItem item
, int value)
151 item
.ForeColor
= Color
.Red
;
153 item
.ForeColor
= Color
.DarkGreen
;
155 item
.ForeColor
= Color
.Black
;
159 /// Called when the timer fires.
161 /// <param name="sender"></param>
162 /// <param name="e"></param>
163 void PollTimerTicked(object sender
, EventArgs e
) {
168 /// Called when the interval is changed by the user.
170 /// <param name="sender"></param>
171 /// <param name="e"></param>
172 private void interval_changed(object sender
, EventArgs e
) {
174 if (int.TryParse(comboBoxInterval
.Text
, out interval
)) {
175 if (timer_
!= null) {
177 timer_
.Interval
= interval
* 1000;
181 comboBoxInterval
.Text
= timer_
.Interval
.ToString();
186 /// Called when the user changes the filter
188 /// <param name="sender"></param>
189 /// <param name="e"></param>
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
)
196 int.TryParse(comboBoxFilter
.Text
.Substring(kStringProcess
.Length
), out filter_pid_
);
198 this.comboBoxFilter
.SelectedIndexChanged
+= new System
.EventHandler(this.filter_changed
);
202 /// Callback when the mouse enters a control
204 /// <param name="sender"></param>
205 /// <param name="e"></param>
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;
213 /// Callback when the mouse leaves a control
215 /// <param name="sender"></param>
216 /// <param name="e"></param>
217 private void mouse_Leave(object sender
, EventArgs e
) {
218 pause_updates_
= false;
222 /// Called when the user clicks the zero-stats button.
224 /// <param name="sender"></param>
225 /// <param name="e"></param>
226 private void buttonZero_Click(object sender
, EventArgs e
) {
227 this.stats_table_
.Zero();
232 /// Called when the user clicks a column heading.
234 /// <param name="sender"></param>
235 /// <param name="e"></param>
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
;
241 if (this.listViewCounters
.Sorting
== SortOrder
.Ascending
)
242 this.listViewCounters
.Sorting
= SortOrder
.Descending
;
244 this.listViewCounters
.Sorting
= SortOrder
.Ascending
;
247 this.listViewCounters
.ListViewItemSorter
=
248 new ListViewItemComparer(e
.Column
, this.listViewCounters
.Sorting
);
249 this.listViewCounters
.Sort();
253 /// Called when the user clicks the button "Export".
255 /// <param name="sender"></param>
256 /// <param name="e"></param>
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;
264 saveFileDialogExport
.CheckFileExists
= false;
265 saveFileDialogExport
.ShowDialog();
266 tw
= new StreamWriter(saveFileDialogExport
.FileName
);
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
);
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
));
277 if (tw
!= null) tw
.Close();
283 class ListViewItemComparer
: IComparer
{
284 public ListViewItemComparer() {
286 this.order_
= SortOrder
.Ascending
;
289 public ListViewItemComparer(int column
, SortOrder order
) {
294 public int Compare(object x
, object y
) {
295 int return_value
= -1;
297 object x_tag
= ((ListViewItem
)x
).SubItems
[col_
].Tag
;
298 object y_tag
= ((ListViewItem
)y
).SubItems
[col_
].Tag
;
300 if (Comparable(x_tag
, y_tag
))
301 return_value
= ((IComparable
)x_tag
).CompareTo(y_tag
);
303 return_value
= String
.Compare(((ListViewItem
)x
).SubItems
[col_
].Text
,
304 ((ListViewItem
)y
).SubItems
[col_
].Text
);
306 if (order_
== SortOrder
.Descending
)
312 #region Private Methods
313 private bool Comparable(object x
, object y
) {
314 if (x
== null || y
== null)
317 return x
is IComparable
&& y
is IComparable
;
321 #region Private Members
323 private SortOrder order_
;
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;
340 #region Private Event Callbacks
341 private void openToolStripMenuItem_Click(object sender
, EventArgs e
)
343 OpenDialog dialog
= new OpenDialog();
348 StatsTable table
= new StatsTable();
349 bool rv
= table
.Open(dialog
.FileName
);
352 MessageBox
.Show("Could not open statsfile: " + dialog
.FileName
);
356 stats_table_
= table
;
360 private void closeToolStripMenuItem_Click(object sender
, EventArgs e
)
365 private void quitToolStripMenuItem_Click(object sender
, EventArgs e
)
373 /// Base class for counter list view items.
375 internal class StatsCounterListViewItem
: ListViewItem
378 /// Create the ListViewItem
380 /// <param name="text"></param>
381 public StatsCounterListViewItem(string text
) : base(text
) { }
384 /// Update the ListViewItem given a new counter value.
386 /// <param name="counter"></param>
387 /// <param name="filter_pid"></param>
388 public virtual void Update(IStatsCounter counter
, int filter_pid
) { }
391 /// Set the background color based on the value
393 /// <param name="value"></param>
394 protected void ColorItem(int value)
397 ForeColor
= Color
.Red
;
399 ForeColor
= Color
.DarkGreen
;
401 ForeColor
= Color
.Black
;
405 /// Create a new subitem with a zeroed Tag.
407 /// <returns></returns>
408 protected ListViewSubItem
NewSubItem()
410 ListViewSubItem item
= new ListViewSubItem();
411 item
.Tag
= -1; // Arbitrarily initialize to -1.
416 /// Set the value for a subitem.
418 /// <param name="item"></param>
419 /// <param name="val"></param>
420 /// <returns>True if the value changed, false otherwise</returns>
421 protected bool SetSubItem(ListViewSubItem item
, int val
)
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
)
429 item
.Text
= val
.ToString();
438 /// A listview item which contains a rate.
440 internal class RateListViewItem
: StatsCounterListViewItem
442 public RateListViewItem(IStatsCounter ctr
, int filter_pid
) :
445 StatsCounterRate rate
= ctr
as StatsCounterRate
;
447 SubItems
.Add(NewSubItem());
448 SubItems
.Add(NewSubItem());
449 SubItems
.Add(NewSubItem());
450 Update(ctr
, filter_pid
);
453 public override void Update(IStatsCounter counter
, int filter_pid
)
455 Debug
.Assert(counter
is StatsCounterRate
);
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
;
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
))
471 private const int column_count_index
= 1;
472 private const int column_time_index
= 2;
473 private const int column_avg_index
= 3;
477 /// A listview item which contains a counter.
479 internal class CounterListViewItem
: StatsCounterListViewItem
481 public CounterListViewItem(IStatsCounter ctr
, int filter_pid
) :
485 SubItems
.Add(NewSubItem());
486 SubItems
.Add(NewSubItem());
487 Update(ctr
, filter_pid
);
490 public override void Update(IStatsCounter counter
, int filter_pid
) {
491 Debug
.Assert(counter
is StatsCounter
|| counter
is StatsTimer
);
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
);
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
))
507 private const int column_value_index
= 1;
508 private const int column_delta_index
= 2;