+# Copyright (c) 2017 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
# Copyright (c) 2007 The Regents of The University of Michigan
# Copyright (c) 2010 The Hewlett-Packard Development Company
# All rights reserved.
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Nathan Binkert
+# Andreas Sandberg
-from m5 import internal
-from m5.internal.stats import schedStatEvent as schedEvent
+import m5
+
+import _m5.stats
from m5.objects import Root
-from m5.util import attrdict
+from m5.util import attrdict, fatal
+
+# Stat exports
+from _m5.stats import schedStatEvent as schedEvent
+from _m5.stats import periodicStatDump
+
+outputList = []
+
+def _url_factory(func):
+ """Wrap a plain Python function with URL parsing helpers
+
+ Wrap a plain Python function f(fn, **kwargs) to expect a URL that
+ has been split using urlparse.urlsplit. First positional argument
+ is assumed to be a filename, this is created as the concatenation
+ of the netloc (~hostname) and path in the parsed URL. Keyword
+ arguments are derived from the query values in the URL.
+
+ For example:
+ wrapped_f(urlparse.urlsplit("text://stats.txt?desc=False")) ->
+ f("stats.txt", desc=False)
+
+ """
+
+ from functools import wraps
+
+ @wraps(func)
+ def wrapper(url):
+ from urlparse import parse_qs
+ from ast import literal_eval
+
+ qs = parse_qs(url.query, keep_blank_values=True)
+
+ # parse_qs returns a list of values for each parameter. Only
+ # use the last value since kwargs don't allow multiple values
+ # per parameter. Use literal_eval to transform string param
+ # values into proper Python types.
+ def parse_value(key, values):
+ if len(values) == 0 or (len(values) == 1 and not values[0]):
+ fatal("%s: '%s' doesn't have a value." % (url.geturl(), key))
+ elif len(values) > 1:
+ fatal("%s: '%s' has multiple values." % (url.geturl(), key))
+ else:
+ try:
+ return key, literal_eval(values[0])
+ except ValueError:
+ fatal("%s: %s isn't a valid Python literal" \
+ % (url.geturl(), values[0]))
+
+ kwargs = dict([ parse_value(k, v) for k, v in qs.items() ])
+
+ try:
+ return func("%s%s" % (url.netloc, url.path), **kwargs)
+ except TypeError:
+ fatal("Illegal stat visitor parameter specified")
+
+ return wrapper
+
+@_url_factory
+def _textFactory(fn, desc=True):
+ """Output stats in text format.
+
+ Text stat files contain one stat per line with an optional
+ description. The description is enabled by default, but can be
+ disabled by setting the desc parameter to False.
+
+ Example: text://stats.txt?desc=False
+
+ """
-def initText(filename, desc=True):
- internal.stats.initText(filename, desc)
+ return _m5.stats.initText(fn, desc)
-def initMySQL(host, database, user='', passwd='', project='test', name='test',
- sample='0'):
- if not user:
- import getpass
- user = getpass.getuser()
+factories = {
+ # Default to the text factory if we're given a naked path
+ "" : _textFactory,
+ "file" : _textFactory,
+ "text" : _textFactory,
+}
- internal.stats.initMySQL(host, database, user, passwd, project, name,
- sample)
+def addStatVisitor(url):
+ """Add a stat visitor specified using a URL string
+
+ Stat visitors are specified using URLs on the following format:
+ format://path[?param=value[;param=value]]
+
+ The available formats are listed in the factories list. Factories
+ are called with the path as the first positional parameter and the
+ parameters are keyword arguments. Parameter values must be valid
+ Python literals.
+
+ """
+
+ from urlparse import urlsplit
+
+ parsed = urlsplit(url)
+
+ try:
+ factory = factories[parsed.scheme]
+ except KeyError:
+ fatal("Illegal stat file type specified.")
+
+ outputList.append(factory(parsed))
def initSimStats():
- internal.stats.initSimStats()
+ _m5.stats.initSimStats()
+ _m5.stats.registerPythonStatsHandlers()
names = []
stats_dict = {}
stats_list = []
-raw_stats_list = []
def enable():
'''Enable the statistics package. Before the statistics package is
enabled, all statistics must be created and initialized and once
the package is enabled, no more statistics can be created.'''
- __dynamic_cast = []
- for k, v in internal.stats.__dict__.iteritems():
- if k.startswith('dynamic_'):
- __dynamic_cast.append(v)
-
- for stat in internal.stats.statsList():
- for cast in __dynamic_cast:
- val = cast(stat)
- if val is not None:
- stats_list.append(val)
- raw_stats_list.append(val)
- break
- else:
- fatal("unknown stat type %s", stat)
+
+ global stats_list
+ stats_list = list(_m5.stats.statsList())
+
+ for stat in stats_list:
+ if not stat.check() or not stat.baseCheck():
+ fatal("statistic '%s' (%d) was not properly initialized " \
+ "by a regStats() function\n", stat.name, stat.id)
+
+ if not (stat.flags & flags.display):
+ stat.name = "__Stat%06d" % stat.id
def less(stat1, stat2):
v1 = stat1.name.split('.')
stats_list.sort(less)
for stat in stats_list:
stats_dict[stat.name] = stat
+ stat.enable()
- internal.stats.enable()
+ _m5.stats.enable();
+def prepare():
+ '''Prepare all stats for data access. This must be done before
+ dumping and serialization.'''
+
+ for stat in stats_list:
+ stat.prepare()
+
+lastDump = 0
def dump():
- # Currently prepare happens in the dump, but we should maybe move
- # that out.
+ '''Dump all statistics data to the registered outputs'''
+
+ curTick = m5.curTick()
+
+ global lastDump
+ assert lastDump <= curTick
+ if lastDump == curTick:
+ return
+ lastDump = curTick
- #internal.stats.prepare()
- internal.stats.dump()
+ _m5.stats.processDumpQueue()
+
+ prepare()
+
+ for output in outputList:
+ if output.valid():
+ output.begin()
+ for stat in stats_list:
+ stat.visit(output)
+ output.end()
def reset():
+ '''Reset all statistics to the base state'''
+
# call reset stats on all SimObjects
root = Root.getInstance()
if root:
for obj in root.descendants(): obj.resetStats()
# call any other registered stats reset callbacks
- internal.stats.reset()
+ for stat in stats_list:
+ stat.reset()
+
+ _m5.stats.processResetQueue()
flags = attrdict({
'none' : 0x0000,