__init__.py revision 14205
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
43import m5
44
45import _m5.stats
46from m5.objects import Root
47from m5.util import attrdict, fatal
48
49# Stat exports
50from _m5.stats import schedStatEvent as schedEvent
51from _m5.stats import periodicStatDump
52
53outputList = []
54
55def _url_factory(func):
56    """Wrap a plain Python function with URL parsing helpers
57
58    Wrap a plain Python function f(fn, **kwargs) to expect a URL that
59    has been split using urlparse.urlsplit. First positional argument
60    is assumed to be a filename, this is created as the concatenation
61    of the netloc (~hostname) and path in the parsed URL. Keyword
62    arguments are derived from the query values in the URL.
63
64    For example:
65        wrapped_f(urlparse.urlsplit("text://stats.txt?desc=False")) ->
66        f("stats.txt", desc=False)
67
68    """
69
70    from functools import wraps
71
72    @wraps(func)
73    def wrapper(url):
74        try:
75            from urllib.parse import parse_qs
76        except ImportError:
77            # Python 2 fallback
78            from urlparse import parse_qs
79        from ast import literal_eval
80
81        qs = parse_qs(url.query, keep_blank_values=True)
82
83        # parse_qs returns a list of values for each parameter. Only
84        # use the last value since kwargs don't allow multiple values
85        # per parameter. Use literal_eval to transform string param
86        # values into proper Python types.
87        def parse_value(key, values):
88            if len(values) == 0 or (len(values) == 1 and not values[0]):
89                fatal("%s: '%s' doesn't have a value." % (url.geturl(), key))
90            elif len(values) > 1:
91                fatal("%s: '%s' has multiple values." % (url.geturl(), key))
92            else:
93                try:
94                    return key, literal_eval(values[0])
95                except ValueError:
96                    fatal("%s: %s isn't a valid Python literal" \
97                          % (url.geturl(), values[0]))
98
99        kwargs = dict([ parse_value(k, v) for k, v in qs.items() ])
100
101        try:
102            return func("%s%s" % (url.netloc, url.path), **kwargs)
103        except TypeError:
104            fatal("Illegal stat visitor parameter specified")
105
106    return wrapper
107
108@_url_factory
109def _textFactory(fn, desc=True):
110    """Output stats in text format.
111
112    Text stat files contain one stat per line with an optional
113    description. The description is enabled by default, but can be
114    disabled by setting the desc parameter to False.
115
116    Example: text://stats.txt?desc=False
117
118    """
119
120    return _m5.stats.initText(fn, desc)
121
122factories = {
123    # Default to the text factory if we're given a naked path
124    "" : _textFactory,
125    "file" : _textFactory,
126    "text" : _textFactory,
127}
128
129def addStatVisitor(url):
130    """Add a stat visitor specified using a URL string
131
132    Stat visitors are specified using URLs on the following format:
133    format://path[?param=value[;param=value]]
134
135    The available formats are listed in the factories list. Factories
136    are called with the path as the first positional parameter and the
137    parameters are keyword arguments. Parameter values must be valid
138    Python literals.
139
140    """
141
142    try:
143        from urllib.parse import urlsplit
144    except ImportError:
145        # Python 2 fallback
146        from urlparse import urlsplit
147
148    parsed = urlsplit(url)
149
150    try:
151        factory = factories[parsed.scheme]
152    except KeyError:
153        fatal("Illegal stat file type specified.")
154
155    outputList.append(factory(parsed))
156
157def initSimStats():
158    _m5.stats.initSimStats()
159    _m5.stats.registerPythonStatsHandlers()
160
161def _visit_groups(root, visitor):
162    for group in root.getStatGroups().values():
163        visitor(group)
164        _visit_groups(group, visitor)
165
166def _visit_stats(root, visitor):
167    def for_each_stat(g):
168        for stat in g.getStats():
169            visitor(g, stat)
170    _visit_groups(root, for_each_stat)
171
172def _bindStatHierarchy(root):
173    def _bind_obj(name, obj):
174        if m5.SimObject.isSimObjectVector(obj):
175            for idx, obj in enumerate(obj):
176                _bind_obj("{}{}".format(name, idx), obj)
177        else:
178            root.addStatGroup(name, obj.getCCObject())
179            _bindStatHierarchy(obj)
180
181    for name, obj in root._children.items():
182        _bind_obj(name, obj)
183
184names = []
185stats_dict = {}
186stats_list = []
187def enable():
188    '''Enable the statistics package.  Before the statistics package is
189    enabled, all statistics must be created and initialized and once
190    the package is enabled, no more statistics can be created.'''
191
192    def check_stat(group, stat):
193        if not stat.check() or not stat.baseCheck():
194            fatal("statistic '%s' (%d) was not properly initialized " \
195                  "by a regStats() function\n", stat.name, stat.id)
196
197        if not (stat.flags & flags.display):
198            stat.name = "__Stat%06d" % stat.id
199
200
201    # Legacy stat
202    global stats_list
203    stats_list = list(_m5.stats.statsList())
204
205    for stat in stats_list:
206        check_stat(None, stat)
207
208    stats_list.sort(key=lambda s: s.name.split('.'))
209    for stat in stats_list:
210        stats_dict[stat.name] = stat
211        stat.enable()
212
213
214    # New stats
215    _visit_stats(Root.getInstance(), check_stat)
216    _visit_stats(Root.getInstance(), lambda g, s: s.enable())
217
218    _m5.stats.enable();
219
220def prepare():
221    '''Prepare all stats for data access.  This must be done before
222    dumping and serialization.'''
223
224    # Legacy stats
225    for stat in stats_list:
226        stat.prepare()
227
228    # New stats
229    _visit_stats(Root.getInstance(), lambda g, s: s.prepare())
230
231lastDump = 0
232
233def _dump_to_visitor(visitor):
234    # Legacy stats
235    for stat in stats_list:
236        stat.visit(visitor)
237
238    # New stats
239    def dump_group(group):
240        for stat in group.getStats():
241            stat.visit(visitor)
242
243        for n, g in group.getStatGroups().items():
244            visitor.beginGroup(n)
245            dump_group(g)
246            visitor.endGroup()
247
248    dump_group(Root.getInstance())
249
250
251def dump():
252    '''Dump all statistics data to the registered outputs'''
253
254    curTick = m5.curTick()
255
256    global lastDump
257    assert lastDump <= curTick
258    if lastDump == curTick:
259        return
260    lastDump = curTick
261
262    _m5.stats.processDumpQueue()
263
264    prepare()
265
266    for output in outputList:
267        if output.valid():
268            output.begin()
269            _dump_to_visitor(output)
270            output.end()
271
272def reset():
273    '''Reset all statistics to the base state'''
274
275    # call reset stats on all SimObjects
276    root = Root.getInstance()
277    if root:
278        root.resetStats()
279
280    # call any other registered legacy stats reset callbacks
281    for stat in stats_list:
282        stat.reset()
283
284    _m5.stats.processResetQueue()
285
286flags = attrdict({
287    'none'    : 0x0000,
288    'init'    : 0x0001,
289    'display' : 0x0002,
290    'total'   : 0x0010,
291    'pdf'     : 0x0020,
292    'cdf'     : 0x0040,
293    'dist'    : 0x0080,
294    'nozero'  : 0x0100,
295    'nonan'   : 0x0200,
296})
297