- add sources.
[platform/framework/web/crosswalk.git] / src / tools / stats_viewer / stats_table.cs
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
4 \r
5 using System;\r
6 using System.Collections;\r
7 using System.Collections.Generic;\r
8 using System.Diagnostics;\r
9 using System.Runtime.InteropServices;\r
10 using System.Text;\r
11 \r
12 namespace StatsViewer\r
13 {\r
14   /// <summary>\r
15   /// The stats table shared memory segment contains this\r
16   /// header structure.\r
17   /// </summary>\r
18   [StructLayout(LayoutKind.Sequential)]\r
19   internal struct StatsFileHeader {\r
20     public int version;\r
21     public int size;\r
22     public int max_counters;\r
23     public int max_threads;\r
24   };\r
25 \r
26   /// <summary>\r
27   /// An entry in the StatsTable.\r
28   /// </summary>\r
29   class StatsTableEntry {\r
30     public StatsTableEntry(int id, string name, StatsTable table) {\r
31       id_ = id;\r
32       name_ = name;\r
33       table_ = table;\r
34     }\r
35 \r
36     /// <summary>\r
37     /// The unique id for this entry\r
38     /// </summary>\r
39     public int id { get { return id_; } }\r
40 \r
41     /// <summary>\r
42     /// The name for this entry.\r
43     /// </summary>\r
44     public string name { get { return name_; } }\r
45 \r
46     /// <summary>\r
47     /// The value of this entry now.  \r
48     /// </summary>\r
49     public int GetValue(int filter_pid) {\r
50       return table_.GetValue(id_, filter_pid);\r
51     }\r
52 \r
53     private int id_;\r
54     private string name_;\r
55     private StatsTable table_;\r
56   }\r
57 \r
58   // An interface for StatsCounters\r
59   interface IStatsCounter {\r
60     // The name of the counter\r
61     string name { get; }\r
62   }\r
63 \r
64   // A counter.\r
65   class StatsCounter : IStatsCounter {\r
66     public StatsCounter(StatsTableEntry entry) {\r
67       entry_ = entry;\r
68     }\r
69 \r
70     public string name { \r
71       get {\r
72         return entry_.name; \r
73       } \r
74     }\r
75 \r
76     public int GetValue(int filter_pid) {\r
77       return entry_.GetValue(filter_pid);\r
78     }\r
79 \r
80     private StatsTableEntry entry_;\r
81   }\r
82 \r
83   // A timer.\r
84   class StatsTimer : IStatsCounter {\r
85     public StatsTimer(StatsTableEntry entry)\r
86     {\r
87       entry_ = entry;\r
88     }\r
89 \r
90     public string name { \r
91       get { \r
92         return entry_.name; \r
93       } \r
94     }\r
95 \r
96     public int GetValue(int filter_pid) {\r
97       return entry_.GetValue(filter_pid);\r
98     }\r
99 \r
100     private StatsTableEntry entry_;\r
101   }\r
102 \r
103   // A rate.\r
104   class StatsCounterRate : IStatsCounter\r
105   {\r
106     public StatsCounterRate(StatsCounter counter, StatsTimer timer) {\r
107       counter_ = counter;\r
108       timer_ = timer;\r
109     }\r
110 \r
111     public string name { get { return counter_.name; } }\r
112 \r
113     public int GetCount(int filter_pid) { \r
114       return counter_.GetValue(filter_pid);\r
115     }\r
116 \r
117     public int GetTime(int filter_pid) {\r
118       return timer_.GetValue(filter_pid);\r
119     }\r
120 \r
121     private StatsCounter counter_;\r
122     private StatsTimer timer_;\r
123   }\r
124 \r
125   /// <summary>\r
126   /// This is a C# reader for the chrome stats_table.\r
127   /// </summary>\r
128   class StatsTable {\r
129     internal const int kMaxThreadNameLength = 32;\r
130     internal const int kMaxCounterNameLength = 32;\r
131 \r
132     /// <summary>\r
133     /// Open a StatsTable\r
134     /// </summary>\r
135     public StatsTable() {\r
136     }\r
137 \r
138     #region Public Properties\r
139     /// <summary>\r
140     /// Get access to the counters in the table.\r
141     /// </summary>\r
142     public StatsTableCounters Counters() {\r
143       return new StatsTableCounters(this);\r
144     }\r
145 \r
146     /// <summary>\r
147     /// Get access to the processes in the table\r
148     /// </summary>\r
149     public ICollection Processes {\r
150       get {\r
151         return new StatsTableProcesses(this);\r
152       }\r
153     }\r
154     #endregion\r
155 \r
156     #region Internal Properties\r
157     // \r
158     // The internal methods are accessible to the enumerators\r
159     // and helper classes below.\r
160     //\r
161     \r
162     /// <summary>\r
163     /// Access to the table header\r
164     /// </summary>\r
165     internal StatsFileHeader Header {\r
166       get { return header_; }\r
167     }\r
168 \r
169     /// <summary>\r
170     /// Get the offset of the ThreadName table\r
171     /// </summary>\r
172     internal long ThreadNamesOffset {\r
173       get {\r
174         return memory_.ToInt64() + Marshal.SizeOf(typeof(StatsFileHeader));\r
175       }\r
176     }\r
177 \r
178     /// <summary>\r
179     /// Get the offset of the PIDs table\r
180     /// </summary>\r
181     internal long PidsOffset {\r
182       get {\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
189         return offset;\r
190       }\r
191     }\r
192 \r
193     /// <summary>\r
194     /// Get the offset of the CounterName table\r
195     /// </summary>\r
196     internal long CounterNamesOffset {\r
197       get {\r
198         long offset = PidsOffset;\r
199         // Thread PID table\r
200         offset += AlignedSize(header_.max_threads * \r
201           Marshal.SizeOf(typeof(int)));\r
202         return offset;\r
203       }\r
204     }\r
205 \r
206     /// <summary>\r
207     /// Get the offset of the Data table\r
208     /// </summary>\r
209     internal long DataOffset {\r
210       get {\r
211         long offset = CounterNamesOffset;\r
212         // Counter names table\r
213         offset += AlignedSize(header_.max_counters * \r
214           kMaxCounterNameLength * 2);\r
215         return offset;\r
216       }\r
217     }\r
218     #endregion\r
219 \r
220     #region Public Methods\r
221     /// <summary>\r
222     /// Opens the memory map\r
223     /// </summary>\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
227       map_handle_ = \r
228         Win32.OpenFileMapping((int)Win32.MapAccess.FILE_MAP_WRITE, false, \r
229                               name);\r
230       if (map_handle_ == IntPtr.Zero)\r
231         return false;\r
232 \r
233       memory_ = \r
234         Win32.MapViewOfFile(map_handle_, (int)Win32.MapAccess.FILE_MAP_WRITE, \r
235                             0,0, 0);\r
236       if (memory_ == IntPtr.Zero) {\r
237         Win32.CloseHandle(map_handle_);\r
238         return false;\r
239       }\r
240 \r
241       header_ = (StatsFileHeader)Marshal.PtrToStructure(memory_, header_.GetType());\r
242       return true;\r
243     }\r
244 \r
245     /// <summary>\r
246     /// Close the mapped file.\r
247     /// </summary>\r
248     public void Close() {\r
249       Win32.UnmapViewOfFile(memory_);\r
250       Win32.CloseHandle(map_handle_);\r
251     }\r
252 \r
253     /// <summary>\r
254     /// Zero out the stats file.\r
255     /// </summary>\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
262         }\r
263       }\r
264     }\r
265 \r
266     /// <summary>\r
267     /// Get the value for a StatsCounterEntry now.\r
268     /// </summary>\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
277       int rv = 0;\r
278       for (int cols = 0; cols < Header.max_threads; cols++)\r
279       {\r
280         int pid = Marshal.ReadInt32((IntPtr)pid_offset);\r
281         if (filter_pid == 0 || filter_pid == pid)\r
282         {\r
283           rv += Marshal.ReadInt32((IntPtr)data_offset);\r
284         }\r
285         data_offset += Marshal.SizeOf(typeof(int));\r
286         pid_offset += Marshal.SizeOf(typeof(int));\r
287       }\r
288       return rv;\r
289     }\r
290     #endregion\r
291 \r
292     #region Private Methods\r
293     /// <summary>\r
294     /// Align to 4-byte boundaries\r
295     /// </summary>\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
301     }\r
302     #endregion\r
303 \r
304     #region Private Members\r
305     private IntPtr memory_;\r
306     private IntPtr map_handle_;\r
307     private StatsFileHeader header_;\r
308     #endregion\r
309   }\r
310 \r
311   /// <summary>\r
312   /// Enumerable list of Counters in the StatsTable\r
313   /// </summary>\r
314   class StatsTableCounters : ICollection {\r
315     /// <summary>\r
316     /// Create the list of counters\r
317     /// </summary>\r
318     /// <param name="table"></param>\r
319     /// pid</param>\r
320     public StatsTableCounters(StatsTable table) {\r
321       table_ = table;\r
322       counter_hi_water_mark_ = -1;\r
323       counters_ = new List<IStatsCounter>();\r
324       FindCounters();\r
325     }\r
326 \r
327     /// <summary>\r
328     /// Scans the table for new entries.\r
329     /// </summary>\r
330     public void Update() {\r
331       FindCounters();\r
332     }\r
333 \r
334     #region IEnumerable Members\r
335     public IEnumerator GetEnumerator() {\r
336       return counters_.GetEnumerator();\r
337     }\r
338     #endregion\r
339 \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
343     }\r
344 \r
345     public int Count {\r
346       get {\r
347         return counters_.Count;\r
348       }\r
349     }\r
350 \r
351     public bool IsSynchronized {\r
352       get { \r
353         throw new Exception("The method or operation is not implemented."); \r
354       }\r
355     }\r
356 \r
357     public object SyncRoot {\r
358       get { \r
359         throw new Exception("The method or operation is not implemented."); \r
360       }\r
361     }\r
362     #endregion\r
363 \r
364     #region Private Methods\r
365     /// <summary>\r
366     /// Create a counter based on an entry\r
367     /// </summary>\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
372     {\r
373       IStatsCounter rv = null;\r
374 \r
375       // check if the name has a type encoded\r
376       if (name.Length > 2 && name[1] == ':')\r
377       {\r
378         StatsTableEntry entry = new StatsTableEntry(id, name.Substring(2), table_);\r
379         switch (name[0])\r
380         {\r
381           case 't':\r
382             rv = new StatsTimer(entry);\r
383             break;\r
384           case 'c':\r
385             rv = new StatsCounter(entry);\r
386             break;\r
387         }\r
388       }\r
389       else\r
390       {\r
391         StatsTableEntry entry = new StatsTableEntry(id, name, table_);\r
392         rv = new StatsCounter(entry);\r
393       }\r
394 \r
395       return rv;\r
396     }\r
397 \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
402     {\r
403       if (old_counter is StatsCounter && counter is StatsTimer)\r
404       {\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
409       }\r
410       else if (old_counter is StatsTimer && counter is StatsCounter)\r
411       {\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
416       }\r
417     }\r
418 \r
419     /// <summary>\r
420     /// Find the counters in the table and insert into the counters_\r
421     /// hash table.\r
422     /// </summary>\r
423     private void FindCounters()\r
424     {\r
425       Debug.Assert(table_.Header.max_counters > 0);\r
426 \r
427       int index = counter_hi_water_mark_;\r
428 \r
429       do\r
430       {\r
431         // Find an entry in the table.\r
432         index++;\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
437           continue;\r
438 \r
439         // Record that we've already looked at this StatsTableEntry.\r
440         counter_hi_water_mark_ = index;\r
441 \r
442         IStatsCounter counter = NameToCounter(index, name);\r
443 \r
444         if (counter != null)\r
445         {\r
446           IStatsCounter old_counter = FindExistingCounter(counter.name);\r
447           if (old_counter != null)\r
448             UpgradeCounter(old_counter, counter);\r
449           else\r
450             counters_.Add(counter);\r
451         }\r
452       } while (index < table_.Header.max_counters - 1);\r
453     }\r
454 \r
455     /// <summary>\r
456     /// Find an existing counter in our table\r
457     /// </summary>\r
458     /// <param name="name"></param>\r
459     private IStatsCounter FindExistingCounter(string name) {\r
460       foreach (IStatsCounter ctr in counters_)\r
461       {\r
462         if (ctr.name == name)\r
463           return ctr;\r
464       }\r
465       return null;\r
466     }\r
467     #endregion\r
468 \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
474     #endregion\r
475   }\r
476 \r
477   /// <summary>\r
478   /// A collection of processes\r
479   /// </summary>\r
480   class StatsTableProcesses : ICollection\r
481   {\r
482     /// <summary>\r
483     /// Constructor\r
484     /// </summary>\r
485     /// <param name="table"></param>\r
486     public StatsTableProcesses(StatsTable table) {\r
487       table_ = table;\r
488       pids_ = new List<int>();\r
489       Initialize();\r
490     }\r
491 \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
495     }\r
496 \r
497     public int Count {\r
498       get {\r
499         return pids_.Count;\r
500       }\r
501     }\r
502 \r
503     public bool IsSynchronized {\r
504       get {\r
505         throw new Exception("The method or operation is not implemented."); \r
506       }\r
507     }\r
508 \r
509     public object SyncRoot {\r
510       get { \r
511         throw new Exception("The method or operation is not implemented."); \r
512       }\r
513     }\r
514     #endregion\r
515 \r
516     #region IEnumerable Members\r
517     public IEnumerator GetEnumerator() {\r
518       return pids_.GetEnumerator();\r
519     }\r
520     #endregion\r
521 \r
522     /// <summary>\r
523     /// Initialize the pid list.\r
524     /// </summary>\r
525     private void Initialize() {\r
526       long offset = table_.ThreadNamesOffset;\r
527 \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
535             pids_.Add(pid);\r
536         }\r
537         offset += StatsTable.kMaxThreadNameLength * 2;\r
538       }\r
539     }\r
540 \r
541     #region Private Members\r
542     private StatsTable table_;\r
543     private List<int> pids_;\r
544     #endregion\r
545   }\r
546 }\r