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
.Diagnostics
;
9 using System
.Runtime
.InteropServices
;
15 /// The stats table shared memory segment contains this
18 [StructLayout(LayoutKind
.Sequential
)]
19 internal struct StatsFileHeader
{
22 public int max_counters
;
23 public int max_threads
;
27 /// An entry in the StatsTable.
29 class StatsTableEntry
{
30 public StatsTableEntry(int id
, string name
, StatsTable table
) {
37 /// The unique id for this entry
39 public int id { get { return id_; }
}
42 /// The name for this entry.
44 public string name { get { return name_; }
}
47 /// The value of this entry now.
49 public int GetValue(int filter_pid
) {
50 return table_
.GetValue(id_
, filter_pid
);
55 private StatsTable table_
;
58 // An interface for StatsCounters
59 interface IStatsCounter
{
60 // The name of the counter
65 class StatsCounter
: IStatsCounter
{
66 public StatsCounter(StatsTableEntry entry
) {
76 public int GetValue(int filter_pid
) {
77 return entry_
.GetValue(filter_pid
);
80 private StatsTableEntry entry_
;
84 class StatsTimer
: IStatsCounter
{
85 public StatsTimer(StatsTableEntry entry
)
96 public int GetValue(int filter_pid
) {
97 return entry_
.GetValue(filter_pid
);
100 private StatsTableEntry entry_
;
104 class StatsCounterRate
: IStatsCounter
106 public StatsCounterRate(StatsCounter counter
, StatsTimer 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_
;
126 /// This is a C# reader for the chrome stats_table.
129 internal const int kMaxThreadNameLength
= 32;
130 internal const int kMaxCounterNameLength
= 32;
133 /// Open a StatsTable
135 public StatsTable() {
138 #region Public Properties
140 /// Get access to the counters in the table.
142 public StatsTableCounters
Counters() {
143 return new StatsTableCounters(this);
147 /// Get access to the processes in the table
149 public ICollection Processes
{
151 return new StatsTableProcesses(this);
156 #region Internal Properties
158 // The internal methods are accessible to the enumerators
159 // and helper classes below.
163 /// Access to the table header
165 internal StatsFileHeader Header
{
166 get { return header_; }
170 /// Get the offset of the ThreadName table
172 internal long ThreadNamesOffset
{
174 return memory_
.ToInt64() + Marshal
.SizeOf(typeof(StatsFileHeader
));
179 /// Get the offset of the PIDs table
181 internal long PidsOffset
{
183 long offset
= ThreadNamesOffset
;
184 // Thread names table
185 offset
+= AlignedSize(header_
.max_threads
* kMaxThreadNameLength
* 2);
187 offset
+= AlignedSize(header_
.max_threads
*
188 Marshal
.SizeOf(typeof(int)));
194 /// Get the offset of the CounterName table
196 internal long CounterNamesOffset
{
198 long offset
= PidsOffset
;
200 offset
+= AlignedSize(header_
.max_threads
*
201 Marshal
.SizeOf(typeof(int)));
207 /// Get the offset of the Data table
209 internal long DataOffset
{
211 long offset
= CounterNamesOffset
;
212 // Counter names table
213 offset
+= AlignedSize(header_
.max_counters
*
214 kMaxCounterNameLength
* 2);
220 #region Public Methods
222 /// Opens the memory map
224 /// <returns></returns>
225 /// <param name="name">The name of the file to open</param>
226 public bool Open(string name
) {
228 Win32
.OpenFileMapping((int)Win32
.MapAccess
.FILE_MAP_WRITE
, false,
230 if (map_handle_
== IntPtr
.Zero
)
234 Win32
.MapViewOfFile(map_handle_
, (int)Win32
.MapAccess
.FILE_MAP_WRITE
,
236 if (memory_
== IntPtr
.Zero
) {
237 Win32
.CloseHandle(map_handle_
);
241 header_
= (StatsFileHeader
)Marshal
.PtrToStructure(memory_
, header_
.GetType());
246 /// Close the mapped file.
248 public void Close() {
249 Win32
.UnmapViewOfFile(memory_
);
250 Win32
.CloseHandle(map_handle_
);
254 /// Zero out the stats file.
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));
267 /// Get the value for a StatsCounterEntry now.
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)));
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));
292 #region Private Methods
294 /// Align to 4-byte boundaries
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);
304 #region Private Members
305 private IntPtr memory_
;
306 private IntPtr map_handle_
;
307 private StatsFileHeader header_
;
312 /// Enumerable list of Counters in the StatsTable
314 class StatsTableCounters
: ICollection
{
316 /// Create the list of counters
318 /// <param name="table"></param>
320 public StatsTableCounters(StatsTable table
) {
322 counter_hi_water_mark_
= -1;
323 counters_
= new List
<IStatsCounter
>();
328 /// Scans the table for new entries.
330 public void Update() {
334 #region IEnumerable Members
335 public IEnumerator
GetEnumerator() {
336 return counters_
.GetEnumerator();
340 #region ICollection Members
341 public void CopyTo(Array array
, int index
) {
342 throw new Exception("The method or operation is not implemented.");
347 return counters_
.Count
;
351 public bool IsSynchronized
{
353 throw new Exception("The method or operation is not implemented.");
357 public object SyncRoot
{
359 throw new Exception("The method or operation is not implemented.");
364 #region Private Methods
366 /// Create a counter based on an entry
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_
);
382 rv
= new StatsTimer(entry
);
385 rv
= new StatsCounter(entry
);
391 StatsTableEntry entry
= new StatsTableEntry(id
, name
, table_
);
392 rv
= new StatsCounter(entry
);
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
);
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
);
420 /// Find the counters in the table and insert into the counters_
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.
433 long offset
= table_
.CounterNamesOffset
+
434 (index
* StatsTable
.kMaxCounterNameLength
* 2);
435 string name
= Marshal
.PtrToStringUni((IntPtr
)offset
);
436 if (name
.Length
== 0)
439 // Record that we've already looked at this StatsTableEntry.
440 counter_hi_water_mark_
= index
;
442 IStatsCounter counter
= NameToCounter(index
, name
);
446 IStatsCounter old_counter
= FindExistingCounter(counter
.name
);
447 if (old_counter
!= null)
448 UpgradeCounter(old_counter
, counter
);
450 counters_
.Add(counter
);
452 } while (index
< table_
.Header
.max_counters
- 1);
456 /// Find an existing counter in our table
458 /// <param name="name"></param>
459 private IStatsCounter
FindExistingCounter(string name
) {
460 foreach (IStatsCounter ctr
in counters_
)
462 if (ctr
.name
== name
)
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_
;
478 /// A collection of processes
480 class StatsTableProcesses
: ICollection
485 /// <param name="table"></param>
486 public StatsTableProcesses(StatsTable table
) {
488 pids_
= new List
<int>();
492 #region ICollection Members
493 public void CopyTo(Array array
, int index
) {
494 throw new Exception("The method or operation is not implemented.");
503 public bool IsSynchronized
{
505 throw new Exception("The method or operation is not implemented.");
509 public object SyncRoot
{
511 throw new Exception("The method or operation is not implemented.");
516 #region IEnumerable Members
517 public IEnumerator
GetEnumerator() {
518 return pids_
.GetEnumerator();
523 /// Initialize the pid list.
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
))
537 offset
+= StatsTable
.kMaxThreadNameLength
* 2;
541 #region Private Members
542 private StatsTable table_
;
543 private List
<int> pids_
;