Use placeholders instead of vertical bars in string formatting.
[chromium-blink-merge.git] / tools / stats_viewer / stats_viewer.cs
blobeebb882c540dbd4424453d4cb4f3fb1d784a3ecc
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.
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;
16 namespace StatsViewer {
17 public partial class StatsViewer : Form {
18 /// <summary>
19 /// Create a StatsViewer.
20 /// </summary>
21 public StatsViewer() {
22 InitializeComponent();
25 #region Protected Methods
26 /// <summary>
27 /// Callback when the form loads.
28 /// </summary>
29 /// <param name="e"></param>
30 protected override void OnLoad(EventArgs e) {
31 base.OnLoad(e);
33 timer_ = new Timer();
34 timer_.Interval = kPollInterval;
35 timer_.Tick += new EventHandler(PollTimerTicked);
36 timer_.Start();
38 #endregion
40 #region Private Methods
41 /// <summary>
42 /// Attempt to open the stats file.
43 /// Return true on success, false otherwise.
44 /// </summary>
45 private bool OpenStatsFile() {
46 StatsTable table = new StatsTable();
47 if (table.Open(kStatsTableName)) {
48 stats_table_ = table;
49 return true;
51 return false;
54 /// <summary>
55 /// Close the open stats file.
56 /// </summary>
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();
66 /// <summary>
67 /// Updates the process list in the UI.
68 /// </summary>
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;
83 /// <summary>
84 /// Updates the UI for a counter.
85 /// </summary>
86 /// <param name="counter"></param>
87 private void UpdateCounter(IStatsCounter counter) {
88 ListView view;
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.
98 // See if the counter is already in the list.
99 ListViewItem item = view.Items[counter.name];
100 if (item != null)
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_);
107 else
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);
120 /// <summary>
121 /// Sample the data and update the UI
122 /// </summary>
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;
129 if (stats_counters_ == null)
130 stats_counters_ = stats_table_.Counters();
132 if (pause_updates_)
133 return;
135 stats_counters_.Update();
137 UpdateProcessList();
139 foreach (IStatsCounter counter in stats_counters_)
140 UpdateCounter(counter);
143 /// <summary>
144 /// Set the background color based on the value
145 /// </summary>
146 /// <param name="item"></param>
147 /// <param name="value"></param>
148 private void ColorItem(ListViewItem item, int value)
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;
158 /// <summary>
159 /// Called when the timer fires.
160 /// </summary>
161 /// <param name="sender"></param>
162 /// <param name="e"></param>
163 void PollTimerTicked(object sender, EventArgs e) {
164 SampleData();
167 /// <summary>
168 /// Called when the interval is changed by the user.
169 /// </summary>
170 /// <param name="sender"></param>
171 /// <param name="e"></param>
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();
180 } else {
181 comboBoxInterval.Text = timer_.Interval.ToString();
185 /// <summary>
186 /// Called when the user changes the filter
187 /// </summary>
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)
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);
201 /// <summary>
202 /// Callback when the mouse enters a control
203 /// </summary>
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;
212 /// <summary>
213 /// Callback when the mouse leaves a control
214 /// </summary>
215 /// <param name="sender"></param>
216 /// <param name="e"></param>
217 private void mouse_Leave(object sender, EventArgs e) {
218 pause_updates_ = false;
221 /// <summary>
222 /// Called when the user clicks the zero-stats button.
223 /// </summary>
224 /// <param name="sender"></param>
225 /// <param name="e"></param>
226 private void buttonZero_Click(object sender, EventArgs e) {
227 this.stats_table_.Zero();
228 SampleData();
231 /// <summary>
232 /// Called when the user clicks a column heading.
233 /// </summary>
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;
240 } else {
241 if (this.listViewCounters.Sorting == SortOrder.Ascending)
242 this.listViewCounters.Sorting = SortOrder.Descending;
243 else
244 this.listViewCounters.Sorting = SortOrder.Ascending;
247 this.listViewCounters.ListViewItemSorter =
248 new ListViewItemComparer(e.Column, this.listViewCounters.Sorting);
249 this.listViewCounters.Sort();
252 /// <summary>
253 /// Called when the user clicks the button "Export".
254 /// </summary>
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;
263 try {
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));
276 finally{
277 if (tw != null) tw.Close();
281 #endregion
283 class ListViewItemComparer : IComparer {
284 public ListViewItemComparer() {
285 this.col_ = 0;
286 this.order_ = SortOrder.Ascending;
289 public ListViewItemComparer(int column, SortOrder order) {
290 this.col_ = column;
291 this.order_ = 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);
302 else
303 return_value = String.Compare(((ListViewItem)x).SubItems[col_].Text,
304 ((ListViewItem)y).SubItems[col_].Text);
306 if (order_ == SortOrder.Descending)
307 return_value *= -1;
309 return return_value;
312 #region Private Methods
313 private bool Comparable(object x, object y) {
314 if (x == null || y == null)
315 return false;
317 return x is IComparable && y is IComparable;
319 #endregion
321 #region Private Members
322 private int col_;
323 private SortOrder order_;
324 #endregion
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
340 #region Private Event Callbacks
341 private void openToolStripMenuItem_Click(object sender, EventArgs e)
343 OpenDialog dialog = new OpenDialog();
344 dialog.ShowDialog();
346 CloseStatsFile();
348 StatsTable table = new StatsTable();
349 bool rv = table.Open(dialog.FileName);
350 if (!rv)
352 MessageBox.Show("Could not open statsfile: " + dialog.FileName);
354 else
356 stats_table_ = table;
360 private void closeToolStripMenuItem_Click(object sender, EventArgs e)
362 CloseStatsFile();
365 private void quitToolStripMenuItem_Click(object sender, EventArgs e)
367 Application.Exit();
369 #endregion
372 /// <summary>
373 /// Base class for counter list view items.
374 /// </summary>
375 internal class StatsCounterListViewItem : ListViewItem
377 /// <summary>
378 /// Create the ListViewItem
379 /// </summary>
380 /// <param name="text"></param>
381 public StatsCounterListViewItem(string text) : base(text) { }
383 /// <summary>
384 /// Update the ListViewItem given a new counter value.
385 /// </summary>
386 /// <param name="counter"></param>
387 /// <param name="filter_pid"></param>
388 public virtual void Update(IStatsCounter counter, int filter_pid) { }
390 /// <summary>
391 /// Set the background color based on the value
392 /// </summary>
393 /// <param name="value"></param>
394 protected void ColorItem(int value)
396 if (value < 0)
397 ForeColor = Color.Red;
398 else if (value > 0)
399 ForeColor = Color.DarkGreen;
400 else
401 ForeColor = Color.Black;
404 /// <summary>
405 /// Create a new subitem with a zeroed Tag.
406 /// </summary>
407 /// <returns></returns>
408 protected ListViewSubItem NewSubItem()
410 ListViewSubItem item = new ListViewSubItem();
411 item.Tag = -1; // Arbitrarily initialize to -1.
412 return item;
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>
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();
430 item.Tag = val;
431 return true;
433 return false;
437 /// <summary>
438 /// A listview item which contains a rate.
439 /// </summary>
440 internal class RateListViewItem : StatsCounterListViewItem
442 public RateListViewItem(IStatsCounter ctr, int filter_pid) :
443 base(ctr.name)
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);
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))
467 ColorItem(delta);
468 Tag = 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;
476 /// <summary>
477 /// A listview item which contains a counter.
478 /// </summary>
479 internal class CounterListViewItem : StatsCounterListViewItem
481 public CounterListViewItem(IStatsCounter ctr, int filter_pid) :
482 base(ctr.name)
484 Name = ctr.name;
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);
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);
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;
507 private const int column_value_index = 1;
508 private const int column_delta_index = 2;