__init__.py revision 14209
114209Sandreas.sandberg@arm.com# Copyright (c) 2017-2019 ARM Limited
211878Sandreas.sandberg@arm.com# All rights reserved.
311878Sandreas.sandberg@arm.com#
411878Sandreas.sandberg@arm.com# The license below extends only to copyright in the software and shall
511878Sandreas.sandberg@arm.com# not be construed as granting a license to any other intellectual
611878Sandreas.sandberg@arm.com# property including but not limited to intellectual property relating
711878Sandreas.sandberg@arm.com# to a hardware implementation of the functionality of the software
811878Sandreas.sandberg@arm.com# licensed hereunder.  You may use the software subject to the license
911878Sandreas.sandberg@arm.com# terms below provided that you ensure that this notice is replicated
1011878Sandreas.sandberg@arm.com# unmodified and in its entirety in all distributions of the software,
1111878Sandreas.sandberg@arm.com# modified or unmodified, in source code or in binary form.
1211878Sandreas.sandberg@arm.com#
134126SN/A# Copyright (c) 2007 The Regents of The University of Michigan
148295Snate@binkert.org# Copyright (c) 2010 The Hewlett-Packard Development Company
154126SN/A# All rights reserved.
164126SN/A#
174126SN/A# Redistribution and use in source and binary forms, with or without
184126SN/A# modification, are permitted provided that the following conditions are
194126SN/A# met: redistributions of source code must retain the above copyright
204126SN/A# notice, this list of conditions and the following disclaimer;
214126SN/A# redistributions in binary form must reproduce the above copyright
224126SN/A# notice, this list of conditions and the following disclaimer in the
234126SN/A# documentation and/or other materials provided with the distribution;
244126SN/A# neither the name of the copyright holders nor the names of its
254126SN/A# contributors may be used to endorse or promote products derived from
264126SN/A# this software without specific prior written permission.
274126SN/A#
284126SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
294126SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
304126SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
314126SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
324126SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
334126SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
344126SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
354126SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
364126SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
374126SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
384126SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
394126SN/A#
404126SN/A# Authors: Nathan Binkert
4111878Sandreas.sandberg@arm.com#          Andreas Sandberg
424126SN/A
438296Snate@binkert.orgimport m5
448296Snate@binkert.org
4511802Sandreas.sandberg@arm.comimport _m5.stats
468295Snate@binkert.orgfrom m5.objects import Root
478296Snate@binkert.orgfrom m5.util import attrdict, fatal
484126SN/A
4911766Sandreas.sandberg@arm.com# Stat exports
5011802Sandreas.sandberg@arm.comfrom _m5.stats import schedStatEvent as schedEvent
5111802Sandreas.sandberg@arm.comfrom _m5.stats import periodicStatDump
5211766Sandreas.sandberg@arm.com
538296Snate@binkert.orgoutputList = []
5411878Sandreas.sandberg@arm.com
5511878Sandreas.sandberg@arm.comdef _url_factory(func):
5611878Sandreas.sandberg@arm.com    """Wrap a plain Python function with URL parsing helpers
5711878Sandreas.sandberg@arm.com
5811878Sandreas.sandberg@arm.com    Wrap a plain Python function f(fn, **kwargs) to expect a URL that
5911878Sandreas.sandberg@arm.com    has been split using urlparse.urlsplit. First positional argument
6011878Sandreas.sandberg@arm.com    is assumed to be a filename, this is created as the concatenation
6111878Sandreas.sandberg@arm.com    of the netloc (~hostname) and path in the parsed URL. Keyword
6211878Sandreas.sandberg@arm.com    arguments are derived from the query values in the URL.
6311878Sandreas.sandberg@arm.com
6411878Sandreas.sandberg@arm.com    For example:
6511878Sandreas.sandberg@arm.com        wrapped_f(urlparse.urlsplit("text://stats.txt?desc=False")) ->
6611878Sandreas.sandberg@arm.com        f("stats.txt", desc=False)
6711878Sandreas.sandberg@arm.com
6811878Sandreas.sandberg@arm.com    """
6911878Sandreas.sandberg@arm.com
7011878Sandreas.sandberg@arm.com    from functools import wraps
7111878Sandreas.sandberg@arm.com
7211878Sandreas.sandberg@arm.com    @wraps(func)
7311878Sandreas.sandberg@arm.com    def wrapper(url):
7413712Sandreas.sandberg@arm.com        try:
7513712Sandreas.sandberg@arm.com            from urllib.parse import parse_qs
7613712Sandreas.sandberg@arm.com        except ImportError:
7713712Sandreas.sandberg@arm.com            # Python 2 fallback
7813712Sandreas.sandberg@arm.com            from urlparse import parse_qs
7911878Sandreas.sandberg@arm.com        from ast import literal_eval
8011878Sandreas.sandberg@arm.com
8111878Sandreas.sandberg@arm.com        qs = parse_qs(url.query, keep_blank_values=True)
8211878Sandreas.sandberg@arm.com
8311878Sandreas.sandberg@arm.com        # parse_qs returns a list of values for each parameter. Only
8411878Sandreas.sandberg@arm.com        # use the last value since kwargs don't allow multiple values
8511878Sandreas.sandberg@arm.com        # per parameter. Use literal_eval to transform string param
8611878Sandreas.sandberg@arm.com        # values into proper Python types.
8711878Sandreas.sandberg@arm.com        def parse_value(key, values):
8811878Sandreas.sandberg@arm.com            if len(values) == 0 or (len(values) == 1 and not values[0]):
8911878Sandreas.sandberg@arm.com                fatal("%s: '%s' doesn't have a value." % (url.geturl(), key))
9011878Sandreas.sandberg@arm.com            elif len(values) > 1:
9111878Sandreas.sandberg@arm.com                fatal("%s: '%s' has multiple values." % (url.geturl(), key))
9211878Sandreas.sandberg@arm.com            else:
9311878Sandreas.sandberg@arm.com                try:
9411878Sandreas.sandberg@arm.com                    return key, literal_eval(values[0])
9511878Sandreas.sandberg@arm.com                except ValueError:
9611878Sandreas.sandberg@arm.com                    fatal("%s: %s isn't a valid Python literal" \
9711878Sandreas.sandberg@arm.com                          % (url.geturl(), values[0]))
9811878Sandreas.sandberg@arm.com
9911878Sandreas.sandberg@arm.com        kwargs = dict([ parse_value(k, v) for k, v in qs.items() ])
10011878Sandreas.sandberg@arm.com
10111878Sandreas.sandberg@arm.com        try:
10211878Sandreas.sandberg@arm.com            return func("%s%s" % (url.netloc, url.path), **kwargs)
10311878Sandreas.sandberg@arm.com        except TypeError:
10411878Sandreas.sandberg@arm.com            fatal("Illegal stat visitor parameter specified")
10511878Sandreas.sandberg@arm.com
10611878Sandreas.sandberg@arm.com    return wrapper
10711878Sandreas.sandberg@arm.com
10811878Sandreas.sandberg@arm.com@_url_factory
10911878Sandreas.sandberg@arm.comdef _textFactory(fn, desc=True):
11011878Sandreas.sandberg@arm.com    """Output stats in text format.
11111878Sandreas.sandberg@arm.com
11211878Sandreas.sandberg@arm.com    Text stat files contain one stat per line with an optional
11311878Sandreas.sandberg@arm.com    description. The description is enabled by default, but can be
11411878Sandreas.sandberg@arm.com    disabled by setting the desc parameter to False.
11511878Sandreas.sandberg@arm.com
11611878Sandreas.sandberg@arm.com    Example: text://stats.txt?desc=False
11711878Sandreas.sandberg@arm.com
11811878Sandreas.sandberg@arm.com    """
11911878Sandreas.sandberg@arm.com
12011878Sandreas.sandberg@arm.com    return _m5.stats.initText(fn, desc)
12111878Sandreas.sandberg@arm.com
12214209Sandreas.sandberg@arm.com@_url_factory
12314209Sandreas.sandberg@arm.comdef _hdf5Factory(fn, chunking=10, desc=True, formulas=True):
12414209Sandreas.sandberg@arm.com    """Output stats in HDF5 format.
12514209Sandreas.sandberg@arm.com
12614209Sandreas.sandberg@arm.com    The HDF5 file format is a structured binary file format. It has
12714209Sandreas.sandberg@arm.com    the multiple benefits over traditional text stat files:
12814209Sandreas.sandberg@arm.com
12914209Sandreas.sandberg@arm.com      * Efficient storage of time series (multiple stat dumps)
13014209Sandreas.sandberg@arm.com      * Fast lookup of stats
13114209Sandreas.sandberg@arm.com      * Plenty of existing tooling (e.g., Python libraries and graphical
13214209Sandreas.sandberg@arm.com        viewers)
13314209Sandreas.sandberg@arm.com      * File format can be used to store frame buffers together with
13414209Sandreas.sandberg@arm.com        normal stats.
13514209Sandreas.sandberg@arm.com
13614209Sandreas.sandberg@arm.com    There are some drawbacks compared to the default text format:
13714209Sandreas.sandberg@arm.com      * Large startup cost (single stat dump larger than text equivalent)
13814209Sandreas.sandberg@arm.com      * Stat dumps are slower than text
13914209Sandreas.sandberg@arm.com
14014209Sandreas.sandberg@arm.com
14114209Sandreas.sandberg@arm.com    Known limitations:
14214209Sandreas.sandberg@arm.com      * Distributions and histograms currently unsupported.
14314209Sandreas.sandberg@arm.com      * No support for forking.
14414209Sandreas.sandberg@arm.com
14514209Sandreas.sandberg@arm.com
14614209Sandreas.sandberg@arm.com    Parameters:
14714209Sandreas.sandberg@arm.com      * chunking (unsigned): Number of time steps to pre-allocate (default: 10)
14814209Sandreas.sandberg@arm.com      * desc (bool): Output stat descriptions (default: True)
14914209Sandreas.sandberg@arm.com      * formulas (bool): Output derived stats (default: True)
15014209Sandreas.sandberg@arm.com
15114209Sandreas.sandberg@arm.com    Example:
15214209Sandreas.sandberg@arm.com      h5://stats.h5?desc=False;chunking=100;formulas=False
15314209Sandreas.sandberg@arm.com
15414209Sandreas.sandberg@arm.com    """
15514209Sandreas.sandberg@arm.com
15614209Sandreas.sandberg@arm.com    if hasattr(_m5.stats, "initHDF5"):
15714209Sandreas.sandberg@arm.com        return _m5.stats.initHDF5(fn, chunking, desc, formulas)
15814209Sandreas.sandberg@arm.com    else:
15914209Sandreas.sandberg@arm.com        fatal("HDF5 support not enabled at compile time")
16014209Sandreas.sandberg@arm.com
16111878Sandreas.sandberg@arm.comfactories = {
16211878Sandreas.sandberg@arm.com    # Default to the text factory if we're given a naked path
16311878Sandreas.sandberg@arm.com    "" : _textFactory,
16411878Sandreas.sandberg@arm.com    "file" : _textFactory,
16511878Sandreas.sandberg@arm.com    "text" : _textFactory,
16614209Sandreas.sandberg@arm.com    "h5" : _hdf5Factory,
16711878Sandreas.sandberg@arm.com}
16811878Sandreas.sandberg@arm.com
16914209Sandreas.sandberg@arm.com
17011878Sandreas.sandberg@arm.comdef addStatVisitor(url):
17111878Sandreas.sandberg@arm.com    """Add a stat visitor specified using a URL string
17211878Sandreas.sandberg@arm.com
17311878Sandreas.sandberg@arm.com    Stat visitors are specified using URLs on the following format:
17411878Sandreas.sandberg@arm.com    format://path[?param=value[;param=value]]
17511878Sandreas.sandberg@arm.com
17611878Sandreas.sandberg@arm.com    The available formats are listed in the factories list. Factories
17711878Sandreas.sandberg@arm.com    are called with the path as the first positional parameter and the
17811878Sandreas.sandberg@arm.com    parameters are keyword arguments. Parameter values must be valid
17911878Sandreas.sandberg@arm.com    Python literals.
18011878Sandreas.sandberg@arm.com
18111878Sandreas.sandberg@arm.com    """
18211878Sandreas.sandberg@arm.com
18313712Sandreas.sandberg@arm.com    try:
18413712Sandreas.sandberg@arm.com        from urllib.parse import urlsplit
18513712Sandreas.sandberg@arm.com    except ImportError:
18613712Sandreas.sandberg@arm.com        # Python 2 fallback
18713712Sandreas.sandberg@arm.com        from urlparse import urlsplit
18811878Sandreas.sandberg@arm.com
18911878Sandreas.sandberg@arm.com    parsed = urlsplit(url)
19011878Sandreas.sandberg@arm.com
19111878Sandreas.sandberg@arm.com    try:
19211878Sandreas.sandberg@arm.com        factory = factories[parsed.scheme]
19311878Sandreas.sandberg@arm.com    except KeyError:
19411878Sandreas.sandberg@arm.com        fatal("Illegal stat file type specified.")
19511878Sandreas.sandberg@arm.com
19611878Sandreas.sandberg@arm.com    outputList.append(factory(parsed))
1974126SN/A
1986001SN/Adef initSimStats():
19911802Sandreas.sandberg@arm.com    _m5.stats.initSimStats()
20011802Sandreas.sandberg@arm.com    _m5.stats.registerPythonStatsHandlers()
2016001SN/A
20214206Sandreas.sandberg@arm.comdef _visit_groups(visitor, root=None):
20314206Sandreas.sandberg@arm.com    if root is None:
20414206Sandreas.sandberg@arm.com        root = Root.getInstance()
20514205Sandreas.sandberg@arm.com    for group in root.getStatGroups().values():
20614205Sandreas.sandberg@arm.com        visitor(group)
20714206Sandreas.sandberg@arm.com        _visit_groups(visitor, root=group)
20814205Sandreas.sandberg@arm.com
20914206Sandreas.sandberg@arm.comdef _visit_stats(visitor, root=None):
21014205Sandreas.sandberg@arm.com    def for_each_stat(g):
21114205Sandreas.sandberg@arm.com        for stat in g.getStats():
21214205Sandreas.sandberg@arm.com            visitor(g, stat)
21314206Sandreas.sandberg@arm.com    _visit_groups(for_each_stat, root=root)
21414205Sandreas.sandberg@arm.com
21514205Sandreas.sandberg@arm.comdef _bindStatHierarchy(root):
21614205Sandreas.sandberg@arm.com    def _bind_obj(name, obj):
21714205Sandreas.sandberg@arm.com        if m5.SimObject.isSimObjectVector(obj):
21814205Sandreas.sandberg@arm.com            for idx, obj in enumerate(obj):
21914205Sandreas.sandberg@arm.com                _bind_obj("{}{}".format(name, idx), obj)
22014205Sandreas.sandberg@arm.com        else:
22114205Sandreas.sandberg@arm.com            root.addStatGroup(name, obj.getCCObject())
22214205Sandreas.sandberg@arm.com            _bindStatHierarchy(obj)
22314205Sandreas.sandberg@arm.com
22414205Sandreas.sandberg@arm.com    for name, obj in root._children.items():
22514205Sandreas.sandberg@arm.com        _bind_obj(name, obj)
22614205Sandreas.sandberg@arm.com
2278295Snate@binkert.orgnames = []
2288295Snate@binkert.orgstats_dict = {}
2298295Snate@binkert.orgstats_list = []
2306001SN/Adef enable():
2318295Snate@binkert.org    '''Enable the statistics package.  Before the statistics package is
2328295Snate@binkert.org    enabled, all statistics must be created and initialized and once
2338295Snate@binkert.org    the package is enabled, no more statistics can be created.'''
2348295Snate@binkert.org
23514205Sandreas.sandberg@arm.com    def check_stat(group, stat):
2368296Snate@binkert.org        if not stat.check() or not stat.baseCheck():
23710169SCurtis.Dunham@arm.com            fatal("statistic '%s' (%d) was not properly initialized " \
23810169SCurtis.Dunham@arm.com                  "by a regStats() function\n", stat.name, stat.id)
2398296Snate@binkert.org
2408296Snate@binkert.org        if not (stat.flags & flags.display):
2418296Snate@binkert.org            stat.name = "__Stat%06d" % stat.id
2428296Snate@binkert.org
24314205Sandreas.sandberg@arm.com
24414205Sandreas.sandberg@arm.com    # Legacy stat
24514205Sandreas.sandberg@arm.com    global stats_list
24614205Sandreas.sandberg@arm.com    stats_list = list(_m5.stats.statsList())
24714205Sandreas.sandberg@arm.com
24814205Sandreas.sandberg@arm.com    for stat in stats_list:
24914205Sandreas.sandberg@arm.com        check_stat(None, stat)
25014205Sandreas.sandberg@arm.com
25113681Sandreas.sandberg@arm.com    stats_list.sort(key=lambda s: s.name.split('.'))
2528295Snate@binkert.org    for stat in stats_list:
2538295Snate@binkert.org        stats_dict[stat.name] = stat
2548296Snate@binkert.org        stat.enable()
2558295Snate@binkert.org
25614205Sandreas.sandberg@arm.com
25714205Sandreas.sandberg@arm.com    # New stats
25814206Sandreas.sandberg@arm.com    _visit_stats(check_stat)
25914206Sandreas.sandberg@arm.com    _visit_stats(lambda g, s: s.enable())
26014205Sandreas.sandberg@arm.com
26111802Sandreas.sandberg@arm.com    _m5.stats.enable();
2628986SAli.Saidi@ARM.com
2638296Snate@binkert.orgdef prepare():
2648296Snate@binkert.org    '''Prepare all stats for data access.  This must be done before
2658296Snate@binkert.org    dumping and serialization.'''
2666001SN/A
26714205Sandreas.sandberg@arm.com    # Legacy stats
2688296Snate@binkert.org    for stat in stats_list:
2698296Snate@binkert.org        stat.prepare()
2708296Snate@binkert.org
27114205Sandreas.sandberg@arm.com    # New stats
27214206Sandreas.sandberg@arm.com    _visit_stats(lambda g, s: s.prepare())
27314205Sandreas.sandberg@arm.com
27414206Sandreas.sandberg@arm.comdef _dump_to_visitor(visitor, root=None):
27514205Sandreas.sandberg@arm.com    # Legacy stats
27614206Sandreas.sandberg@arm.com    if root is None:
27714206Sandreas.sandberg@arm.com        for stat in stats_list:
27814206Sandreas.sandberg@arm.com            stat.visit(visitor)
27914205Sandreas.sandberg@arm.com
28014205Sandreas.sandberg@arm.com    # New stats
28114205Sandreas.sandberg@arm.com    def dump_group(group):
28214205Sandreas.sandberg@arm.com        for stat in group.getStats():
28314205Sandreas.sandberg@arm.com            stat.visit(visitor)
28414205Sandreas.sandberg@arm.com
28514205Sandreas.sandberg@arm.com        for n, g in group.getStatGroups().items():
28614205Sandreas.sandberg@arm.com            visitor.beginGroup(n)
28714205Sandreas.sandberg@arm.com            dump_group(g)
28814205Sandreas.sandberg@arm.com            visitor.endGroup()
28914205Sandreas.sandberg@arm.com
29014206Sandreas.sandberg@arm.com    if root is not None:
29114206Sandreas.sandberg@arm.com        for p in root.path_list():
29214206Sandreas.sandberg@arm.com            visitor.beginGroup(p)
29314206Sandreas.sandberg@arm.com    dump_group(root if root is not None else Root.getInstance())
29414206Sandreas.sandberg@arm.com    if root is not None:
29514206Sandreas.sandberg@arm.com        for p in reversed(root.path_list()):
29614206Sandreas.sandberg@arm.com            visitor.endGroup()
29714205Sandreas.sandberg@arm.com
29814206Sandreas.sandberg@arm.comlastDump = 0
29914205Sandreas.sandberg@arm.com
30014206Sandreas.sandberg@arm.comdef dump(root=None):
3018296Snate@binkert.org    '''Dump all statistics data to the registered outputs'''
3026001SN/A
30314206Sandreas.sandberg@arm.com    now = m5.curTick()
30414206Sandreas.sandberg@arm.com    global lastDump
30514206Sandreas.sandberg@arm.com    assert lastDump <= now
30614206Sandreas.sandberg@arm.com    new_dump = lastDump != now
30714206Sandreas.sandberg@arm.com    lastDump = now
3088296Snate@binkert.org
30914206Sandreas.sandberg@arm.com    # Don't allow multiple global stat dumps in the same tick. It's
31014206Sandreas.sandberg@arm.com    # still possible to dump a multiple sub-trees.
31114206Sandreas.sandberg@arm.com    if not new_dump and root is None:
3128296Snate@binkert.org        return
3138296Snate@binkert.org
31414206Sandreas.sandberg@arm.com    # Only prepare stats the first time we dump them in the same tick.
31514206Sandreas.sandberg@arm.com    if new_dump:
31614206Sandreas.sandberg@arm.com        _m5.stats.processDumpQueue()
31714206Sandreas.sandberg@arm.com        prepare()
3188296Snate@binkert.org
3198296Snate@binkert.org    for output in outputList:
3208296Snate@binkert.org        if output.valid():
3218296Snate@binkert.org            output.begin()
32214206Sandreas.sandberg@arm.com            _dump_to_visitor(output, root=root)
3238296Snate@binkert.org            output.end()
3246001SN/A
3256001SN/Adef reset():
3268296Snate@binkert.org    '''Reset all statistics to the base state'''
3278296Snate@binkert.org
3287527SN/A    # call reset stats on all SimObjects
3297527SN/A    root = Root.getInstance()
3307802SN/A    if root:
33114205Sandreas.sandberg@arm.com        root.resetStats()
3327802SN/A
33314205Sandreas.sandberg@arm.com    # call any other registered legacy stats reset callbacks
3348296Snate@binkert.org    for stat in stats_list:
3358296Snate@binkert.org        stat.reset()
3368296Snate@binkert.org
33711802Sandreas.sandberg@arm.com    _m5.stats.processResetQueue()
3388295Snate@binkert.org
3398295Snate@binkert.orgflags = attrdict({
3408295Snate@binkert.org    'none'    : 0x0000,
3418295Snate@binkert.org    'init'    : 0x0001,
3428295Snate@binkert.org    'display' : 0x0002,
3438295Snate@binkert.org    'total'   : 0x0010,
3448295Snate@binkert.org    'pdf'     : 0x0020,
3458295Snate@binkert.org    'cdf'     : 0x0040,
3468295Snate@binkert.org    'dist'    : 0x0080,
3478295Snate@binkert.org    'nozero'  : 0x0100,
3488295Snate@binkert.org    'nonan'   : 0x0200,
3498295Snate@binkert.org})
350