__init__.py revision 14265
1# Copyright (c) 2017-2019 ARM Limited 2# All rights reserved. 3# 4# The license below extends only to copyright in the software and shall 5# not be construed as granting a license to any other intellectual 6# property including but not limited to intellectual property relating 7# to a hardware implementation of the functionality of the software 8# licensed hereunder. You may use the software subject to the license 9# terms below provided that you ensure that this notice is replicated 10# unmodified and in its entirety in all distributions of the software, 11# modified or unmodified, in source code or in binary form. 12# 13# Copyright (c) 2007 The Regents of The University of Michigan 14# Copyright (c) 2010 The Hewlett-Packard Development Company 15# All rights reserved. 16# 17# Redistribution and use in source and binary forms, with or without 18# modification, are permitted provided that the following conditions are 19# met: redistributions of source code must retain the above copyright 20# notice, this list of conditions and the following disclaimer; 21# redistributions in binary form must reproduce the above copyright 22# notice, this list of conditions and the following disclaimer in the 23# documentation and/or other materials provided with the distribution; 24# neither the name of the copyright holders nor the names of its 25# contributors may be used to endorse or promote products derived from 26# this software without specific prior written permission. 27# 28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39# 40# Authors: Nathan Binkert 41# Andreas Sandberg 42 43from __future__ import print_function 44from __future__ import absolute_import 45 46import m5 47 48import _m5.stats 49from m5.objects import Root 50from m5.util import attrdict, fatal 51 52# Stat exports 53from _m5.stats import schedStatEvent as schedEvent 54from _m5.stats import periodicStatDump 55 56outputList = [] 57 58# Dictionary of stat visitor factories populated by the _url_factory 59# visitor. 60factories = { } 61 62# List of all factories. Contains tuples of (factory, schemes, 63# enabled). 64all_factories = [] 65 66def _url_factory(schemes, enable=True): 67 """Wrap a plain Python function with URL parsing helpers 68 69 Wrap a plain Python function f(fn, **kwargs) to expect a URL that 70 has been split using urlparse.urlsplit. First positional argument 71 is assumed to be a filename, this is created as the concatenation 72 of the netloc (~hostname) and path in the parsed URL. Keyword 73 arguments are derived from the query values in the URL. 74 75 Arguments: 76 schemes: A list of URL schemes to use for this function. 77 78 Keyword arguments: 79 enable: Enable/disable this factory. Typically used when the 80 presence of a function depends on some runtime property. 81 82 For example: 83 wrapped_f(urlparse.urlsplit("text://stats.txt?desc=False")) -> 84 f("stats.txt", desc=False) 85 86 """ 87 88 from functools import wraps 89 90 def decorator(func): 91 @wraps(func) 92 def wrapper(url): 93 try: 94 from urllib.parse import parse_qs 95 except ImportError: 96 # Python 2 fallback 97 from urlparse import parse_qs 98 from ast import literal_eval 99 100 qs = parse_qs(url.query, keep_blank_values=True) 101 102 # parse_qs returns a list of values for each parameter. Only 103 # use the last value since kwargs don't allow multiple values 104 # per parameter. Use literal_eval to transform string param 105 # values into proper Python types. 106 def parse_value(key, values): 107 if len(values) == 0 or (len(values) == 1 and not values[0]): 108 fatal("%s: '%s' doesn't have a value." % ( 109 url.geturl(), key)) 110 elif len(values) > 1: 111 fatal("%s: '%s' has multiple values." % ( 112 url.geturl(), key)) 113 else: 114 try: 115 return key, literal_eval(values[0]) 116 except ValueError: 117 fatal("%s: %s isn't a valid Python literal" \ 118 % (url.geturl(), values[0])) 119 120 kwargs = dict([ parse_value(k, v) for k, v in qs.items() ]) 121 122 try: 123 return func("%s%s" % (url.netloc, url.path), **kwargs) 124 except TypeError: 125 fatal("Illegal stat visitor parameter specified") 126 127 all_factories.append((wrapper, schemes, enable)) 128 for scheme in schemes: 129 assert scheme not in factories 130 factories[scheme] = wrapper if enable else None 131 return wrapper 132 133 return decorator 134 135@_url_factory([ None, "", "text", "file", ]) 136def _textFactory(fn, desc=True): 137 """Output stats in text format. 138 139 Text stat files contain one stat per line with an optional 140 description. The description is enabled by default, but can be 141 disabled by setting the desc parameter to False. 142 143 Parameters: 144 * desc (bool): Output stat descriptions (default: True) 145 146 Example: 147 text://stats.txt?desc=False 148 149 """ 150 151 return _m5.stats.initText(fn, desc) 152 153@_url_factory([ "h5", ], enable=hasattr(_m5.stats, "initHDF5")) 154def _hdf5Factory(fn, chunking=10, desc=True, formulas=True): 155 """Output stats in HDF5 format. 156 157 The HDF5 file format is a structured binary file format. It has 158 the multiple benefits over traditional text stat files: 159 160 * Efficient storage of time series (multiple stat dumps) 161 * Fast lookup of stats 162 * Plenty of existing tooling (e.g., Python libraries and graphical 163 viewers) 164 * File format can be used to store frame buffers together with 165 normal stats. 166 167 There are some drawbacks compared to the default text format: 168 * Large startup cost (single stat dump larger than text equivalent) 169 * Stat dumps are slower than text 170 171 172 Known limitations: 173 * Distributions and histograms currently unsupported. 174 * No support for forking. 175 176 177 Parameters: 178 * chunking (unsigned): Number of time steps to pre-allocate (default: 10) 179 * desc (bool): Output stat descriptions (default: True) 180 * formulas (bool): Output derived stats (default: True) 181 182 Example: 183 h5://stats.h5?desc=False;chunking=100;formulas=False 184 185 """ 186 187 return _m5.stats.initHDF5(fn, chunking, desc, formulas) 188 189def addStatVisitor(url): 190 """Add a stat visitor specified using a URL string 191 192 Stat visitors are specified using URLs on the following format: 193 format://path[?param=value[;param=value]] 194 195 The available formats are listed in the factories list. Factories 196 are called with the path as the first positional parameter and the 197 parameters are keyword arguments. Parameter values must be valid 198 Python literals. 199 200 """ 201 202 try: 203 from urllib.parse import urlsplit 204 except ImportError: 205 # Python 2 fallback 206 from urlparse import urlsplit 207 208 parsed = urlsplit(url) 209 210 try: 211 factory = factories[parsed.scheme] 212 except KeyError: 213 fatal("Illegal stat file type '%s' specified." % parsed.scheme) 214 215 if factory is None: 216 fatal("Stat type '%s' disabled at compile time" % parsed.scheme) 217 218 outputList.append(factory(parsed)) 219 220def printStatVisitorTypes(): 221 """List available stat visitors and their documentation""" 222 223 import inspect 224 225 def print_doc(doc): 226 for line in doc.splitlines(): 227 print("| %s" % line) 228 print() 229 230 enabled_visitors = [ x for x in all_factories if x[2] ] 231 for factory, schemes, _ in enabled_visitors: 232 print("%s:" % ", ".join(filter(lambda x: x is not None, schemes))) 233 234 # Try to extract the factory doc string 235 print_doc(inspect.getdoc(factory)) 236 237def initSimStats(): 238 _m5.stats.initSimStats() 239 _m5.stats.registerPythonStatsHandlers() 240 241def _visit_groups(visitor, root=None): 242 if root is None: 243 root = Root.getInstance() 244 for group in root.getStatGroups().values(): 245 visitor(group) 246 _visit_groups(visitor, root=group) 247 248def _visit_stats(visitor, root=None): 249 def for_each_stat(g): 250 for stat in g.getStats(): 251 visitor(g, stat) 252 _visit_groups(for_each_stat, root=root) 253 254def _bindStatHierarchy(root): 255 def _bind_obj(name, obj): 256 if m5.SimObject.isSimObjectVector(obj): 257 for idx, obj in enumerate(obj): 258 _bind_obj("{}{}".format(name, idx), obj) 259 else: 260 # We need this check because not all obj.getCCObject() is an 261 # instance of Stat::Group. For example, sc_core::sc_module, the C++ 262 # class of SystemC_ScModule, is not a subclass of Stat::Group. So 263 # it will cause a type error if obj is a SystemC_ScModule when 264 # calling addStatGroup(). 265 if isinstance(obj.getCCObject(), _m5.stats.Group): 266 parent = root 267 while parent: 268 if hasattr(parent, 'addStatGroup'): 269 parent.addStatGroup(name, obj.getCCObject()) 270 break 271 parent = parent.get_parent(); 272 273 _bindStatHierarchy(obj) 274 275 for name, obj in root._children.items(): 276 _bind_obj(name, obj) 277 278names = [] 279stats_dict = {} 280stats_list = [] 281def enable(): 282 '''Enable the statistics package. Before the statistics package is 283 enabled, all statistics must be created and initialized and once 284 the package is enabled, no more statistics can be created.''' 285 286 def check_stat(group, stat): 287 if not stat.check() or not stat.baseCheck(): 288 fatal("statistic '%s' (%d) was not properly initialized " \ 289 "by a regStats() function\n", stat.name, stat.id) 290 291 if not (stat.flags & flags.display): 292 stat.name = "__Stat%06d" % stat.id 293 294 295 # Legacy stat 296 global stats_list 297 stats_list = list(_m5.stats.statsList()) 298 299 for stat in stats_list: 300 check_stat(None, stat) 301 302 stats_list.sort(key=lambda s: s.name.split('.')) 303 for stat in stats_list: 304 stats_dict[stat.name] = stat 305 stat.enable() 306 307 308 # New stats 309 _visit_stats(check_stat) 310 _visit_stats(lambda g, s: s.enable()) 311 312 _m5.stats.enable(); 313 314def prepare(): 315 '''Prepare all stats for data access. This must be done before 316 dumping and serialization.''' 317 318 # Legacy stats 319 for stat in stats_list: 320 stat.prepare() 321 322 # New stats 323 _visit_stats(lambda g, s: s.prepare()) 324 325def _dump_to_visitor(visitor, root=None): 326 # Legacy stats 327 if root is None: 328 for stat in stats_list: 329 stat.visit(visitor) 330 331 # New stats 332 def dump_group(group): 333 for stat in group.getStats(): 334 stat.visit(visitor) 335 336 for n, g in group.getStatGroups().items(): 337 visitor.beginGroup(n) 338 dump_group(g) 339 visitor.endGroup() 340 341 if root is not None: 342 for p in root.path_list(): 343 visitor.beginGroup(p) 344 dump_group(root if root is not None else Root.getInstance()) 345 if root is not None: 346 for p in reversed(root.path_list()): 347 visitor.endGroup() 348 349lastDump = 0 350 351def dump(root=None): 352 '''Dump all statistics data to the registered outputs''' 353 354 now = m5.curTick() 355 global lastDump 356 assert lastDump <= now 357 new_dump = lastDump != now 358 lastDump = now 359 360 # Don't allow multiple global stat dumps in the same tick. It's 361 # still possible to dump a multiple sub-trees. 362 if not new_dump and root is None: 363 return 364 365 # Only prepare stats the first time we dump them in the same tick. 366 if new_dump: 367 _m5.stats.processDumpQueue() 368 prepare() 369 370 for output in outputList: 371 if output.valid(): 372 output.begin() 373 _dump_to_visitor(output, root=root) 374 output.end() 375 376def reset(): 377 '''Reset all statistics to the base state''' 378 379 # call reset stats on all SimObjects 380 root = Root.getInstance() 381 if root: 382 root.resetStats() 383 384 # call any other registered legacy stats reset callbacks 385 for stat in stats_list: 386 stat.reset() 387 388 _m5.stats.processResetQueue() 389 390flags = attrdict({ 391 'none' : 0x0000, 392 'init' : 0x0001, 393 'display' : 0x0002, 394 'total' : 0x0010, 395 'pdf' : 0x0020, 396 'cdf' : 0x0040, 397 'dist' : 0x0080, 398 'nozero' : 0x0100, 399 'nonan' : 0x0200, 400}) 401