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