__init__.py revision 14213:6eabb443ab3f
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            root.addStatGroup(name, obj.getCCObject())
261            _bindStatHierarchy(obj)
262
263    for name, obj in root._children.items():
264        _bind_obj(name, obj)
265
266names = []
267stats_dict = {}
268stats_list = []
269def enable():
270    '''Enable the statistics package.  Before the statistics package is
271    enabled, all statistics must be created and initialized and once
272    the package is enabled, no more statistics can be created.'''
273
274    def check_stat(group, stat):
275        if not stat.check() or not stat.baseCheck():
276            fatal("statistic '%s' (%d) was not properly initialized " \
277                  "by a regStats() function\n", stat.name, stat.id)
278
279        if not (stat.flags & flags.display):
280            stat.name = "__Stat%06d" % stat.id
281
282
283    # Legacy stat
284    global stats_list
285    stats_list = list(_m5.stats.statsList())
286
287    for stat in stats_list:
288        check_stat(None, stat)
289
290    stats_list.sort(key=lambda s: s.name.split('.'))
291    for stat in stats_list:
292        stats_dict[stat.name] = stat
293        stat.enable()
294
295
296    # New stats
297    _visit_stats(check_stat)
298    _visit_stats(lambda g, s: s.enable())
299
300    _m5.stats.enable();
301
302def prepare():
303    '''Prepare all stats for data access.  This must be done before
304    dumping and serialization.'''
305
306    # Legacy stats
307    for stat in stats_list:
308        stat.prepare()
309
310    # New stats
311    _visit_stats(lambda g, s: s.prepare())
312
313def _dump_to_visitor(visitor, root=None):
314    # Legacy stats
315    if root is None:
316        for stat in stats_list:
317            stat.visit(visitor)
318
319    # New stats
320    def dump_group(group):
321        for stat in group.getStats():
322            stat.visit(visitor)
323
324        for n, g in group.getStatGroups().items():
325            visitor.beginGroup(n)
326            dump_group(g)
327            visitor.endGroup()
328
329    if root is not None:
330        for p in root.path_list():
331            visitor.beginGroup(p)
332    dump_group(root if root is not None else Root.getInstance())
333    if root is not None:
334        for p in reversed(root.path_list()):
335            visitor.endGroup()
336
337lastDump = 0
338
339def dump(root=None):
340    '''Dump all statistics data to the registered outputs'''
341
342    now = m5.curTick()
343    global lastDump
344    assert lastDump <= now
345    new_dump = lastDump != now
346    lastDump = now
347
348    # Don't allow multiple global stat dumps in the same tick. It's
349    # still possible to dump a multiple sub-trees.
350    if not new_dump and root is None:
351        return
352
353    # Only prepare stats the first time we dump them in the same tick.
354    if new_dump:
355        _m5.stats.processDumpQueue()
356        prepare()
357
358    for output in outputList:
359        if output.valid():
360            output.begin()
361            _dump_to_visitor(output, root=root)
362            output.end()
363
364def reset():
365    '''Reset all statistics to the base state'''
366
367    # call reset stats on all SimObjects
368    root = Root.getInstance()
369    if root:
370        root.resetStats()
371
372    # call any other registered legacy stats reset callbacks
373    for stat in stats_list:
374        stat.reset()
375
376    _m5.stats.processResetQueue()
377
378flags = attrdict({
379    'none'    : 0x0000,
380    'init'    : 0x0001,
381    'display' : 0x0002,
382    'total'   : 0x0010,
383    'pdf'     : 0x0020,
384    'cdf'     : 0x0040,
385    'dist'    : 0x0080,
386    'nozero'  : 0x0100,
387    'nonan'   : 0x0200,
388})
389