diff options
Diffstat (limited to 'tools/stats_viewer/stats_table.cs')
-rw-r--r-- | tools/stats_viewer/stats_table.cs | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/tools/stats_viewer/stats_table.cs b/tools/stats_viewer/stats_table.cs new file mode 100644 index 0000000..a2f87cf --- /dev/null +++ b/tools/stats_viewer/stats_table.cs @@ -0,0 +1,571 @@ +// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace StatsViewer
+{
+ /// <summary>
+ /// The stats table shared memory segment contains this
+ /// header structure.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct StatsFileHeader {
+ public int version;
+ public int size;
+ public int max_counters;
+ public int max_threads;
+ };
+
+ /// <summary>
+ /// An entry in the StatsTable.
+ /// </summary>
+ class StatsTableEntry {
+ public StatsTableEntry(int id, string name, StatsTable table) {
+ id_ = id;
+ name_ = name;
+ table_ = table;
+ }
+
+ /// <summary>
+ /// The unique id for this entry
+ /// </summary>
+ public int id { get { return id_; } }
+
+ /// <summary>
+ /// The name for this entry.
+ /// </summary>
+ public string name { get { return name_; } }
+
+ /// <summary>
+ /// The value of this entry now.
+ /// </summary>
+ public int GetValue(int filter_pid) {
+ return table_.GetValue(id_, filter_pid);
+ }
+
+ private int id_;
+ private string name_;
+ private StatsTable table_;
+ }
+
+ // An interface for StatsCounters
+ interface IStatsCounter {
+ // The name of the counter
+ string name { get; }
+ }
+
+ // A counter.
+ class StatsCounter : IStatsCounter {
+ public StatsCounter(StatsTableEntry entry) {
+ entry_ = entry;
+ }
+
+ public string name {
+ get {
+ return entry_.name;
+ }
+ }
+
+ public int GetValue(int filter_pid) {
+ return entry_.GetValue(filter_pid);
+ }
+
+ private StatsTableEntry entry_;
+ }
+
+ // A timer.
+ class StatsTimer : IStatsCounter {
+ public StatsTimer(StatsTableEntry entry)
+ {
+ entry_ = entry;
+ }
+
+ public string name {
+ get {
+ return entry_.name;
+ }
+ }
+
+ public int GetValue(int filter_pid) {
+ return entry_.GetValue(filter_pid);
+ }
+
+ private StatsTableEntry entry_;
+ }
+
+ // A rate.
+ class StatsCounterRate : IStatsCounter
+ {
+ public StatsCounterRate(StatsCounter counter, StatsTimer timer) {
+ counter_ = counter;
+ timer_ = timer;
+ }
+
+ public string name { get { return counter_.name; } }
+
+ public int GetCount(int filter_pid) {
+ return counter_.GetValue(filter_pid);
+ }
+
+ public int GetTime(int filter_pid) {
+ return timer_.GetValue(filter_pid);
+ }
+
+ private StatsCounter counter_;
+ private StatsTimer timer_;
+ }
+
+ /// <summary>
+ /// This is a C# reader for the chrome stats_table.
+ /// </summary>
+ class StatsTable {
+ internal const int kMaxThreadNameLength = 32;
+ internal const int kMaxCounterNameLength = 32;
+
+ /// <summary>
+ /// Open a StatsTable
+ /// </summary>
+ public StatsTable() {
+ }
+
+ #region Public Properties
+ /// <summary>
+ /// Get access to the counters in the table.
+ /// </summary>
+ public StatsTableCounters Counters() {
+ return new StatsTableCounters(this);
+ }
+
+ /// <summary>
+ /// Get access to the processes in the table
+ /// </summary>
+ public ICollection Processes {
+ get {
+ return new StatsTableProcesses(this);
+ }
+ }
+ #endregion
+
+ #region Internal Properties
+ //
+ // The internal methods are accessible to the enumerators
+ // and helper classes below.
+ //
+
+ /// <summary>
+ /// Access to the table header
+ /// </summary>
+ internal StatsFileHeader Header {
+ get { return header_; }
+ }
+
+ /// <summary>
+ /// Get the offset of the ThreadName table
+ /// </summary>
+ internal long ThreadNamesOffset {
+ get {
+ return memory_.ToInt64() + Marshal.SizeOf(typeof(StatsFileHeader));
+ }
+ }
+
+ /// <summary>
+ /// Get the offset of the PIDs table
+ /// </summary>
+ internal long PidsOffset {
+ get {
+ long offset = ThreadNamesOffset;
+ // Thread names table
+ offset += AlignedSize(header_.max_threads * kMaxThreadNameLength * 2);
+ // Thread TID table
+ offset += AlignedSize(header_.max_threads *
+ Marshal.SizeOf(typeof(int)));
+ return offset;
+ }
+ }
+
+ /// <summary>
+ /// Get the offset of the CounterName table
+ /// </summary>
+ internal long CounterNamesOffset {
+ get {
+ long offset = PidsOffset;
+ // Thread PID table
+ offset += AlignedSize(header_.max_threads *
+ Marshal.SizeOf(typeof(int)));
+ return offset;
+ }
+ }
+
+ /// <summary>
+ /// Get the offset of the Data table
+ /// </summary>
+ internal long DataOffset {
+ get {
+ long offset = CounterNamesOffset;
+ // Counter names table
+ offset += AlignedSize(header_.max_counters *
+ kMaxCounterNameLength * 2);
+ return offset;
+ }
+ }
+ #endregion
+
+ #region Public Methods
+ /// <summary>
+ /// Opens the memory map
+ /// </summary>
+ /// <returns></returns>
+ /// <param name="name">The name of the file to open</param>
+ public bool Open(string name) {
+ map_handle_ =
+ Win32.OpenFileMapping((int)Win32.MapAccess.FILE_MAP_WRITE, false,
+ name);
+ if (map_handle_ == IntPtr.Zero)
+ return false;
+
+ memory_ =
+ Win32.MapViewOfFile(map_handle_, (int)Win32.MapAccess.FILE_MAP_WRITE,
+ 0,0, 0);
+ if (memory_ == IntPtr.Zero) {
+ Win32.CloseHandle(map_handle_);
+ return false;
+ }
+
+ header_ = (StatsFileHeader)Marshal.PtrToStructure(memory_, header_.GetType());
+ return true;
+ }
+
+ /// <summary>
+ /// Close the mapped file.
+ /// </summary>
+ public void Close() {
+ Win32.UnmapViewOfFile(memory_);
+ Win32.CloseHandle(map_handle_);
+ }
+
+ /// <summary>
+ /// Zero out the stats file.
+ /// </summary>
+ public void Zero() {
+ long offset = DataOffset;
+ for (int threads = 0; threads < header_.max_threads; threads++) {
+ for (int counters = 0; counters < header_.max_counters; counters++) {
+ Marshal.WriteInt32((IntPtr) offset, 0);
+ offset += Marshal.SizeOf(typeof(int));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Get the value for a StatsCounterEntry now.
+ /// </summary>
+ /// <returns></returns>
+ /// <param name="filter_pid">If a specific PID is being queried, filter to this PID. 0 means use all data.</param>
+ /// <param name="id">The id of the CounterEntry to get the value for.</param>
+ public int GetValue(int id, int filter_pid) {
+ long pid_offset = PidsOffset;
+ long data_offset = DataOffset;
+ data_offset += id * (Header.max_threads *
+ Marshal.SizeOf(typeof(int)));
+ int rv = 0;
+ for (int cols = 0; cols < Header.max_threads; cols++)
+ {
+ int pid = Marshal.ReadInt32((IntPtr)pid_offset);
+ if (filter_pid == 0 || filter_pid == pid)
+ {
+ rv += Marshal.ReadInt32((IntPtr)data_offset);
+ }
+ data_offset += Marshal.SizeOf(typeof(int));
+ pid_offset += Marshal.SizeOf(typeof(int));
+ }
+ return rv;
+ }
+ #endregion
+
+ #region Private Methods
+ /// <summary>
+ /// Align to 4-byte boundaries
+ /// </summary>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ private long AlignedSize(long size) {
+ Debug.Assert(sizeof(int) == 4);
+ return size + (sizeof(int) - (size % sizeof(int))) % sizeof(int);
+ }
+ #endregion
+
+ #region Private Members
+ private IntPtr memory_;
+ private IntPtr map_handle_;
+ private StatsFileHeader header_;
+ #endregion
+ }
+
+ /// <summary>
+ /// Enumerable list of Counters in the StatsTable
+ /// </summary>
+ class StatsTableCounters : ICollection {
+ /// <summary>
+ /// Create the list of counters
+ /// </summary>
+ /// <param name="table"></param>
+ /// pid</param>
+ public StatsTableCounters(StatsTable table) {
+ table_ = table;
+ counter_hi_water_mark_ = -1;
+ counters_ = new List<IStatsCounter>();
+ FindCounters();
+ }
+
+ /// <summary>
+ /// Scans the table for new entries.
+ /// </summary>
+ public void Update() {
+ FindCounters();
+ }
+
+ #region IEnumerable Members
+ public IEnumerator GetEnumerator() {
+ return counters_.GetEnumerator();
+ }
+ #endregion
+
+ #region ICollection Members
+ public void CopyTo(Array array, int index) {
+ throw new Exception("The method or operation is not implemented.");
+ }
+
+ public int Count {
+ get {
+ return counters_.Count;
+ }
+ }
+
+ public bool IsSynchronized {
+ get {
+ throw new Exception("The method or operation is not implemented.");
+ }
+ }
+
+ public object SyncRoot {
+ get {
+ throw new Exception("The method or operation is not implemented.");
+ }
+ }
+ #endregion
+
+ #region Private Methods
+ /// <summary>
+ /// Create a counter based on an entry
+ /// </summary>
+ /// <param name="id"></param>
+ /// <param name="name"></param>
+ /// <returns></returns>
+ private IStatsCounter NameToCounter(int id, string name)
+ {
+ IStatsCounter rv = null;
+
+ // check if the name has a type encoded
+ if (name.Length > 2 && name[1] == ':')
+ {
+ StatsTableEntry entry = new StatsTableEntry(id, name.Substring(2), table_);
+ switch (name[0])
+ {
+ case 't':
+ rv = new StatsTimer(entry);
+ break;
+ case 'c':
+ rv = new StatsCounter(entry);
+ break;
+ }
+ }
+ else
+ {
+ StatsTableEntry entry = new StatsTableEntry(id, name, table_);
+ rv = new StatsCounter(entry);
+ }
+
+ return rv;
+ }
+
+ // If we have two StatsTableEntries with the same name,
+ // attempt to upgrade them to a higher level type.
+ // Example: A counter + a timer == a rate!
+ private void UpgradeCounter(IStatsCounter old_counter, IStatsCounter counter)
+ {
+ if (old_counter is StatsCounter && counter is StatsTimer)
+ {
+ StatsCounterRate rate = new StatsCounterRate(old_counter as StatsCounter,
+ counter as StatsTimer);
+ counters_.Remove(old_counter);
+ counters_.Add(rate);
+ }
+ else if (old_counter is StatsTimer && counter is StatsCounter)
+ {
+ StatsCounterRate rate = new StatsCounterRate(counter as StatsCounter,
+ old_counter as StatsTimer);
+ counters_.Remove(old_counter);
+ counters_.Add(rate);
+ }
+ }
+
+ /// <summary>
+ /// Find the counters in the table and insert into the counters_
+ /// hash table.
+ /// </summary>
+ private void FindCounters()
+ {
+ Debug.Assert(table_.Header.max_counters > 0);
+
+ int index = counter_hi_water_mark_;
+
+ do
+ {
+ // Find an entry in the table.
+ index++;
+ long offset = table_.CounterNamesOffset +
+ (index * StatsTable.kMaxCounterNameLength * 2);
+ string name = Marshal.PtrToStringUni((IntPtr)offset);
+ if (name.Length == 0)
+ continue;
+
+ // Record that we've already looked at this StatsTableEntry.
+ counter_hi_water_mark_ = index;
+
+ IStatsCounter counter = NameToCounter(index, name);
+
+ if (counter != null)
+ {
+ IStatsCounter old_counter = FindExistingCounter(counter.name);
+ if (old_counter != null)
+ UpgradeCounter(old_counter, counter);
+ else
+ counters_.Add(counter);
+ }
+ } while (index < table_.Header.max_counters - 1);
+ }
+
+ /// <summary>
+ /// Find an existing counter in our table
+ /// </summary>
+ /// <param name="name"></param>
+ private IStatsCounter FindExistingCounter(string name) {
+ foreach (IStatsCounter ctr in counters_)
+ {
+ if (ctr.name == name)
+ return ctr;
+ }
+ return null;
+ }
+ #endregion
+
+ #region Private Members
+ private StatsTable table_;
+ private List<IStatsCounter> counters_;
+ // Highest index of counters processed.
+ private int counter_hi_water_mark_;
+ #endregion
+ }
+
+ /// <summary>
+ /// A collection of processes
+ /// </summary>
+ class StatsTableProcesses : ICollection
+ {
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ /// <param name="table"></param>
+ public StatsTableProcesses(StatsTable table) {
+ table_ = table;
+ pids_ = new List<int>();
+ Initialize();
+ }
+
+ #region ICollection Members
+ public void CopyTo(Array array, int index) {
+ throw new Exception("The method or operation is not implemented.");
+ }
+
+ public int Count {
+ get {
+ return pids_.Count;
+ }
+ }
+
+ public bool IsSynchronized {
+ get {
+ throw new Exception("The method or operation is not implemented.");
+ }
+ }
+
+ public object SyncRoot {
+ get {
+ throw new Exception("The method or operation is not implemented.");
+ }
+ }
+ #endregion
+
+ #region IEnumerable Members
+ public IEnumerator GetEnumerator() {
+ return pids_.GetEnumerator();
+ }
+ #endregion
+
+ /// <summary>
+ /// Initialize the pid list.
+ /// </summary>
+ private void Initialize() {
+ long offset = table_.ThreadNamesOffset;
+
+ for (int index = 0; index < table_.Header.max_threads; index++) {
+ string thread_name = Marshal.PtrToStringUni((IntPtr)offset);
+ if (thread_name.Length > 0) {
+ long pidOffset = table_.PidsOffset + index *
+ Marshal.SizeOf(typeof(int));
+ int pid = Marshal.ReadInt32((IntPtr)pidOffset);
+ if (!pids_.Contains(pid))
+ pids_.Add(pid);
+ }
+ offset += StatsTable.kMaxThreadNameLength * 2;
+ }
+ }
+
+ #region Private Members
+ private StatsTable table_;
+ private List<int> pids_;
+ #endregion
+ }
+}
|