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.Diagnostics;
\r
9 using System.Runtime.InteropServices;
\r
12 namespace StatsViewer
\r
15 /// The stats table shared memory segment contains this
\r
16 /// header structure.
\r
18 [StructLayout(LayoutKind.Sequential)]
\r
19 internal struct StatsFileHeader {
\r
22 public int max_counters;
\r
23 public int max_threads;
\r
27 /// An entry in the StatsTable.
\r
29 class StatsTableEntry {
\r
30 public StatsTableEntry(int id, string name, StatsTable table) {
\r
37 /// The unique id for this entry
\r
39 public int id { get { return id_; } }
\r
42 /// The name for this entry.
\r
44 public string name { get { return name_; } }
\r
47 /// The value of this entry now.
\r
49 public int GetValue(int filter_pid) {
\r
50 return table_.GetValue(id_, filter_pid);
\r
54 private string name_;
\r
55 private StatsTable table_;
\r
58 // An interface for StatsCounters
\r
59 interface IStatsCounter {
\r
60 // The name of the counter
\r
61 string name { get; }
\r
65 class StatsCounter : IStatsCounter {
\r
66 public StatsCounter(StatsTableEntry entry) {
\r
70 public string name {
\r
72 return entry_.name;
\r
76 public int GetValue(int filter_pid) {
\r
77 return entry_.GetValue(filter_pid);
\r
80 private StatsTableEntry entry_;
\r
84 class StatsTimer : IStatsCounter {
\r
85 public StatsTimer(StatsTableEntry entry)
\r
90 public string name {
\r
92 return entry_.name;
\r
96 public int GetValue(int filter_pid) {
\r
97 return entry_.GetValue(filter_pid);
\r
100 private StatsTableEntry entry_;
\r
104 class StatsCounterRate : IStatsCounter
\r
106 public StatsCounterRate(StatsCounter counter, StatsTimer timer) {
\r
107 counter_ = counter;
\r
111 public string name { get { return counter_.name; } }
\r
113 public int GetCount(int filter_pid) {
\r
114 return counter_.GetValue(filter_pid);
\r
117 public int GetTime(int filter_pid) {
\r
118 return timer_.GetValue(filter_pid);
\r
121 private StatsCounter counter_;
\r
122 private StatsTimer timer_;
\r
126 /// This is a C# reader for the chrome stats_table.
\r
129 internal const int kMaxThreadNameLength = 32;
\r
130 internal const int kMaxCounterNameLength = 32;
\r
133 /// Open a StatsTable
\r
135 public StatsTable() {
\r
138 #region Public Properties
\r
140 /// Get access to the counters in the table.
\r
142 public StatsTableCounters Counters() {
\r
143 return new StatsTableCounters(this);
\r
147 /// Get access to the processes in the table
\r
149 public ICollection Processes {
\r
151 return new StatsTableProcesses(this);
\r
156 #region Internal Properties
\r
158 // The internal methods are accessible to the enumerators
\r
159 // and helper classes below.
\r
163 /// Access to the table header
\r
165 internal StatsFileHeader Header {
\r
166 get { return header_; }
\r
170 /// Get the offset of the ThreadName table
\r
172 internal long ThreadNamesOffset {
\r
174 return memory_.ToInt64() + Marshal.SizeOf(typeof(StatsFileHeader));
\r
179 /// Get the offset of the PIDs table
\r
181 internal long PidsOffset {
\r
183 long offset = ThreadNamesOffset;
\r
184 // Thread names table
\r
185 offset += AlignedSize(header_.max_threads * kMaxThreadNameLength * 2);
\r
186 // Thread TID table
\r
187 offset += AlignedSize(header_.max_threads *
\r
188 Marshal.SizeOf(typeof(int)));
\r
194 /// Get the offset of the CounterName table
\r
196 internal long CounterNamesOffset {
\r
198 long offset = PidsOffset;
\r
199 // Thread PID table
\r
200 offset += AlignedSize(header_.max_threads *
\r
201 Marshal.SizeOf(typeof(int)));
\r
207 /// Get the offset of the Data table
\r
209 internal long DataOffset {
\r
211 long offset = CounterNamesOffset;
\r
212 // Counter names table
\r
213 offset += AlignedSize(header_.max_counters *
\r
214 kMaxCounterNameLength * 2);
\r
220 #region Public Methods
\r
222 /// Opens the memory map
\r
224 /// <returns></returns>
\r
225 /// <param name="name">The name of the file to open</param>
\r
226 public bool Open(string name) {
\r
228 Win32.OpenFileMapping((int)Win32.MapAccess.FILE_MAP_WRITE, false,
\r
230 if (map_handle_ == IntPtr.Zero)
\r
234 Win32.MapViewOfFile(map_handle_, (int)Win32.MapAccess.FILE_MAP_WRITE,
\r
236 if (memory_ == IntPtr.Zero) {
\r
237 Win32.CloseHandle(map_handle_);
\r
241 header_ = (StatsFileHeader)Marshal.PtrToStructure(memory_, header_.GetType());
\r
246 /// Close the mapped file.
\r
248 public void Close() {
\r
249 Win32.UnmapViewOfFile(memory_);
\r
250 Win32.CloseHandle(map_handle_);
\r
254 /// Zero out the stats file.
\r
256 public void Zero() {
\r
257 long offset = DataOffset;
\r
258 for (int threads = 0; threads < header_.max_threads; threads++) {
\r
259 for (int counters = 0; counters < header_.max_counters; counters++) {
\r
260 Marshal.WriteInt32((IntPtr) offset, 0);
\r
261 offset += Marshal.SizeOf(typeof(int));
\r
267 /// Get the value for a StatsCounterEntry now.
\r
269 /// <returns></returns>
\r
270 /// <param name="filter_pid">If a specific PID is being queried, filter to this PID. 0 means use all data.</param>
\r
271 /// <param name="id">The id of the CounterEntry to get the value for.</param>
\r
272 public int GetValue(int id, int filter_pid) {
\r
273 long pid_offset = PidsOffset;
\r
274 long data_offset = DataOffset;
\r
275 data_offset += id * (Header.max_threads *
\r
276 Marshal.SizeOf(typeof(int)));
\r
278 for (int cols = 0; cols < Header.max_threads; cols++)
\r
280 int pid = Marshal.ReadInt32((IntPtr)pid_offset);
\r
281 if (filter_pid == 0 || filter_pid == pid)
\r
283 rv += Marshal.ReadInt32((IntPtr)data_offset);
\r
285 data_offset += Marshal.SizeOf(typeof(int));
\r
286 pid_offset += Marshal.SizeOf(typeof(int));
\r
292 #region Private Methods
\r
294 /// Align to 4-byte boundaries
\r
296 /// <param name="size"></param>
\r
297 /// <returns></returns>
\r
298 private long AlignedSize(long size) {
\r
299 Debug.Assert(sizeof(int) == 4);
\r
300 return size + (sizeof(int) - (size % sizeof(int))) % sizeof(int);
\r
304 #region Private Members
\r
305 private IntPtr memory_;
\r
306 private IntPtr map_handle_;
\r
307 private StatsFileHeader header_;
\r
312 /// Enumerable list of Counters in the StatsTable
\r
314 class StatsTableCounters : ICollection {
\r
316 /// Create the list of counters
\r
318 /// <param name="table"></param>
\r
320 public StatsTableCounters(StatsTable table) {
\r
322 counter_hi_water_mark_ = -1;
\r
323 counters_ = new List<IStatsCounter>();
\r
328 /// Scans the table for new entries.
\r
330 public void Update() {
\r
334 #region IEnumerable Members
\r
335 public IEnumerator GetEnumerator() {
\r
336 return counters_.GetEnumerator();
\r
340 #region ICollection Members
\r
341 public void CopyTo(Array array, int index) {
\r
342 throw new Exception("The method or operation is not implemented.");
\r
347 return counters_.Count;
\r
351 public bool IsSynchronized {
\r
353 throw new Exception("The method or operation is not implemented.");
\r
357 public object SyncRoot {
\r
359 throw new Exception("The method or operation is not implemented.");
\r
364 #region Private Methods
\r
366 /// Create a counter based on an entry
\r
368 /// <param name="id"></param>
\r
369 /// <param name="name"></param>
\r
370 /// <returns></returns>
\r
371 private IStatsCounter NameToCounter(int id, string name)
\r
373 IStatsCounter rv = null;
\r
375 // check if the name has a type encoded
\r
376 if (name.Length > 2 && name[1] == ':')
\r
378 StatsTableEntry entry = new StatsTableEntry(id, name.Substring(2), table_);
\r
382 rv = new StatsTimer(entry);
\r
385 rv = new StatsCounter(entry);
\r
391 StatsTableEntry entry = new StatsTableEntry(id, name, table_);
\r
392 rv = new StatsCounter(entry);
\r
398 // If we have two StatsTableEntries with the same name,
\r
399 // attempt to upgrade them to a higher level type.
\r
400 // Example: A counter + a timer == a rate!
\r
401 private void UpgradeCounter(IStatsCounter old_counter, IStatsCounter counter)
\r
403 if (old_counter is StatsCounter && counter is StatsTimer)
\r
405 StatsCounterRate rate = new StatsCounterRate(old_counter as StatsCounter,
\r
406 counter as StatsTimer);
\r
407 counters_.Remove(old_counter);
\r
408 counters_.Add(rate);
\r
410 else if (old_counter is StatsTimer && counter is StatsCounter)
\r
412 StatsCounterRate rate = new StatsCounterRate(counter as StatsCounter,
\r
413 old_counter as StatsTimer);
\r
414 counters_.Remove(old_counter);
\r
415 counters_.Add(rate);
\r
420 /// Find the counters in the table and insert into the counters_
\r
423 private void FindCounters()
\r
425 Debug.Assert(table_.Header.max_counters > 0);
\r
427 int index = counter_hi_water_mark_;
\r
431 // Find an entry in the table.
\r
433 long offset = table_.CounterNamesOffset +
\r
434 (index * StatsTable.kMaxCounterNameLength * 2);
\r
435 string name = Marshal.PtrToStringUni((IntPtr)offset);
\r
436 if (name.Length == 0)
\r
439 // Record that we've already looked at this StatsTableEntry.
\r
440 counter_hi_water_mark_ = index;
\r
442 IStatsCounter counter = NameToCounter(index, name);
\r
444 if (counter != null)
\r
446 IStatsCounter old_counter = FindExistingCounter(counter.name);
\r
447 if (old_counter != null)
\r
448 UpgradeCounter(old_counter, counter);
\r
450 counters_.Add(counter);
\r
452 } while (index < table_.Header.max_counters - 1);
\r
456 /// Find an existing counter in our table
\r
458 /// <param name="name"></param>
\r
459 private IStatsCounter FindExistingCounter(string name) {
\r
460 foreach (IStatsCounter ctr in counters_)
\r
462 if (ctr.name == name)
\r
469 #region Private Members
\r
470 private StatsTable table_;
\r
471 private List<IStatsCounter> counters_;
\r
472 // Highest index of counters processed.
\r
473 private int counter_hi_water_mark_;
\r
478 /// A collection of processes
\r
480 class StatsTableProcesses : ICollection
\r
485 /// <param name="table"></param>
\r
486 public StatsTableProcesses(StatsTable table) {
\r
488 pids_ = new List<int>();
\r
492 #region ICollection Members
\r
493 public void CopyTo(Array array, int index) {
\r
494 throw new Exception("The method or operation is not implemented.");
\r
499 return pids_.Count;
\r
503 public bool IsSynchronized {
\r
505 throw new Exception("The method or operation is not implemented.");
\r
509 public object SyncRoot {
\r
511 throw new Exception("The method or operation is not implemented.");
\r
516 #region IEnumerable Members
\r
517 public IEnumerator GetEnumerator() {
\r
518 return pids_.GetEnumerator();
\r
523 /// Initialize the pid list.
\r
525 private void Initialize() {
\r
526 long offset = table_.ThreadNamesOffset;
\r
528 for (int index = 0; index < table_.Header.max_threads; index++) {
\r
529 string thread_name = Marshal.PtrToStringUni((IntPtr)offset);
\r
530 if (thread_name.Length > 0) {
\r
531 long pidOffset = table_.PidsOffset + index *
\r
532 Marshal.SizeOf(typeof(int));
\r
533 int pid = Marshal.ReadInt32((IntPtr)pidOffset);
\r
534 if (!pids_.Contains(pid))
\r
537 offset += StatsTable.kMaxThreadNameLength * 2;
\r
541 #region Private Members
\r
542 private StatsTable table_;
\r
543 private List<int> pids_;
\r