Add ICU message format support
[chromium-blink-merge.git] / tools / stats_viewer / stats_table.cs
blobe5d0894e37c579d090e023812e7d7a7c43784e93
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.Diagnostics;
9 using System.Runtime.InteropServices;
10 using System.Text;
12 namespace StatsViewer
14 /// <summary>
15 /// The stats table shared memory segment contains this
16 /// header structure.
17 /// </summary>
18 [StructLayout(LayoutKind.Sequential)]
19 internal struct StatsFileHeader {
20 public int version;
21 public int size;
22 public int max_counters;
23 public int max_threads;
26 /// <summary>
27 /// An entry in the StatsTable.
28 /// </summary>
29 class StatsTableEntry {
30 public StatsTableEntry(int id, string name, StatsTable table) {
31 id_ = id;
32 name_ = name;
33 table_ = table;
36 /// <summary>
37 /// The unique id for this entry
38 /// </summary>
39 public int id { get { return id_; } }
41 /// <summary>
42 /// The name for this entry.
43 /// </summary>
44 public string name { get { return name_; } }
46 /// <summary>
47 /// The value of this entry now.
48 /// </summary>
49 public int GetValue(int filter_pid) {
50 return table_.GetValue(id_, filter_pid);
53 private int id_;
54 private string name_;
55 private StatsTable table_;
58 // An interface for StatsCounters
59 interface IStatsCounter {
60 // The name of the counter
61 string name { get; }
64 // A counter.
65 class StatsCounter : IStatsCounter {
66 public StatsCounter(StatsTableEntry entry) {
67 entry_ = entry;
70 public string name {
71 get {
72 return entry_.name;
76 public int GetValue(int filter_pid) {
77 return entry_.GetValue(filter_pid);
80 private StatsTableEntry entry_;
83 // A timer.
84 class StatsTimer : IStatsCounter {
85 public StatsTimer(StatsTableEntry entry)
87 entry_ = entry;
90 public string name {
91 get {
92 return entry_.name;
96 public int GetValue(int filter_pid) {
97 return entry_.GetValue(filter_pid);
100 private StatsTableEntry entry_;
103 // A rate.
104 class StatsCounterRate : IStatsCounter
106 public StatsCounterRate(StatsCounter counter, StatsTimer timer) {
107 counter_ = counter;
108 timer_ = timer;
111 public string name { get { return counter_.name; } }
113 public int GetCount(int filter_pid) {
114 return counter_.GetValue(filter_pid);
117 public int GetTime(int filter_pid) {
118 return timer_.GetValue(filter_pid);
121 private StatsCounter counter_;
122 private StatsTimer timer_;
125 /// <summary>
126 /// This is a C# reader for the chrome stats_table.
127 /// </summary>
128 class StatsTable {
129 internal const int kMaxThreadNameLength = 32;
130 internal const int kMaxCounterNameLength = 32;
132 /// <summary>
133 /// Open a StatsTable
134 /// </summary>
135 public StatsTable() {
138 #region Public Properties
139 /// <summary>
140 /// Get access to the counters in the table.
141 /// </summary>
142 public StatsTableCounters Counters() {
143 return new StatsTableCounters(this);
146 /// <summary>
147 /// Get access to the processes in the table
148 /// </summary>
149 public ICollection Processes {
150 get {
151 return new StatsTableProcesses(this);
154 #endregion
156 #region Internal Properties
158 // The internal methods are accessible to the enumerators
159 // and helper classes below.
162 /// <summary>
163 /// Access to the table header
164 /// </summary>
165 internal StatsFileHeader Header {
166 get { return header_; }
169 /// <summary>
170 /// Get the offset of the ThreadName table
171 /// </summary>
172 internal long ThreadNamesOffset {
173 get {
174 return memory_.ToInt64() + Marshal.SizeOf(typeof(StatsFileHeader));
178 /// <summary>
179 /// Get the offset of the PIDs table
180 /// </summary>
181 internal long PidsOffset {
182 get {
183 long offset = ThreadNamesOffset;
184 // Thread names table
185 offset += AlignedSize(header_.max_threads * kMaxThreadNameLength * 2);
186 // Thread TID table
187 offset += AlignedSize(header_.max_threads *
188 Marshal.SizeOf(typeof(int)));
189 return offset;
193 /// <summary>
194 /// Get the offset of the CounterName table
195 /// </summary>
196 internal long CounterNamesOffset {
197 get {
198 long offset = PidsOffset;
199 // Thread PID table
200 offset += AlignedSize(header_.max_threads *
201 Marshal.SizeOf(typeof(int)));
202 return offset;
206 /// <summary>
207 /// Get the offset of the Data table
208 /// </summary>
209 internal long DataOffset {
210 get {
211 long offset = CounterNamesOffset;
212 // Counter names table
213 offset += AlignedSize(header_.max_counters *
214 kMaxCounterNameLength * 2);
215 return offset;
218 #endregion
220 #region Public Methods
221 /// <summary>
222 /// Opens the memory map
223 /// </summary>
224 /// <returns></returns>
225 /// <param name="name">The name of the file to open</param>
226 public bool Open(string name) {
227 map_handle_ =
228 Win32.OpenFileMapping((int)Win32.MapAccess.FILE_MAP_WRITE, false,
229 name);
230 if (map_handle_ == IntPtr.Zero)
231 return false;
233 memory_ =
234 Win32.MapViewOfFile(map_handle_, (int)Win32.MapAccess.FILE_MAP_WRITE,
235 0,0, 0);
236 if (memory_ == IntPtr.Zero) {
237 Win32.CloseHandle(map_handle_);
238 return false;
241 header_ = (StatsFileHeader)Marshal.PtrToStructure(memory_, header_.GetType());
242 return true;
245 /// <summary>
246 /// Close the mapped file.
247 /// </summary>
248 public void Close() {
249 Win32.UnmapViewOfFile(memory_);
250 Win32.CloseHandle(map_handle_);
253 /// <summary>
254 /// Zero out the stats file.
255 /// </summary>
256 public void Zero() {
257 long offset = DataOffset;
258 for (int threads = 0; threads < header_.max_threads; threads++) {
259 for (int counters = 0; counters < header_.max_counters; counters++) {
260 Marshal.WriteInt32((IntPtr) offset, 0);
261 offset += Marshal.SizeOf(typeof(int));
266 /// <summary>
267 /// Get the value for a StatsCounterEntry now.
268 /// </summary>
269 /// <returns></returns>
270 /// <param name="filter_pid">If a specific PID is being queried, filter to this PID. 0 means use all data.</param>
271 /// <param name="id">The id of the CounterEntry to get the value for.</param>
272 public int GetValue(int id, int filter_pid) {
273 long pid_offset = PidsOffset;
274 long data_offset = DataOffset;
275 data_offset += id * (Header.max_threads *
276 Marshal.SizeOf(typeof(int)));
277 int rv = 0;
278 for (int cols = 0; cols < Header.max_threads; cols++)
280 int pid = Marshal.ReadInt32((IntPtr)pid_offset);
281 if (filter_pid == 0 || filter_pid == pid)
283 rv += Marshal.ReadInt32((IntPtr)data_offset);
285 data_offset += Marshal.SizeOf(typeof(int));
286 pid_offset += Marshal.SizeOf(typeof(int));
288 return rv;
290 #endregion
292 #region Private Methods
293 /// <summary>
294 /// Align to 4-byte boundaries
295 /// </summary>
296 /// <param name="size"></param>
297 /// <returns></returns>
298 private long AlignedSize(long size) {
299 Debug.Assert(sizeof(int) == 4);
300 return size + (sizeof(int) - (size % sizeof(int))) % sizeof(int);
302 #endregion
304 #region Private Members
305 private IntPtr memory_;
306 private IntPtr map_handle_;
307 private StatsFileHeader header_;
308 #endregion
311 /// <summary>
312 /// Enumerable list of Counters in the StatsTable
313 /// </summary>
314 class StatsTableCounters : ICollection {
315 /// <summary>
316 /// Create the list of counters
317 /// </summary>
318 /// <param name="table"></param>
319 /// pid</param>
320 public StatsTableCounters(StatsTable table) {
321 table_ = table;
322 counter_hi_water_mark_ = -1;
323 counters_ = new List<IStatsCounter>();
324 FindCounters();
327 /// <summary>
328 /// Scans the table for new entries.
329 /// </summary>
330 public void Update() {
331 FindCounters();
334 #region IEnumerable Members
335 public IEnumerator GetEnumerator() {
336 return counters_.GetEnumerator();
338 #endregion
340 #region ICollection Members
341 public void CopyTo(Array array, int index) {
342 throw new Exception("The method or operation is not implemented.");
345 public int Count {
346 get {
347 return counters_.Count;
351 public bool IsSynchronized {
352 get {
353 throw new Exception("The method or operation is not implemented.");
357 public object SyncRoot {
358 get {
359 throw new Exception("The method or operation is not implemented.");
362 #endregion
364 #region Private Methods
365 /// <summary>
366 /// Create a counter based on an entry
367 /// </summary>
368 /// <param name="id"></param>
369 /// <param name="name"></param>
370 /// <returns></returns>
371 private IStatsCounter NameToCounter(int id, string name)
373 IStatsCounter rv = null;
375 // check if the name has a type encoded
376 if (name.Length > 2 && name[1] == ':')
378 StatsTableEntry entry = new StatsTableEntry(id, name.Substring(2), table_);
379 switch (name[0])
381 case 't':
382 rv = new StatsTimer(entry);
383 break;
384 case 'c':
385 rv = new StatsCounter(entry);
386 break;
389 else
391 StatsTableEntry entry = new StatsTableEntry(id, name, table_);
392 rv = new StatsCounter(entry);
395 return rv;
398 // If we have two StatsTableEntries with the same name,
399 // attempt to upgrade them to a higher level type.
400 // Example: A counter + a timer == a rate!
401 private void UpgradeCounter(IStatsCounter old_counter, IStatsCounter counter)
403 if (old_counter is StatsCounter && counter is StatsTimer)
405 StatsCounterRate rate = new StatsCounterRate(old_counter as StatsCounter,
406 counter as StatsTimer);
407 counters_.Remove(old_counter);
408 counters_.Add(rate);
410 else if (old_counter is StatsTimer && counter is StatsCounter)
412 StatsCounterRate rate = new StatsCounterRate(counter as StatsCounter,
413 old_counter as StatsTimer);
414 counters_.Remove(old_counter);
415 counters_.Add(rate);
419 /// <summary>
420 /// Find the counters in the table and insert into the counters_
421 /// hash table.
422 /// </summary>
423 private void FindCounters()
425 Debug.Assert(table_.Header.max_counters > 0);
427 int index = counter_hi_water_mark_;
431 // Find an entry in the table.
432 index++;
433 long offset = table_.CounterNamesOffset +
434 (index * StatsTable.kMaxCounterNameLength * 2);
435 string name = Marshal.PtrToStringUni((IntPtr)offset);
436 if (name.Length == 0)
437 continue;
439 // Record that we've already looked at this StatsTableEntry.
440 counter_hi_water_mark_ = index;
442 IStatsCounter counter = NameToCounter(index, name);
444 if (counter != null)
446 IStatsCounter old_counter = FindExistingCounter(counter.name);
447 if (old_counter != null)
448 UpgradeCounter(old_counter, counter);
449 else
450 counters_.Add(counter);
452 } while (index < table_.Header.max_counters - 1);
455 /// <summary>
456 /// Find an existing counter in our table
457 /// </summary>
458 /// <param name="name"></param>
459 private IStatsCounter FindExistingCounter(string name) {
460 foreach (IStatsCounter ctr in counters_)
462 if (ctr.name == name)
463 return ctr;
465 return null;
467 #endregion
469 #region Private Members
470 private StatsTable table_;
471 private List<IStatsCounter> counters_;
472 // Highest index of counters processed.
473 private int counter_hi_water_mark_;
474 #endregion
477 /// <summary>
478 /// A collection of processes
479 /// </summary>
480 class StatsTableProcesses : ICollection
482 /// <summary>
483 /// Constructor
484 /// </summary>
485 /// <param name="table"></param>
486 public StatsTableProcesses(StatsTable table) {
487 table_ = table;
488 pids_ = new List<int>();
489 Initialize();
492 #region ICollection Members
493 public void CopyTo(Array array, int index) {
494 throw new Exception("The method or operation is not implemented.");
497 public int Count {
498 get {
499 return pids_.Count;
503 public bool IsSynchronized {
504 get {
505 throw new Exception("The method or operation is not implemented.");
509 public object SyncRoot {
510 get {
511 throw new Exception("The method or operation is not implemented.");
514 #endregion
516 #region IEnumerable Members
517 public IEnumerator GetEnumerator() {
518 return pids_.GetEnumerator();
520 #endregion
522 /// <summary>
523 /// Initialize the pid list.
524 /// </summary>
525 private void Initialize() {
526 long offset = table_.ThreadNamesOffset;
528 for (int index = 0; index < table_.Header.max_threads; index++) {
529 string thread_name = Marshal.PtrToStringUni((IntPtr)offset);
530 if (thread_name.Length > 0) {
531 long pidOffset = table_.PidsOffset + index *
532 Marshal.SizeOf(typeof(int));
533 int pid = Marshal.ReadInt32((IntPtr)pidOffset);
534 if (!pids_.Contains(pid))
535 pids_.Add(pid);
537 offset += StatsTable.kMaxThreadNameLength * 2;
541 #region Private Members
542 private StatsTable table_;
543 private List<int> pids_;
544 #endregion