1e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke#!/usr/bin/env python
2e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke#
3a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# Copyright 2008 the V8 project authors. All rights reserved.
4a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# Redistribution and use in source and binary forms, with or without
5a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# modification, are permitted provided that the following conditions are
6a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# met:
7a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#
8a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#     * Redistributions of source code must retain the above copyright
9a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       notice, this list of conditions and the following disclaimer.
10a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#     * Redistributions in binary form must reproduce the above
11a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       copyright notice, this list of conditions and the following
12a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       disclaimer in the documentation and/or other materials provided
13a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       with the distribution.
14a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#     * Neither the name of Google Inc. nor the names of its
15a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       contributors may be used to endorse or promote products derived
16a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       from this software without specific prior written permission.
17a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#
18a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
30a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
31a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block"""A cross-platform execution counter viewer.
32a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
33a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockThe stats viewer reads counters from a binary file and displays them
34a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockin a window, re-reading and re-displaying with regular intervals.
35a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block"""
36a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
37a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport mmap
388defd9ff6930b4e24729971a61cf7469daf119beSteve Blockimport optparse
39a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport os
40e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarkeimport re
41a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport struct
42a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport sys
43a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport time
44a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport Tkinter
45a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
46a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
47a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# The interval, in milliseconds, between ui updates
48a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockUPDATE_INTERVAL_MS = 100
49a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
50a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
51a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# Mapping from counter prefix to the formatting to be used for the counter
52a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockCOUNTER_LABELS = {"t": "%i ms.", "c": "%i"}
53a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
54a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
55e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke# The magic numbers used to check if a file is not a counters file
56a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockCOUNTERS_FILE_MAGIC_NUMBER = 0xDEADFACE
57e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon ClarkeCHROME_COUNTERS_FILE_MAGIC_NUMBER = 0x13131313
58a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
59a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
60a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass StatsViewer(object):
61a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """The main class that keeps the data used by the stats viewer."""
62a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
638defd9ff6930b4e24729971a61cf7469daf119beSteve Block  def __init__(self, data_name, name_filter):
64a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Creates a new instance.
65a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
66a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
67a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      data_name: the name of the file containing the counters.
688defd9ff6930b4e24729971a61cf7469daf119beSteve Block      name_filter: The regexp filter to apply to counter names.
69a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
70a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.data_name = data_name
718defd9ff6930b4e24729971a61cf7469daf119beSteve Block    self.name_filter = name_filter
72a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
73a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # The handle created by mmap.mmap to the counters file.  We need
74a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # this to clean it up on exit.
75a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.shared_mmap = None
76a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
77a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # A mapping from counter names to the ui element that displays
78a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # them
79a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.ui_counters = {}
80a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
81a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # The counter collection used to access the counters file
82a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.data = None
83a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
84a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # The Tkinter root window object
85a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root = None
86a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
87a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def Run(self):
88a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """The main entry-point to running the stats viewer."""
89a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    try:
90a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.data = self.MountSharedData()
91a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      # OpenWindow blocks until the main window is closed
92a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.OpenWindow()
93a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    finally:
94a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.CleanUp()
95a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
96a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def MountSharedData(self):
97a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Mount the binary counters file as a memory-mapped file.  If
98a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    something goes wrong print an informative message and exit the
99a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    program."""
100a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if not os.path.exists(self.data_name):
101e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      maps_name = "/proc/%s/maps" % self.data_name
102e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      if not os.path.exists(maps_name):
103e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke        print "\"%s\" is neither a counter file nor a PID." % self.data_name
104e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke        sys.exit(1)
105e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      maps_file = open(maps_name, "r")
106e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      try:
1073fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        self.data_name = None
1083fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        for m in re.finditer(r"/dev/shm/\S*", maps_file.read()):
1093fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch          if os.path.exists(m.group(0)):
1103fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch            self.data_name = m.group(0)
1113fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch            break
1123fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        if self.data_name is None:
113e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke          print "Can't find counter file in maps for PID %s." % self.data_name
114e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke          sys.exit(1)
115e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      finally:
116e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke        maps_file.close()
117a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    data_file = open(self.data_name, "r")
118a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    size = os.fstat(data_file.fileno()).st_size
119a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    fileno = data_file.fileno()
120a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.shared_mmap = mmap.mmap(fileno, size, access=mmap.ACCESS_READ)
121a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    data_access = SharedDataAccess(self.shared_mmap)
122e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    if data_access.IntAt(0) == COUNTERS_FILE_MAGIC_NUMBER:
123e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      return CounterCollection(data_access)
124e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    elif data_access.IntAt(0) == CHROME_COUNTERS_FILE_MAGIC_NUMBER:
125e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      return ChromeCounterCollection(data_access)
126e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    print "File %s is not stats data." % self.data_name
127e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    sys.exit(1)
128a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
129a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def CleanUp(self):
130a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Cleans up the memory mapped file if necessary."""
131a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if self.shared_mmap:
132a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.shared_mmap.close()
133a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
134a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def UpdateCounters(self):
135a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Read the contents of the memory-mapped file and update the ui if
136a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    necessary.  If the same counters are present in the file as before
137a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    we just update the existing labels.  If any counters have been added
138a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    or removed we scrap the existing ui and draw a new one.
139a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
140a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    changed = False
141a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    counters_in_use = self.data.CountersInUse()
142a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if counters_in_use != len(self.ui_counters):
143a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.RefreshCounters()
144a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      changed = True
145a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    else:
146a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      for i in xrange(self.data.CountersInUse()):
147a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        counter = self.data.Counter(i)
148a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        name = counter.Name()
149a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        if name in self.ui_counters:
150a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          value = counter.Value()
151a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          ui_counter = self.ui_counters[name]
152a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          counter_changed = ui_counter.Set(value)
153a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          changed = (changed or counter_changed)
154a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        else:
155a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          self.RefreshCounters()
156a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          changed = True
157a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          break
158a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if changed:
159a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      # The title of the window shows the last time the file was
160a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      # changed.
161a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.UpdateTime()
162a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.ScheduleUpdate()
163a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
164a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def UpdateTime(self):
165a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Update the title of the window with the current time."""
166a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root.title("Stats Viewer [updated %s]" % time.strftime("%H:%M:%S"))
167a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
168a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def ScheduleUpdate(self):
169a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Schedules the next ui update."""
170a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root.after(UPDATE_INTERVAL_MS, lambda: self.UpdateCounters())
171a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
172a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def RefreshCounters(self):
173a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Tear down and rebuild the controls in the main window."""
174a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    counters = self.ComputeCounters()
175a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.RebuildMainWindow(counters)
176a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
177a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def ComputeCounters(self):
178a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Group the counters by the suffix of their name.
179a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
180a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Since the same code-level counter (for instance "X") can result in
181a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    several variables in the binary counters file that differ only by a
182a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    two-character prefix (for instance "c:X" and "t:X") counters are
183a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    grouped by suffix and then displayed with custom formatting
184a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    depending on their prefix.
185a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
186a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Returns:
187a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      A mapping from suffixes to a list of counters with that suffix,
188a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      sorted by prefix.
189a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
190a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    names = {}
191a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for i in xrange(self.data.CountersInUse()):
192a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      counter = self.data.Counter(i)
193a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      name = counter.Name()
194a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      names[name] = counter
195a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
196a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # By sorting the keys we ensure that the prefixes always come in the
197a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # same order ("c:" before "t:") which looks more consistent in the
198a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # ui.
199a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    sorted_keys = names.keys()
200a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    sorted_keys.sort()
201a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
202a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # Group together the names whose suffix after a ':' are the same.
203a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    groups = {}
204a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for name in sorted_keys:
205a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      counter = names[name]
206a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      if ":" in name:
207a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        name = name[name.find(":")+1:]
208a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      if not name in groups:
209a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        groups[name] = []
210a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      groups[name].append(counter)
211a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
212a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return groups
213a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
214a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def RebuildMainWindow(self, groups):
215a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Tear down and rebuild the main window.
216a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
217a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
218a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      groups: the groups of counters to display
219a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
220a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # Remove elements in the current ui
221a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.ui_counters.clear()
222a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for child in self.root.children.values():
223a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      child.destroy()
224a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
225a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # Build new ui
226a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    index = 0
227a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    sorted_groups = groups.keys()
228a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    sorted_groups.sort()
229a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for counter_name in sorted_groups:
230a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      counter_objs = groups[counter_name]
2318defd9ff6930b4e24729971a61cf7469daf119beSteve Block      if self.name_filter.match(counter_name):
2328defd9ff6930b4e24729971a61cf7469daf119beSteve Block        name = Tkinter.Label(self.root, width=50, anchor=Tkinter.W,
2338defd9ff6930b4e24729971a61cf7469daf119beSteve Block                             text=counter_name)
2348defd9ff6930b4e24729971a61cf7469daf119beSteve Block        name.grid(row=index, column=0, padx=1, pady=1)
235a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      count = len(counter_objs)
236a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      for i in xrange(count):
237a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        counter = counter_objs[i]
238a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        name = counter.Name()
239a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        var = Tkinter.StringVar()
2408defd9ff6930b4e24729971a61cf7469daf119beSteve Block        if self.name_filter.match(name):
2418defd9ff6930b4e24729971a61cf7469daf119beSteve Block          value = Tkinter.Label(self.root, width=15, anchor=Tkinter.W,
2428defd9ff6930b4e24729971a61cf7469daf119beSteve Block                                textvariable=var)
2438defd9ff6930b4e24729971a61cf7469daf119beSteve Block          value.grid(row=index, column=(1 + i), padx=1, pady=1)
244a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
245a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        # If we know how to interpret the prefix of this counter then
246a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        # add an appropriate formatting to the variable
247a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        if (":" in name) and (name[0] in COUNTER_LABELS):
248a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          format = COUNTER_LABELS[name[0]]
249a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        else:
250a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          format = "%i"
251a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        ui_counter = UiCounter(var, format)
252a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        self.ui_counters[name] = ui_counter
253a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        ui_counter.Set(counter.Value())
254a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      index += 1
255a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root.update()
256a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
257a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def OpenWindow(self):
258a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Create and display the root window."""
259a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root = Tkinter.Tk()
260a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
261a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    # Tkinter is no good at resizing so we disable it
262a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root.resizable(width=False, height=False)
263a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.RefreshCounters()
264a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.ScheduleUpdate()
265a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.root.mainloop()
266a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
267a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
268a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass UiCounter(object):
269a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """A counter in the ui."""
270a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
271a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def __init__(self, var, format):
272a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Creates a new ui counter.
273a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
274a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
275a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      var: the Tkinter string variable for updating the ui
276a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      format: the format string used to format this counter
277a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
278a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.var = var
279a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.format = format
280a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.last_value = None
281a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
282a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def Set(self, value):
283a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Updates the ui for this counter.
284a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
285a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
286a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      value: The value to display
287a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
288a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Returns:
289a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      True if the value had changed, otherwise False.  The first call
290a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      always returns True.
291a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
292a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if value == self.last_value:
293a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      return False
294a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    else:
295a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.last_value = value
296a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      self.var.set(self.format % value)
297a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      return True
298a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
299a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
300a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass SharedDataAccess(object):
301a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """A utility class for reading data from the memory-mapped binary
302a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  counters file."""
303a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
304a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def __init__(self, data):
305a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Create a new instance.
306a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
307a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
308a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      data: A handle to the memory-mapped file, as returned by mmap.mmap.
309a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
310a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.data = data
311a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
312a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def ByteAt(self, index):
313a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the (unsigned) byte at the specified byte index."""
314a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return ord(self.CharAt(index))
315a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
316a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IntAt(self, index):
317a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the little-endian 32-byte int at the specified byte index."""
318a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    word_str = self.data[index:index+4]
319a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    result, = struct.unpack("I", word_str)
320a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return result
321a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
322a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def CharAt(self, index):
323a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the ascii character at the specified byte index."""
324a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return self.data[index]
325a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
326a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
327a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass Counter(object):
328a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """A pointer to a single counter withing a binary counters file."""
329a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
330a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def __init__(self, data, offset):
331a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Create a new instance.
332a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
333a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
334a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      data: the shared data access object containing the counter
335a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      offset: the byte offset of the start of this counter
336a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
337a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.data = data
338a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.offset = offset
339a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
340a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def Value(self):
341a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the integer value of this counter."""
342a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return self.data.IntAt(self.offset)
343a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
344a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def Name(self):
345a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the ascii name of this counter."""
346a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    result = ""
347a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    index = self.offset + 4
348a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    current = self.data.ByteAt(index)
349a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    while current:
350a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      result += chr(current)
351a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      index += 1
352a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      current = self.data.ByteAt(index)
353a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return result
354a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
355a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
356a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass CounterCollection(object):
357a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """An overlay over a counters file that provides access to the
358a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  individual counters contained in the file."""
359a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
360a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def __init__(self, data):
361a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Create a new instance.
362a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
363a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    Args:
364a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      data: the shared data access object
365a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """
366a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.data = data
367a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.max_counters = data.IntAt(4)
368a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    self.max_name_size = data.IntAt(8)
369a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
370a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def CountersInUse(self):
371a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the number of counters in active use."""
372a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return self.data.IntAt(12)
373a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
374a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def Counter(self, index):
375a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the index'th counter."""
376a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return Counter(self.data, 16 + index * self.CounterSize())
377a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
378a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def CounterSize(self):
379a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    """Return the size of a single counter."""
380a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return 4 + self.max_name_size
381a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
382a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
383e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarkeclass ChromeCounter(object):
384e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  """A pointer to a single counter withing a binary counters file."""
385e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
386e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  def __init__(self, data, name_offset, value_offset):
387e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """Create a new instance.
388e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
389e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    Args:
390e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      data: the shared data access object containing the counter
391e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      name_offset: the byte offset of the start of this counter's name
392e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      value_offset: the byte offset of the start of this counter's value
393e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """
394e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.data = data
395e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.name_offset = name_offset
396e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.value_offset = value_offset
397e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
398e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  def Value(self):
399e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """Return the integer value of this counter."""
400e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    return self.data.IntAt(self.value_offset)
401e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
402e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  def Name(self):
403e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """Return the ascii name of this counter."""
404e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    result = ""
405e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    index = self.name_offset
406e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    current = self.data.ByteAt(index)
407e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    while current:
408e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      result += chr(current)
409e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      index += 1
410e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      current = self.data.ByteAt(index)
411e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    return result
412e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
413e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
414e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarkeclass ChromeCounterCollection(object):
415e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  """An overlay over a counters file that provides access to the
416e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  individual counters contained in the file."""
417e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
418e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  _HEADER_SIZE = 4 * 4
4193fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch  _COUNTER_NAME_SIZE = 64
4203fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch  _THREAD_NAME_SIZE = 32
421e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
422e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  def __init__(self, data):
423e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """Create a new instance.
424e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
425e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    Args:
426e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke      data: the shared data access object
427e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """
428e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.data = data
429e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.max_counters = data.IntAt(8)
430e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.max_threads = data.IntAt(12)
431e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.counter_names_offset = \
4323fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        self._HEADER_SIZE + self.max_threads * (self._THREAD_NAME_SIZE + 2 * 4)
433e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    self.counter_values_offset = \
4343fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch        self.counter_names_offset + self.max_counters * self._COUNTER_NAME_SIZE
435e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
436e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  def CountersInUse(self):
437e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """Return the number of counters in active use."""
438e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    for i in xrange(self.max_counters):
4393fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch      name_offset = self.counter_names_offset + i * self._COUNTER_NAME_SIZE
4403fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch      if self.data.ByteAt(name_offset) == 0:
441e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke        return i
442e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    return self.max_counters
443e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
444e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke  def Counter(self, i):
445e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    """Return the i'th counter."""
4463fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch    name_offset = self.counter_names_offset + i * self._COUNTER_NAME_SIZE
4473fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch    value_offset = self.counter_values_offset + i * self.max_threads * 4
4483fb3ca8c7ca439d408449a395897395c0faae8d1Ben Murdoch    return ChromeCounter(self.data, name_offset, value_offset)
449e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
450e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke
4518defd9ff6930b4e24729971a61cf7469daf119beSteve Blockdef Main(data_file, name_filter):
452a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """Run the stats counter.
453a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
454a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  Args:
455a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    data_file: The counters file to monitor.
4568defd9ff6930b4e24729971a61cf7469daf119beSteve Block    name_filter: The regexp filter to apply to counter names.
457a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
4588defd9ff6930b4e24729971a61cf7469daf119beSteve Block  StatsViewer(data_file, name_filter).Run()
459a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
460a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
461a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockif __name__ == "__main__":
4628defd9ff6930b4e24729971a61cf7469daf119beSteve Block  parser = optparse.OptionParser("usage: %prog [--filter=re] "
4638defd9ff6930b4e24729971a61cf7469daf119beSteve Block                                 "<stats data>|<test_shell pid>")
4648defd9ff6930b4e24729971a61cf7469daf119beSteve Block  parser.add_option("--filter",
4658defd9ff6930b4e24729971a61cf7469daf119beSteve Block                    default=".*",
4668defd9ff6930b4e24729971a61cf7469daf119beSteve Block                    help=("regexp filter for counter names "
4678defd9ff6930b4e24729971a61cf7469daf119beSteve Block                          "[default: %default]"))
4688defd9ff6930b4e24729971a61cf7469daf119beSteve Block  (options, args) = parser.parse_args()
4698defd9ff6930b4e24729971a61cf7469daf119beSteve Block  if len(args) != 1:
4708defd9ff6930b4e24729971a61cf7469daf119beSteve Block    parser.print_help()
471a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    sys.exit(1)
4728defd9ff6930b4e24729971a61cf7469daf119beSteve Block  Main(args[0], re.compile(options.filter))
473