__init__.py revision 14286
11689SN/A# Copyright (c) 2017-2019 ARM Limited
21689SN/A# All rights reserved.
31689SN/A#
41689SN/A# The license below extends only to copyright in the software and shall
51689SN/A# not be construed as granting a license to any other intellectual
61689SN/A# property including but not limited to intellectual property relating
71689SN/A# to a hardware implementation of the functionality of the software
81689SN/A# licensed hereunder.  You may use the software subject to the license
91689SN/A# terms below provided that you ensure that this notice is replicated
101689SN/A# unmodified and in its entirety in all distributions of the software,
111689SN/A# modified or unmodified, in source code or in binary form.
121689SN/A#
131689SN/A# Copyright (c) 2007 The Regents of The University of Michigan
141689SN/A# Copyright (c) 2010 The Hewlett-Packard Development Company
151689SN/A# All rights reserved.
161689SN/A#
171689SN/A# Redistribution and use in source and binary forms, with or without
181689SN/A# modification, are permitted provided that the following conditions are
191689SN/A# met: redistributions of source code must retain the above copyright
201689SN/A# notice, this list of conditions and the following disclaimer;
211689SN/A# redistributions in binary form must reproduce the above copyright
221689SN/A# notice, this list of conditions and the following disclaimer in the
231689SN/A# documentation and/or other materials provided with the distribution;
241689SN/A# neither the name of the copyright holders nor the names of its
251689SN/A# contributors may be used to endorse or promote products derived from
261689SN/A# this software without specific prior written permission.
272665Ssaidi@eecs.umich.edu#
282665Ssaidi@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
292756Sksewell@umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
301689SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
311689SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
322325SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
332325SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
341060SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
351060SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
361060SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
372292SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
382292SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
391681SN/A#
401060SN/A# Authors: Nathan Binkert
412669Sktlim@umich.edu#          Andreas Sandberg
421060SN/A
431060SN/Afrom __future__ import print_function
441858SN/Afrom __future__ import absolute_import
452325SN/A
461717SN/Aimport m5
472683Sktlim@umich.edu
481717SN/Aimport _m5.stats
491717SN/Afrom m5.objects import Root
502292SN/Afrom m5.params import isNullPointer
512292SN/Afrom m5.util import attrdict, fatal
521060SN/A
531060SN/A# Stat exports
542316SN/Afrom _m5.stats import schedStatEvent as schedEvent
552316SN/Afrom _m5.stats import periodicStatDump
562680Sktlim@umich.edu
572669Sktlim@umich.eduoutputList = []
581060SN/A
591060SN/A# Dictionary of stat visitor factories populated by the _url_factory
601060SN/A# visitor.
611060SN/Afactories = { }
621060SN/A
631060SN/A# List of all factories. Contains tuples of (factory, schemes,
641464SN/A# enabled).
651061SN/Aall_factories = []
662292SN/A
672292SN/Adef _url_factory(schemes, enable=True):
682292SN/A    """Wrap a plain Python function with URL parsing helpers
692632Sstever@eecs.umich.edu
702669Sktlim@umich.edu    Wrap a plain Python function f(fn, **kwargs) to expect a URL that
711681SN/A    has been split using urlparse.urlsplit. First positional argument
721685SN/A    is assumed to be a filename, this is created as the concatenation
731681SN/A    of the netloc (~hostname) and path in the parsed URL. Keyword
741060SN/A    arguments are derived from the query values in the URL.
751060SN/A
762348SN/A    Arguments:
772348SN/A        schemes: A list of URL schemes to use for this function.
782348SN/A
792348SN/A    Keyword arguments:
802348SN/A        enable: Enable/disable this factory. Typically used when the
811060SN/A                presence of a function depends on some runtime property.
821755SN/A
831060SN/A    For example:
841060SN/A        wrapped_f(urlparse.urlsplit("text://stats.txt?desc=False")) ->
852669Sktlim@umich.edu        f("stats.txt", desc=False)
862669Sktlim@umich.edu
872669Sktlim@umich.edu    """
882325SN/A
891060SN/A    from functools import wraps
901060SN/A
911061SN/A    def decorator(func):
921060SN/A        @wraps(func)
932292SN/A        def wrapper(url):
942292SN/A            try:
952292SN/A                from urllib.parse import parse_qs
962292SN/A            except ImportError:
971060SN/A                # Python 2 fallback
981060SN/A                from urlparse import parse_qs
991060SN/A            from ast import literal_eval
1001060SN/A
1011060SN/A            qs = parse_qs(url.query, keep_blank_values=True)
1022307SN/A
1032307SN/A            # parse_qs returns a list of values for each parameter. Only
1041060SN/A            # use the last value since kwargs don't allow multiple values
1051060SN/A            # per parameter. Use literal_eval to transform string param
1062292SN/A            # values into proper Python types.
1071060SN/A            def parse_value(key, values):
1081060SN/A                if len(values) == 0 or (len(values) == 1 and not values[0]):
1091060SN/A                    fatal("%s: '%s' doesn't have a value." % (
1101060SN/A                        url.geturl(), key))
1111060SN/A                elif len(values) > 1:
1121060SN/A                    fatal("%s: '%s' has multiple values." % (
1132292SN/A                        url.geturl(), key))
1141755SN/A                else:
1151060SN/A                    try:
1161060SN/A                        return key, literal_eval(values[0])
1172292SN/A                    except ValueError:
1181755SN/A                        fatal("%s: %s isn't a valid Python literal" \
1192292SN/A                              % (url.geturl(), values[0]))
1202292SN/A
1211060SN/A            kwargs = dict([ parse_value(k, v) for k, v in qs.items() ])
1222292SN/A
1231060SN/A            try:
1241060SN/A                return func("%s%s" % (url.netloc, url.path), **kwargs)
1251060SN/A            except TypeError:
1262292SN/A                fatal("Illegal stat visitor parameter specified")
1271060SN/A
1281060SN/A        all_factories.append((wrapper, schemes, enable))
1292292SN/A        for scheme in schemes:
1301060SN/A            assert scheme not in factories
1311060SN/A            factories[scheme] = wrapper if enable else None
1321060SN/A        return wrapper
1332307SN/A
1341060SN/A    return decorator
1352307SN/A
1361060SN/A@_url_factory([ None, "", "text", "file", ])
1371060SN/Adef _textFactory(fn, desc=True):
1382292SN/A    """Output stats in text format.
1391060SN/A
1401060SN/A    Text stat files contain one stat per line with an optional
1411060SN/A    description. The description is enabled by default, but can be
1421060SN/A    disabled by setting the desc parameter to False.
1431060SN/A
1441060SN/A    Parameters:
1451060SN/A      * desc (bool): Output stat descriptions (default: True)
1462292SN/A
1472292SN/A    Example:
1482292SN/A      text://stats.txt?desc=False
1491755SN/A
1501060SN/A    """
1512292SN/A
1521684SN/A    return _m5.stats.initText(fn, desc)
1531684SN/A
1542292SN/A@_url_factory([ "h5", ], enable=hasattr(_m5.stats, "initHDF5"))
1552292SN/Adef _hdf5Factory(fn, chunking=10, desc=True, formulas=True):
1562292SN/A    """Output stats in HDF5 format.
1571684SN/A
1581684SN/A    The HDF5 file format is a structured binary file format. It has
1592292SN/A    the multiple benefits over traditional text stat files:
1601060SN/A
1611060SN/A      * Efficient storage of time series (multiple stat dumps)
1622292SN/A      * Fast lookup of stats
1632292SN/A      * Plenty of existing tooling (e.g., Python libraries and graphical
1641060SN/A        viewers)
1652292SN/A      * File format can be used to store frame buffers together with
1662292SN/A        normal stats.
1672292SN/A
1682292SN/A    There are some drawbacks compared to the default text format:
1692292SN/A      * Large startup cost (single stat dump larger than text equivalent)
1702292SN/A      * Stat dumps are slower than text
1712292SN/A
1722292SN/A
1732292SN/A    Known limitations:
1742292SN/A      * Distributions and histograms currently unsupported.
1752292SN/A      * No support for forking.
1762292SN/A
1772292SN/A
1782292SN/A    Parameters:
1792292SN/A      * chunking (unsigned): Number of time steps to pre-allocate (default: 10)
1802292SN/A      * desc (bool): Output stat descriptions (default: True)
1812292SN/A      * formulas (bool): Output derived stats (default: True)
1822292SN/A
1832292SN/A    Example:
1842292SN/A      h5://stats.h5?desc=False;chunking=100;formulas=False
1852292SN/A
1862292SN/A    """
1872292SN/A
1882292SN/A    return _m5.stats.initHDF5(fn, chunking, desc, formulas)
1892292SN/A
1902292SN/Adef addStatVisitor(url):
1912292SN/A    """Add a stat visitor specified using a URL string
1922292SN/A
1932292SN/A    Stat visitors are specified using URLs on the following format:
1942292SN/A    format://path[?param=value[;param=value]]
1952292SN/A
1962292SN/A    The available formats are listed in the factories list. Factories
1972292SN/A    are called with the path as the first positional parameter and the
1982292SN/A    parameters are keyword arguments. Parameter values must be valid
1992292SN/A    Python literals.
2002292SN/A
2012292SN/A    """
2022292SN/A
2032292SN/A    try:
2042292SN/A        from urllib.parse import urlsplit
2052292SN/A    except ImportError:
2062292SN/A        # Python 2 fallback
2072292SN/A        from urlparse import urlsplit
2082292SN/A
2092325SN/A    parsed = urlsplit(url)
2102292SN/A
2112348SN/A    try:
2122307SN/A        factory = factories[parsed.scheme]
2132292SN/A    except KeyError:
2142348SN/A        fatal("Illegal stat file type '%s' specified." % parsed.scheme)
2152316SN/A
2162316SN/A    if factory is None:
2172348SN/A        fatal("Stat type '%s' disabled at compile time" % parsed.scheme)
2181060SN/A
2191060SN/A    outputList.append(factory(parsed))
2201060SN/A
2212316SN/Adef printStatVisitorTypes():
2222316SN/A    """List available stat visitors and their documentation"""
2231060SN/A
2241858SN/A    import inspect
2251060SN/A
2261060SN/A    def print_doc(doc):
2271060SN/A        for line in doc.splitlines():
2281060SN/A            print("| %s" % line)
2291060SN/A        print()
2301060SN/A
2311060SN/A    enabled_visitors = [ x for x in all_factories if x[2] ]
2322292SN/A    for factory, schemes, _ in enabled_visitors:
2332292SN/A        print("%s:" % ", ".join(filter(lambda x: x is not None, schemes)))
2341060SN/A
2351060SN/A        # Try to extract the factory doc string
2362292SN/A        print_doc(inspect.getdoc(factory))
2372292SN/A
2381060SN/Adef initSimStats():
2392292SN/A    _m5.stats.initSimStats()
2402292SN/A    _m5.stats.registerPythonStatsHandlers()
2412683Sktlim@umich.edu
2421060SN/Adef _visit_groups(visitor, root=None):
2432292SN/A    if root is None:
2442292SN/A        root = Root.getInstance()
2452683Sktlim@umich.edu    for group in root.getStatGroups().values():
2461060SN/A        visitor(group)
2471060SN/A        _visit_groups(visitor, root=group)
2481060SN/A
2492348SN/Adef _visit_stats(visitor, root=None):
2501060SN/A    def for_each_stat(g):
2511060SN/A        for stat in g.getStats():
2522455SN/A            visitor(g, stat)
2531060SN/A    _visit_groups(for_each_stat, root=root)
2542455SN/A
2551060SN/Adef _bindStatHierarchy(root):
2562455SN/A    def _bind_obj(name, obj):
2572455SN/A        if isNullPointer(obj):
2582455SN/A            return
2591060SN/A        if m5.SimObject.isSimObjectVector(obj):
2601060SN/A            for idx, obj in enumerate(obj):
2611060SN/A                _bind_obj("{}{}".format(name, idx), obj)
2622669Sktlim@umich.edu        else:
2631060SN/A            # We need this check because not all obj.getCCObject() is an
2642455SN/A            # instance of Stat::Group. For example, sc_core::sc_module, the C++
2651060SN/A            # class of SystemC_ScModule, is not a subclass of Stat::Group. So
2662455SN/A            # it will cause a type error if obj is a SystemC_ScModule when
2672455SN/A            # calling addStatGroup().
2682669Sktlim@umich.edu            if isinstance(obj.getCCObject(), _m5.stats.Group):
2691060SN/A                parent = root
2702292SN/A                while parent:
2711060SN/A                    if hasattr(parent, 'addStatGroup'):
2722292SN/A                        parent.addStatGroup(name, obj.getCCObject())
2731060SN/A                        break
2742292SN/A                    parent = parent.get_parent();
2752292SN/A
2762292SN/A            _bindStatHierarchy(obj)
2772292SN/A
2782348SN/A    for name, obj in root._children.items():
2792348SN/A        _bind_obj(name, obj)
2802348SN/A
2812348SN/Anames = []
2822348SN/Astats_dict = {}
2832292SN/Astats_list = []
2842292SN/Adef enable():
2852292SN/A    '''Enable the statistics package.  Before the statistics package is
2862292SN/A    enabled, all statistics must be created and initialized and once
2872292SN/A    the package is enabled, no more statistics can be created.'''
2882292SN/A
2892292SN/A    def check_stat(group, stat):
2902292SN/A        if not stat.check() or not stat.baseCheck():
2912348SN/A            fatal("statistic '%s' (%d) was not properly initialized " \
2922292SN/A                  "by a regStats() function\n", stat.name, stat.id)
2932292SN/A
2942348SN/A        if not (stat.flags & flags.display):
2952348SN/A            stat.name = "__Stat%06d" % stat.id
2962292SN/A
2972348SN/A
2982292SN/A    # Legacy stat
2992292SN/A    global stats_list
3002348SN/A    stats_list = list(_m5.stats.statsList())
3012348SN/A
3021060SN/A    for stat in stats_list:
3032756Sksewell@umich.edu        check_stat(None, stat)
3042756Sksewell@umich.edu
3052756Sksewell@umich.edu    stats_list.sort(key=lambda s: s.name.split('.'))
3062756Sksewell@umich.edu    for stat in stats_list:
3072756Sksewell@umich.edu        stats_dict[stat.name] = stat
3082756Sksewell@umich.edu        stat.enable()
3091060SN/A
3101060SN/A
3111060SN/A    # New stats
3122292SN/A    _visit_stats(check_stat)
3131060SN/A    _visit_stats(lambda g, s: s.enable())
3141060SN/A
3152292SN/A    _m5.stats.enable();
3161060SN/A
3172292SN/Adef prepare():
3182292SN/A    '''Prepare all stats for data access.  This must be done before
3191060SN/A    dumping and serialization.'''
3202325SN/A
3212325SN/A    # Legacy stats
3221060SN/A    for stat in stats_list:
3231061SN/A        stat.prepare()
3241060SN/A
3251060SN/A    # New stats
3262292SN/A    _visit_stats(lambda g, s: s.prepare())
3271060SN/A
3281062SN/Adef _dump_to_visitor(visitor, root=None):
3292292SN/A    # Legacy stats
3302292SN/A    if root is None:
3312348SN/A        for stat in stats_list:
3322292SN/A            stat.visit(visitor)
3332292SN/A
3342348SN/A    # New stats
3352292SN/A    def dump_group(group):
3361062SN/A        for stat in group.getStats():
3372348SN/A            stat.visit(visitor)
3381060SN/A
3391060SN/A        for n, g in group.getStatGroups().items():
3401060SN/A            visitor.beginGroup(n)
3411060SN/A            dump_group(g)
3422292SN/A            visitor.endGroup()
3431060SN/A
3442292SN/A    if root is not None:
3452292SN/A        for p in root.path_list():
3462292SN/A            visitor.beginGroup(p)
3472292SN/A    dump_group(root if root is not None else Root.getInstance())
3482292SN/A    if root is not None:
3492325SN/A        for p in reversed(root.path_list()):
3502348SN/A            visitor.endGroup()
3512348SN/A
3522348SN/AlastDump = 0
3532292SN/A
3542325SN/Adef dump(root=None):
3552292SN/A    '''Dump all statistics data to the registered outputs'''
3562325SN/A
3572325SN/A    now = m5.curTick()
3582292SN/A    global lastDump
3592292SN/A    assert lastDump <= now
3602292SN/A    new_dump = lastDump != now
3611060SN/A    lastDump = now
3621060SN/A
3631060SN/A    # Don't allow multiple global stat dumps in the same tick. It's
3641060SN/A    # still possible to dump a multiple sub-trees.
3651060SN/A    if not new_dump and root is None:
3661060SN/A        return
3671060SN/A
3681060SN/A    # Only prepare stats the first time we dump them in the same tick.
3691060SN/A    if new_dump:
3701060SN/A        _m5.stats.processDumpQueue()
3711060SN/A        prepare()
3721060SN/A
3731060SN/A    for output in outputList:
3741060SN/A        if output.valid():
3751060SN/A            output.begin()
3761060SN/A            _dump_to_visitor(output, root=root)
3771060SN/A            output.end()
3781060SN/A
3791060SN/Adef reset():
3801060SN/A    '''Reset all statistics to the base state'''
3811060SN/A
3821060SN/A    # call reset stats on all SimObjects
3831060SN/A    root = Root.getInstance()
3842292SN/A    if root:
3852292SN/A        root.resetStats()
3862292SN/A
3872292SN/A    # call any other registered legacy stats reset callbacks
3881060SN/A    for stat in stats_list:
3891060SN/A        stat.reset()
3901060SN/A
3911060SN/A    _m5.stats.processResetQueue()
3922292SN/A
3932292SN/Aflags = attrdict({
3942292SN/A    'none'    : 0x0000,
3952292SN/A    'init'    : 0x0001,
3962292SN/A    'display' : 0x0002,
3972292SN/A    'total'   : 0x0010,
3981060SN/A    'pdf'     : 0x0020,
3992292SN/A    'cdf'     : 0x0040,
4002292SN/A    'dist'    : 0x0080,
4012292SN/A    'nozero'  : 0x0100,
4022292SN/A    'nonan'   : 0x0200,
4032292SN/A})
4042292SN/A